chore: clean up geth - remove plugin/evm and broken cmd tools

geth is a low-level go-ethereum fork that should not depend on
higher-level luxfi packages like evm, node, consensus, etc.

- Remove plugin/evm directory (belongs in luxfi/evm)
- Remove plugin/main.go stub
- Remove broken/outdated cmd tools for regenesis
- Clean up go.mod to only require luxfi/crypto
- Build and tests pass
This commit is contained in:
Zach Kelling
2025-11-28 08:24:24 +00:00
parent 05cca74af3
commit 3daca889e2
137 changed files with 201 additions and 25629 deletions
-121
View File
@@ -1,121 +0,0 @@
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"github.com/cockroachdb/pebble"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/rlp"
)
func main() {
pebblePath := flag.String("pebble", "/home/z/work/lux/state/chaindata/lux-mainnet-96369/db/pebbledb", "PebbleDB path")
targetPath := flag.String("target", "/home/z/.luxd/db/C", "Target database path")
startBlock := flag.Uint64("start", 0, "Start block")
endBlock := flag.Uint64("end", 10, "End block")
dryRun := flag.Bool("dry-run", false, "Dry run mode")
scanOnly := flag.Bool("scan", false, "Only scan and show database structure")
flag.Parse()
// Open PebbleDB
opts := &pebble.Options{}
pdb, err := pebble.Open(*pebblePath, opts)
if err != nil {
log.Fatal("Failed to open PebbleDB:", err)
}
defer pdb.Close()
if *scanOnly {
scanDatabase(pdb)
return
}
// Open target database if not dry run
var targetDB *pebble.DB
if !*dryRun {
targetOpts := &pebble.Options{}
targetDB, err = pebble.Open(*targetPath, targetOpts)
if err != nil {
log.Fatal("Failed to open target database:", err)
}
defer targetDB.Close()
}
fmt.Printf("Starting PebbleDB extraction\n")
fmt.Printf("Source: %s\n", *pebblePath)
fmt.Printf("Target: %s\n", *targetPath)
fmt.Printf("Blocks: %d to %d\n", *startBlock, *endBlock)
fmt.Printf("Dry run: %v\n\n", *dryRun)
// Extract and migrate blocks
fmt.Println("Starting block extraction...")
// For now, just scan and understand the key structure
// We'll implement actual extraction once we understand the format
}
func scanDatabase(pdb *pebble.DB) {
fmt.Println("Scanning PebbleDB structure...")
fmt.Println("================================")
iter, _ := pdb.NewIter(nil)
defer iter.Close()
count := 0
patterns := make(map[string]int)
for iter.First(); iter.Valid() && count < 1000; iter.Next() {
key := iter.Key()
value, _ := iter.ValueAndErr()
// Analyze key patterns
if len(key) > 0 {
prefix := hex.EncodeToString(key[:min(4, len(key))])
patterns[prefix]++
}
// Show first few entries
if count < 20 {
fmt.Printf("Key %d:\n", count)
fmt.Printf(" Hex: %s\n", hex.EncodeToString(key))
fmt.Printf(" Len: %d\n", len(key))
if len(value) > 0 {
fmt.Printf(" Val len: %d\n", len(value))
// Try to decode as header
if len(key) > 8 && key[0] == 'h' {
var header types.Header
if err := rlp.DecodeBytes(value, &header); err == nil {
fmt.Printf(" Decoded as header: Block #%s\n", header.Number.String())
}
}
// Try to decode as body
if len(key) > 8 && key[0] == 'b' {
var body types.Body
if err := rlp.DecodeBytes(value, &body); err == nil {
fmt.Printf(" Decoded as body: %d txs\n", len(body.Transactions))
}
}
}
fmt.Println()
}
count++
}
fmt.Printf("\nKey patterns found (first 4 bytes):\n")
for pattern, c := range patterns {
if c > 10 {
fmt.Printf(" %s: %d occurrences\n", pattern, c)
}
}
fmt.Printf("\nTotal keys scanned: %d\n", count)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
-77
View File
@@ -1,77 +0,0 @@
//go:build ignore
// +build ignore
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"github.com/luxfi/geth/core"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/node"
)
func main() {
// Open the BadgerDB database
dbPath := "/home/z/work/lux/test-migration/dest-badgerdb"
// Create database config
dbConfig := &node.Config{
DataDir: dbPath,
DBEngine: "badgerdb",
}
// Open database
db, err := rawdb.NewBadgerDatabase(dbPath, 256, 256, "", false)
if err != nil {
log.Fatal("Failed to open database:", err)
}
defer db.Close()
// Read the actual genesis from the database
genesis, err := core.ReadGenesis(db)
if err != nil {
log.Fatal("Failed to read genesis from database:", err)
}
fmt.Printf("Successfully read genesis from database\n")
fmt.Printf("Chain ID: %v\n", genesis.Config.ChainID)
fmt.Printf("Timestamp: 0x%x\n", genesis.Timestamp)
fmt.Printf("Difficulty: %v\n", genesis.Difficulty)
fmt.Printf("Gas Limit: 0x%x\n", genesis.GasLimit)
fmt.Printf("Number of allocations: %d\n", len(genesis.Alloc))
// Convert to C-Chain genesis format
cChainGenesis := map[string]interface{}{
"config": genesis.Config,
"nonce": fmt.Sprintf("0x%x", genesis.Nonce),
"timestamp": fmt.Sprintf("0x%x", genesis.Timestamp),
"extraData": fmt.Sprintf("0x%x", genesis.ExtraData),
"gasLimit": fmt.Sprintf("0x%x", genesis.GasLimit),
"difficulty": fmt.Sprintf("0x%x", genesis.Difficulty),
"mixHash": genesis.Mixhash.Hex(),
"coinbase": genesis.Coinbase.Hex(),
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"alloc": genesis.Alloc,
}
// Marshal to JSON
jsonData, err := json.MarshalIndent(cChainGenesis, "", " ")
if err != nil {
log.Fatal("Failed to marshal genesis:", err)
}
// Save the extracted genesis
outputFile := "/home/z/work/lux/extracted_real_genesis.json"
if err := os.WriteFile(outputFile, jsonData, 0644); err != nil {
log.Fatal("Failed to write genesis file:", err)
}
fmt.Printf("\nExtracted genesis saved to %s\n", outputFile)
fmt.Printf("Genesis hash: %s\n", genesis.ToBlock().Hash().Hex())
}
-97
View File
@@ -1,97 +0,0 @@
package main
import (
"encoding/binary"
"flag"
"fmt"
"log"
"github.com/dgraph-io/badger/v4"
"github.com/luxfi/geth/common"
)
var (
dbPath = flag.String("db", "", "BadgerDB path (required)")
)
func main() {
flag.Parse()
if *dbPath == "" {
log.Fatal("--db flag is required")
}
fmt.Printf("Opening database: %s\n", *dbPath)
opts := badger.DefaultOptions(*dbPath)
opts.ReadOnly = false
db, err := badger.Open(opts)
if err != nil {
log.Fatalf("Failed to open BadgerDB: %v", err)
}
defer db.Close()
// Read the tip block number and hash
var tipNumber uint64 = 1082780
var tipHash common.Hash
// Read canonical hash for tip block
err = db.View(func(txn *badger.Txn) error {
// Canonical hash key: 'h' + number + 'n'
key := make([]byte, 1+8+1)
key[0] = 'h'
binary.BigEndian.PutUint64(key[1:9], tipNumber)
key[9] = 'n'
item, err := txn.Get(key)
if err != nil {
return fmt.Errorf("failed to get canonical hash for block %d: %v", tipNumber, err)
}
return item.Value(func(val []byte) error {
copy(tipHash[:], val)
return nil
})
})
if err != nil {
log.Fatalf("Error reading tip hash: %v", err)
}
fmt.Printf("Tip block: %d\n", tipNumber)
fmt.Printf("Tip hash: %s\n", tipHash.Hex())
// Write head pointers
err = db.Update(func(txn *badger.Txn) error {
// Write LastHeader
lastHeaderKey := []byte("LastHeader")
fmt.Printf("Writing LastHeader key\n")
if err := txn.Set(lastHeaderKey, tipHash.Bytes()); err != nil {
return fmt.Errorf("failed to write LastHeader: %v", err)
}
// Write LastBlock
lastBlockKey := []byte("LastBlock")
fmt.Printf("Writing LastBlock key\n")
if err := txn.Set(lastBlockKey, tipHash.Bytes()); err != nil {
return fmt.Errorf("failed to write LastBlock: %v", err)
}
// Write headHeaderKey (format: 'H' for header)
headHeaderKey := []byte("LastHeader")
fmt.Printf("Writing headHeaderKey\n")
if err := txn.Set(headHeaderKey, tipHash.Bytes()); err != nil {
return fmt.Errorf("failed to write headHeaderKey: %v", err)
}
return nil
})
if err != nil {
log.Fatalf("Error writing head pointers: %v", err)
}
fmt.Println("\n✅ Successfully wrote all head pointers!")
fmt.Printf(" Tip block: %d\n", tipNumber)
fmt.Printf(" Tip hash: %s\n", tipHash.Hex())
}
-480
View File
@@ -1,480 +0,0 @@
// cmd/rehash-state/main.go
// Comprehensive state rehashing tool for migrating SubnetEVM state to C-Chain
package main
import (
"encoding/binary"
"encoding/hex"
"flag"
"log"
"os"
"path/filepath"
"bytes"
"github.com/cockroachdb/pebble"
badger "github.com/dgraph-io/badger/v4"
// Using only luxfi packages as required
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/crypto"
)
var (
srcPath = flag.String("src", "", "source Pebble DB path (SubnetEVM, read-only)")
dstPath = flag.String("dst", "", "destination ethdb path (Badger for C-Chain)")
nsHex = flag.String("ns", "", "32-byte namespace hex (auto-detect if empty)")
stateRootHex = flag.String("state-root", "", "state root hash to rebuild (0x...)")
tipHeight = flag.Uint64("tip", 1082780, "tip block height")
batchSize = flag.Int("batch", 10000, "batch size for writes")
verify = flag.Bool("verify", false, "verify state after migration")
)
const (
// Trie node prefix used by luxfi/geth - adjust if different
TRIE_NODE_PREFIX = "n"
// Known values for LUX mainnet
GENESIS_HASH = "0xee89f9f276924c72f378db9cb4ee871d9de167f37945e3a1302fd20ab757a1b6"
TIP_HASH = "0x32dede1fc8e0f11ecde12fb42aef7933fc6c5fcf863bc277b5eac08ae4d461f0"
STATE_ROOT = "0xaedd8be7a060b082b0cb3195d0b5ba017c058468851ed93dd07eca274de000c2"
)
func mustDecodeHash(s string) common.Hash {
var h common.Hash
if s == "" {
return h
}
if len(s) >= 2 && s[:2] == "0x" {
s = s[2:]
}
b, err := hex.DecodeString(s)
if err != nil || len(b) != 32 {
log.Fatalf("bad hash %s: %v", s, err)
}
copy(h[:], b)
return h
}
func be8(n uint64) []byte {
var b [8]byte
binary.BigEndian.PutUint64(b[:], n)
return b[:]
}
func detectNamespace(pdb *pebble.DB) []byte {
log.Println("Auto-detecting namespace...")
it, err := pdb.NewIter(&pebble.IterOptions{})
if err != nil {
log.Fatal("Failed to create iterator:", err)
}
defer it.Close()
candidates := make(map[string]int)
count := 0
for it.First(); it.Valid() && count < 10000; it.Next() {
k := it.Key()
if len(k) >= 33 {
// Check for common SubnetEVM key patterns
switch k[32] {
case 'h', 'b', 'r', 'H', 's', 'c', 't', 'n':
ns := hex.EncodeToString(k[:32])
candidates[ns]++
}
}
count++
}
// Find most common namespace
maxCount := 0
var bestNS string
for ns, cnt := range candidates {
if cnt > maxCount {
maxCount = cnt
bestNS = ns
}
}
if bestNS != "" {
b, _ := hex.DecodeString(bestNS)
log.Printf("Detected namespace: 0x%s (found in %d keys)", bestNS, maxCount)
return b
}
log.Fatal("Cannot auto-detect namespace; please specify with --ns")
return nil
}
func makeHashedNodeKey(hash common.Hash) []byte {
// Create key for hashed trie node as used by luxfi/geth
key := make([]byte, len(TRIE_NODE_PREFIX)+len(hash))
copy(key[:len(TRIE_NODE_PREFIX)], []byte(TRIE_NODE_PREFIX))
copy(key[len(TRIE_NODE_PREFIX):], hash[:])
return key
}
func makeCodeKey(hash common.Hash) []byte {
// Code key format: "c" + hash
key := make([]byte, 1+len(hash))
key[0] = 'c'
copy(key[1:], hash[:])
return key
}
func main() {
flag.Parse()
if *srcPath == "" || *dstPath == "" {
log.Fatal("--src and --dst are required")
}
stateRoot := mustDecodeHash(*stateRootHex)
if stateRoot == (common.Hash{}) {
stateRoot = mustDecodeHash(STATE_ROOT)
}
log.Printf("=== State Migration Tool ===")
log.Printf("Source: %s", *srcPath)
log.Printf("Destination: %s", *dstPath)
log.Printf("State Root: %s", stateRoot.Hex())
log.Printf("Tip Height: %d", *tipHeight)
// Step 1: Open source PebbleDB (read-only)
log.Println("\n📂 Opening source PebbleDB...")
sdb, err := pebble.Open(filepath.Clean(*srcPath), &pebble.Options{ReadOnly: true})
if err != nil {
log.Fatalf("Failed to open source: %v", err)
}
defer sdb.Close()
// Step 2: Auto-detect or use provided namespace
var ns []byte
if *nsHex != "" {
ns, err = hex.DecodeString(*nsHex)
if err != nil || len(ns) != 32 {
log.Fatal("--ns must be 32 bytes hex")
}
} else {
ns = detectNamespace(sdb)
}
// Step 3: Clean and prepare destination
log.Println("\n🧹 Preparing destination...")
os.RemoveAll(*dstPath + ".old")
os.Rename(*dstPath, *dstPath+".old")
os.MkdirAll(*dstPath, 0755)
// Step 4: Open destination BadgerDB
log.Println("📂 Opening destination BadgerDB...")
opts := badger.DefaultOptions(filepath.Clean(*dstPath))
opts.Logger = nil
bdb, err := badger.Open(opts)
if err != nil {
log.Fatalf("Failed to open destination: %v", err)
}
defer bdb.Close()
// Step 5: Migrate blocks and canonical mappings
log.Println("\n🔄 Migrating blocks and canonical mappings...")
migrateBlocks(sdb, bdb, ns, *tipHeight)
// Step 6: Rehash state trie nodes
log.Println("\n🔄 Rehashing state trie nodes...")
rehashStateNodes(sdb, bdb, ns, stateRoot)
// Step 7: Copy code entries
log.Println("\n📋 Copying code entries...")
copyCodeEntries(sdb, bdb, ns)
// Step 8: Write VM metadata
log.Println("\n📝 Writing VM metadata...")
writeVMMetadata(bdb, *tipHeight)
// Step 9: Verify if requested
if *verify {
log.Println("\n🔍 Verifying migration...")
verifyMigration(bdb, stateRoot, *tipHeight)
}
log.Println("\n✅ Migration complete!")
log.Printf("State root %s should now be accessible", stateRoot.Hex())
log.Printf("Next: Launch luxd with --skip-bootstrap to verify")
}
func migrateBlocks(sdb *pebble.DB, bdb *badger.DB, ns []byte, tipHeight uint64) {
txn := bdb.NewTransaction(true)
defer txn.Discard()
genesisHash := mustDecodeHash(GENESIS_HASH)
tipHash := mustDecodeHash(TIP_HASH)
blocksWritten := 0
// Write key blocks for quick verification
keyHeights := []uint64{0, 1, 10, 100, 1000, 10000, 100000, 500000, 1000000, tipHeight}
for _, height := range keyHeights {
if height > tipHeight {
continue
}
// Canonical mapping: 'H' + height -> hash
canonicalKey := append([]byte{'H'}, be8(height)...)
hash := tipHash.Bytes()
if height == 0 {
hash = genesisHash.Bytes()
}
if err := txn.Set(canonicalKey, hash); err != nil {
log.Printf("Warning: failed to write canonical for block %d: %v", height, err)
}
// Hash to number mapping: 'H' + hash -> height
hashKey := append([]byte{'H'}, hash...)
if err := txn.Set(hashKey, be8(height)); err != nil {
log.Printf("Warning: failed to write hash->num for block %d: %v", height, err)
}
blocksWritten++
// Try to read actual header from source
headerKey := append([]byte{}, ns...)
headerKey = append(headerKey, 'h')
headerKey = append(headerKey, be8(height)...)
headerKey = append(headerKey, hash...)
if val, closer, err := sdb.Get(headerKey); err == nil {
defer closer.Close()
// Write header to destination
destHeaderKey := append([]byte{'h'}, be8(height)...)
destHeaderKey = append(destHeaderKey, hash...)
txn.Set(destHeaderKey, val)
}
if blocksWritten%100 == 0 {
log.Printf(" Written %d block mappings", blocksWritten)
txn.Commit()
txn = bdb.NewTransaction(true)
}
}
// Write head pointers
finalHash := tipHash.Bytes()
txn.Set([]byte("LastHeader"), finalHash)
txn.Set([]byte("LastBlock"), finalHash)
txn.Set([]byte("LastFast"), finalHash)
// Write total difficulty (simplified - use block number as TD for PoA)
tdKey := append([]byte{'t'}, be8(tipHeight)...)
tdKey = append(tdKey, finalHash...)
td := make([]byte, 32)
binary.BigEndian.PutUint64(td[24:], tipHeight+1)
txn.Set(tdKey, td)
txn.Commit()
log.Printf("✓ Migrated %d block references", blocksWritten)
}
func rehashStateNodes(sdb *pebble.DB, bdb *badger.DB, ns []byte, stateRoot common.Hash) {
txn := bdb.NewTransaction(true)
defer txn.Discard()
it, err := sdb.NewIter(&pebble.IterOptions{})
if err != nil {
log.Fatal("Failed to create iterator:", err)
}
defer it.Close()
nodesWritten := 0
totalScanned := 0
for it.First(); it.Valid(); it.Next() {
k := it.Key()
// Skip non-namespace keys
if len(k) < len(ns)+1 {
continue
}
if !bytes.Equal(k[:len(ns)], ns) {
continue
}
totalScanned++
// Identify state nodes - they typically have specific byte patterns
kind := k[len(ns)]
// State nodes in SubnetEVM often use 's' prefix or low nibbles
isStateNode := false
switch kind {
case 's': // Explicit state node marker
isStateNode = true
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09:
// Low nibbles often indicate path-based trie nodes
isStateNode = true
}
if !isStateNode {
continue
}
// Value is the RLP-encoded trie node
val := it.Value()
if len(val) == 0 {
continue
}
// Compute hash of the node (keccak256 of RLP)
nodeHash := crypto.Keccak256Hash(val)
// Create hashed node key for destination
destKey := makeHashedNodeKey(nodeHash)
// Write to destination
if err := txn.Set(destKey, append([]byte{}, val...)); err != nil {
log.Printf("Warning: failed to write node %x: %v", nodeHash[:8], err)
continue
}
nodesWritten++
// Batch commit
if nodesWritten%*batchSize == 0 {
log.Printf(" Written %d state nodes (scanned %d keys)", nodesWritten, totalScanned)
txn.Commit()
txn = bdb.NewTransaction(true)
}
}
// Final commit
txn.Commit()
log.Printf("✓ Rehashed %d state nodes (scanned %d keys total)", nodesWritten, totalScanned)
}
func copyCodeEntries(sdb *pebble.DB, bdb *badger.DB, ns []byte) {
txn := bdb.NewTransaction(true)
defer txn.Discard()
it, err := sdb.NewIter(&pebble.IterOptions{})
if err != nil {
log.Fatal("Failed to create iterator:", err)
}
defer it.Close()
codeCount := 0
for it.First(); it.Valid(); it.Next() {
k := it.Key()
// Look for code entries (usually 'c' + hash)
if len(k) < len(ns)+33 {
continue
}
if !bytes.Equal(k[:len(ns)], ns) {
continue
}
kind := k[len(ns)]
if kind != 'c' {
continue
}
// Extract code hash and value
if len(k) >= len(ns)+33 {
codeHash := k[len(ns)+1 : len(ns)+33]
val := it.Value()
// Write to destination
destKey := make([]byte, 33)
destKey[0] = 'c'
copy(destKey[1:], codeHash)
txn.Set(destKey, append([]byte{}, val...))
codeCount++
if codeCount%100 == 0 {
log.Printf(" Copied %d code entries", codeCount)
txn.Commit()
txn = bdb.NewTransaction(true)
}
}
}
txn.Commit()
log.Printf("✓ Copied %d code entries", codeCount)
}
func writeVMMetadata(bdb *badger.DB, tipHeight uint64) {
txn := bdb.NewTransaction(true)
defer txn.Discard()
tipHash := mustDecodeHash(TIP_HASH)
// Write various metadata keys that C-Chain VM expects
metadata := map[string][]byte{
"lastAccepted": tipHash.Bytes(),
"lastAcceptedHeight": be8(tipHeight),
"database-initialized": []byte("true"),
"vm-initialized": []byte("true"),
}
for key, val := range metadata {
if err := txn.Set([]byte(key), val); err != nil {
log.Printf("Warning: failed to write %s: %v", key, err)
}
}
txn.Commit()
log.Printf("✓ Wrote VM metadata")
}
func verifyMigration(bdb *badger.DB, stateRoot common.Hash, tipHeight uint64) {
txn := bdb.NewTransaction(false)
defer txn.Discard()
// Check LastBlock
if item, err := txn.Get([]byte("LastBlock")); err == nil {
var hash []byte
item.Value(func(val []byte) error {
hash = append([]byte{}, val...)
return nil
})
log.Printf("✓ LastBlock: 0x%x", hash)
} else {
log.Printf("✗ LastBlock not found")
}
// Check canonical at height 0
key := append([]byte{'H'}, be8(0)...)
if item, err := txn.Get(key); err == nil {
var hash []byte
item.Value(func(val []byte) error {
hash = append([]byte{}, val...)
return nil
})
log.Printf("✓ Canonical[0]: 0x%x", hash)
} else {
log.Printf("✗ Canonical[0] not found")
}
// Check canonical at tip
key = append([]byte{'H'}, be8(tipHeight)...)
if item, err := txn.Get(key); err == nil {
var hash []byte
item.Value(func(val []byte) error {
hash = append([]byte{}, val...)
return nil
})
log.Printf("✓ Canonical[%d]: 0x%x", tipHeight, hash)
} else {
log.Printf("✗ Canonical[%d] not found", tipHeight)
}
// Try to verify state root (would need full trie package integration)
log.Printf("State root to verify: %s", stateRoot.Hex())
log.Println("Note: Full state verification requires launching the node")
}
-210
View File
@@ -1,210 +0,0 @@
package main
import (
"encoding/binary"
"fmt"
"log"
"os"
"strconv"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/ethdb"
badger "github.com/dgraph-io/badger/v4"
"github.com/cockroachdb/pebble"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: replay-import <test|full>")
os.Exit(1)
}
mode := os.Args[1]
blockLimit := uint64(100)
if mode == "full" {
blockLimit = 1082780
}
fmt.Printf("🔄 REPLAY IMPORT MODE: %s (limit: %d blocks)\n", mode, blockLimit)
fmt.Println("================================================")
// Configuration
pebbleSourcePath := "/home/z/work/lux/state/chaindata/lux-mainnet-96369/db/pebbledb"
badgerTargetPath := "/home/z/.luxd/chainData/C/db/badgerdb/ethdb"
namespace := "337fb73f9bcdac8c31a2d5f7b877ab1e8a2b7f2a1e9bf02a0a0e6c6fd164f1d1"
tipHash := common.HexToHash("0x32dede1fc8e0f11ecde12fb42aef7933fc6c5fcf863bc277b5eac08ae4d461f0")
genesisHash := common.HexToHash("0xee89f9f276924c72f378db9cb4ee871d9de167f37945e3a1302fd20ab757a1b6")
// Step 1: Open source PebbleDB
fmt.Println("\n📂 Opening source PebbleDB...")
pebbleOpts := &pebble.Options{
ReadOnly: true,
}
pebbleDB, err := pebble.Open(pebbleSourcePath, pebbleOpts)
if err != nil {
log.Fatal("Failed to open PebbleDB:", err)
}
defer pebbleDB.Close()
// Step 2: Open target BadgerDB
fmt.Println("📂 Opening target BadgerDB...")
os.MkdirAll(badgerTargetPath, 0755)
badgerOpts := badger.DefaultOptions(badgerTargetPath)
badgerOpts.Logger = nil
badgerDB, err := badger.Open(badgerOpts)
if err != nil {
log.Fatal("Failed to open BadgerDB:", err)
}
defer badgerDB.Close()
// Step 3: Convert and write blocks with proper metadata
fmt.Printf("\n🔄 Converting %d blocks...\n", blockLimit)
blocksConverted := 0
err = badgerDB.Update(func(txn *badger.Txn) error {
// Write genesis canonical mapping first
canonicalKey := make([]byte, 9)
canonicalKey[0] = 'H' // Canonical hash prefix
binary.BigEndian.PutUint64(canonicalKey[1:], 0)
if err := txn.Set(canonicalKey, genesisHash.Bytes()); err != nil {
return fmt.Errorf("failed to write genesis canonical: %v", err)
}
fmt.Println(" ✓ Genesis canonical mapping written")
// Process blocks from PebbleDB
nsBytes := common.FromHex(namespace)
// Simplified: For test, just write key blocks
keyHeights := []uint64{0, 1, 10, 50, 99}
if mode == "full" {
keyHeights = append(keyHeights, 100, 1000, 10000, 100000, 500000, 1000000, 1082780)
}
for _, height := range keyHeights {
if height > blockLimit {
continue
}
// Create block key with namespace
blockKey := make([]byte, len(nsBytes)+9)
copy(blockKey, nsBytes)
blockKey[len(nsBytes)] = 'h' // header prefix
binary.BigEndian.PutUint64(blockKey[len(nsBytes)+1:], height)
// Read from PebbleDB
value, closer, err := pebbleDB.Get(blockKey)
if err != nil {
// For test, just use dummy data
fmt.Printf(" ⚠️ Block %d not found in source, using placeholder\n", height)
} else {
defer closer.Close()
// Write to BadgerDB
targetKey := make([]byte, 9)
targetKey[0] = 'h'
binary.BigEndian.PutUint64(targetKey[1:], height)
if err := txn.Set(targetKey, value); err != nil {
return fmt.Errorf("failed to write block %d: %v", height, err)
}
}
// Write canonical mapping
canonicalKey := make([]byte, 9)
canonicalKey[0] = 'H'
binary.BigEndian.PutUint64(canonicalKey[1:], height)
hash := tipHash.Bytes()
if height == 0 {
hash = genesisHash.Bytes()
}
if err := txn.Set(canonicalKey, hash); err != nil {
return fmt.Errorf("failed to write canonical for %d: %v", height, err)
}
blocksConverted++
fmt.Printf(" ✓ Block %d converted\n", height)
}
// Write head pointers
finalHash := genesisHash.Bytes()
if blockLimit > 0 {
finalHash = tipHash.Bytes() // In reality, should be hash at blockLimit-1
}
txn.Set([]byte("LastHeader"), finalHash)
txn.Set([]byte("LastBlock"), finalHash)
txn.Set([]byte("LastFast"), finalHash)
fmt.Println("\n ✓ Head pointers written")
// Write hash->number mapping for tip
hashKey := make([]byte, 33)
hashKey[0] = 'H'
copy(hashKey[1:], finalHash)
heightBytes := make([]byte, 8)
if blockLimit > 0 {
binary.BigEndian.PutUint64(heightBytes, blockLimit-1)
}
txn.Set(hashKey, heightBytes)
// Mark database as initialized
txn.Set([]byte("database-initialized"), []byte("true"))
return nil
})
if err != nil {
log.Fatal("Failed to convert blocks:", err)
}
fmt.Printf("\n✅ Successfully converted %d blocks\n", blocksConverted)
// Step 4: Verify the import
fmt.Println("\n🔍 Verifying import...")
err = badgerDB.View(func(txn *badger.Txn) error {
// Check LastBlock
item, err := txn.Get([]byte("LastBlock"))
if err != nil {
fmt.Println(" ❌ LastBlock not found")
} else {
var hash []byte
item.Value(func(val []byte) error {
hash = append([]byte{}, val...)
return nil
})
fmt.Printf(" ✓ LastBlock: 0x%x\n", hash)
}
// Check canonical at height 0
key := make([]byte, 9)
key[0] = 'H'
binary.BigEndian.PutUint64(key[1:], 0)
item, err = txn.Get(key)
if err != nil {
fmt.Println(" ❌ Canonical block 0 not found")
} else {
var hash []byte
item.Value(func(val []byte) error {
hash = append([]byte{}, val...)
return nil
})
fmt.Printf(" ✓ Canonical block 0: 0x%x\n", hash)
}
return nil
})
if err != nil {
fmt.Printf("Verification error: %v\n", err)
}
fmt.Println("\n🎉 Import complete! You can now launch luxd.")
}
-240
View File
@@ -1,240 +0,0 @@
// Simple state replay tool for migrated blocks
package main
import (
"flag"
"log"
"math/big"
"path/filepath"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/state"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/core/vm"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/geth/params"
"github.com/luxfi/geth/triedb"
"github.com/holiman/uint256"
)
var (
dbPath = flag.String("db", "/home/z/.luxd/chainData/C/db", "Database path")
startBlock = flag.Uint64("start", 1, "Start block number")
endBlock = flag.Uint64("end", 100, "End block number")
testAddr = flag.String("addr", "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714", "Test address")
verbose = flag.Bool("v", false, "Verbose logging")
)
func openDB(path string) (ethdb.Database, error) {
// Try opening as LevelDB
return rawdb.NewLevelDBDatabase(path, 256, 1024, "", false)
}
func main() {
flag.Parse()
// Open database
log.Printf("Opening database at %s", *dbPath)
db, err := openDB(*dbPath)
if err != nil {
// Try badgerdb path
badgerPath := filepath.Join(*dbPath, "badgerdb", "ethdb")
log.Printf("LevelDB failed, trying BadgerDB at %s", badgerPath)
db, err = openDB(badgerPath)
if err != nil {
log.Fatalf("Failed to open database: %v", err)
}
}
defer db.Close()
// Chain config
chainConfig := &params.ChainConfig{
ChainID: big.NewInt(96369),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
}
// Check if we have blocks
headHash := rawdb.ReadHeadBlockHash(db)
if headHash == (common.Hash{}) {
log.Fatal("No head block found in database")
}
headNumber := rawdb.ReadHeaderNumber(db, headHash)
if headNumber == nil {
log.Fatal("Head block number not found")
}
log.Printf("Head block: %d (hash: %x)", *headNumber, headHash)
// Initialize state database
trieDB := triedb.NewDatabase(db, &triedb.Config{
Preimages: true,
})
stateDB := state.NewDatabase(trieDB, nil)
// Get genesis state
genesisHeader := rawdb.ReadHeader(db, rawdb.ReadCanonicalHash(db, 0), 0)
if genesisHeader == nil {
log.Fatal("Genesis header not found")
}
// Initialize or load state
var currentState *state.StateDB
currentState, err = state.New(genesisHeader.Root, stateDB)
if err != nil {
log.Printf("Could not load genesis state (root: %x), creating new", genesisHeader.Root)
currentState, _ = state.New(common.Hash{}, stateDB)
// Set up initial balance for test address
if *testAddr != "" {
addr := common.HexToAddress(*testAddr)
// Set a reasonable initial balance (not 2T)
initialBalance := new(big.Int).Mul(big.NewInt(1000000), big.NewInt(1e18)) // 1M LUX
currentState.SetBalance(addr, uint256.MustFromBig(initialBalance), 0)
log.Printf("Set initial balance for %s: 1,000,000 LUX", *testAddr)
}
}
// Check current balance
if *testAddr != "" {
addr := common.HexToAddress(*testAddr)
balance := currentState.GetBalance(addr)
log.Printf("Current balance of %s: %s LUX", *testAddr,
new(big.Int).Div(balance.ToBig(), big.NewInt(1e18)).String())
}
// Determine block range
end := *endBlock
if end == 0 || end > *headNumber {
end = *headNumber
}
log.Printf("Processing blocks %d to %d", *startBlock, end)
// Process blocks
for blockNum := *startBlock; blockNum <= end; blockNum++ {
hash := rawdb.ReadCanonicalHash(db, blockNum)
if hash == (common.Hash{}) {
log.Printf("Block %d: hash not found", blockNum)
continue
}
block := rawdb.ReadBlock(db, hash, blockNum)
if block == nil {
log.Printf("Block %d: not found", blockNum)
continue
}
header := block.Header()
txCount := len(block.Transactions())
if *verbose || blockNum%100 == 0 {
log.Printf("Block %d: %d transactions", blockNum, txCount)
}
// Process transactions
if txCount > 0 {
// Create EVM context
context := core.NewEVMBlockContext(header, nil, &header.Coinbase)
vmConfig := vm.Config{}
for i, tx := range block.Transactions() {
// Get sender
signer := types.LatestSigner(chainConfig)
from, err := types.Sender(signer, tx)
if err != nil {
if *verbose {
log.Printf(" TX %d: Could not get sender: %v", i, err)
}
continue
}
// Create EVM
txContext := core.NewEVMTxContext(from, tx.GasPrice())
evm := vm.NewEVM(context, txContext, currentState, chainConfig, vmConfig)
// Apply transaction
msg := &core.Message{
From: from,
To: tx.To(),
Value: tx.Value(),
GasLimit: tx.Gas(),
GasPrice: tx.GasPrice(),
GasFeeCap: tx.GasFeeCap(),
GasTipCap: tx.GasTipCap(),
Data: tx.Data(),
AccessList: tx.AccessList(),
SkipNonceChecks: true,
SkipFromEOACheck: true,
}
currentState.SetTxContext(tx.Hash(), i)
_, err = core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(block.GasLimit()))
if err != nil && *verbose {
log.Printf(" TX %d: Failed to apply: %v", i, err)
}
}
}
// Commit state periodically
if blockNum%100 == 0 {
root, err := currentState.Commit(blockNum, false)
if err != nil {
log.Printf("Failed to commit at block %d: %v", blockNum, err)
} else if *verbose {
log.Printf("Committed state at block %d, root: %x", blockNum, root)
}
// Check balance periodically
if *testAddr != "" && blockNum%1000 == 0 {
addr := common.HexToAddress(*testAddr)
balance := currentState.GetBalance(addr)
log.Printf("Balance at block %d: %s LUX", blockNum,
new(big.Int).Div(balance.ToBig(), big.NewInt(1e18)).String())
}
// Recreate state to avoid memory issues
currentState, err = state.New(root, stateDB)
if err != nil {
log.Printf("Failed to recreate state: %v", err)
currentState, _ = state.New(common.Hash{}, stateDB)
}
}
}
// Final commit
finalRoot, err := currentState.Commit(end, false)
if err != nil {
log.Printf("Failed final commit: %v", err)
} else {
log.Printf("Final state root: %x", finalRoot)
// Update head if successful
if hash := rawdb.ReadCanonicalHash(db, end); hash != (common.Hash{}) {
rawdb.WriteHeadBlockHash(db, hash)
rawdb.WriteHeadHeaderHash(db, hash)
}
}
// Final balance check
if *testAddr != "" {
addr := common.HexToAddress(*testAddr)
balance := currentState.GetBalance(addr)
log.Printf("\nFinal balance of %s: %s LUX", *testAddr,
new(big.Int).Div(balance.ToBig(), big.NewInt(1e18)).String())
}
log.Printf("\nState replay complete!")
}
-258
View File
@@ -1,258 +0,0 @@
// Copyright 2025 Lux Industries, Inc.
// Standalone replay tool for importing blockchain data
package main
import (
"context"
"flag"
"fmt"
"math/big"
"os"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/replay"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/geth/ethdb/leveldb"
"github.com/luxfi/geth/log"
"github.com/luxfi/geth/params"
)
var (
badgerDBPath = flag.String("badgerdb", "/home/z/work/lux/state/chaindata/lux-mainnet-96369/db", "Path to BadgerDB source")
targetDBPath = flag.String("targetdb", "/home/z/.luxd/db/C", "Path to target database")
startBlock = flag.Uint64("start", 0, "Starting block number")
endBlock = flag.Uint64("end", 1000000, "Ending block number")
batchSize = flag.Int("batch", 1000, "Batch size for processing")
networkID = flag.Uint64("network", 96369, "Network ID")
verbosity = flag.Int("verbosity", 3, "Logging verbosity (0=silent, 1=error, 2=warn, 3=info, 4=debug)")
validateBlocks = flag.Bool("validate", true, "Validate blocks during replay")
dryRun = flag.Bool("dry-run", false, "Perform a dry run without writing to database")
)
func main() {
flag.Parse()
// Setup logging
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(*verbosity), true)))
log.Info("LUX Blockchain Replay Tool",
"version", "1.0.0",
"badgerdb", *badgerDBPath,
"targetdb", *targetDBPath,
"network", *networkID,
)
// Validate paths
if _, err := os.Stat(*badgerDBPath); err != nil {
log.Error("BadgerDB path does not exist", "path", *badgerDBPath, "error", err)
os.Exit(1)
}
// Open or create target database
targetDB, err := openDatabase(*targetDBPath)
if err != nil {
log.Error("Failed to open target database", "error", err)
os.Exit(1)
}
defer targetDB.Close()
// Get chain config
chainConfig := getChainConfig(*networkID)
// Create replay configuration
replayConfig := &replay.ReplayConfig{
BadgerDBPath: *badgerDBPath,
TargetDB: targetDB,
ChainConfig: chainConfig,
NetworkID: *networkID,
Layer: 0, // C-Chain
ConsensusType: 0, // POA
StartBlock: *startBlock,
EndBlock: *endBlock,
BatchSize: *batchSize,
UpgradeToQuantum: false,
}
// Add progress callback
replayConfig.OnProgress = func(current, total uint64) {
if current%100 == 0 || current == total {
progress := float64(current) / float64(total) * 100
log.Info("Replay progress",
"current", current,
"total", total,
"progress", fmt.Sprintf("%.2f%%", progress),
)
}
}
// Perform replay
ctx := context.Background()
if *dryRun {
log.Warn("DRY RUN MODE - No data will be written to database")
// In dry run, use memory database
replayConfig.TargetDB = rawdb.NewMemoryDatabase()
}
log.Info("Starting replay operation",
"start_block", *startBlock,
"end_block", *endBlock,
"batch_size", *batchSize,
"validate", *validateBlocks,
)
result, err := replay.FullReplay(ctx, replayConfig)
if err != nil {
log.Error("Replay failed", "error", err)
os.Exit(1)
}
// Print results
log.Info("Replay completed successfully",
"total_blocks", result.TotalBlocks,
"processed", result.ProcessedBlocks,
"failed", result.FailedBlocks,
"duration", result.Duration,
"blocks_per_second", float64(result.ProcessedBlocks)/result.Duration.Seconds(),
)
if len(result.Errors) > 0 {
log.Warn("Replay completed with errors", "error_count", len(result.Errors))
for i, err := range result.Errors {
if i < 10 { // Show first 10 errors
log.Error("Replay error", "index", i, "error", err)
}
}
}
// Verify final state if not dry run
if !*dryRun && *validateBlocks {
if err := verifyDatabase(targetDB, result.ProcessedBlocks); err != nil {
log.Error("Database verification failed", "error", err)
os.Exit(1)
}
log.Info("Database verification completed successfully")
}
log.Info("Replay tool completed successfully")
}
// openDatabase opens or creates the target database
func openDatabase(path string) (ethdb.Database, error) {
// Ensure directory exists
if err := os.MkdirAll(path, 0755); err != nil {
return nil, fmt.Errorf("failed to create database directory: %w", err)
}
// Open the LevelDB instance directly
kvdb, err := leveldb.New(path, 256, 256, "replay/db/", false)
if err != nil {
return nil, fmt.Errorf("failed to open LevelDB: %w", err)
}
// Create database options for freezer support
opts := rawdb.OpenOptions{
ReadOnly: false,
}
// Open the database with freezer support
return rawdb.Open(kvdb, opts)
}
// getChainConfig returns the chain configuration for the given network ID
func getChainConfig(networkID uint64) *params.ChainConfig {
switch networkID {
case 96369:
// LUX Mainnet configuration
return &params.ChainConfig{
ChainID: big.NewInt(96369),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
CancunTime: nil,
// POA configuration
Clique: &params.CliqueConfig{
Period: 1,
Epoch: 30000,
},
}
case 96368:
// LUX Testnet configuration
config := getChainConfig(96369)
config.ChainID = big.NewInt(96368)
return config
default:
// Default configuration
return &params.ChainConfig{
ChainID: big.NewInt(int64(networkID)),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
}
}
}
// verifyDatabase performs basic verification of the replayed data
func verifyDatabase(db ethdb.Database, expectedBlocks uint64) error {
// Check head block
headHash := rawdb.ReadHeadBlockHash(db)
if headHash == (common.Hash{}) {
return fmt.Errorf("no head block found in database")
}
// Check head number
headNumber, found := rawdb.ReadHeaderNumber(db, headHash)
if !found {
return fmt.Errorf("no head block number found")
}
log.Info("Database verification",
"head_hash", headHash.Hex(),
"head_number", headNumber,
"expected_blocks", expectedBlocks,
)
// Check if we have at least some blocks
if headNumber == 0 && expectedBlocks > 0 {
return fmt.Errorf("no blocks were imported (head at genesis)")
}
// Sample check: verify some random blocks exist
for i := uint64(0); i < min(10, headNumber); i++ {
blockNum := i * (headNumber / 10)
hash := rawdb.ReadCanonicalHash(db, blockNum)
if hash == (common.Hash{}) {
log.Warn("Missing canonical hash", "block", blockNum)
} else {
header := rawdb.ReadHeader(db, hash, blockNum)
if header == nil {
log.Warn("Missing header", "block", blockNum, "hash", hash.Hex())
}
}
}
return nil
}
func min(a, b uint64) uint64 {
if a < b {
return a
}
return b
}
-112
View File
@@ -1,112 +0,0 @@
// Test replay with corrected paths
package main
import (
"context"
"flag"
"fmt"
"math/big"
"os"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/replay"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/geth/ethdb/leveldb"
"github.com/luxfi/geth/log"
"github.com/luxfi/geth/params"
)
var (
badgerDBPath = flag.String("badgerdb", "/Users/z/work/lux/state/chaindata/lux-mainnet-96369/db/pebbledb", "Path to source PebbleDB")
targetDBPath = flag.String("targetdb", "/Users/z/.luxd/db/C-test", "Path to target database")
startBlock = flag.Uint64("start", 0, "Starting block number")
endBlock = flag.Uint64("end", 100, "Ending block number") // Test with 100 blocks first
batchSize = flag.Int("batch", 50, "Batch size for processing")
networkID = flag.Uint64("network", 96369, "Network ID")
verbosity = flag.Int("verbosity", 4, "Logging verbosity (0=silent, 1=error, 2=warn, 3=info, 4=debug)")
validateBlocks = flag.Bool("validate", true, "Validate blocks during replay")
dryRun = flag.Bool("dry-run", true, "Perform a dry run without writing to database")
)
func main() {
flag.Parse()
// Setup logging
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(*verbosity), true)))
log.Info("🚀 LUX REGENESIS - Block Replay Tool",
"version", "1.0.0",
"source", *badgerDBPath,
"target", *targetDBPath,
"network", *networkID,
"blocks", fmt.Sprintf("%d-%d", *startBlock, *endBlock),
"dryRun", *dryRun,
)
// Validate source path
if _, err := os.Stat(*badgerDBPath); err != nil {
log.Error("❌ Source database path does not exist", "path", *badgerDBPath, "error", err)
os.Exit(1)
}
log.Info("✅ Source database found", "size", getDirSize(*badgerDBPath))
// Check if target exists
if err := os.MkdirAll(*targetDBPath, 0755); err != nil {
log.Error("❌ Failed to create target directory", "error", err)
os.Exit(1)
}
if *dryRun {
log.Info("🔍 DRY RUN MODE - No data will be written")
log.Info("📊 Migration would process:",
"blocks", *endBlock - *startBlock,
"batchSize", *batchSize,
)
log.Info("✅ Dry run validation complete")
return
}
// Open or create target database
targetDB, err := openDatabase(*targetDBPath)
if err != nil {
log.Error("❌ Failed to open target database", "error", err)
os.Exit(1)
}
defer targetDB.Close()
// Get chain config
chainConfig := getChainConfig(*networkID)
// Create replay configuration
replayConfig := &replay.ReplayConfig{
BadgerDBPath: *badgerDBPath,
TargetDB: targetDB,
ChainConfig: chainConfig,
NetworkID: *networkID,
Layer: 0, // C-Chain
ConsensusType: 0, // POA
StartBlock: *startBlock,
EndBlock: *endBlock,
BatchSize: *batchSize,
UpgradeToQuantum: false,
}
log.Info("🔄 Starting block replay...")
// TODO: Implement actual replay logic here
log.Info("✅ Replay complete!")
}
func getDirSize(path string) string {
// Simplified - just return "found"
return "7.1 GB"
}
func openDatabase(path string) (ethdb.Database, error) {
return leveldb.New(path, 0, 0, "lux", false)
}
func getChainConfig(networkID uint64) *params.ChainConfig {
return params.LuxMainnetChainConfig
}
-79
View File
@@ -1,79 +0,0 @@
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"github.com/dgraph-io/badger/v4"
)
func main() {
dbPath := flag.String("db", "/home/z/work/lux/state/chaindata/lux-mainnet-96369/db", "BadgerDB path")
limit := flag.Int("limit", 100, "Number of keys to scan")
flag.Parse()
opts := badger.DefaultOptions(*dbPath)
opts.ReadOnly = true
db, err := badger.Open(opts)
if err != nil {
log.Fatal(err)
}
defer db.Close()
count := 0
err = db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.PrefetchSize = 10
it := txn.NewIterator(opts)
defer it.Close()
fmt.Println("Scanning BadgerDB keys...")
fmt.Println("=========================")
for it.Rewind(); it.Valid(); it.Next() {
if count >= *limit {
break
}
item := it.Item()
key := item.Key()
// Get value size
valueSize := item.ValueSize()
// Try to get a sample of the value
var valueSample []byte
err := item.Value(func(val []byte) error {
if len(val) > 32 {
valueSample = val[:32]
} else {
valueSample = val
}
return nil
})
if err != nil {
valueSample = []byte("error reading")
}
fmt.Printf("Key %d:\n", count)
fmt.Printf(" Raw: %s\n", hex.EncodeToString(key))
fmt.Printf(" ASCII: %q\n", string(key))
fmt.Printf(" Length: %d bytes\n", len(key))
fmt.Printf(" Value size: %d bytes\n", valueSize)
if len(valueSample) > 0 {
fmt.Printf(" Value sample: %s...\n", hex.EncodeToString(valueSample))
}
fmt.Println()
count++
}
fmt.Printf("Total keys scanned: %d\n", count)
return nil
})
if err != nil {
log.Fatal("Error scanning database:", err)
}
}
-129
View File
@@ -1,129 +0,0 @@
// Simple state rebuilder for migrated blocks
package main
import (
"flag"
"fmt"
"log"
"math/big"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/state"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/ethdb/badgerdb"
"github.com/luxfi/geth/params"
"github.com/luxfi/geth/triedb"
"github.com/holiman/uint256"
)
func main() {
dbPath := flag.String("db", "/home/z/.luxd/chainData/C/db/badgerdb/ethdb", "Database path")
testAddr := flag.String("addr", "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714", "Test address")
flag.Parse()
// Open BadgerDB
log.Printf("Opening database at %s", *dbPath)
db, err := badgerdb.New(*dbPath, false)
if err != nil {
log.Fatalf("Failed to open BadgerDB: %v", err)
}
defer db.Close()
// Get head block info
headHash := rawdb.ReadHeadBlockHash(db)
if headHash == (common.Hash{}) {
log.Fatal("No head block found")
}
headNumber := rawdb.ReadHeaderNumber(db, headHash)
if headNumber == nil {
log.Fatal("No head number found")
}
log.Printf("Head block: %d", *headNumber)
// Initialize state DB
trieDB := triedb.NewDatabase(db, &triedb.Config{
Preimages: true,
})
stateDB := state.NewDatabase(trieDB, nil)
// Try to load existing state or create new
genesis := rawdb.ReadHeader(db, rawdb.ReadCanonicalHash(db, 0), 0)
if genesis == nil {
log.Fatal("Genesis not found")
}
currentState, err := state.New(genesis.Root, stateDB)
if err != nil {
log.Printf("Creating new state (genesis root %x not found)", genesis.Root)
currentState, _ = state.New(common.Hash{}, stateDB)
}
// Process first 100 blocks to rebuild state
chainConfig := params.MainnetChainConfig
chainConfig.ChainID = big.NewInt(96369)
processed := 0
for blockNum := uint64(1); blockNum <= 100 && blockNum <= *headNumber; blockNum++ {
hash := rawdb.ReadCanonicalHash(db, blockNum)
if hash == (common.Hash{}) {
continue
}
block := rawdb.ReadBlock(db, hash, blockNum)
if block == nil {
continue
}
// Process transactions
for _, tx := range block.Transactions() {
signer := types.LatestSigner(chainConfig)
from, err := types.Sender(signer, tx)
if err != nil {
continue
}
// Update sender balance (deduct tx cost)
if tx.Value() != nil && tx.Value().Sign() > 0 {
fromBalance := currentState.GetBalance(from)
if fromBalance.Cmp(uint256.MustFromBig(tx.Value())) >= 0 {
currentState.SubBalance(from, uint256.MustFromBig(tx.Value()), 0)
if tx.To() != nil {
currentState.AddBalance(*tx.To(), uint256.MustFromBig(tx.Value()), 0)
}
}
}
}
processed++
if blockNum%10 == 0 {
log.Printf("Processed block %d (%d txs)", blockNum, len(block.Transactions()))
}
}
// Commit state
root, err := currentState.Commit(uint64(processed), true, false)
if err != nil {
log.Printf("Failed to commit: %v", err)
} else {
log.Printf("State committed, new root: %x", root)
}
// Check test address balance
if *testAddr != "" {
addr := common.HexToAddress(*testAddr)
balance := currentState.GetBalance(addr)
balanceInLux := new(big.Int).Div(balance.ToBig(), big.NewInt(1e18))
log.Printf("\nAddress %s balance: %s LUX", *testAddr, balanceInLux.String())
if balanceInLux.Cmp(big.NewInt(2000000000000)) == 0 {
log.Printf("⚠️ Balance still shows genesis amount (2T LUX)")
log.Printf("State replay needs deeper integration with C-Chain VM")
} else {
log.Printf("✅ Balance updated from genesis!")
}
}
fmt.Printf("\nProcessed %d blocks\n", processed)
}
-255
View File
@@ -1,255 +0,0 @@
// State replay tool for rebuilding EVM state from migrated blocks
package main
import (
"context"
"flag"
"fmt"
"log"
"math/big"
"os"
"time"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/consensus"
"github.com/luxfi/geth/core"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/state"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/core/vm"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/geth/params"
"github.com/luxfi/geth/rpc"
"github.com/luxfi/geth/trie"
"github.com/holiman/uint256"
)
var (
dbPath = flag.String("db", "/home/z/.luxd/chainData/C/db/badgerdb/ethdb", "Database path")
startBlock = flag.Uint64("start", 1, "Start block number")
endBlock = flag.Uint64("end", 0, "End block number (0 for all)")
testAddr = flag.String("addr", "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714", "Address to check balance")
dryRun = flag.Bool("dry", false, "Dry run without writing state")
verbose = flag.Bool("v", false, "Verbose output")
)
// Simple consensus engine for replay
type replayEngine struct{}
func (e *replayEngine) Author(header *types.Header) (common.Address, error) {
return header.Coinbase, nil
}
func (e *replayEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
return nil
}
func (e *replayEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) {
abort := make(chan struct{})
results := make(chan error, len(headers))
for range headers {
results <- nil
}
return abort, results
}
func (e *replayEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
return nil
}
func (e *replayEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
return nil
}
func (e *replayEngine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
}
func (e *replayEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil
}
func (e *replayEngine) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
results <- block
return nil
}
func (e *replayEngine) SealHash(header *types.Header) common.Hash {
return header.Hash()
}
func (e *replayEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return big.NewInt(1)
}
func (e *replayEngine) APIs(chain consensus.ChainHeaderReader) []rpc.API {
return nil
}
func (e *replayEngine) Close() error {
return nil
}
func main() {
flag.Parse()
log.Printf("Opening database at %s", *dbPath)
// Open the database directly
db, err := rawdb.OpenDatabase(&rawdb.DatabaseConfig{
Directory: *dbPath,
ReadOnly: *dryRun,
})
if err != nil {
log.Fatalf("Failed to open database: %v", err)
}
defer db.Close()
// Get chain config
chainConfig := &params.ChainConfig{
ChainID: big.NewInt(96369),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
TerminalTotalDifficulty: big.NewInt(0),
ShanghaiTime: new(uint64),
CancunTime: new(uint64),
}
// Initialize blockchain
genesis := &core.Genesis{
Config: chainConfig,
Difficulty: big.NewInt(1),
GasLimit: 30000000,
Timestamp: 1640000000,
}
// Create blockchain without state initialization
bcConfig := core.DefaultConfig()
bcConfig.SnapshotLimit = 0 // Disable snapshots for replay
blockchain, err := core.NewBlockChain(db, genesis, &replayEngine{}, bcConfig)
if err != nil {
log.Fatalf("Failed to create blockchain: %v", err)
}
defer blockchain.Stop()
// Get current state
stateDB, err := blockchain.State()
if err != nil {
log.Printf("Warning: Could not get current state: %v", err)
}
// Check initial balance
if stateDB != nil && *testAddr != "" {
addr := common.HexToAddress(*testAddr)
balance := stateDB.GetBalance(addr)
balanceBig := balance.ToBig()
log.Printf("Current balance of %s: %s LUX", *testAddr, new(big.Int).Div(balanceBig, big.NewInt(1e18)).String())
}
// Get head block
head := blockchain.CurrentBlock()
log.Printf("Current head: block %d (hash: %x)", head.Number.Uint64(), head.Hash())
// Determine range
end := *endBlock
if end == 0 || end > head.Number.Uint64() {
end = head.Number.Uint64()
}
log.Printf("Replaying blocks from %d to %d", *startBlock, end)
// Create processor
processor := core.NewStateProcessor(chainConfig, blockchain.HeaderChain())
// Process blocks
for num := *startBlock; num <= end; num++ {
block := blockchain.GetBlockByNumber(num)
if block == nil {
log.Printf("Block %d: not found, skipping", num)
continue
}
// Get parent state
var parentState *state.StateDB
if num > 0 {
parent := blockchain.GetBlockByNumber(num - 1)
if parent != nil {
parentState, err = blockchain.StateAt(parent.Root())
if err != nil {
log.Printf("Block %d: Could not get parent state: %v", num, err)
continue
}
}
} else {
parentState, err = blockchain.StateAt(block.Root())
if err != nil {
log.Printf("Block %d: Could not get genesis state: %v", num, err)
continue
}
}
if parentState == nil {
log.Printf("Block %d: No parent state available, skipping", num)
continue
}
// Process the block
header := block.Header()
receipts, _, usedGas, err := processor.Process(block, parentState, vm.Config{})
if err != nil {
log.Printf("Block %d: Failed to process: %v", num, err)
continue
}
// Finalize the block
processor.Finalize(blockchain, header, parentState, block.Body())
// Commit state
newRoot, err := parentState.Commit(num, true)
if err != nil {
log.Printf("Block %d: Failed to commit state: %v", num, err)
continue
}
if *verbose || num%100 == 0 {
log.Printf("Block %d: Processed %d txs, gas used: %d, new root: %x",
num, len(block.Transactions()), usedGas, newRoot)
// Check test balance periodically
if *testAddr != "" && num%1000 == 0 {
testState, err := blockchain.StateAt(newRoot)
if err == nil {
addr := common.HexToAddress(*testAddr)
balance := testState.GetBalance(addr)
balanceBig := balance.ToBig()
log.Printf(" Balance at block %d: %s LUX", num, new(big.Int).Div(balanceBig, big.NewInt(1e18)).String())
}
}
}
// Update blockchain head if not dry run
if !*dryRun {
rawdb.WriteHeadBlockHash(db, block.Hash())
rawdb.WriteHeadHeaderHash(db, block.Hash())
}
}
// Final state check
finalState, err := blockchain.State()
if err == nil && *testAddr != "" {
addr := common.HexToAddress(*testAddr)
balance := finalState.GetBalance(addr)
balanceBig := balance.ToBig()
log.Printf("\nFinal balance of %s: %s LUX", *testAddr, new(big.Int).Div(balanceBig, big.NewInt(1e18)).String())
}
log.Printf("\nReplay complete!")
}
-413
View File
@@ -1,413 +0,0 @@
package main
import (
"bytes"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"time"
"github.com/cockroachdb/pebble"
badger "github.com/dgraph-io/badger/v4"
"github.com/golang/snappy"
)
const (
// SubnetEVM namespace (32 bytes)
namespaceSize = 32
)
var (
sourceDir = flag.String("source", "", "Source PebbleDB directory (required)")
targetDir = flag.String("target", "", "Target BadgerDB directory (required)")
maxBlocks = flag.Int("blocks", 1000, "Maximum number of blocks to process (0 for all)")
dryRun = flag.Bool("dry-run", false, "Dry run - don't write to target")
verbose = flag.Bool("verbose", false, "Verbose output")
verifyOnly = flag.Bool("verify", false, "Only verify namespace presence, don't convert")
)
// Key prefixes used in the database
var (
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
lastHeaderKey = []byte("LastHeader")
lastBlockKey = []byte("LastBlock")
lastFinalizedKey = []byte("LastFinalized")
trieNodePrefix = []byte("t") // trieNodePrefix + hash -> trie node
)
type Stats struct {
TotalKeys int64
ProcessedKeys int64
SkippedKeys int64
HeaderKeys int64
BodyKeys int64
ReceiptKeys int64
TrieKeys int64
CanonicalKeys int64
OtherKeys int64
BytesProcessed int64
StartTime time.Time
}
func (s *Stats) Print() {
elapsed := time.Since(s.StartTime)
fmt.Printf("\n=== Conversion Statistics ===\n")
fmt.Printf("Total keys examined: %d\n", s.TotalKeys)
fmt.Printf("Keys processed: %d\n", s.ProcessedKeys)
fmt.Printf("Keys skipped: %d\n", s.SkippedKeys)
fmt.Printf(" Headers: %d\n", s.HeaderKeys)
fmt.Printf(" Bodies: %d\n", s.BodyKeys)
fmt.Printf(" Receipts: %d\n", s.ReceiptKeys)
fmt.Printf(" Canonical: %d\n", s.CanonicalKeys)
fmt.Printf(" Trie nodes: %d\n", s.TrieKeys)
fmt.Printf(" Other: %d\n", s.OtherKeys)
fmt.Printf("Bytes processed: %.2f MB\n", float64(s.BytesProcessed)/(1024*1024))
fmt.Printf("Time elapsed: %v\n", elapsed)
if elapsed.Seconds() > 0 {
fmt.Printf("Keys per second: %.0f\n", float64(s.TotalKeys)/elapsed.Seconds())
}
}
func main() {
flag.Parse()
if *sourceDir == "" {
flag.Usage()
log.Fatal("-source directory is required")
}
// Ensure source exists
if _, err := os.Stat(*sourceDir); err != nil {
log.Fatalf("Source directory does not exist: %v", err)
}
if *verifyOnly {
verifyNamespace()
return
}
// For conversion, target is required
if *targetDir == "" {
flag.Usage()
log.Fatal("-target directory is required for conversion")
}
// Ensure target directory exists (create parent if needed)
if err := os.MkdirAll(filepath.Dir(*targetDir), 0755); err != nil {
log.Fatalf("Failed to create target parent directory: %v", err)
}
convertDatabase()
}
func verifyNamespace() {
fmt.Printf("Verifying namespace in source database: %s\n", *sourceDir)
// Open source PebbleDB
srcDB, err := pebble.Open(*sourceDir, &pebble.Options{
ReadOnly: true,
})
if err != nil {
log.Fatalf("Failed to open source PebbleDB: %v", err)
}
defer srcDB.Close()
// Check first few keys
iter, err := srcDB.NewIter(nil)
if err != nil {
log.Fatalf("Failed to create iterator: %v", err)
}
defer iter.Close()
count := 0
hasNamespace := 0
noNamespace := 0
fmt.Println("\nFirst 10 keys (showing hex):")
for iter.First(); iter.Valid() && count < 10; iter.Next() {
key := iter.Key()
keyHex := hex.EncodeToString(key)
if len(key) > namespaceSize {
// Check if first 32 bytes look like a namespace
namespace := key[:namespaceSize]
nsHex := hex.EncodeToString(namespace)
// The expected namespace based on your analysis
expectedNS := "337fb73f9bcdac8c31a2d5f7b877ab1e8a2b7f2a1e9bf02a0a0e6c6fd164f1d1"
if nsHex == expectedNS {
hasNamespace++
fmt.Printf(" [NS] Key: %s... (len=%d)\n", keyHex[:80], len(key))
if *verbose {
fmt.Printf(" Namespace: %s\n", nsHex)
fmt.Printf(" After NS: %s\n", hex.EncodeToString(key[namespaceSize:]))
}
} else {
noNamespace++
fmt.Printf(" [??] Key: %s... (len=%d)\n", keyHex[:min(80, len(keyHex))], len(key))
}
} else {
noNamespace++
fmt.Printf(" [NO] Key: %s (len=%d)\n", keyHex, len(key))
}
count++
}
if err := iter.Error(); err != nil {
log.Fatalf("Iterator error: %v", err)
}
fmt.Printf("\nResults: %d keys with expected namespace, %d without\n", hasNamespace, noNamespace)
}
func convertDatabase() {
stats := &Stats{StartTime: time.Now()}
fmt.Printf("Converting database from SubnetEVM to C-Chain format\n")
fmt.Printf("Source (PebbleDB): %s\n", *sourceDir)
fmt.Printf("Target (BadgerDB): %s\n", *targetDir)
if *dryRun {
fmt.Println("DRY RUN - no changes will be made")
}
fmt.Println()
// Open source PebbleDB
srcDB, err := pebble.Open(*sourceDir, &pebble.Options{
ReadOnly: true,
})
if err != nil {
log.Fatalf("Failed to open source PebbleDB: %v", err)
}
defer srcDB.Close()
var tgtDB *badger.DB
if !*dryRun {
// Open target BadgerDB
tgtDB, err = badger.Open(badger.DefaultOptions(*targetDir))
if err != nil {
log.Fatalf("Failed to open target BadgerDB: %v", err)
}
defer tgtDB.Close()
}
// Process all keys
iter, err := srcDB.NewIter(nil)
if err != nil {
log.Fatalf("Failed to create iterator: %v", err)
}
defer iter.Close()
batch := make(map[string][]byte)
batchSize := 0
maxBatchSize := 10000 // Write in batches
blocksProcessed := make(map[uint64]bool) // Track unique block numbers
for iter.First(); iter.Valid(); iter.Next() {
stats.TotalKeys++
key := make([]byte, len(iter.Key()))
copy(key, iter.Key())
value := make([]byte, len(iter.Value()))
copy(value, iter.Value())
stats.BytesProcessed += int64(len(key) + len(value))
// Skip keys that are too short to have namespace
if len(key) < namespaceSize {
stats.SkippedKeys++
if *verbose {
fmt.Printf("Skipping short key: %x\n", key)
}
continue
}
// Extract and verify namespace
namespace := key[:namespaceSize]
expectedNS, _ := hex.DecodeString("337fb73f9bcdac8c31a2d5f7b877ab1e8a2b7f2a1e9bf02a0a0e6c6fd164f1d1")
if !bytes.Equal(namespace, expectedNS) {
stats.SkippedKeys++
if *verbose {
fmt.Printf("Skipping key with unexpected namespace: %x\n", namespace)
}
continue
}
// Strip namespace
strippedKey := key[namespaceSize:]
// Categorize and potentially transform the key
transformedKey := transformKey(strippedKey, stats)
if transformedKey != nil {
stats.ProcessedKeys++
if *verbose && stats.ProcessedKeys <= 10 {
fmt.Printf("Processing: %x -> %x\n", key[:min(64, len(key))], transformedKey[:min(32, len(transformedKey))])
}
if !*dryRun {
batch[string(transformedKey)] = value
batchSize++
// Write batch if it's full
if batchSize >= maxBatchSize {
writeBatch(tgtDB, batch)
batch = make(map[string][]byte)
batchSize = 0
}
}
// Check if we've processed enough blocks
if blockNum := getBlockNumber(strippedKey); blockNum != nil {
blocksProcessed[*blockNum] = true
if *maxBlocks > 0 && len(blocksProcessed) >= *maxBlocks {
fmt.Printf("Reached block limit (%d blocks)\n", *maxBlocks)
break
}
}
}
// Show progress
if stats.TotalKeys%10000 == 0 {
fmt.Printf("Progress: %d keys examined, %d processed\n", stats.TotalKeys, stats.ProcessedKeys)
}
}
// Write remaining batch
if !*dryRun && batchSize > 0 {
writeBatch(tgtDB, batch)
}
if err := iter.Error(); err != nil {
log.Fatalf("Iterator error: %v", err)
}
stats.Print()
}
func transformKey(key []byte, stats *Stats) []byte {
if len(key) == 0 {
return nil
}
// Identify key type and transform accordingly
switch key[0] {
case 'h':
// Header-related keys
if len(key) > 1 && key[len(key)-1] == 'n' {
// h[num]n -> h[num]n (header hash by number)
stats.HeaderKeys++
return key
} else if len(key) == 1+8+32 {
// h[num][hash] -> h[num][hash] (header by number and hash)
stats.HeaderKeys++
return key
}
case 'H':
// H[hash] -> H[hash] (canonical header number)
stats.CanonicalKeys++
return key
case 'b':
// b[num][hash] -> b[num][hash] (block body)
stats.BodyKeys++
return key
case 'r':
// r[num][hash] -> r[num][hash] (receipts)
stats.ReceiptKeys++
return key
case 't':
// t[hash] -> t[hash] (trie node)
stats.TrieKeys++
return key
case 'L':
// Last* keys
if bytes.Equal(key, lastHeaderKey) || bytes.Equal(key, lastBlockKey) || bytes.Equal(key, lastFinalizedKey) {
stats.OtherKeys++
return key
}
}
// Handle other keys
stats.OtherKeys++
return key
}
func getBlockNumber(key []byte) *uint64 {
if len(key) == 0 {
return nil
}
// Check if this is a header key with block number (h + 8 bytes + hash/suffix)
if key[0] == 'h' && len(key) >= 9 {
num := binary.BigEndian.Uint64(key[1:9])
return &num
}
// Check if this is a body key with block number (b + 8 bytes + hash)
if key[0] == 'b' && len(key) >= 9 {
num := binary.BigEndian.Uint64(key[1:9])
return &num
}
return nil
}
func writeBatch(db *badger.DB, batch map[string][]byte) {
txn := db.NewTransaction(true)
defer txn.Discard()
for k, v := range batch {
// Decompress value if needed
decompressed, err := snappy.Decode(nil, v)
if err == nil {
v = decompressed
}
if err := txn.Set([]byte(k), v); err != nil {
if err == badger.ErrTxnTooBig {
// Commit current transaction and start new one
if err := txn.Commit(); err != nil {
log.Printf("Failed to commit transaction: %v", err)
}
txn = db.NewTransaction(true)
// Retry the failed key
if err := txn.Set([]byte(k), v); err != nil {
log.Printf("Failed to set key %x: %v", k, err)
}
} else {
log.Printf("Failed to set key %x: %v", k, err)
}
}
}
if err := txn.Commit(); err != nil {
log.Printf("Failed to commit final transaction: %v", err)
}
}
func encodeBlockNumber(number uint64) []byte {
enc := make([]byte, 8)
binary.BigEndian.PutUint64(enc, number)
return enc
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
-34
View File
@@ -1,34 +0,0 @@
#!/bin/bash
# Strip namespace converter script
# Converts SubnetEVM database to C-Chain format
SOURCE_DB="/home/z/work/lux/state/chaindata/lux-mainnet-96369/db/pebbledb"
TARGET_DB="/home/z/.luxd-node1/chainData/C/db-converted"
# Build the tool
echo "Building strip-namespace tool..."
cd /home/z/work/lux/geth
go build -o bin/strip-namespace ./cmd/strip-namespace || exit 1
# Run the converter
echo "Converting database (first 1000 blocks)..."
echo "Source: $SOURCE_DB"
echo "Target: $TARGET_DB"
# Create target directory if it doesn't exist
mkdir -p "$(dirname "$TARGET_DB")"
# Run conversion
./bin/strip-namespace \
-source "$SOURCE_DB" \
-target "$TARGET_DB" \
-blocks 1000 \
"$@"
echo "Conversion complete!"
echo "To test the converted database:"
echo " 1. Stop any running luxd"
echo " 2. Backup existing C-Chain database"
echo " 3. Replace with converted database"
echo " 4. Start luxd and verify"
-107
View File
@@ -1,107 +0,0 @@
package main
import (
"encoding/binary"
"fmt"
"os"
"github.com/dgraph-io/badger/v4"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/rlp"
)
func main() {
dbPath := "/home/z/.lux-cli/mainnet/chainData/network-96369/4aYc2FXx3EDKf98wqmxaRkkLERa7QSbbNnKRL7awjHqVqGgxj/db/ethdb"
tipHash := common.HexToHash("0x32dede1fc8e0f11ecde12fb42aef7933fc6c5fcf863bc277b5eac08ae4d461f0")
tipNumber := uint64(1082780)
opts := badger.DefaultOptions(dbPath)
opts.Logger = nil
db, err := badger.Open(opts)
if err != nil {
fmt.Printf("Failed to open: %v\n", err)
os.Exit(1)
}
defer db.Close()
// Build header key: 'h' + blockNumber(8 bytes) + hash(32 bytes)
headerKey := make([]byte, 41)
headerKey[0] = 'h'
binary.BigEndian.PutUint64(headerKey[1:9], tipNumber)
copy(headerKey[9:41], tipHash[:])
fmt.Printf("Verifying migrated headers...\n\n")
fmt.Printf("Checking tip header (block %d):\n", tipNumber)
fmt.Printf(" Hash: %s\n\n", tipHash.Hex())
err = db.View(func(txn *badger.Txn) error {
item, err := txn.Get(headerKey)
if err == badger.ErrKeyNotFound {
fmt.Printf("❌ Header key NOT FOUND\n")
return nil
} else if err != nil {
fmt.Printf("❌ Error: %v\n", err)
return err
}
return item.Value(func(val []byte) error {
fmt.Printf("✅ Header key FOUND\n")
fmt.Printf(" RLP data length: %d bytes\n", len(val))
// Try to decode the header
header := new(types.Header)
if err := rlp.DecodeBytes(val, header); err != nil {
fmt.Printf(" ❌ Failed to decode header: %v\n", err)
return nil
}
fmt.Printf(" ✅ Header decoded successfully!\n")
fmt.Printf(" Block number: %d\n", header.Number.Uint64())
fmt.Printf(" Block hash: %s\n", header.Hash().Hex())
fmt.Printf(" Parent hash: %s\n", header.ParentHash.Hex())
fmt.Printf(" State root: %s\n", header.Root.Hex())
fmt.Printf(" Gas used: %d\n", header.GasUsed)
fmt.Printf(" Timestamp: %d\n", header.Time)
// Check optional fields
fmt.Printf("\n Optional fields:\n")
if header.BaseFee != nil {
fmt.Printf(" BaseFee: %s\n", header.BaseFee.String())
} else {
fmt.Printf(" BaseFee: nil\n")
}
if header.WithdrawalsHash != nil {
fmt.Printf(" WithdrawalsHash: %s\n", header.WithdrawalsHash.Hex())
} else {
fmt.Printf(" WithdrawalsHash: nil ✅\n")
}
if header.BlobGasUsed != nil {
fmt.Printf(" BlobGasUsed: %d\n", *header.BlobGasUsed)
} else {
fmt.Printf(" BlobGasUsed: nil ✅\n")
}
if header.ExcessBlobGas != nil {
fmt.Printf(" ExcessBlobGas: %d\n", *header.ExcessBlobGas)
} else {
fmt.Printf(" ExcessBlobGas: nil ✅\n")
}
if header.ParentBeaconRoot != nil {
fmt.Printf(" ParentBeaconRoot: %s\n", header.ParentBeaconRoot.Hex())
} else {
fmt.Printf(" ParentBeaconRoot: nil ✅\n")
}
return nil
})
})
if err != nil {
fmt.Printf("Transaction error: %v\n", err)
os.Exit(1)
}
fmt.Printf("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
fmt.Printf("✅ SUCCESS: Headers migrated correctly and can be decoded by geth!\n")
fmt.Printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
}
+3 -41
View File
@@ -36,7 +36,6 @@ require (
github.com/golang/snappy v1.0.0
github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.6.0
github.com/gorilla/rpc v1.2.1
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
github.com/graph-gophers/graphql-go v1.3.0
github.com/hashicorp/go-bexpr v0.1.14
@@ -50,15 +49,7 @@ require (
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52
github.com/kylelemons/godebug v1.1.0
github.com/luxfi/consensus v1.21.2
github.com/luxfi/crypto v1.17.6
github.com/luxfi/database v1.2.7
github.com/luxfi/evm v1.16.19
github.com/luxfi/log v1.1.22
github.com/luxfi/math v0.1.4
github.com/luxfi/metric v1.4.5
github.com/luxfi/node v1.20.1
github.com/luxfi/warp v1.16.16
github.com/mattn/go-colorable v0.1.14
github.com/mattn/go-isatty v0.0.20
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
@@ -69,7 +60,6 @@ require (
github.com/protolambda/ztyp v0.2.2
github.com/rs/cors v1.11.1
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cast v1.10.0
github.com/status-im/keycard-go v0.2.0
github.com/stretchr/testify v1.11.1
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe
@@ -77,7 +67,6 @@ require (
github.com/urfave/cli/v2 v2.27.7
go.uber.org/automaxprocs v1.6.0
go.uber.org/goleak v1.3.0
go.uber.org/mock v0.6.0
golang.org/x/crypto v0.43.0
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
golang.org/x/sync v0.17.0
@@ -91,32 +80,11 @@ require (
)
require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cloudflare/circl v1.6.2-0.20251027185721-da1faa40b98c // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/luxfi/go-bip39 v1.1.1 // indirect
github.com/luxfi/mock v0.1.0 // indirect
github.com/luxfi/trace v0.1.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
google.golang.org/grpc v1.75.1 // indirect
)
require (
@@ -166,7 +134,7 @@ require (
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/luxfi/ids v1.1.2
github.com/luxfi/ids v1.1.2 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -180,7 +148,7 @@ require (
github.com/pion/transport/v3 v3.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.23.2
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
@@ -207,9 +175,3 @@ tool (
// Exclude old monolithic genproto to avoid conflicts
exclude google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
replace github.com/luxfi/consensus => ../consensus
replace github.com/luxfi/node => ../node
replace github.com/luxfi/evm => ../evm
+198 -136
View File
@@ -10,14 +10,12 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 h1:gggzg0SUMs6SQbEw+
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
@@ -50,35 +48,40 @@ github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8=
github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.24.3 h1:Bte86SlO3lwPQqww+7BE9ZuUCKIjfqnG5jtEyqA9y9Y=
github.com/bits-and-blooms/bitset v1.24.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cloudflare/circl v1.6.2-0.20251027185721-da1faa40b98c h1:F5S4vPVkSyE784opdnWsbhCR+NpOv+sN8wBuPFNLLZ4=
github.com/cloudflare/circl v1.6.2-0.20251027185721-da1faa40b98c/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudflare/cloudflare-go v0.114.0 h1:ucoti4/7Exo0XQ+rzpn1H+IfVVe++zgiM+tyKtf0HUA=
github.com/cloudflare/cloudflare-go v0.114.0/go.mod h1:O7fYfFfA6wKqKFn2QIR9lhj7FDw6VQCGOY6hd2TBtd0=
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=
github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g=
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 h1:pU88SPhIFid6/k0egdR5V6eALQYq2qbSmukrkgIh/0A=
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k=
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo=
github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/consensys/gnark-crypto v0.18.1 h1:RyLV6UhPRoYYzaFnPQA4qK3DyuDgkTgskDdoGqFt3fI=
github.com/consensys/gnark-crypto v0.18.1/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cockroachdb/redact v1.1.6 h1:zXJBwDZ84xJNlHl1rMyCojqyIxv+7YUpQiJLQ7n4314=
github.com/cockroachdb/redact v1.1.6/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb h1:3bCgBvB8PbJVMX1ouCcSIxvsqKPYM7gs72o0zC76n9g=
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80=
github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
@@ -86,19 +89,27 @@ github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwz
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0=
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU=
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
github.com/dgraph-io/badger/v4 v4.8.0 h1:JYph1ChBijCw8SLeybvPINizbDKWZ5n/GYbz2yhN/bs=
github.com/dgraph-io/badger/v4 v4.8.0/go.mod h1:U6on6e8k/RTbUWxqKR0MvugJuVmkxSNc79ap4917h4w=
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
@@ -107,46 +118,58 @@ github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5O
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 h1:C7t6eeMaEQVy6e8CarIhscYQlNmw5e3G36y7l7Y21Ao=
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 h1:+3HCtB74++ClLy8GgjUQYeC8R4ILzVcIe8+5edAJJnE=
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo=
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emicklei/dot v1.9.0 h1:FyaJNctdMfaEIbTQ1FkKZ1UCZyJJSkyvkrXOVoNZPKU=
github.com/emicklei/dot v1.9.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ=
github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
github.com/fjl/gencodec v0.1.0 h1:B3K0xPfc52cw52BBgUbSPxYo+HlLfAgWMVKRWXUXBcs=
github.com/fjl/gencodec v0.1.0/go.mod h1:Um1dFHPONZGTHog1qD1NaWjXJW/SPB38wPv0O8uZ2fI=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/ferranbt/fastssz v1.0.0 h1:9EXXYsracSqQRBQiHeaVsG/KQeYblPf40hsQPb9Dzk8=
github.com/ferranbt/fastssz v1.0.0/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
github.com/fjl/gencodec v0.1.1 h1:DhQY29Q6JLXB/GgMqE86NbOEuvckiYcJCbXFu02toms=
github.com/fjl/gencodec v0.1.1/go.mod h1:chDHL3wKXuBgauP8x3XNZkl5EIAR5SoCTmmmDTZRzmw=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc=
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays=
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/getsentry/sentry-go v0.35.1 h1:iopow6UVLE2aXu46xKVIs8Z9D/YZkJrHkgozrxa+tOQ=
github.com/getsentry/sentry-go v0.35.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
@@ -156,41 +179,45 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0=
github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
github.com/hashicorp/go-bexpr v0.1.14 h1:uKDeyuOhWhT1r5CiMTjdVY4Aoxdxs6EtwgTGnlosyp4=
github.com/hashicorp/go-bexpr v0.1.14/go.mod h1:gN7hRKB3s7yT+YvTdnhZVLTENejvhlkZ8UE4YVBS+Q8=
github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330=
github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
@@ -201,13 +228,15 @@ github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXei
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k=
github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 h1:vilfsDSy7TDxedi9gyBkMvAirat/oRcL0lFdJBf6tdM=
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY=
@@ -218,15 +247,16 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52 h1:msKODTL1m0wigztaqILOtla9HeW1ciscYG4xjLtvk5I=
github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52/go.mod h1:qk1sX/IBgppQNcGCRoj90u6EGC056EBoIc1oEjCWla8=
github.com/kasperdi/SPHINCSPLUS-golang v0.0.0-20231223193046-84468b93f7e9 h1:G8fshCtNb60L5IM2tuYD81uh6YQFqJ78MAGUCMks7Bg=
github.com/kasperdi/SPHINCSPLUS-golang v0.0.0-20231223193046-84468b93f7e9/go.mod h1:XWeSWo+UqzMi1uh/Td/gKlVHaPQjUj92s3omn7eccUM=
github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4=
github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@@ -242,44 +272,58 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/luxfi/crypto v1.17.6 h1:rXcGVPTpi9pwyBEVJLlWlhlcVntmNU72K/2lSJ7dpLM=
github.com/luxfi/crypto v1.17.6/go.mod h1:PmkMdhJ/ksNeW/LeIoApAZwDs2O/F7L/4PxRcEHL2i0=
github.com/luxfi/ids v1.1.2 h1:+qCUzE9Ga4slSHbnYl7T3I6c8y+n4/kKk4rzSEkLv/k=
github.com/luxfi/ids v1.1.2/go.mod h1:cMbto3Q8N3IaBxdz4Lzaq9wYl33coGXUdlr2+YwXeUw=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw=
github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
@@ -302,18 +346,19 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/protolambda/bls12-381-util v0.1.0 h1:05DU2wJN7DTU7z28+Q+zejXkIsA/MF8JZQGhtBZZiWk=
github.com/protolambda/bls12-381-util v0.1.0/go.mod h1:cdkysJTRpeFeuUVx/TXGDQNMTiRAalk1vQw3TYTHcE4=
github.com/protolambda/zrnt v0.34.1 h1:qW55rnhZJDnOb3TwFiFRJZi3yTXFrJdGOFQM7vCwYGg=
@@ -322,18 +367,19 @@ github.com/protolambda/ztyp v0.2.2 h1:rVcL3vBu9W/aV646zF6caLS/dyn9BN8NYiuJzicLNy
github.com/protolambda/ztyp v0.2.2/go.mod h1:9bYgKGqg3wJqT9ac1gI2hnVb0STQq7p/1lapqrqY1dU=
github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4=
github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -342,23 +388,23 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw=
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI=
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
@@ -367,10 +413,22 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBi
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -379,43 +437,44 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -426,30 +485,31 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -458,7 +518,6 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -467,34 +526,37 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-105
View File
@@ -1,105 +0,0 @@
[
{
"inputs": [
{
"internalType": "bytes",
"name": "payload",
"type": "bytes"
}
],
"name": "sendWarpMessage",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "blockchainID",
"type": "bytes32"
}
],
"name": "validateGetBlockchainID",
"outputs": [],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "index",
"type": "uint32"
}
],
"name": "validateInvalidWarpBlockHash",
"outputs": [],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "index",
"type": "uint32"
}
],
"name": "validateInvalidWarpMessage",
"outputs": [],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "index",
"type": "uint32"
},
{
"internalType": "bytes32",
"name": "sourceChainID",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
}
],
"name": "validateWarpBlockHash",
"outputs": [],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "index",
"type": "uint32"
},
{
"internalType": "bytes32",
"name": "sourceChainID",
"type": "bytes32"
},
{
"internalType": "address",
"name": "originSenderAddress",
"type": "address"
},
{
"internalType": "bytes",
"name": "payload",
"type": "bytes"
}
],
"name": "validateWarpMessage",
"outputs": [],
"stateMutability": "view",
"type": "function"
}
]
File diff suppressed because one or more lines are too long
-23
View File
@@ -1,23 +0,0 @@
# EVM Package
The EVM package implements the Luxd VM interface.
## VM
The VM creates the Ethereum backend and provides basic block building, parsing, and retrieval logic to the consensus engine.
## APIs
The VM creates APIs for the node through the function `CreateHandlers()`. CreateHandlers returns the `Service` struct to serve Subnet-EVM specific APIs. Additionally, the Ethereum backend APIs are also returned at the `/rpc` extension.
## Block Handling
The VM implements `buildBlock`, `parseBlock`, and `getBlock` and uses the `chain` package from Luxd to construct a metered state, which uses these functions to implement an efficient caching layer and maintain the required invariants for blocks that get returned to the consensus engine.
To do this, the VM uses a modified version of the Ethereum RLP block type [here](../../core/types/block.go) and uses the core package's BlockChain type [here](../../core/blockchain.go) to handle the insertion and storage of blocks into the chain.
## Block
The Block type implements the Luxd ChainVM Block interface. The key functions for this interface are `Verify()`, `Accept()`, `Reject()`, and `Status()`.
The Block type wraps the stateless block type [here](../../core/types/block.go) and implements these functions to allow the consensus engine to verify blocks as valid, perform consensus, and mark them as accepted or rejected. See the documentation in Luxd for the more detailed VM invariants that are maintained here.
-193
View File
@@ -1,193 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"context"
"time"
"github.com/luxfi/ids"
// Node interfaces that the VM plugin must implement (from engine/chain/block)
nodeblock "github.com/luxfi/consensus/engine/chain/block"
// Consensus interfaces that our implementation uses (from protocol/chain)
consensusblock "github.com/luxfi/consensus/protocol/chain"
// Network interfaces
"github.com/luxfi/consensus/core/appsender"
"github.com/luxfi/consensus/utils/set"
)
// BlockAdapter adapts consensus Block to node Block interface
type BlockAdapter struct {
consensus consensusblock.Block
}
// NewBlockAdapter creates a new block adapter
func NewBlockAdapter(consensusBlock consensusblock.Block) nodeblock.Block {
return &BlockAdapter{consensus: consensusBlock}
}
// ID returns the block ID
func (b *BlockAdapter) ID() ids.ID {
return b.consensus.ID()
}
// Parent returns the parent block ID (alias for ParentID)
func (b *BlockAdapter) Parent() ids.ID {
return b.consensus.ParentID()
}
// ParentID returns the parent block ID
func (b *BlockAdapter) ParentID() ids.ID {
return b.consensus.ParentID()
}
// Height returns the block height
func (b *BlockAdapter) Height() uint64 {
return b.consensus.Height()
}
// Timestamp returns the block timestamp
func (b *BlockAdapter) Timestamp() time.Time {
return b.consensus.Timestamp()
}
// Status returns the block status
func (b *BlockAdapter) Status() uint8 {
return uint8(b.consensus.Status())
}
// Verify verifies the block
func (b *BlockAdapter) Verify(ctx context.Context) error {
return b.consensus.Verify(ctx)
}
// Accept accepts the block
func (b *BlockAdapter) Accept(ctx context.Context) error {
return b.consensus.Accept(ctx)
}
// Reject rejects the block
func (b *BlockAdapter) Reject(ctx context.Context) error {
return b.consensus.Reject(ctx)
}
// Bytes returns the block bytes
func (b *BlockAdapter) Bytes() []byte {
return b.consensus.Bytes()
}
// ContextAdapter adapts between node and consensus context types
type ContextAdapter struct {
nodeCtx *nodeblock.Context
}
// NewContextAdapter creates a context adapter from node to consensus
func NewContextAdapter(nodeCtx *nodeblock.Context) *nodeblock.Context {
// Both use the same Context type from engine/chain/block package
return nodeCtx
}
// NewNodeContextAdapter creates a node context from consensus context
func NewNodeContextAdapter(consensusCtx *nodeblock.Context) *nodeblock.Context {
// Both use the same Context type from engine/chain/block package
return consensusCtx
}
// AppSenderAdapter adapts consensus AppSender to node AppSender interface
type AppSenderAdapter struct {
consensus appsender.AppSender
}
// NewAppSenderAdapter creates an AppSender adapter
func NewAppSenderAdapter(consensusAppSender appsender.AppSender) nodeblock.AppSender {
return &AppSenderAdapter{consensus: consensusAppSender}
}
// SendAppRequest sends an app request
func (a *AppSenderAdapter) SendAppRequest(ctx context.Context, nodeIDs []ids.NodeID, requestID uint32, appRequestBytes []byte) error {
// Convert slice to set for consensus interface
nodeIDSet := set.NewSet[ids.NodeID](len(nodeIDs))
for _, nodeID := range nodeIDs {
nodeIDSet.Add(nodeID)
}
return a.consensus.SendAppRequest(ctx, nodeIDSet, requestID, appRequestBytes)
}
// SendAppResponse sends an app response
func (a *AppSenderAdapter) SendAppResponse(ctx context.Context, nodeID ids.NodeID, requestID uint32, appResponseBytes []byte) error {
return a.consensus.SendAppResponse(ctx, nodeID, requestID, appResponseBytes)
}
// SendAppError sends an app error
func (a *AppSenderAdapter) SendAppError(ctx context.Context, nodeID ids.NodeID, requestID uint32, errorCode int32, errorMessage string) error {
return a.consensus.SendAppError(ctx, nodeID, requestID, errorCode, errorMessage)
}
// SendAppGossip sends app gossip
func (a *AppSenderAdapter) SendAppGossip(ctx context.Context, nodeIDs []ids.NodeID, appGossipBytes []byte) error {
// Convert slice to set for consensus interface
nodeIDSet := set.NewSet[ids.NodeID](len(nodeIDs))
for _, nodeID := range nodeIDs {
nodeIDSet.Add(nodeID)
}
return a.consensus.SendAppGossip(ctx, nodeIDSet, appGossipBytes)
}
// ReverseAppSenderAdapter adapts node AppSender to consensus AppSender interface
type ReverseAppSenderAdapter struct {
node nodeblock.AppSender
}
// NewReverseAppSenderAdapter creates a reverse AppSender adapter
func NewReverseAppSenderAdapter(nodeAppSender nodeblock.AppSender) appsender.AppSender {
return &ReverseAppSenderAdapter{node: nodeAppSender}
}
// SendAppRequest sends an app request (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppRequest(ctx context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, appRequestBytes []byte) error {
// Convert set to slice for node interface
nodeIDSlice := make([]ids.NodeID, 0, nodeIDs.Len())
for nodeID := range nodeIDs {
nodeIDSlice = append(nodeIDSlice, nodeID)
}
return a.node.SendAppRequest(ctx, nodeIDSlice, requestID, appRequestBytes)
}
// SendAppResponse sends an app response (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppResponse(ctx context.Context, nodeID ids.NodeID, requestID uint32, appResponseBytes []byte) error {
return a.node.SendAppResponse(ctx, nodeID, requestID, appResponseBytes)
}
// SendAppError sends an app error (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppError(ctx context.Context, nodeID ids.NodeID, requestID uint32, errorCode int32, errorMessage string) error {
return a.node.SendAppError(ctx, nodeID, requestID, errorCode, errorMessage)
}
// SendAppGossip sends app gossip (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppGossip(ctx context.Context, nodeIDs set.Set[ids.NodeID], appGossipBytes []byte) error {
// Convert set to slice for node interface
nodeIDSlice := make([]ids.NodeID, 0, nodeIDs.Len())
for nodeID := range nodeIDs {
nodeIDSlice = append(nodeIDSlice, nodeID)
}
return a.node.SendAppGossip(ctx, nodeIDSlice, appGossipBytes)
}
// SendAppGossipSpecific sends app gossip to specific nodes (consensus to node)
func (a *ReverseAppSenderAdapter) SendAppGossipSpecific(ctx context.Context, nodeIDs set.Set[ids.NodeID], appGossipBytes []byte) error {
// Convert set to slice for node interface
nodeIDSlice := make([]ids.NodeID, 0, nodeIDs.Len())
for nodeID := range nodeIDs {
nodeIDSlice = append(nodeIDSlice, nodeID)
}
return a.node.SendAppGossip(ctx, nodeIDSlice, appGossipBytes)
}
-84
View File
@@ -1,84 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"fmt"
"net/http"
"github.com/luxfi/evm/plugin/evm/client"
"github.com/luxfi/log"
"github.com/luxfi/node/api"
"github.com/luxfi/node/utils/profiler"
)
// Admin is the API service for admin API calls
type Admin struct {
vm *VM
profiler profiler.Profiler
}
func NewAdminService(vm *VM, performanceDir string) *Admin {
return &Admin{
vm: vm,
profiler: profiler.New(performanceDir),
}
}
// StartCPUProfiler starts a cpu profile writing to the specified file
func (p *Admin) StartCPUProfiler(_ *http.Request, _ *struct{}, _ *api.EmptyReply) error {
log.Info("Admin: StartCPUProfiler called")
p.vm.vmLock.Lock()
defer p.vm.vmLock.Unlock()
return p.profiler.StartCPUProfiler()
}
// StopCPUProfiler stops the cpu profile
func (p *Admin) StopCPUProfiler(r *http.Request, _ *struct{}, _ *api.EmptyReply) error {
log.Info("Admin: StopCPUProfiler called")
p.vm.vmLock.Lock()
defer p.vm.vmLock.Unlock()
return p.profiler.StopCPUProfiler()
}
// MemoryProfile runs a memory profile writing to the specified file
func (p *Admin) MemoryProfile(_ *http.Request, _ *struct{}, _ *api.EmptyReply) error {
log.Info("Admin: MemoryProfile called")
p.vm.vmLock.Lock()
defer p.vm.vmLock.Unlock()
return p.profiler.MemoryProfile()
}
// LockProfile runs a mutex profile writing to the specified file
func (p *Admin) LockProfile(_ *http.Request, _ *struct{}, _ *api.EmptyReply) error {
log.Info("Admin: LockProfile called")
p.vm.vmLock.Lock()
defer p.vm.vmLock.Unlock()
return p.profiler.LockProfile()
}
func (p *Admin) SetLogLevel(_ *http.Request, args *client.SetLogLevelArgs, reply *api.EmptyReply) error {
log.Info("EVM: SetLogLevel called", "logLevel", args.Level)
p.vm.vmLock.Lock()
defer p.vm.vmLock.Unlock()
if err := p.vm.logger.SetLogLevel(args.Level); err != nil {
return fmt.Errorf("failed to parse log level: %w ", err)
}
return nil
}
func (p *Admin) GetVMConfig(_ *http.Request, _ *struct{}, reply *client.ConfigReply) error {
reply.Config = &p.vm.config
return nil
}
-28
View File
@@ -1,28 +0,0 @@
// Copyright (C) 2019-2025, Lux Partners Limited All rights reserved.
// See the file LICENSE for licensing terms.
package atomic
import (
"errors"
"github.com/luxfi/ids"
"github.com/luxfi/geth/common"
)
var (
ErrNotImplemented = errors.New("atomic operations not implemented")
)
// Tx represents an atomic transaction
type Tx struct {
ID ids.ID
SourceChain ids.ID
Inputs []common.Address
Outputs []common.Address
}
// SharedMemory provides access to shared memory for atomic operations
type SharedMemory interface {
GetAtomicTxs(chainID ids.ID) ([]*Tx, error)
}
-304
View File
@@ -1,304 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"bytes"
"context"
"errors"
"fmt"
"time"
"github.com/luxfi/geth/rlp"
"github.com/luxfi/log"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/plugin/evm/header"
"github.com/luxfi/evm/precompile/precompileconfig"
"github.com/luxfi/evm/predicate"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/consensus/core/choices"
"github.com/luxfi/consensus/engine/chain/block"
consensusBlock "github.com/luxfi/consensus/engine/chain/block"
"github.com/luxfi/consensus/protocol/chain"
"github.com/luxfi/ids"
)
var (
// Block implements the chain.Block interface
_ chain.Block = (*Block)(nil)
)
// Block implements the chain.Block interface
type Block struct {
id ids.ID
ethBlock *types.Block
vm *VM
}
// newBlock returns a new Block wrapping the ethBlock type and implementing the chain.Block interface
func (vm *VM) newBlock(ethBlock *types.Block) *Block {
return &Block{
id: ids.ID(ethBlock.Hash()),
ethBlock: ethBlock,
vm: vm,
}
}
// ID implements the chain.Block interface
func (b *Block) ID() ids.ID { return b.id }
// Accept implements the chain.Block interface
func (b *Block) Accept(context.Context) error {
vm := b.vm
// Although returning an error from Accept is considered fatal, it is good
// practice to cleanup the batch we were modifying in the case of an error.
defer vm.versiondb.Abort()
blkID := b.ID()
log.Debug("accepting block",
"hash", blkID.Hex(),
"id", blkID,
"height", b.Height(),
)
// Call Accept for relevant precompile logs. Note we do this prior to
// calling Accept on the blockChain so any side effects (eg warp signatures)
// take place before the accepted log is emitted to subscribers.
rules := b.vm.rules(b.ethBlock.Number(), b.ethBlock.Time())
if err := b.handlePrecompileAccept(rules); err != nil {
return err
}
if err := vm.blockChain.Accept(b.ethBlock); err != nil {
return fmt.Errorf("chain could not accept %s: %w", blkID, err)
}
if err := vm.acceptedBlockDB.Put(lastAcceptedKey, blkID[:]); err != nil {
return fmt.Errorf("failed to put %s as the last accepted block: %w", blkID, err)
}
return b.vm.versiondb.Commit()
}
// handlePrecompileAccept calls Accept on any logs generated with an active precompile address that implements
// contract.Accepter
func (b *Block) handlePrecompileAccept(rules extras.Rules) error {
// Short circuit early if there are no precompile accepters to execute
if len(rules.AccepterPrecompiles) == 0 {
return nil
}
// Read receipts from disk
receipts := rawdb.ReadReceipts(b.vm.chaindb, b.ethBlock.Hash(), b.ethBlock.NumberU64(), b.ethBlock.Time(), b.vm.chainConfig)
// If there are no receipts, ReadReceipts may be nil, so we check the length and confirm the ReceiptHash
// is empty to ensure that missing receipts results in an error on accept.
if len(receipts) == 0 && b.ethBlock.ReceiptHash() != types.EmptyRootHash {
return fmt.Errorf("failed to fetch receipts for accepted block with non-empty root hash (%s) (Block: %s, Height: %d)", b.ethBlock.ReceiptHash(), b.ethBlock.Hash(), b.ethBlock.NumberU64())
}
acceptCtx := &precompileconfig.AcceptContext{
ConsensusCtx: context.Background(),
Warp: b.vm.warpBackend,
}
for _, receipt := range receipts {
for logIdx, log := range receipt.Logs {
accepter, ok := rules.AccepterPrecompiles[log.Address]
if !ok {
continue
}
if err := accepter.Accept(acceptCtx, log.BlockHash, log.BlockNumber, log.TxHash, logIdx, log.Topics, log.Data); err != nil {
return err
}
}
}
return nil
}
// Reject implements the chain.Block interface
func (b *Block) Reject(context.Context) error {
blkID := b.ID()
log.Debug("rejecting block",
"hash", blkID.Hex(),
"id", blkID,
"height", b.Height(),
)
return b.vm.blockChain.Reject(b.ethBlock)
}
// Parent implements the chain.Block interface
func (b *Block) Parent() ids.ID {
return ids.ID(b.ethBlock.ParentHash())
}
// ParentID implements the chain.Block interface (same as Parent)
func (b *Block) ParentID() ids.ID {
return ids.ID(b.ethBlock.ParentHash())
}
// Height implements the chain.Block interface
func (b *Block) Height() uint64 {
return b.ethBlock.NumberU64()
}
// Timestamp implements the chain.Block interface
func (b *Block) Timestamp() time.Time {
return time.Unix(int64(b.ethBlock.Time()), 0)
}
// syntacticVerify verifies that a *Block is well-formed.
func (b *Block) syntacticVerify() error {
if b == nil || b.ethBlock == nil {
return errInvalidBlock
}
header := b.ethBlock.Header()
rules := b.vm.chainConfig.Rules(header.Number, params.IsMergeTODO, header.Time)
return b.vm.syntacticBlockValidator.SyntacticVerify(b, rules)
}
// Verify implements the chain.Block interface
func (b *Block) Verify(context.Context) error {
return b.verify(&precompileconfig.PredicateContext{
ConsensusCtx: context.Background(),
ProposerVMBlockCtx: nil,
}, true)
}
// ShouldVerifyWithContext implements the block.WithVerifyContext interface
func (b *Block) ShouldVerifyWithContext(context.Context) (bool, error) {
rules := b.vm.rules(b.ethBlock.Number(), b.ethBlock.Time())
predicates := rules.Predicaters
// Short circuit early if there are no predicates to verify
if len(predicates) == 0 {
return false, nil
}
// Check if any of the transactions in the block specify a precompile that enforces a predicate, which requires
// the ProposerVMBlockCtx.
for _, tx := range b.ethBlock.Transactions() {
for _, accessTuple := range tx.AccessList() {
if _, ok := predicates[accessTuple.Address]; ok {
log.Debug("Block verification requires proposerVM context", "block", b.ID(), "height", b.Height())
return true, nil
}
}
}
log.Debug("Block verification does not require proposerVM context", "block", b.ID(), "height", b.Height())
return false, nil
}
// VerifyWithContext implements the block.WithVerifyContext interface
func (b *Block) VerifyWithContext(ctx context.Context, proposerVMBlockCtx *block.Context) error {
// Convert from node's block.Context to consensus's block.Context
var consensusBlockCtx *consensusBlock.Context
if proposerVMBlockCtx != nil {
consensusBlockCtx = &consensusBlock.Context{
PChainHeight: proposerVMBlockCtx.PChainHeight,
}
}
return b.verify(&precompileconfig.PredicateContext{
ConsensusCtx: context.Background(),
ProposerVMBlockCtx: consensusBlockCtx,
}, true)
}
// Verify the block is valid.
// Enforces that the predicates are valid within [predicateContext].
// Writes the block details to disk and the state to the trie manager iff writes=true.
func (b *Block) verify(predicateContext *precompileconfig.PredicateContext, writes bool) error {
if predicateContext.ProposerVMBlockCtx != nil {
log.Debug("Verifying block with context", "block", b.ID(), "height", b.Height())
} else {
log.Debug("Verifying block without context", "block", b.ID(), "height", b.Height())
}
if err := b.syntacticVerify(); err != nil {
return fmt.Errorf("syntactic block verification failed: %w", err)
}
// Only enforce predicates if the chain has already bootstrapped.
// If the chain is still bootstrapping, we can assume that all blocks we are verifying have
// been accepted by the network (so the predicate was validated by the network when the
// block was originally verified).
if b.vm.bootstrapped.Get() {
if err := b.verifyPredicates(predicateContext); err != nil {
return fmt.Errorf("failed to verify predicates: %w", err)
}
}
// The engine may call VerifyWithContext multiple times on the same block with different contexts.
// Since the engine will only call Accept/Reject once, we should only call InsertBlockManual once.
// Additionally, if a block is already in processing, then it has already passed verification and
// at this point we have checked the predicates are still valid in the different context so we
// can return nil.
if b.vm.IsProcessing(b.id) {
return nil
}
return b.vm.blockChain.InsertBlockManual(b.ethBlock, writes)
}
// verifyPredicates verifies the predicates in the block are valid according to predicateContext.
func (b *Block) verifyPredicates(predicateContext *precompileconfig.PredicateContext) error {
rules := b.vm.chainConfig.Rules(b.ethBlock.Number(), params.IsMergeTODO, b.ethBlock.Time())
rulesExtra := params.GetRulesExtra(rules)
switch {
case !rulesExtra.IsDurango && rulesExtra.PredicatersExist:
return errors.New("cannot enable predicates before Durango activation")
case !rulesExtra.IsDurango:
return nil
}
predicateResults := predicate.NewResults()
for _, tx := range b.ethBlock.Transactions() {
results, err := core.CheckPredicates(rules, predicateContext, tx)
if err != nil {
return err
}
predicateResults.SetTxResults(tx.Hash(), results)
}
// TODO: document required gas constraints to ensure marshalling predicate results does not error
predicateResultsBytes, err := predicateResults.Bytes()
if err != nil {
return fmt.Errorf("failed to marshal predicate results: %w", err)
}
extraData := b.ethBlock.Extra()
headerPredicateResultsBytes := header.PredicateBytesFromExtra(extraData)
if !bytes.Equal(headerPredicateResultsBytes, predicateResultsBytes) {
return fmt.Errorf("%w (remote: %x local: %x)", errInvalidHeaderPredicateResults, headerPredicateResultsBytes, predicateResultsBytes)
}
return nil
}
// Bytes implements the chain.Block interface
func (b *Block) Bytes() []byte {
res, err := rlp.EncodeToBytes(b.ethBlock)
if err != nil {
panic(err)
}
return res
}
func (b *Block) String() string { return fmt.Sprintf("EVM block, ID = %s", b.ID()) }
// Status implements the chain.Block interface
func (b *Block) Status() uint8 {
// Return a simple status based on if the block is in the blockchain
// 0 = unknown, 1 = processing, 2 = accepted, 3 = rejected
// For simplicity, we'll return 2 (accepted) for now since this is mainly used in consensus
return 2
}
// SetStatus implements the chain.Block interface
// This is required for chain.Block but not used in our implementation
func (b *Block) SetStatus(status choices.Status) {
// No-op: EVM blocks manage their status internally through the blockchain
}
-144
View File
@@ -1,144 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"context"
"sync"
"time"
"github.com/holiman/uint256"
commonEng "github.com/luxfi/consensus/core"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/txpool"
"github.com/luxfi/log"
)
const (
// Minimum amount of time to wait after building a block before attempting to build a block
// a second time without changing the contents of the mempool.
minBlockBuildingRetryDelay = 500 * time.Millisecond
)
type blockBuilder struct {
ctx context.Context
txPool *txpool.TxPool
shutdownChan <-chan struct{}
shutdownWg *sync.WaitGroup
pendingSignal *sync.Cond
buildBlockLock sync.Mutex
// lastBuildTime is the time when the last block was built.
// This is used to ensure that we don't build blocks too frequently,
// but at least after a minimum delay of minBlockBuildingRetryDelay.
lastBuildTime time.Time
}
func (vm *VM) NewBlockBuilder() *blockBuilder {
b := &blockBuilder{
ctx: context.Background(),
txPool: vm.txPool,
shutdownChan: vm.shutdownChan,
shutdownWg: &vm.shutdownWg,
}
b.pendingSignal = sync.NewCond(&b.buildBlockLock)
return b
}
// handleGenerateBlock is called from the VM immediately after BuildBlock.
func (b *blockBuilder) handleGenerateBlock() {
b.buildBlockLock.Lock()
defer b.buildBlockLock.Unlock()
b.lastBuildTime = time.Now()
}
// needToBuild returns true if there are outstanding transactions to be issued
// into a block.
func (b *blockBuilder) needToBuild() bool {
size := b.txPool.PendingSize(txpool.PendingFilter{
MinTip: uint256.MustFromBig(b.txPool.GasTip()),
})
return size > 0
}
// signalCanBuild signals that a new block can be built.
func (b *blockBuilder) signalCanBuild() {
b.buildBlockLock.Lock()
defer b.buildBlockLock.Unlock()
b.pendingSignal.Broadcast()
}
// awaitSubmittedTxs waits for new transactions to be submitted
// and notifies the VM when the tx pool has transactions to be
// put into a new block.
func (b *blockBuilder) awaitSubmittedTxs() {
// txSubmitChan is invoked when new transactions are issued as well as on re-orgs which
// may orphan transactions that were previously in a preferred block.
txSubmitChan := make(chan core.NewTxsEvent)
b.txPool.SubscribeTransactions(txSubmitChan, true)
b.shutdownWg.Add(1)
go func() {
defer func() {
if r := recover(); r != nil {
log.Error("panic in awaitSubmittedTxs", "error", r)
panic(r)
}
}()
defer b.shutdownWg.Done()
for {
select {
case <-txSubmitChan:
log.Trace("New tx detected, trying to generate a block")
b.signalCanBuild()
case <-b.shutdownChan:
return
}
}
}()
}
// waitForEvent waits until a block needs to be built.
// It returns only after at least [minBlockBuildingRetryDelay] passed from the last time a block was built.
func (b *blockBuilder) waitForEvent(ctx context.Context) (commonEng.Message, error) {
lastBuildTime, err := b.waitForNeedToBuild(ctx)
if err != nil {
return commonEng.Message{}, err
}
timeSinceLastBuildTime := time.Since(lastBuildTime)
if b.lastBuildTime.IsZero() || timeSinceLastBuildTime >= minBlockBuildingRetryDelay {
log.Debug("Last time we built a block was long enough ago, no need to wait", "timeSinceLastBuildTime", timeSinceLastBuildTime)
return commonEng.Message{Type: commonEng.PendingTxs}, nil
}
timeUntilNextBuild := minBlockBuildingRetryDelay - timeSinceLastBuildTime
log.Debug("Last time we built a block was too recent, waiting", "timeUntilNextBuild", timeUntilNextBuild)
select {
case <-ctx.Done():
return commonEng.Message{}, ctx.Err()
case <-time.After(timeUntilNextBuild):
return commonEng.Message{Type: commonEng.PendingTxs}, nil
}
}
// waitForNeedToBuild waits until needToBuild returns true.
// It returns the last time a block was built.
func (b *blockBuilder) waitForNeedToBuild(ctx context.Context) (time.Time, error) {
b.buildBlockLock.Lock()
defer b.buildBlockLock.Unlock()
for !b.needToBuild() {
// Check if context is cancelled
select {
case <-ctx.Done():
return time.Time{}, ctx.Err()
default:
}
b.pendingSignal.Wait()
}
return b.lastBuildTime, nil
}
-100
View File
@@ -1,100 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"math/big"
"testing"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/precompile/precompileconfig"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/trie"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
func TestHandlePrecompileAccept(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
db := rawdb.NewMemoryDatabase()
vm := &VM{
chaindb: db,
chainConfig: params.TestChainConfig,
}
precompileAddr := common.Address{0x05}
otherAddr := common.Address{0x06}
// Prepare a receipt with 3 logs, two of which are from the precompile
receipt := &types.Receipt{
Logs: []*types.Log{
{
Address: precompileAddr,
Topics: []common.Hash{{0x01}, {0x02}, {0x03}},
Data: []byte("log1"),
},
{
Address: otherAddr,
Topics: []common.Hash{{0x01}, {0x02}, {0x04}},
Data: []byte("log2"),
},
{
Address: precompileAddr,
Topics: []common.Hash{{0x01}, {0x02}, {0x05}},
Data: []byte("log3"),
},
},
}
ethBlock := types.NewBlock(
&types.Header{Number: big.NewInt(1)},
&types.Body{
Transactions: []*types.Transaction{types.NewTx(&types.LegacyTx{})},
Uncles: nil,
},
[]*types.Receipt{receipt},
trie.NewStackTrie(nil),
)
// Write the block to the db
rawdb.WriteBlock(db, ethBlock)
rawdb.WriteReceipts(db, ethBlock.Hash(), ethBlock.NumberU64(), []*types.Receipt{receipt})
// Set up the mock with the expected calls to Accept
txIndex := 0
mockAccepter := precompileconfig.NewMockAccepter(ctrl)
gomock.InOrder(
mockAccepter.EXPECT().Accept(
gomock.Not(gomock.Nil()), // acceptCtx
ethBlock.Hash(), // blockHash
ethBlock.NumberU64(), // blockNumber
ethBlock.Transactions()[txIndex].Hash(), // txHash
0, // logIndex
receipt.Logs[0].Topics, // topics
receipt.Logs[0].Data, // logData
),
mockAccepter.EXPECT().Accept(
gomock.Not(gomock.Nil()), // acceptCtx
ethBlock.Hash(), // blockHash
ethBlock.NumberU64(), // blockNumber
ethBlock.Transactions()[txIndex].Hash(), // txHash
2, // logIndex
receipt.Logs[2].Topics, // topics
receipt.Logs[2].Data, // logData
),
)
// Call handlePrecompileAccept
blk := vm.newBlock(ethBlock)
rules := extras.Rules{
AccepterPrecompiles: map[common.Address]precompileconfig.Accepter{
precompileAddr: mockAccepter,
},
}
require.NoError(blk.handlePrecompileAccept(rules))
}
-156
View File
@@ -1,156 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"errors"
"fmt"
"math/big"
"github.com/luxfi/geth/common"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/plugin/evm/customtypes"
"github.com/luxfi/evm/plugin/evm/header"
"github.com/luxfi/evm/plugin/evm/upgrade/legacy"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/trie"
)
var legacyMinGasPrice = big.NewInt(legacy.BaseFee)
type BlockValidator interface {
SyntacticVerify(b *Block, rules params.Rules) error
}
type blockValidator struct{}
func NewBlockValidator() BlockValidator {
return &blockValidator{}
}
func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error {
rulesExtra := params.GetRulesExtra(rules)
if b == nil || b.ethBlock == nil {
return errInvalidBlock
}
ethHeader := b.ethBlock.Header()
blockHash := b.ethBlock.Hash()
// Skip verification of the genesis block since it should already be marked as accepted.
if blockHash == b.vm.genesisHash {
return nil
}
// Perform block and header sanity checks
if ethHeader.Number == nil || !ethHeader.Number.IsUint64() {
return errInvalidBlock
}
if ethHeader.Difficulty == nil || !ethHeader.Difficulty.IsUint64() ||
ethHeader.Difficulty.Uint64() != 1 {
return fmt.Errorf("invalid difficulty: %d", ethHeader.Difficulty)
}
if ethHeader.Nonce.Uint64() != 0 {
return fmt.Errorf(
"expected nonce to be 0 but got %d: %w",
ethHeader.Nonce.Uint64(), errInvalidNonce,
)
}
if ethHeader.MixDigest != (common.Hash{}) {
return fmt.Errorf("invalid mix digest: %v", ethHeader.MixDigest)
}
// Verify the extra data is well-formed.
if err := header.VerifyExtra(rulesExtra.LuxRules, ethHeader.Extra); err != nil {
return err
}
if rulesExtra.IsSubnetEVM {
if ethHeader.BaseFee == nil {
return errNilBaseFeeSubnetEVM
}
if bfLen := ethHeader.BaseFee.BitLen(); bfLen > 256 {
return fmt.Errorf("too large base fee: bitlen %d", bfLen)
}
}
// Check that the tx hash in the header matches the body
txsHash := types.DeriveSha(b.ethBlock.Transactions(), trie.NewStackTrie(nil))
if txsHash != ethHeader.TxHash {
return fmt.Errorf("invalid txs hash %v does not match calculated txs hash %v", ethHeader.TxHash, txsHash)
}
// Check that the uncle hash in the header matches the body
uncleHash := types.CalcUncleHash(b.ethBlock.Uncles())
if uncleHash != ethHeader.UncleHash {
return fmt.Errorf("invalid uncle hash %v does not match calculated uncle hash %v", ethHeader.UncleHash, uncleHash)
}
// Block must not have any uncles
if len(b.ethBlock.Uncles()) > 0 {
return errUnclesUnsupported
}
// Block must not be empty
txs := b.ethBlock.Transactions()
if len(txs) == 0 {
return errEmptyBlock
}
if !rulesExtra.IsSubnetEVM {
// Make sure that all the txs have the correct fee set.
for _, tx := range txs {
if tx.GasPrice().Cmp(legacyMinGasPrice) < 0 {
return fmt.Errorf("block contains tx %s with gas price too low (%d < %d)", tx.Hash(), tx.GasPrice(), legacyMinGasPrice)
}
}
}
// Make sure the block isn't too far in the future
blockTimestamp := b.ethBlock.Time()
if maxBlockTime := uint64(b.vm.clock.Time().Add(maxFutureBlockTime).Unix()); blockTimestamp > maxBlockTime {
return fmt.Errorf("block timestamp is too far in the future: %d > allowed %d", blockTimestamp, maxBlockTime)
}
if rulesExtra.IsSubnetEVM {
blockGasCost := customtypes.GetHeaderExtra(ethHeader).BlockGasCost
switch {
// Make sure BlockGasCost is not nil
// NOTE: ethHeader.BlockGasCost correctness is checked in header verification
case blockGasCost == nil:
return errNilBlockGasCostSubnetEVM
case !blockGasCost.IsUint64():
return fmt.Errorf("too large blockGasCost: %d", blockGasCost)
}
}
// Verify the existence / non-existence of excessBlobGas
cancun := rules.IsCancun
if !cancun && ethHeader.ExcessBlobGas != nil {
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", *ethHeader.ExcessBlobGas)
}
if !cancun && ethHeader.BlobGasUsed != nil {
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", *ethHeader.BlobGasUsed)
}
if cancun && ethHeader.ExcessBlobGas == nil {
return errors.New("header is missing excessBlobGas")
}
if cancun && ethHeader.BlobGasUsed == nil {
return errors.New("header is missing blobGasUsed")
}
if !cancun && ethHeader.ParentBeaconRoot != nil {
return fmt.Errorf("invalid parentBeaconRoot: have %x, expected nil", *ethHeader.ParentBeaconRoot)
}
// TODO: decide what to do after Cancun
// currently we are enforcing it to be empty hash
if cancun {
switch {
case ethHeader.ParentBeaconRoot == nil:
return errors.New("header is missing parentBeaconRoot")
case *ethHeader.ParentBeaconRoot != (common.Hash{}):
return fmt.Errorf("invalid parentBeaconRoot: have %x, expected empty hash", ethHeader.ParentBeaconRoot)
}
}
return nil
}
-59
View File
@@ -1,59 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// blockgascost implements the block gas cost logic
package blockgascost
import (
"math"
"github.com/luxfi/evm/commontype"
safemath "github.com/luxfi/node/utils/math"
)
// BlockGasCost calculates the required block gas cost.
//
// cost = parentCost + step * (TargetBlockRate - timeElapsed)
//
// The returned cost is clamped to [MinBlockGasCost, MaxBlockGasCost].
func BlockGasCost(
feeConfig commontype.FeeConfig,
parentCost uint64,
step uint64,
timeElapsed uint64,
) uint64 {
deviation := safemath.AbsDiff(feeConfig.TargetBlockRate, timeElapsed)
change, err := safemath.Mul64(step, deviation)
if err != nil {
change = math.MaxUint64
}
var (
minBlockGasCost = feeConfig.MinBlockGasCost.Uint64()
maxBlockGasCost = feeConfig.MaxBlockGasCost.Uint64()
)
var cost uint64
if timeElapsed > feeConfig.TargetBlockRate {
cost, err = safemath.Sub(parentCost, change)
if err != nil {
cost = minBlockGasCost
}
} else {
cost, err = safemath.Add64(parentCost, change)
if err != nil {
cost = maxBlockGasCost
}
}
switch {
case cost < minBlockGasCost:
// This is technically dead code because [MinBlockGasCost] is 0, but it
// makes the code more clear.
return minBlockGasCost
case cost > maxBlockGasCost:
return maxBlockGasCost
default:
return cost
}
}
-100
View File
@@ -1,100 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package blockgascost
import (
"math"
"math/big"
"testing"
"github.com/luxfi/evm/commontype"
"github.com/stretchr/testify/require"
)
func TestBlockGasCost(t *testing.T) {
testFeeConfig := commontype.FeeConfig{
MinBlockGasCost: big.NewInt(0),
MaxBlockGasCost: big.NewInt(1_000_000),
TargetBlockRate: 2,
BlockGasCostStep: big.NewInt(50_000),
}
BlockGasCostTest(t, testFeeConfig)
testFeeConfigDouble := commontype.FeeConfig{
MinBlockGasCost: big.NewInt(2),
MaxBlockGasCost: big.NewInt(2_000_000),
TargetBlockRate: 4,
BlockGasCostStep: big.NewInt(100_000),
}
BlockGasCostTest(t, testFeeConfigDouble)
}
func BlockGasCostTest(t *testing.T, testFeeConfig commontype.FeeConfig) {
targetBlockRate := testFeeConfig.TargetBlockRate
maxBlockGasCost := testFeeConfig.MaxBlockGasCost.Uint64()
tests := []struct {
name string
parentCost uint64
step uint64
timeElapsed uint64
want uint64
}{
{
name: "timeElapsed_under_target",
parentCost: 500,
step: 100,
timeElapsed: 0,
want: 500 + 100*targetBlockRate,
},
{
name: "timeElapsed_at_target",
parentCost: 3,
step: 100,
timeElapsed: targetBlockRate,
want: 3,
},
{
name: "timeElapsed_over_target",
parentCost: 500,
step: 100,
timeElapsed: 2 * targetBlockRate,
want: 500 - 100*targetBlockRate,
},
{
name: "change_overflow",
parentCost: 500,
step: math.MaxUint64,
timeElapsed: 0,
want: maxBlockGasCost,
},
{
name: "cost_overflow",
parentCost: math.MaxUint64,
step: 1,
timeElapsed: 0,
want: maxBlockGasCost,
},
{
name: "clamp_to_max",
parentCost: maxBlockGasCost,
step: 100,
timeElapsed: targetBlockRate - 1,
want: maxBlockGasCost,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require.Equal(
t,
test.want,
BlockGasCost(
testFeeConfig,
test.parentCost,
test.step,
test.timeElapsed,
),
)
})
}
}
-121
View File
@@ -1,121 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package client
import (
"context"
"fmt"
"golang.org/x/exp/slog"
"github.com/luxfi/evm/plugin/evm/config"
"github.com/luxfi/ids"
"github.com/luxfi/node/api"
"github.com/luxfi/node/utils/rpc"
)
// Interface compliance
var _ Client = (*client)(nil)
type CurrentValidator struct {
ValidationID ids.ID `json:"validationID"`
NodeID ids.NodeID `json:"nodeID"`
Weight uint64 `json:"weight"`
StartTimestamp uint64 `json:"startTimestamp"`
IsActive bool `json:"isActive"`
IsL1Validator bool `json:"isL1Validator"`
IsConnected bool `json:"isConnected"`
UptimePercentage float32 `json:"uptimePercentage"`
UptimeSeconds uint64 `json:"uptimeSeconds"`
}
// Client interface for interacting with EVM [chain]
type Client interface {
StartCPUProfiler(ctx context.Context, options ...rpc.Option) error
StopCPUProfiler(ctx context.Context, options ...rpc.Option) error
MemoryProfile(ctx context.Context, options ...rpc.Option) error
LockProfile(ctx context.Context, options ...rpc.Option) error
SetLogLevel(ctx context.Context, level slog.Level, options ...rpc.Option) error
GetVMConfig(ctx context.Context, options ...rpc.Option) (*config.Config, error)
GetCurrentValidators(ctx context.Context, nodeIDs []ids.NodeID, options ...rpc.Option) ([]CurrentValidator, error)
}
// Client implementation for interacting with EVM [chain]
type client struct {
adminRequester rpc.EndpointRequester
validatorsRequester rpc.EndpointRequester
}
// NewClient returns a Client for interacting with EVM [chain]
func NewClient(uri, chain string) Client {
requestUri := fmt.Sprintf("%s/ext/bc/%s", uri, chain)
return NewClientWithURL(requestUri)
}
// NewClientWithURL returns a Client for interacting with EVM [chain]
func NewClientWithURL(url string) Client {
return &client{
adminRequester: rpc.NewEndpointRequester(
url + "/admin",
),
validatorsRequester: rpc.NewEndpointRequester(
url + "/validators",
),
}
}
func (c *client) StartCPUProfiler(ctx context.Context, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.startCPUProfiler", struct{}{}, &api.EmptyReply{}, options...)
}
func (c *client) StopCPUProfiler(ctx context.Context, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.stopCPUProfiler", struct{}{}, &api.EmptyReply{}, options...)
}
func (c *client) MemoryProfile(ctx context.Context, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.memoryProfile", struct{}{}, &api.EmptyReply{}, options...)
}
func (c *client) LockProfile(ctx context.Context, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.lockProfile", struct{}{}, &api.EmptyReply{}, options...)
}
type SetLogLevelArgs struct {
Level string `json:"level"`
}
// SetLogLevel dynamically sets the log level for the C Chain
func (c *client) SetLogLevel(ctx context.Context, level slog.Level, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.setLogLevel", &SetLogLevelArgs{
Level: level.String(),
}, &api.EmptyReply{}, options...)
}
type ConfigReply struct {
Config *config.Config `json:"config"`
}
// GetVMConfig returns the current config of the VM
func (c *client) GetVMConfig(ctx context.Context, options ...rpc.Option) (*config.Config, error) {
res := &ConfigReply{}
err := c.adminRequester.SendRequest(ctx, "admin.getVMConfig", struct{}{}, res, options...)
return res.Config, err
}
type GetCurrentValidatorsRequest struct {
NodeIDs []ids.NodeID `json:"nodeIDs"`
}
type GetCurrentValidatorsResponse struct {
Validators []CurrentValidator `json:"validators"`
}
// GetCurrentValidators returns the current validators
func (c *client) GetCurrentValidators(ctx context.Context, nodeIDs []ids.NodeID, options ...rpc.Option) ([]CurrentValidator, error) {
res := &GetCurrentValidatorsResponse{}
err := c.validatorsRequester.SendRequest(ctx, "validators.getCurrentValidators", &GetCurrentValidatorsRequest{
NodeIDs: nodeIDs,
}, res, options...)
return res.Validators, err
}
-21
View File
@@ -1,21 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"github.com/luxfi/evm/core/txpool/legacypool"
"github.com/luxfi/evm/plugin/evm/config"
)
// defaultTxPoolConfig uses [legacypool.DefaultConfig] to make a [config.TxPoolConfig]
// that can be passed to [config.Config.SetDefaults].
var defaultTxPoolConfig = config.TxPoolConfig{
PriceLimit: legacypool.DefaultConfig.PriceLimit,
PriceBump: legacypool.DefaultConfig.PriceBump,
AccountSlots: legacypool.DefaultConfig.AccountSlots,
GlobalSlots: legacypool.DefaultConfig.GlobalSlots,
AccountQueue: legacypool.DefaultConfig.AccountQueue,
GlobalQueue: legacypool.DefaultConfig.GlobalQueue,
Lifetime: legacypool.DefaultConfig.Lifetime,
}
-403
View File
@@ -1,403 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package config
import (
"encoding/json"
"fmt"
"time"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/common/hexutil"
"github.com/spf13/cast"
)
const (
defaultAcceptorQueueLimit = 64 // Provides 2 minutes of buffer (2s block target) for a commit delay
defaultPruningEnabled = true
defaultCommitInterval = 4096
defaultTrieCleanCache = 512
defaultTrieDirtyCache = 512
defaultTrieDirtyCommitTarget = 20
defaultTriePrefetcherParallelism = 16
defaultSnapshotCache = 256
defaultSyncableCommitInterval = defaultCommitInterval * 4
defaultSnapshotWait = false
defaultRpcGasCap = 50_000_000 // Default to 50M Gas Limit
defaultRpcTxFeeCap = 100 // 100 LUX
defaultMetricsExpensiveEnabled = true
defaultApiMaxDuration = 0 // Default to no maximum API call duration
defaultWsCpuRefillRate = 0 // Default to no maximum WS CPU usage
defaultWsCpuMaxStored = 0 // Default to no maximum WS CPU usage
defaultMaxBlocksPerRequest = 0 // Default to no maximum on the number of blocks per getLogs request
defaultContinuousProfilerFrequency = 15 * time.Minute
defaultContinuousProfilerMaxFiles = 5
defaultPushGossipPercentStake = .9
defaultPushGossipNumValidators = 100
defaultPushGossipNumPeers = 0
defaultPushRegossipNumValidators = 10
defaultPushRegossipNumPeers = 0
defaultPushGossipFrequency = 100 * time.Millisecond
defaultPullGossipFrequency = 1 * time.Second
defaultRegossipFrequency = 30 * time.Second
defaultOfflinePruningBloomFilterSize uint64 = 512 // Default size (MB) for the offline pruner to use
defaultLogLevel = "info"
defaultLogJSONFormat = false
defaultMaxOutboundActiveRequests = 16
defaultPopulateMissingTriesParallelism = 1024
defaultStateSyncServerTrieCache = 64 // MB
defaultAcceptedCacheSize = 32 // blocks
// defaultStateSyncMinBlocks is the minimum number of blocks the blockchain
// should be ahead of local last accepted to perform state sync.
// This constant is chosen so normal bootstrapping is preferred when it would
// be faster than state sync.
// time assumptions:
// - normal bootstrap processing time: ~14 blocks / second
// - state sync time: ~6 hrs.
defaultStateSyncMinBlocks = 300_000
defaultStateSyncRequestSize = 1024 // the number of key/values to ask peers for per request
defaultDBType = "pebbledb"
defaultValidatorAPIEnabled = true
// RPC batch limits
defaultBatchRequestLimit = 1000
defaultBatchResponseMaxSize = 25 * 1000 * 1000 // 25MB
estimatedBlockAcceptPeriod = 2 * time.Second
defaultHistoricalProofQueryWindow = uint64(24 * time.Hour / estimatedBlockAcceptPeriod)
defaultStateHistory = uint64(32)
)
type PBool bool
var (
defaultEnabledAPIs = []string{
"eth",
"eth-filter",
"net",
"web3",
"internal-eth",
"internal-blockchain",
"internal-transaction",
}
defaultAllowUnprotectedTxHashes = []common.Hash{
common.HexToHash("0xfefb2da535e927b85fe68eb81cb2e4a5827c905f78381a01ef2322aa9b0aee8e"), // EIP-1820: https://eips.ethereum.org/EIPS/eip-1820
}
)
type Duration struct {
time.Duration
}
// Config ...
type Config struct {
// Airdrop
AirdropFile string `json:"airdrop"`
// Subnet EVM APIs
ValidatorsAPIEnabled bool `json:"validators-api-enabled"`
AdminAPIEnabled bool `json:"admin-api-enabled"`
AdminAPIDir string `json:"admin-api-dir"`
WarpAPIEnabled bool `json:"warp-api-enabled"`
// EnabledEthAPIs is a list of Ethereum services that should be enabled
// If none is specified, then we use the default list [defaultEnabledAPIs]
EnabledEthAPIs []string `json:"eth-apis"`
// Continuous Profiler
ContinuousProfilerDir string `json:"continuous-profiler-dir"` // If set to non-empty string creates a continuous profiler
ContinuousProfilerFrequency Duration `json:"continuous-profiler-frequency"` // Frequency to run continuous profiler if enabled
ContinuousProfilerMaxFiles int `json:"continuous-profiler-max-files"` // Maximum number of files to maintain
// API Gas/Price Caps
RPCGasCap uint64 `json:"rpc-gas-cap"`
RPCTxFeeCap float64 `json:"rpc-tx-fee-cap"`
// Cache settings
TrieCleanCache int `json:"trie-clean-cache"` // Size of the trie clean cache (MB)
TrieDirtyCache int `json:"trie-dirty-cache"` // Size of the trie dirty cache (MB)
TrieDirtyCommitTarget int `json:"trie-dirty-commit-target"` // Memory limit to target in the dirty cache before performing a commit (MB)
TriePrefetcherParallelism int `json:"trie-prefetcher-parallelism"` // Max concurrent disk reads trie prefetcher should perform at once
SnapshotCache int `json:"snapshot-cache"` // Size of the snapshot disk layer clean cache (MB)
// Eth Settings
Preimages bool `json:"preimages-enabled"`
SnapshotWait bool `json:"snapshot-wait"`
SnapshotVerify bool `json:"snapshot-verification-enabled"`
// Pruning Settings
Pruning bool `json:"pruning-enabled"` // If enabled, trie roots are only persisted every 4096 blocks
AcceptorQueueLimit int `json:"accepted-queue-limit"` // Maximum blocks to queue before blocking during acceptance
CommitInterval uint64 `json:"commit-interval"` // Specifies the commit interval at which to persist EVM and atomic tries.
AllowMissingTries bool `json:"allow-missing-tries"` // If enabled, warnings preventing an incomplete trie index are suppressed
PopulateMissingTries *uint64 `json:"populate-missing-tries,omitempty"` // Sets the starting point for re-populating missing tries. Disables re-generation if nil.
PopulateMissingTriesParallelism int `json:"populate-missing-tries-parallelism"` // Number of concurrent readers to use when re-populating missing tries on startup.
PruneWarpDB bool `json:"prune-warp-db-enabled"` // Determines if the warpDB should be cleared on startup
// HistoricalProofQueryWindow is, when running in archive mode only, the number of blocks before the
// last accepted block to be accepted for proof state queries.
HistoricalProofQueryWindow uint64 `json:"historical-proof-query-window,omitempty"`
// Metric Settings
MetricsExpensiveEnabled bool `json:"metrics-expensive-enabled"` // Debug-level metrics that might impact runtime performance
// API Settings
LocalTxsEnabled bool `json:"local-txs-enabled"`
TxPoolPriceLimit uint64 `json:"tx-pool-price-limit"`
TxPoolPriceBump uint64 `json:"tx-pool-price-bump"`
TxPoolAccountSlots uint64 `json:"tx-pool-account-slots"`
TxPoolGlobalSlots uint64 `json:"tx-pool-global-slots"`
TxPoolAccountQueue uint64 `json:"tx-pool-account-queue"`
TxPoolGlobalQueue uint64 `json:"tx-pool-global-queue"`
TxPoolLifetime Duration `json:"tx-pool-lifetime"`
APIMaxDuration Duration `json:"api-max-duration"`
WSCPURefillRate Duration `json:"ws-cpu-refill-rate"`
WSCPUMaxStored Duration `json:"ws-cpu-max-stored"`
MaxBlocksPerRequest int64 `json:"api-max-blocks-per-request"`
AllowUnfinalizedQueries bool `json:"allow-unfinalized-queries"`
AllowUnprotectedTxs bool `json:"allow-unprotected-txs"`
AllowUnprotectedTxHashes []common.Hash `json:"allow-unprotected-tx-hashes"`
// Keystore Settings
KeystoreDirectory string `json:"keystore-directory"` // both absolute and relative supported
KeystoreExternalSigner string `json:"keystore-external-signer"`
KeystoreInsecureUnlockAllowed bool `json:"keystore-insecure-unlock-allowed"`
// Gossip Settings
PushGossipPercentStake float64 `json:"push-gossip-percent-stake"`
PushGossipNumValidators int `json:"push-gossip-num-validators"`
PushGossipNumPeers int `json:"push-gossip-num-peers"`
PushRegossipNumValidators int `json:"push-regossip-num-validators"`
PushRegossipNumPeers int `json:"push-regossip-num-peers"`
PushGossipFrequency Duration `json:"push-gossip-frequency"`
PullGossipFrequency Duration `json:"pull-gossip-frequency"`
RegossipFrequency Duration `json:"regossip-frequency"`
PriorityRegossipAddresses []common.Address `json:"priority-regossip-addresses"`
// Log
LogLevel string `json:"log-level"`
LogJSONFormat bool `json:"log-json-format"`
// Address for Tx Fees (must be empty if not supported by blockchain)
FeeRecipient string `json:"feeRecipient"`
// Offline Pruning Settings
OfflinePruning bool `json:"offline-pruning-enabled"`
OfflinePruningBloomFilterSize uint64 `json:"offline-pruning-bloom-filter-size"`
OfflinePruningDataDirectory string `json:"offline-pruning-data-directory"`
// VM2VM network
MaxOutboundActiveRequests int64 `json:"max-outbound-active-requests"`
// Sync settings
StateSyncEnabled bool `json:"state-sync-enabled"`
StateSyncSkipResume bool `json:"state-sync-skip-resume"` // Forces state sync to use the highest available summary block
StateSyncServerTrieCache int `json:"state-sync-server-trie-cache"`
StateSyncIDs string `json:"state-sync-ids"`
StateSyncCommitInterval uint64 `json:"state-sync-commit-interval"`
StateSyncMinBlocks uint64 `json:"state-sync-min-blocks"`
StateSyncRequestSize uint16 `json:"state-sync-request-size"`
// Database Settings
InspectDatabase bool `json:"inspect-database"` // Inspects the database on startup if enabled.
// SkipUpgradeCheck disables checking that upgrades must take place before the last
// accepted block. Skipping this check is useful when a node operator does not update
// their node before the network upgrade and their node accepts blocks that have
// identical state with the pre-upgrade ruleset.
SkipUpgradeCheck bool `json:"skip-upgrade-check"`
// AcceptedCacheSize is the depth to keep in the accepted headers cache and the
// accepted logs cache at the accepted tip.
//
// This is particularly useful for improving the performance of eth_getLogs
// on RPC nodes.
AcceptedCacheSize int `json:"accepted-cache-size"`
// TransactionHistory is the maximum number of blocks from head whose tx indices
// are reserved:
// * 0: means no limit
// * N: means N block limit [HEAD-N+1, HEAD] and delete extra indexes
TransactionHistory uint64 `json:"transaction-history"`
// The maximum number of blocks from head whose state histories are reserved for pruning blockchains.
StateHistory uint64 `json:"state-history"`
// Deprecated, use 'TransactionHistory' instead.
TxLookupLimit uint64 `json:"tx-lookup-limit"`
// SkipTxIndexing skips indexing transactions.
// This is useful for validators that don't need to index transactions.
// TxLookupLimit can be still used to control unindexing old transactions.
SkipTxIndexing bool `json:"skip-tx-indexing"`
// WarpOffChainMessages encodes off-chain messages (unrelated to any on-chain event ie. block or AddressedCall)
// that the node should be willing to sign.
// Note: only supports AddressedCall payloads as defined here:
// https://github.com/luxfi/node/tree/7623ffd4be915a5185c9ed5e11fa9be15a6e1f00/vms/platformvm/warp/payload#addressedcall
WarpOffChainMessages []hexutil.Bytes `json:"warp-off-chain-messages"`
// RPC settings
HttpBodyLimit uint64 `json:"http-body-limit"`
BatchRequestLimit uint64 `json:"batch-request-limit"`
BatchResponseMaxSize uint64 `json:"batch-response-max-size"`
// Database settings
UseStandaloneDatabase *PBool `json:"use-standalone-database"`
DatabaseConfigContent string `json:"database-config"`
DatabaseConfigFile string `json:"database-config-file"`
DatabaseType string `json:"database-type"`
DatabasePath string `json:"database-path"`
DatabaseReadOnly bool `json:"database-read-only"`
// Database Scheme
StateScheme string `json:"state-scheme"`
}
// TxPoolConfig contains the transaction pool config to be passed
// to [Config.SetDefaults].
type TxPoolConfig struct {
PriceLimit uint64
PriceBump uint64
AccountSlots uint64
GlobalSlots uint64
AccountQueue uint64
GlobalQueue uint64
Lifetime time.Duration
}
// EthAPIs returns an array of strings representing the Eth APIs that should be enabled
func (c Config) EthAPIs() []string {
return c.EnabledEthAPIs
}
func (c *Config) SetDefaults(txPoolConfig TxPoolConfig) {
c.EnabledEthAPIs = defaultEnabledAPIs
c.RPCGasCap = defaultRpcGasCap
c.RPCTxFeeCap = defaultRpcTxFeeCap
c.MetricsExpensiveEnabled = defaultMetricsExpensiveEnabled
// TxPool settings
c.TxPoolPriceLimit = txPoolConfig.PriceLimit
c.TxPoolPriceBump = txPoolConfig.PriceBump
c.TxPoolAccountSlots = txPoolConfig.AccountSlots
c.TxPoolGlobalSlots = txPoolConfig.GlobalSlots
c.TxPoolAccountQueue = txPoolConfig.AccountQueue
c.TxPoolGlobalQueue = txPoolConfig.GlobalQueue
c.TxPoolLifetime.Duration = txPoolConfig.Lifetime
c.APIMaxDuration.Duration = defaultApiMaxDuration
c.WSCPURefillRate.Duration = defaultWsCpuRefillRate
c.WSCPUMaxStored.Duration = defaultWsCpuMaxStored
c.MaxBlocksPerRequest = defaultMaxBlocksPerRequest
c.ContinuousProfilerFrequency.Duration = defaultContinuousProfilerFrequency
c.ContinuousProfilerMaxFiles = defaultContinuousProfilerMaxFiles
c.Pruning = defaultPruningEnabled
c.TrieCleanCache = defaultTrieCleanCache
c.TrieDirtyCache = defaultTrieDirtyCache
c.TrieDirtyCommitTarget = defaultTrieDirtyCommitTarget
c.TriePrefetcherParallelism = defaultTriePrefetcherParallelism
c.SnapshotCache = defaultSnapshotCache
c.AcceptorQueueLimit = defaultAcceptorQueueLimit
c.CommitInterval = defaultCommitInterval
c.SnapshotWait = defaultSnapshotWait
c.PushGossipPercentStake = defaultPushGossipPercentStake
c.PushGossipNumValidators = defaultPushGossipNumValidators
c.PushGossipNumPeers = defaultPushGossipNumPeers
c.PushRegossipNumValidators = defaultPushRegossipNumValidators
c.PushRegossipNumPeers = defaultPushRegossipNumPeers
c.PushGossipFrequency.Duration = defaultPushGossipFrequency
c.PullGossipFrequency.Duration = defaultPullGossipFrequency
c.RegossipFrequency.Duration = defaultRegossipFrequency
c.OfflinePruningBloomFilterSize = defaultOfflinePruningBloomFilterSize
c.LogLevel = defaultLogLevel
c.LogJSONFormat = defaultLogJSONFormat
c.MaxOutboundActiveRequests = defaultMaxOutboundActiveRequests
c.PopulateMissingTriesParallelism = defaultPopulateMissingTriesParallelism
c.StateSyncServerTrieCache = defaultStateSyncServerTrieCache
c.StateSyncCommitInterval = defaultSyncableCommitInterval
c.StateSyncMinBlocks = defaultStateSyncMinBlocks
c.StateSyncRequestSize = defaultStateSyncRequestSize
c.AllowUnprotectedTxHashes = defaultAllowUnprotectedTxHashes
c.AcceptedCacheSize = defaultAcceptedCacheSize
c.DatabaseType = defaultDBType
c.ValidatorsAPIEnabled = defaultValidatorAPIEnabled
c.HistoricalProofQueryWindow = defaultHistoricalProofQueryWindow
c.StateHistory = defaultStateHistory
// RPC batch limits
c.BatchRequestLimit = defaultBatchRequestLimit
c.BatchResponseMaxSize = defaultBatchResponseMaxSize
}
func (d *Duration) UnmarshalJSON(data []byte) (err error) {
var v interface{}
if err := json.Unmarshal(data, &v); err != nil {
return err
}
d.Duration, err = cast.ToDurationE(v)
return err
}
// String implements the stringer interface.
func (d Duration) String() string {
return d.Duration.String()
}
// String implements the stringer interface.
func (d Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(d.Duration.String())
}
// Validate returns an error if this is an invalid config.
func (c *Config) Validate() error {
if c.PopulateMissingTries != nil && (c.OfflinePruning || c.Pruning) {
return fmt.Errorf("cannot enable populate missing tries while offline pruning (enabled: %t)/pruning (enabled: %t) are enabled", c.OfflinePruning, c.Pruning)
}
if c.PopulateMissingTries != nil && c.PopulateMissingTriesParallelism < 1 {
return fmt.Errorf("cannot enable populate missing tries without at least one reader (parallelism: %d)", c.PopulateMissingTriesParallelism)
}
if !c.Pruning && c.OfflinePruning {
return fmt.Errorf("cannot run offline pruning while pruning is disabled")
}
// If pruning is enabled, the commit interval must be non-zero so the node commits state tries every CommitInterval blocks.
if c.Pruning && c.CommitInterval == 0 {
return fmt.Errorf("cannot use commit interval of 0 with pruning enabled")
}
if c.Pruning && c.StateHistory == 0 {
return fmt.Errorf("cannot use state history of 0 with pruning enabled")
}
if c.PushGossipPercentStake < 0 || c.PushGossipPercentStake > 1 {
return fmt.Errorf("push-gossip-percent-stake is %f but must be in the range [0, 1]", c.PushGossipPercentStake)
}
return nil
}
func (c *Config) Deprecate() string {
msg := ""
// Deprecate the old config options and set the new ones.
if c.TxLookupLimit != 0 {
msg += "tx-lookup-limit is deprecated, use transaction-history instead. "
c.TransactionHistory = c.TxLookupLimit
}
return msg
}
func (p *PBool) String() string {
if p == nil {
return "nil"
}
return fmt.Sprintf("%t", *p)
}
func (p *PBool) Bool() bool {
if p == nil {
return false
}
return bool(*p)
}
-290
View File
@@ -1,290 +0,0 @@
# Subnet-EVM Configuration
> **Note**: These are the configuration options available in the Subnet-EVM codebase. To set these values, you need to create a configuration file at `~/.luxd/configs/chains/<blockchainID>/config.json`.
>
> For the Luxd node configuration options, see the Luxd Configuration page.
This document describes all configuration options available for Subnet-EVM.
## Example Configuration
```json
{
"eth-apis": ["eth", "eth-filter", "net", "web3"],
"pruning-enabled": true,
"commit-interval": 4096,
"trie-clean-cache": 512,
"trie-dirty-cache": 512,
"snapshot-cache": 256,
"rpc-gas-cap": 50000000,
"log-level": "info",
"metrics-expensive-enabled": true,
"continuous-profiler-dir": "./profiles",
"state-sync-enabled": false,
"accepted-cache-size": 32
}
```
## Configuration Format
Configuration is provided as a JSON object. All fields are optional unless otherwise specified.
## API Configuration
### Ethereum APIs
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `eth-apis` | array of strings | List of Ethereum services that should be enabled | `["eth", "eth-filter", "net", "web3", "internal-eth", "internal-blockchain", "internal-transaction"]` |
### Subnet-EVM Specific APIs
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `validators-api-enabled` | bool | Enable the validators API | `true` |
| `admin-api-enabled` | bool | Enable the admin API for administrative operations | `false` |
| `admin-api-dir` | string | Directory for admin API operations | - |
| `warp-api-enabled` | bool | Enable the Warp API for cross-chain messaging | `false` |
### API Limits and Security
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `rpc-gas-cap` | uint64 | Maximum gas limit for RPC calls | `50,000,000` |
| `rpc-tx-fee-cap` | float64 | Maximum transaction fee cap in LUX | `100` |
| `api-max-duration` | duration | Maximum duration for API calls (0 = no limit) | `0` |
| `api-max-blocks-per-request` | int64 | Maximum number of blocks per getLogs request (0 = no limit) | `0` |
| `http-body-limit` | uint64 | Maximum size of HTTP request bodies | - |
### WebSocket Settings
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `ws-cpu-refill-rate` | duration | Rate at which WebSocket CPU usage quota is refilled (0 = no limit) | `0` |
| `ws-cpu-max-stored` | duration | Maximum stored WebSocket CPU usage quota (0 = no limit) | `0` |
## Cache Configuration
### Trie Caches
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `trie-clean-cache` | int | Size of the trie clean cache in MB | `512` |
| `trie-dirty-cache` | int | Size of the trie dirty cache in MB | `512` |
| `trie-dirty-commit-target` | int | Memory limit to target in the dirty cache before performing a commit in MB | `20` |
| `trie-prefetcher-parallelism` | int | Maximum concurrent disk reads trie prefetcher should perform | `16` |
### Other Caches
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `snapshot-cache` | int | Size of the snapshot disk layer clean cache in MB | `256` |
| `accepted-cache-size` | int | Depth to keep in the accepted headers and logs cache (blocks) | `32` |
| `state-sync-server-trie-cache` | int | Trie cache size for state sync server in MB | `64` |
## Ethereum Settings
### Transaction Processing
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `preimages-enabled` | bool | Enable preimage recording | `false` |
| `allow-unfinalized-queries` | bool | Allow queries for unfinalized blocks | `false` |
| `allow-unprotected-txs` | bool | Allow unprotected transactions (without EIP-155) | `false` |
| `allow-unprotected-tx-hashes` | array | List of specific transaction hashes allowed to be unprotected | EIP-1820 registry tx |
| `local-txs-enabled` | bool | Enable treatment of transactions from local accounts as local | `false` |
### Snapshots
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `snapshot-wait` | bool | Wait for snapshot generation on startup | `false` |
| `snapshot-verification-enabled` | bool | Enable snapshot verification | `false` |
## Pruning and State Management
### Basic Pruning
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `pruning-enabled` | bool | Enable state pruning to save disk space | `true` |
| `commit-interval` | uint64 | Interval at which to persist EVM and atomic tries (blocks) | `4096` |
| `accepted-queue-limit` | int | Maximum blocks to queue before blocking during acceptance | `64` |
### State Reconstruction
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `allow-missing-tries` | bool | Suppress warnings about incomplete trie index | `false` |
| `populate-missing-tries` | uint64 | Starting block for re-populating missing tries (null = disabled) | `null` |
| `populate-missing-tries-parallelism` | int | Concurrent readers for re-populating missing tries | `1024` |
### Offline Pruning
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `offline-pruning-enabled` | bool | Enable offline pruning | `false` |
| `offline-pruning-bloom-filter-size` | uint64 | Bloom filter size for offline pruning in MB | `512` |
| `offline-pruning-data-directory` | string | Directory for offline pruning data | - |
### Historical Data
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `historical-proof-query-window` | uint64 | Number of blocks before last accepted for proof queries (archive mode only, ~24 hours) | `43200` |
| `state-history` | uint64 | Number of most recent states that are accesible on disk (pruning mode only) | `32` |
## Transaction Pool Configuration
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `tx-pool-price-limit` | uint64 | Minimum gas price for transaction acceptance | - |
| `tx-pool-price-bump` | uint64 | Minimum price bump percentage for transaction replacement | - |
| `tx-pool-account-slots` | uint64 | Maximum number of executable transaction slots per account | - |
| `tx-pool-global-slots` | uint64 | Maximum number of executable transaction slots for all accounts | - |
| `tx-pool-account-queue` | uint64 | Maximum number of non-executable transaction slots per account | - |
| `tx-pool-global-queue` | uint64 | Maximum number of non-executable transaction slots for all accounts | - |
| `tx-pool-lifetime` | duration | Maximum time transactions can stay in the pool | - |
## Gossip Configuration
### Push Gossip Settings
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `push-gossip-percent-stake` | float64 | Percentage of total stake to push gossip to (range: [0, 1]) | `0.9` |
| `push-gossip-num-validators` | int | Number of validators to push gossip to | `100` |
| `push-gossip-num-peers` | int | Number of non-validator peers to push gossip to | `0` |
### Regossip Settings
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `push-regossip-num-validators` | int | Number of validators to regossip to | `10` |
| `push-regossip-num-peers` | int | Number of non-validator peers to regossip to | `0` |
| `priority-regossip-addresses` | array | Addresses to prioritize for regossip | - |
### Timing Configuration
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `push-gossip-frequency` | duration | Frequency of push gossip | `100ms` |
| `pull-gossip-frequency` | duration | Frequency of pull gossip | `1s` |
| `regossip-frequency` | duration | Frequency of regossip | `30s` |
## Logging and Monitoring
### Logging
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `log-level` | string | Logging level (trace, debug, info, warn, error, crit) | `"info"` |
| `log-json-format` | bool | Use JSON format for logs | `false` |
### Profiling
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `continuous-profiler-dir` | string | Directory for continuous profiler output (empty = disabled) | - |
| `continuous-profiler-frequency` | duration | Frequency to run continuous profiler | `15m` |
| `continuous-profiler-max-files` | int | Maximum number of profiler files to maintain | `5` |
### Metrics
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `metrics-expensive-enabled` | bool | Enable expensive debug-level metrics; this includes Firewood metrics | `true` |
## Security and Access
### Keystore
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `keystore-directory` | string | Directory for keystore files (absolute or relative path) | - |
| `keystore-external-signer` | string | External signer configuration | - |
| `keystore-insecure-unlock-allowed` | bool | Allow insecure account unlocking | `false` |
### Fee Configuration
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `feeRecipient` | string | Address to send transaction fees to (leave empty if not supported) | - |
## Network and Sync
### Network
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `max-outbound-active-requests` | int64 | Maximum number of outbound active requests for VM2VM network | `16` |
### State Sync
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `state-sync-enabled` | bool | Enable state sync | `false` |
| `state-sync-skip-resume` | bool | Force state sync to use highest available summary block | `false` |
| `state-sync-ids` | string | Comma-separated list of state sync IDs | - |
| `state-sync-commit-interval` | uint64 | Commit interval for state sync (blocks) | `16384` |
| `state-sync-min-blocks` | uint64 | Minimum blocks ahead required for state sync | `300000` |
| `state-sync-request-size` | uint16 | Number of key/values to request per state sync request | `1024` |
## Database Configuration
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `database-type` | string | Type of database to use | `"pebbledb"` |
| `database-path` | string | Path to database directory | - |
| `database-read-only` | bool | Open database in read-only mode | `false` |
| `database-config` | string | Inline database configuration | - |
| `database-config-file` | string | Path to database configuration file | - |
| `use-standalone-database` | bool | Use standalone database instead of shared one | - |
| `inspect-database` | bool | Inspect database on startup | `false` |
| `state-scheme` | string | EXPERIMENTAL: specifies the database scheme to store state data; can be one of `hash` or `firewood` | `hash` |
## Transaction Indexing
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `transaction-history` | uint64 | Maximum number of blocks from head whose transaction indices are reserved (0 = no limit) | - |
| `tx-lookup-limit` | uint64 | **Deprecated** - use `transaction-history` instead | - |
| `skip-tx-indexing` | bool | Skip indexing transactions entirely | `false` |
## Warp Configuration
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `warp-off-chain-messages` | array | Off-chain messages the node should be willing to sign | - |
| `prune-warp-db-enabled` | bool | Clear warp database on startup | `false` |
## Miscellaneous
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `airdrop` | string | Path to airdrop file | - |
| `skip-upgrade-check` | bool | Skip checking that upgrades occur before last accepted block ⚠️ **Warning**: Only use when you understand the implications | `false` |
## Gossip Constants
The following constants are defined for transaction gossip behavior and cannot be configured without a custom build of Subnet-EVM:
| Constant | Type | Description | Value |
|----------|------|-------------|-------|
| Bloom Filter Min Target Elements | int | Minimum target elements for bloom filter | `8,192` |
| Bloom Filter Target False Positive Rate | float | Target false positive rate | `1%` |
| Bloom Filter Reset False Positive Rate | float | Reset false positive rate | `5%` |
| Bloom Filter Churn Multiplier | int | Churn multiplier | `3` |
| Push Gossip Discarded Elements | int | Number of discarded elements | `16,384` |
| Tx Gossip Target Message Size | size | Target message size for transaction gossip | `20 KiB` |
| Tx Gossip Throttling Period | duration | Throttling period | `10s` |
| Tx Gossip Throttling Limit | int | Throttling limit | `2` |
| Tx Gossip Poll Size | int | Poll size | `1` |
## Validation Notes
- Cannot enable `populate-missing-tries` while pruning or offline pruning is enabled
- Cannot run offline pruning while pruning is disabled
- Commit interval must be non-zero when pruning is enabled
- `push-gossip-percent-stake` must be in range `[0, 1]`
- Some settings may require node restart to take effect
-129
View File
@@ -1,129 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package config
import (
"encoding/json"
"fmt"
"testing"
"time"
"github.com/luxfi/geth/common"
"github.com/stretchr/testify/assert"
)
func TestUnmarshalConfig(t *testing.T) {
tests := []struct {
name string
givenJSON []byte
expected Config
expectedErr bool
}{
{
"string durations parsed",
[]byte(`{"api-max-duration": "1m", "continuous-profiler-frequency": "2m"}`),
Config{APIMaxDuration: Duration{1 * time.Minute}, ContinuousProfilerFrequency: Duration{2 * time.Minute}},
false,
},
{
"integer durations parsed",
[]byte(fmt.Sprintf(`{"api-max-duration": "%v", "continuous-profiler-frequency": "%v"}`, 1*time.Minute, 2*time.Minute)),
Config{APIMaxDuration: Duration{1 * time.Minute}, ContinuousProfilerFrequency: Duration{2 * time.Minute}},
false,
},
{
"nanosecond durations parsed",
[]byte(`{"api-max-duration": 5000000000, "continuous-profiler-frequency": 5000000000}`),
Config{APIMaxDuration: Duration{5 * time.Second}, ContinuousProfilerFrequency: Duration{5 * time.Second}},
false,
},
{
"bad durations",
[]byte(`{"api-max-duration": "bad-duration"}`),
Config{},
true,
},
{
"tx pool configurations",
[]byte(`{"tx-pool-price-limit": 1, "tx-pool-price-bump": 2, "tx-pool-account-slots": 3, "tx-pool-global-slots": 4, "tx-pool-account-queue": 5, "tx-pool-global-queue": 6}`),
Config{
TxPoolPriceLimit: 1,
TxPoolPriceBump: 2,
TxPoolAccountSlots: 3,
TxPoolGlobalSlots: 4,
TxPoolAccountQueue: 5,
TxPoolGlobalQueue: 6,
},
false,
},
{
"state sync enabled",
[]byte(`{"state-sync-enabled":true}`),
Config{StateSyncEnabled: true},
false,
},
{
"state sync sources",
[]byte(`{"state-sync-ids": "NodeID-CaBYJ9kzHvrQFiYWowMkJGAQKGMJqZoat"}`),
Config{StateSyncIDs: "NodeID-CaBYJ9kzHvrQFiYWowMkJGAQKGMJqZoat"},
false,
},
{
"empty transaction history ",
[]byte(`{}`),
Config{TransactionHistory: 0},
false,
},
{
"zero transaction history",
[]byte(`{"transaction-history": 0}`),
func() Config {
return Config{TransactionHistory: 0}
}(),
false,
},
{
"1 transaction history",
[]byte(`{"transaction-history": 1}`),
func() Config {
return Config{TransactionHistory: 1}
}(),
false,
},
{
"-1 transaction history",
[]byte(`{"transaction-history": -1}`),
Config{},
true,
},
{
"deprecated tx lookup limit",
[]byte(`{"tx-lookup-limit": 1}`),
Config{TransactionHistory: 1, TxLookupLimit: 1},
false,
},
{
"allow unprotected tx hashes",
[]byte(`{"allow-unprotected-tx-hashes": ["0x803351deb6d745e91545a6a3e1c0ea3e9a6a02a1a4193b70edfcd2f40f71a01c"]}`),
Config{AllowUnprotectedTxHashes: []common.Hash{common.HexToHash("0x803351deb6d745e91545a6a3e1c0ea3e9a6a02a1a4193b70edfcd2f40f71a01c")}},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var tmp Config
err := json.Unmarshal(tt.givenJSON, &tmp)
if tt.expectedErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
tmp.Deprecate()
assert.Equal(t, tt.expected, tmp)
}
})
}
}
-22
View File
@@ -1,22 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package config
import (
"time"
"github.com/luxfi/node/utils/units"
)
const (
TxGossipBloomMinTargetElements = 8 * 1024
TxGossipBloomTargetFalsePositiveRate = 0.01
TxGossipBloomResetFalsePositiveRate = 0.05
TxGossipBloomChurnMultiplier = 3
PushGossipDiscardedElements = 16_384
TxGossipTargetMessageSize = 20 * units.KiB
TxGossipThrottlingPeriod = 10 * time.Second
TxGossipThrottlingLimit = 2
TxGossipPollSize = 1
)
-108
View File
@@ -1,108 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package config
import (
"time"
"github.com/luxfi/geth/common"
)
const (
// pebbledbName is the database type name for PebbleDB
pebbledbName = "pebbledb"
)
// defaultCommitInterval is defined in config.go
func NewDefaultConfig() Config {
return Config{
AllowUnprotectedTxHashes: []common.Hash{
common.HexToHash("0xfefb2da535e927b85fe68eb81cb2e4a5827c905f78381a01ef2322aa9b0aee8e"), // EIP-1820: https://eips.ethereum.org/EIPS/eip-1820
},
EnabledEthAPIs: []string{
"eth",
"eth-filter",
"net",
"web3",
"internal-eth",
"internal-blockchain",
"internal-transaction",
},
// Provides 2 minutes of buffer (2s block target) for a commit delay
AcceptorQueueLimit: 64,
Pruning: true,
CommitInterval: defaultCommitInterval,
TrieCleanCache: 512,
TrieDirtyCache: 512,
TrieDirtyCommitTarget: 20,
TriePrefetcherParallelism: 16,
SnapshotCache: 256,
StateSyncCommitInterval: defaultCommitInterval * 4,
SnapshotWait: false,
RPCGasCap: 50_000_000, // 50M Gas Limit
RPCTxFeeCap: 100, // 100 LUX
MetricsExpensiveEnabled: true,
// Default to no maximum API call duration
APIMaxDuration: timeToDuration(0),
// Default to no maximum WS CPU usage
WSCPURefillRate: timeToDuration(0),
// Default to no maximum WS CPU usage
WSCPUMaxStored: timeToDuration(0),
// Default to no maximum on the number of blocks per getLogs request
MaxBlocksPerRequest: 0,
ContinuousProfilerFrequency: timeToDuration(15 * time.Minute),
ContinuousProfilerMaxFiles: 5,
PushGossipPercentStake: .9,
PushGossipNumValidators: 100,
PushGossipNumPeers: 0,
PushRegossipNumValidators: 10,
PushRegossipNumPeers: 0,
PushGossipFrequency: timeToDuration(100 * time.Millisecond),
PullGossipFrequency: timeToDuration(1 * time.Second),
RegossipFrequency: timeToDuration(30 * time.Second),
// Default size (MB) for the offline pruner to use
OfflinePruningBloomFilterSize: uint64(512),
LogLevel: "info",
LogJSONFormat: false,
MaxOutboundActiveRequests: 16,
PopulateMissingTriesParallelism: 1024,
StateSyncServerTrieCache: 64, // MB
AcceptedCacheSize: 32, // blocks
// StateSyncMinBlocks is the minimum number of blocks the blockchain
// should be ahead of local last accepted to perform state sync.
// This constant is chosen so normal bootstrapping is preferred when it would
// be faster than state sync.
// time assumptions:
// - normal bootstrap processing time: ~14 blocks / second
// - state sync time: ~6 hrs.
StateSyncMinBlocks: 300_000,
// the number of key/values to ask peers for per request
StateSyncRequestSize: 1024,
StateHistory: uint64(32),
// Estimated block count in 24 hours with 2s block accept period
HistoricalProofQueryWindow: uint64(24 * time.Hour / (2 * time.Second)),
// Mempool settings
TxPoolPriceLimit: 1,
TxPoolPriceBump: 10,
TxPoolAccountSlots: 16,
TxPoolGlobalSlots: 4096 + 1024, // urgent + floating queue capacity with 4:1 ratio,
TxPoolAccountQueue: 64,
TxPoolGlobalQueue: 1024,
TxPoolLifetime: timeToDuration(10 * time.Minute),
// RPC settings
BatchRequestLimit: 1000,
BatchResponseMaxSize: 25 * 1000 * 1000, // 25MB
// Subnet EVM API settings
ValidatorsAPIEnabled: true,
// Database settings
DatabaseType: pebbledbName,
// Additional settings with sensible defaults
AllowUnprotectedTxs: false,
}
}
func timeToDuration(t time.Duration) Duration {
return Duration{t}
}
-14
View File
@@ -1,14 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customlogs
import ethtypes "github.com/luxfi/geth/core/types"
// FlattenLogs converts a nested array of logs to a single array of logs.
func FlattenLogs(list [][]*ethtypes.Log) []*ethtypes.Log {
var flat []*ethtypes.Log
for _, logs := range list {
flat = append(flat, logs...)
}
return flat
}
@@ -1,57 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customrawdb
import (
"encoding/binary"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/ethdb"
)
// ReadBloomBits retrieves the bloom bits for a specific section and bit index.
func ReadBloomBits(db ethdb.KeyValueReader, bit uint, section uint64, head common.Hash) ([]byte, error) {
// Use the same key scheme as go-ethereum
key := make([]byte, 10+1+8+32)
copy(key, []byte("blt")) // Bloom trie key prefix
key[3] = byte(bit) // Bloom bit index
binary.BigEndian.PutUint64(key[4:12], section) // Section index
copy(key[12:], head[:]) // Block hash
return db.Get(key)
}
// WriteBloomBits stores the bloom bits for a specific section and bit index.
func WriteBloomBits(db ethdb.KeyValueWriter, bit uint, section uint64, head common.Hash, bits []byte) error {
key := make([]byte, 10+1+8+32)
copy(key, []byte("blt"))
key[3] = byte(bit)
binary.BigEndian.PutUint64(key[4:12], section)
copy(key[12:], head[:])
return db.Put(key, bits)
}
// DeleteBloombits deletes all bloom bits for a given section range.
func DeleteBloombits(db ethdb.Database, start, end uint64) {
// Iterate through all possible bit indices (0-2047)
for bit := uint(0); bit < 2048; bit++ {
for section := start; section <= end; section++ {
// Read canonical hash for the section
head := rawdb.ReadCanonicalHash(db, (section+1)*4096-1)
if head == (common.Hash{}) {
continue
}
key := make([]byte, 10+1+8+32)
copy(key, []byte("blt"))
key[3] = byte(bit)
binary.BigEndian.PutUint64(key[4:12], section)
copy(key[12:], head[:])
db.Delete(key)
}
}
}
@@ -1,153 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customrawdb
import (
"encoding/json"
"fmt"
"time"
"github.com/luxfi/evm/params"
"github.com/luxfi/geth/common"
ethrawdb "github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/geth/rlp"
"github.com/luxfi/log"
)
// writeCurrentTimeMarker writes a marker of the current time in the db at `key`.
func writeCurrentTimeMarker(db ethdb.KeyValueStore, key []byte) error {
data, err := rlp.EncodeToBytes(uint64(time.Now().Unix()))
if err != nil {
return err
}
return db.Put(key, data)
}
// readTimeMarker reads the timestamp stored at `key`
func readTimeMarker(db ethdb.KeyValueStore, key []byte) (time.Time, error) {
data, err := db.Get(key)
if err != nil {
return time.Time{}, err
}
var unix uint64
if err := rlp.DecodeBytes(data, &unix); err != nil {
return time.Time{}, err
}
return time.Unix(int64(unix), 0), nil
}
// WriteOfflinePruning writes a time marker of the last attempt to run offline pruning.
// The marker is written when offline pruning completes and is deleted when the node
// is started successfully with offline pruning disabled. This ensures users must
// disable offline pruning and start their node successfully between runs of offline
// pruning.
func WriteOfflinePruning(db ethdb.KeyValueStore) error {
return writeCurrentTimeMarker(db, offlinePruningKey)
}
// ReadOfflinePruning reads the most recent timestamp of an attempt to run offline
// pruning if present.
func ReadOfflinePruning(db ethdb.KeyValueStore) (time.Time, error) {
return readTimeMarker(db, offlinePruningKey)
}
// DeleteOfflinePruning deletes any marker of the last attempt to run offline pruning.
func DeleteOfflinePruning(db ethdb.KeyValueStore) error {
return db.Delete(offlinePruningKey)
}
// WritePopulateMissingTries writes a marker for the current attempt to populate
// missing tries.
func WritePopulateMissingTries(db ethdb.KeyValueStore) error {
return writeCurrentTimeMarker(db, populateMissingTriesKey)
}
// ReadPopulateMissingTries reads the most recent timestamp of an attempt to
// re-populate missing trie nodes.
func ReadPopulateMissingTries(db ethdb.KeyValueStore) (time.Time, error) {
return readTimeMarker(db, populateMissingTriesKey)
}
// DeletePopulateMissingTries deletes any marker of the last attempt to
// re-populate missing trie nodes.
func DeletePopulateMissingTries(db ethdb.KeyValueStore) error {
return db.Delete(populateMissingTriesKey)
}
// WritePruningDisabled writes a marker to track whether the node has ever run
// with pruning disabled.
func WritePruningDisabled(db ethdb.KeyValueStore) error {
return db.Put(pruningDisabledKey, nil)
}
// HasPruningDisabled returns true if there is a marker present indicating that
// the node has run with pruning disabled at some point.
func HasPruningDisabled(db ethdb.KeyValueStore) (bool, error) {
return db.Has(pruningDisabledKey)
}
// WriteAcceptorTip writes `hash` as the last accepted block that has been fully processed.
func WriteAcceptorTip(db ethdb.KeyValueWriter, hash common.Hash) error {
return db.Put(acceptorTipKey, hash[:])
}
// ReadAcceptorTip reads the hash of the last accepted block that was fully processed.
// If there is no value present (the index is being initialized for the first time), then the
// empty hash is returned.
func ReadAcceptorTip(db ethdb.KeyValueReader) (common.Hash, error) {
has, err := db.Has(acceptorTipKey)
if err != nil {
return common.Hash{}, err
}
if !has {
// If the index is not present on disk, the [acceptorTipKey] index has not been initialized yet.
return common.Hash{}, nil
}
h, err := db.Get(acceptorTipKey)
if err != nil {
return common.Hash{}, err
}
if len(h) != common.HashLength {
return common.Hash{}, fmt.Errorf("value has incorrect length %d", len(h))
}
return common.BytesToHash(h), nil
}
// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
func ReadChainConfig(db ethdb.KeyValueReader, hash common.Hash) *params.ChainConfig {
config := ethrawdb.ReadChainConfig(db, hash)
upgrade, _ := db.Get(upgradeConfigKey(hash))
if len(upgrade) == 0 {
return config
}
extra := params.GetExtra(config)
if err := json.Unmarshal(upgrade, &extra.UpgradeConfig); err != nil {
log.Error("Invalid upgrade config JSON", "err", err)
return nil
}
return config
}
// WriteChainConfig writes the chain config settings to the database.
func WriteChainConfig(db ethdb.KeyValueWriter, hash common.Hash, config *params.ChainConfig) {
ethrawdb.WriteChainConfig(db, hash, config)
if config == nil {
return
}
extra := params.GetExtra(config)
data, err := json.Marshal(extra.UpgradeConfig)
if err != nil {
log.Crit("Failed to JSON encode upgrade config", "err", err)
}
if err := db.Put(upgradeConfigKey(hash), data); err != nil {
log.Crit("Failed to store upgrade config", "err", err)
}
}
@@ -1,46 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customrawdb
import (
"github.com/luxfi/geth/common"
ethrawdb "github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/log"
)
// ReadSnapshotBlockHash retrieves the hash of the block whose state is contained in
// the persisted snapshot.
func ReadSnapshotBlockHash(db ethdb.KeyValueReader) common.Hash {
data, _ := db.Get(snapshotBlockHashKey)
if len(data) != common.HashLength {
return common.Hash{}
}
return common.BytesToHash(data)
}
// WriteSnapshotBlockHash stores the root of the block whose state is contained in
// the persisted snapshot.
func WriteSnapshotBlockHash(db ethdb.KeyValueWriter, blockHash common.Hash) {
if err := db.Put(snapshotBlockHashKey, blockHash[:]); err != nil {
log.Crit("Failed to store snapshot block hash", "err", err)
}
}
// DeleteSnapshotBlockHash deletes the hash of the block whose state is contained in
// the persisted snapshot. Since snapshots are not immutable, this method can
// be used during updates, so a crash or failure will mark the entire snapshot
// invalid.
func DeleteSnapshotBlockHash(db ethdb.KeyValueWriter) {
if err := db.Delete(snapshotBlockHashKey); err != nil {
log.Crit("Failed to remove snapshot block hash", "err", err)
}
}
// IterateAccountSnapshots returns an iterator for walking all of the accounts in the snapshot
func IterateAccountSnapshots(db ethdb.Iteratee) ethdb.Iterator {
it := db.NewIterator(ethrawdb.SnapshotAccountPrefix, nil)
keyLen := len(ethrawdb.SnapshotAccountPrefix) + common.HashLength
return ethrawdb.NewKeyLengthIterator(it, keyLen)
}
@@ -1,222 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customrawdb
import (
"encoding/binary"
"github.com/luxfi/geth/common"
ethrawdb "github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/log"
"github.com/luxfi/node/utils/wrappers"
)
// ReadSyncRoot reads the root corresponding to the main trie of an in-progress
// sync and returns common.Hash{} if no in-progress sync was found.
func ReadSyncRoot(db ethdb.KeyValueReader) (common.Hash, error) {
has, err := db.Has(syncRootKey)
if err != nil || !has {
return common.Hash{}, err
}
root, err := db.Get(syncRootKey)
if err != nil {
return common.Hash{}, err
}
return common.BytesToHash(root), nil
}
// WriteSyncRoot writes root as the root of the main trie of the in-progress sync.
func WriteSyncRoot(db ethdb.KeyValueWriter, root common.Hash) error {
return db.Put(syncRootKey, root[:])
}
// AddCodeToFetch adds a marker that we need to fetch the code for `hash`.
func AddCodeToFetch(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Put(codeToFetchKey(hash), nil); err != nil {
log.Crit("Failed to put code to fetch", "codeHash", hash, "err", err)
}
}
// DeleteCodeToFetch removes the marker that the code corresponding to `hash` needs to be fetched.
func DeleteCodeToFetch(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Delete(codeToFetchKey(hash)); err != nil {
log.Crit("Failed to delete code to fetch", "codeHash", hash, "err", err)
}
}
// NewCodeToFetchIterator returns a KeyLength iterator over all code
// hashes that are pending syncing. It is the caller's responsibility to
// unpack the key and call Release on the returned iterator.
func NewCodeToFetchIterator(db ethdb.Iteratee) ethdb.Iterator {
return ethrawdb.NewKeyLengthIterator(
db.NewIterator(CodeToFetchPrefix, nil),
codeToFetchKeyLength,
)
}
func codeToFetchKey(hash common.Hash) []byte {
codeToFetchKey := make([]byte, codeToFetchKeyLength)
copy(codeToFetchKey, CodeToFetchPrefix)
copy(codeToFetchKey[len(CodeToFetchPrefix):], hash[:])
return codeToFetchKey
}
// NewSyncSegmentsIterator returns a KeyLength iterator over all trie segments
// added for root. It is the caller's responsibility to unpack the key and call
// Release on the returned iterator.
func NewSyncSegmentsIterator(db ethdb.Iteratee, root common.Hash) ethdb.Iterator {
segmentsPrefix := make([]byte, len(syncSegmentsPrefix)+common.HashLength)
copy(segmentsPrefix, syncSegmentsPrefix)
copy(segmentsPrefix[len(syncSegmentsPrefix):], root[:])
return ethrawdb.NewKeyLengthIterator(
db.NewIterator(segmentsPrefix, nil),
syncSegmentsKeyLength,
)
}
// WriteSyncSegment adds a trie segment for root at the given start position.
func WriteSyncSegment(db ethdb.KeyValueWriter, root common.Hash, start common.Hash) error {
return db.Put(packSyncSegmentKey(root, start), []byte{0x01})
}
// ClearSyncSegments removes segment markers for root from db
func ClearSyncSegments(db ethdb.KeyValueStore, root common.Hash) error {
segmentsPrefix := make([]byte, len(syncSegmentsPrefix)+common.HashLength)
copy(segmentsPrefix, syncSegmentsPrefix)
copy(segmentsPrefix[len(syncSegmentsPrefix):], root[:])
return clearPrefix(db, segmentsPrefix, syncSegmentsKeyLength)
}
// ClearAllSyncSegments removes all segment markers from db
func ClearAllSyncSegments(db ethdb.KeyValueStore) error {
return clearPrefix(db, syncSegmentsPrefix, syncSegmentsKeyLength)
}
// UnpackSyncSegmentKey returns the root and start position for a trie segment
// key returned from NewSyncSegmentsIterator.
func UnpackSyncSegmentKey(keyBytes []byte) (common.Hash, []byte) {
keyBytes = keyBytes[len(syncSegmentsPrefix):] // skip prefix
root := common.BytesToHash(keyBytes[:common.HashLength])
start := keyBytes[common.HashLength:]
return root, start
}
// packSyncSegmentKey packs root and account into a key for storage in db.
func packSyncSegmentKey(root common.Hash, start common.Hash) []byte {
bytes := make([]byte, syncSegmentsKeyLength)
copy(bytes, syncSegmentsPrefix)
copy(bytes[len(syncSegmentsPrefix):], root[:])
copy(bytes[len(syncSegmentsPrefix)+common.HashLength:], start.Bytes())
return bytes
}
// NewSyncStorageTriesIterator returns a KeyLength iterator over all storage tries
// added for syncing (beginning at seek). It is the caller's responsibility to unpack
// the key and call Release on the returned iterator.
func NewSyncStorageTriesIterator(db ethdb.Iteratee, seek []byte) ethdb.Iterator {
return ethrawdb.NewKeyLengthIterator(db.NewIterator(syncStorageTriesPrefix, seek), syncStorageTriesKeyLength)
}
// WriteSyncStorageTrie adds a storage trie for account (with the given root) to be synced.
func WriteSyncStorageTrie(db ethdb.KeyValueWriter, root common.Hash, account common.Hash) error {
return db.Put(packSyncStorageTrieKey(root, account), []byte{0x01})
}
// ClearSyncStorageTrie removes all storage trie accounts (with the given root) from db.
// Intended for use when the trie with root has completed syncing.
func ClearSyncStorageTrie(db ethdb.KeyValueStore, root common.Hash) error {
accountsPrefix := make([]byte, len(syncStorageTriesPrefix)+common.HashLength)
copy(accountsPrefix, syncStorageTriesPrefix)
copy(accountsPrefix[len(syncStorageTriesPrefix):], root[:])
return clearPrefix(db, accountsPrefix, syncStorageTriesKeyLength)
}
// ClearAllSyncStorageTries removes all storage tries added for syncing from db
func ClearAllSyncStorageTries(db ethdb.KeyValueStore) error {
return clearPrefix(db, syncStorageTriesPrefix, syncStorageTriesKeyLength)
}
// UnpackSyncStorageTrieKey returns the root and account for a storage trie
// key returned from NewSyncStorageTriesIterator.
func UnpackSyncStorageTrieKey(keyBytes []byte) (common.Hash, common.Hash) {
keyBytes = keyBytes[len(syncStorageTriesPrefix):] // skip prefix
root := common.BytesToHash(keyBytes[:common.HashLength])
account := common.BytesToHash(keyBytes[common.HashLength:])
return root, account
}
// packSyncStorageTrieKey packs root and account into a key for storage in db.
func packSyncStorageTrieKey(root common.Hash, account common.Hash) []byte {
bytes := make([]byte, 0, syncStorageTriesKeyLength)
bytes = append(bytes, syncStorageTriesPrefix...)
bytes = append(bytes, root[:]...)
bytes = append(bytes, account[:]...)
return bytes
}
// WriteSyncPerformed logs an entry in `db` indicating the VM state synced to `blockNumber`.
func WriteSyncPerformed(db ethdb.KeyValueWriter, blockNumber uint64) error {
syncPerformedPrefixLen := len(syncPerformedPrefix)
bytes := make([]byte, syncPerformedPrefixLen+wrappers.LongLen)
copy(bytes[:syncPerformedPrefixLen], syncPerformedPrefix)
binary.BigEndian.PutUint64(bytes[syncPerformedPrefixLen:], blockNumber)
return db.Put(bytes, []byte{0x01})
}
// NewSyncPerformedIterator returns an iterator over all block numbers the VM
// has state synced to.
func NewSyncPerformedIterator(db ethdb.Iteratee) ethdb.Iterator {
return ethrawdb.NewKeyLengthIterator(db.NewIterator(syncPerformedPrefix, nil), syncPerformedKeyLength)
}
// UnpackSyncPerformedKey returns the block number from keys the iterator returned
// from NewSyncPerformedIterator.
func UnpackSyncPerformedKey(key []byte) uint64 {
return binary.BigEndian.Uint64(key[len(syncPerformedPrefix):])
}
// GetLatestSyncPerformed returns the latest block number state synced performed to.
func GetLatestSyncPerformed(db ethdb.Iteratee) uint64 {
it := NewSyncPerformedIterator(db)
defer it.Release()
var latestSyncPerformed uint64
for it.Next() {
syncPerformed := UnpackSyncPerformedKey(it.Key())
if syncPerformed > latestSyncPerformed {
latestSyncPerformed = syncPerformed
}
}
return latestSyncPerformed
}
// clearPrefix removes all keys in db that begin with prefix and match an
// expected key length. `keyLen` must include the length of the prefix.
func clearPrefix(db ethdb.KeyValueStore, prefix []byte, keyLen int) error {
it := db.NewIterator(prefix, nil)
defer it.Release()
batch := db.NewBatch()
for it.Next() {
key := common.CopyBytes(it.Key())
if len(key) != keyLen {
continue
}
if err := batch.Delete(key); err != nil {
return err
}
if batch.ValueSize() > ethdb.IdealBatchSize {
if err := batch.Write(); err != nil {
return err
}
batch.Reset()
}
}
if err := it.Error(); err != nil {
return err
}
return batch.Write()
}
@@ -1,34 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customrawdb
import (
"testing"
"github.com/luxfi/geth/common"
ethrawdb "github.com/luxfi/geth/core/rawdb"
"github.com/stretchr/testify/require"
)
func TestClearPrefix(t *testing.T) {
require := require.New(t)
db := ethrawdb.NewMemoryDatabase()
// add a key that should be cleared
require.NoError(WriteSyncSegment(db, common.Hash{1}, common.Hash{}))
// add a key that should not be cleared
key := append(syncSegmentsPrefix, []byte("foo")...)
require.NoError(db.Put(key, []byte("bar")))
require.NoError(ClearAllSyncSegments(db))
count := 0
it := db.NewIterator(syncSegmentsPrefix, nil)
defer it.Release()
for it.Next() {
count++
}
require.NoError(it.Error())
require.Equal(1, count)
}
-35
View File
@@ -1,35 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customrawdb
import (
"fmt"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/ethdb"
)
// InspectDatabase traverses the entire database and checks the size
// of all different categories of data.
func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
// For now, just delegate to the standard rawdb.InspectDatabase
// TODO: Add custom statistics tracking for evm-specific data
return rawdb.InspectDatabase(db, keyPrefix, keyStart)
}
// ParseStateSchemeExt parses the state scheme from the provided string.
func ParseStateSchemeExt(provided string, disk ethdb.Database) (string, error) {
// Check for custom scheme
if provided == FirewoodScheme {
if diskScheme := rawdb.ReadStateScheme(disk); diskScheme != "" {
// Valid scheme on disk mismatched
return "", fmt.Errorf("State scheme %s already set on disk, can't use Firewood", diskScheme)
}
// If no conflicting scheme is found, is valid.
return FirewoodScheme, nil
}
// Check for valid eth scheme
return rawdb.ParseStateScheme(provided, disk)
}
-140
View File
@@ -1,140 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customrawdb
import (
"fmt"
"github.com/luxfi/geth/common"
ethrawdb "github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/ethdb"
)
// Disabled test due to output format changes
func DisabledExampleInspectDatabase() {
db := &stubDatabase{
iterator: &stubIterator{},
}
// Extra metadata keys: (17 + 32) + (12 + 32) = 93 bytes
WriteSnapshotBlockHash(db, common.Hash{})
ethrawdb.WriteSnapshotRoot(db, common.Hash{})
// Trie segments: (77 + 2) + 1 = 80 bytes
_ = WriteSyncSegment(db, common.Hash{}, common.Hash{})
// Storage tries to fetch: 76 + 1 = 77 bytes
_ = WriteSyncStorageTrie(db, common.Hash{}, common.Hash{})
// Code to fetch: 34 + 0 = 34 bytes
AddCodeToFetch(db, common.Hash{})
// Block numbers synced to: 22 + 1 = 23 bytes
_ = WriteSyncPerformed(db, 0)
keyPrefix := []byte(nil)
keyStart := []byte(nil)
err := InspectDatabase(db, keyPrefix, keyStart)
if err != nil {
fmt.Println(err)
}
// Output:
// ┌──────────────────────────────┬─────────────────────────────┬──────────┬───────┐
// │ DATABASE │ CATEGORY │ SIZE │ ITEMS │
// ├──────────────────────────────┼─────────────────────────────┼──────────┼───────┤
// │ Key-Value store │ Headers │ 0.00 B │ 0 │
// │ Key-Value store │ Bodies │ 0.00 B │ 0 │
// │ Key-Value store │ Receipt lists │ 0.00 B │ 0 │
// | Key-Value store | Block number->hash | 0.00 B | 0 |
// | Key-Value store | Block hash->number | 0.00 B | 0 |
// | Key-Value store | Transaction index | 0.00 B | 0 |
// | Key-Value store | Bloombit index | 0.00 B | 0 |
// | Key-Value store | Contract codes | 0.00 B | 0 |
// | Key-Value store | Hash trie nodes | 0.00 B | 0 |
// | Key-Value store | Path trie state lookups | 0.00 B | 0 |
// | Key-Value store | Path trie account nodes | 0.00 B | 0 |
// | Key-Value store | Path trie storage nodes | 0.00 B | 0 |
// | Key-Value store | Trie preimages | 0.00 B | 0 |
// | Key-Value store | Account snapshot | 0.00 B | 0 |
// | Key-Value store | Storage snapshot | 0.00 B | 0 |
// | Key-Value store | Clique snapshots | 0.00 B | 0 |
// | Key-Value store | Singleton metadata | 93.00 B | 2 |
// | Light client | CHT trie nodes | 0.00 B | 0 |
// | Light client | Bloom trie nodes | 0.00 B | 0 |
// | State sync | Trie segments | 78.00 B | 1 |
// | State sync | Storage tries to fetch | 77.00 B | 1 |
// | State sync | Code to fetch | 34.00 B | 1 |
// | State sync | Block numbers synced to | 23.00 B | 1 |
// +-----------------+-------------------------+----------+-------+
// | TOTAL | 305.00 B | |
// +-----------------+-------------------------+----------+-------+
}
type stubDatabase struct {
ethdb.Database
iterator *stubIterator
}
func (s *stubDatabase) NewIterator(keyPrefix, keyStart []byte) ethdb.Iterator {
return s.iterator
}
// AncientSize is used in [InspectDatabase] to determine the ancient sizes.
func (s *stubDatabase) AncientSize(kind string) (uint64, error) {
return 0, nil
}
func (s *stubDatabase) Ancients() (uint64, error) {
return 0, nil
}
func (s *stubDatabase) Tail() (uint64, error) {
return 0, nil
}
func (s *stubDatabase) AncientDatadir() (string, error) {
return "", nil
}
func (s *stubDatabase) Put(key, value []byte) error {
s.iterator.kvs = append(s.iterator.kvs, keyValue{key: key, value: value})
return nil
}
func (s *stubDatabase) Get(key []byte) ([]byte, error) {
return nil, nil
}
func (s *stubDatabase) ReadAncients(fn func(ethdb.AncientReaderOp) error) error {
return nil
}
type stubIterator struct {
ethdb.Iterator
i int // see [stubIterator.pos]
kvs []keyValue
}
type keyValue struct {
key []byte
value []byte
}
// pos returns the true iterator position, which is otherwise off by one because
// Next() is called _before_ usage.
func (s *stubIterator) pos() int {
return s.i - 1
}
func (s *stubIterator) Next() bool {
s.i++
return s.pos() < len(s.kvs)
}
func (s *stubIterator) Release() {}
func (s *stubIterator) Key() []byte {
return s.kvs[s.pos()].key
}
func (s *stubIterator) Value() []byte {
return s.kvs[s.pos()].value
}
-63
View File
@@ -1,63 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customrawdb
import (
"github.com/luxfi/geth/common"
"github.com/luxfi/node/utils/wrappers"
)
var (
// snapshotBlockHashKey tracks the block hash of the last snapshot.
snapshotBlockHashKey = []byte("SnapshotBlockHash")
// offlinePruningKey tracks runs of offline pruning
offlinePruningKey = []byte("OfflinePruning")
// populateMissingTriesKey tracks runs of trie backfills
populateMissingTriesKey = []byte("PopulateMissingTries")
// pruningDisabledKey tracks whether the node has ever run in archival mode
// to ensure that a user does not accidentally corrupt an archival node.
pruningDisabledKey = []byte("PruningDisabled")
// acceptorTipKey tracks the tip of the last accepted block that has been fully processed.
acceptorTipKey = []byte("AcceptorTipKey")
// upgradeConfigPrefix prefixes upgrade bytes passed to the chain
upgradeConfigPrefix = []byte("upgrade-config-")
)
// State sync progress keys and prefixes
var (
// syncRootKey indicates the root of the main account trie currently being synced
syncRootKey = []byte("sync_root")
// syncStorageTriesPrefix is the prefix for storage tries that need to be fetched.
// syncStorageTriesPrefix + trie root + account hash: indicates a storage trie must be fetched for the account
syncStorageTriesPrefix = []byte("sync_storage")
// syncSegmentsPrefix is the prefix for segments.
// syncSegmentsPrefix + trie root + 32-byte start key: indicates the trie at root has a segment starting at the specified key
syncSegmentsPrefix = []byte("sync_segments")
// CodeToFetchPrefix is the prefix for code hashes that need to be fetched.
// CodeToFetchPrefix + code hash -> empty value tracks the outstanding code hashes we need to fetch.
CodeToFetchPrefix = []byte("CP")
)
// State sync progress key lengths
var (
syncStorageTriesKeyLength = len(syncStorageTriesPrefix) + 2*common.HashLength
syncSegmentsKeyLength = len(syncSegmentsPrefix) + 2*common.HashLength
codeToFetchKeyLength = len(CodeToFetchPrefix) + common.HashLength
)
// State sync metadata
var (
syncPerformedPrefix = []byte("sync_performed")
// syncPerformedKeyLength is the length of the key for the sync performed metadata key,
// and is equal to [syncPerformedPrefix] + block number as uint64.
syncPerformedKeyLength = len(syncPerformedPrefix) + wrappers.LongLen
)
// var FirewoodScheme = "firewood"
var FirewoodScheme = "" // Disabled - use HashScheme instead
// upgradeConfigKey = upgradeConfigPrefix + hash
func upgradeConfigKey(hash common.Hash) []byte {
return append(upgradeConfigPrefix, hash.Bytes()...)
}
-18
View File
@@ -1,18 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customtypes
import (
"math/big"
ethtypes "github.com/luxfi/geth/core/types"
)
func BlockGasCost(b *ethtypes.Block) *big.Int {
cost := GetHeaderExtra(b.Header()).BlockGasCost
if cost == nil {
return nil
}
return new(big.Int).Set(cost)
}
-113
View File
@@ -1,113 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customtypes
import (
"math/big"
"reflect"
"testing"
"unsafe"
"github.com/luxfi/geth/common"
"github.com/stretchr/testify/assert"
// package so assume the presence of identifiers. A dot-import reduces PR
// noise during the refactoring.
. "github.com/luxfi/geth/core/types"
)
func TestCopyHeader(t *testing.T) {
// Test with current Lux header structure
t.Parallel()
t.Run("empty_header", func(t *testing.T) {
t.Parallel()
empty := &Header{}
headerExtra := &HeaderExtra{}
extras.Header.Set(empty, headerExtra)
cpy := CopyHeader(empty)
want := &Header{
Difficulty: new(big.Int),
Number: new(big.Int),
}
headerExtra = &HeaderExtra{}
extras.Header.Set(want, headerExtra)
assert.Equal(t, want, cpy)
})
t.Run("filled_header", func(t *testing.T) {
t.Parallel()
header, _ := headerWithNonZeroFields() // the header carries the [HeaderExtra] so we can ignore it
gotHeader := CopyHeader(header)
gotExtra := GetHeaderExtra(gotHeader)
wantHeader, wantExtra := headerWithNonZeroFields()
assert.Equal(t, wantHeader, gotHeader)
assert.Equal(t, wantExtra, gotExtra)
exportedFieldsPointToDifferentMemory(t, header, gotHeader)
exportedFieldsPointToDifferentMemory(t, GetHeaderExtra(header), gotExtra)
})
}
func exportedFieldsPointToDifferentMemory[T interface {
Header | HeaderExtra
}](t *testing.T, original, cpy *T) {
t.Helper()
v := reflect.ValueOf(*original)
typ := v.Type()
cp := reflect.ValueOf(*cpy)
for i := range v.NumField() {
field := typ.Field(i)
if !field.IsExported() {
continue
}
switch field.Type.Kind() {
case reflect.Array, reflect.Uint64:
// Not pointers, but using explicit Kinds for safety
continue
}
t.Run(field.Name, func(t *testing.T) {
fieldCp := cp.Field(i).Interface()
switch f := v.Field(i).Interface().(type) {
case *big.Int:
assertDifferentPointers(t, f, fieldCp)
case *common.Hash:
assertDifferentPointers(t, f, fieldCp)
case *uint64:
assertDifferentPointers(t, f, fieldCp)
case []uint8:
assertDifferentPointers(t, unsafe.SliceData(f), unsafe.SliceData(fieldCp.([]uint8)))
default:
t.Errorf("field %q type %T needs to be added to switch cases of exportedFieldsDeepCopied", field.Name, f)
}
})
}
}
// assertDifferentPointers asserts that `a` and `b` are both non-nil
// pointers pointing to different memory locations.
func assertDifferentPointers[T any](t *testing.T, a *T, b any) {
t.Helper()
switch {
case a == nil:
t.Errorf("a (%T) cannot be nil", a)
case b == nil:
t.Errorf("b (%T) cannot be nil", b)
case a == b:
t.Errorf("pointers to same memory")
}
// Note: no need to check `b` is of the same type as `a`, otherwise
// the memory address would be different as well.
}
-356
View File
@@ -1,356 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//
// This file is a derived work, based on the go-ethereum library whose original
// notices appear below.
//
// It is distributed under a license compatible with the licensing terms of the
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package customtypes_test
import (
"bytes"
"math/big"
"reflect"
"testing"
"github.com/luxfi/crypto"
"github.com/luxfi/evm/internal/blocktest"
"github.com/luxfi/evm/params"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/common/math"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/rlp"
// This test file has to be in package types_test to avoid a circular
// dependency when importing `params`. We dot-import the package to mimic
// regular same-package behaviour.
. "github.com/luxfi/evm/plugin/evm/customtypes"
)
func TestBlockEncoding(t *testing.T) {
blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
var block types.Block
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
t.Fatal("decode error: ", err)
}
check := func(f string, got, want interface{}) {
if !reflect.DeepEqual(got, want) {
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
}
}
check("ParentHash", block.ParentHash(), common.HexToHash("0x83cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55"))
check("UncleHash", block.UncleHash(), common.HexToHash("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"))
check("Coinbase", block.Coinbase(), common.HexToAddress("0x8888f1F195AFa192CfeE860698584c030f4c9dB1"))
check("Root", block.Root(), common.HexToHash("0xef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("TxHash", block.TxHash(), common.HexToHash("0x5fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67"))
check("ReceiptHash", block.ReceiptHash(), common.HexToHash("0xbc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52"))
check("Bloom", block.Bloom(), types.BytesToBloom(common.FromHex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")))
check("Difficulty", block.Difficulty(), big.NewInt(131072))
check("BlockNumber", block.NumberU64(), uint64(1))
check("GasLimit", block.GasLimit(), uint64(3141592))
check("GasUsed", block.GasUsed(), uint64(21000))
check("Time", block.Time(), uint64(1426516743))
check("Extra", block.Extra(), common.FromHex(""))
check("MixDigest", block.MixDigest(), common.HexToHash("0xbd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
check("Nonce", block.Nonce(), uint64(11617697748499542468))
check("BaseFee", block.BaseFee(), (*big.Int)(nil))
check("BlockGasCost", BlockGasCost(&block), (*big.Int)(nil))
check("Size", block.Size(), uint64(len(blockEnc)))
check("BlockHash", block.Hash(), common.HexToHash("0x0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e"))
txHash := common.HexToHash("0x77b19baa4de67e45a7b26e4a220bccdbb6731885aa9927064e239ca232023215")
check("len(Transactions)", len(block.Transactions()), 1)
check("Transactions[0].Hash", block.Transactions()[0].Hash(), txHash)
ourBlockEnc, err := rlp.EncodeToBytes(&block)
if err != nil {
t.Fatal("encode error: ", err)
}
if !bytes.Equal(ourBlockEnc, blockEnc) {
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
}
}
func TestEIP1559BlockEncoding(t *testing.T) {
blockEnc := common.FromHex("f9030bf901fea083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4843b9aca00f90106f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b8a302f8a0018080843b9aca008301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8c0")
var block types.Block
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
t.Fatal("decode error: ", err)
}
check := func(f string, got, want interface{}) {
if !reflect.DeepEqual(got, want) {
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
}
}
check("Difficulty", block.Difficulty(), big.NewInt(131072))
check("GasLimit", block.GasLimit(), uint64(3141592))
check("GasUsed", block.GasUsed(), uint64(21000))
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("Hash", block.Hash(), common.HexToHash("c7252048cd273fe0dac09630027d07f0e3da4ee0675ebbb26627cea92729c372"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), uint64(len(blockEnc)))
check("BaseFee", block.BaseFee(), new(big.Int).SetUint64(1000000000))
check("BlockGasCost", BlockGasCost(&block), (*big.Int)(nil))
tx1 := types.NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil)
tx1, _ = tx1.WithSignature(types.HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
addr := common.HexToAddress("0x0000000000000000000000000000000000000001")
accesses := types.AccessList{types.AccessTuple{
Address: addr,
StorageKeys: []common.Hash{
{0},
},
}}
to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
txdata := &types.DynamicFeeTx{
ChainID: big.NewInt(1),
Nonce: 0,
To: &to,
Gas: 123457,
GasFeeCap: new(big.Int).Set(block.BaseFee()),
GasTipCap: big.NewInt(0),
AccessList: accesses,
Data: []byte{},
}
tx2 := types.NewTx(txdata)
tx2, err := tx2.WithSignature(types.LatestSignerForChainID(big.NewInt(1)), common.Hex2Bytes("fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a800"))
if err != nil {
t.Fatal("invalid signature error: ", err)
}
check("len(Transactions)", len(block.Transactions()), 2)
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash())
check("Transactions[1].Type", block.Transactions()[1].Type(), tx2.Type())
ourBlockEnc, err := rlp.EncodeToBytes(&block)
if err != nil {
t.Fatal("encode error: ", err)
}
if !bytes.Equal(ourBlockEnc, blockEnc) {
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
}
}
func TestEIP2718BlockEncoding(t *testing.T) {
blockEnc := common.FromHex("f90319f90211a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a0e6e49996c7ec59f7a23d22b83239a60151512c65613bf84a0d7da336399ebc4aa0cafe75574d59780665a97fbfd11365c7545aa8f1abf4e5e12e8243334ef7286bb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000820200832fefd882a410845506eb0796636f6f6c65737420626c6f636b206f6e20636861696ea0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f90101f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b89e01f89b01800a8301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000001a03dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335a0476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef14c0")
var block types.Block
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
t.Fatal("decode error: ", err)
}
check := func(f string, got, want interface{}) {
if !reflect.DeepEqual(got, want) {
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
}
}
check("Difficulty", block.Difficulty(), big.NewInt(131072))
check("GasLimit", block.GasLimit(), uint64(3141592))
check("GasUsed", block.GasUsed(), uint64(42000))
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), uint64(len(blockEnc)))
check("BaseFee", block.BaseFee(), (*big.Int)(nil))
check("BlockGasCost", BlockGasCost(&block), (*big.Int)(nil))
// Create legacy tx.
to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
tx1 := types.NewTx(&types.LegacyTx{
Nonce: 0,
To: &to,
Value: big.NewInt(10),
Gas: 50000,
GasPrice: big.NewInt(10),
})
sig := common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")
tx1, _ = tx1.WithSignature(types.HomesteadSigner{}, sig)
// Create ACL tx.
addr := common.HexToAddress("0x0000000000000000000000000000000000000001")
tx2 := types.NewTx(&types.AccessListTx{
ChainID: big.NewInt(1),
Nonce: 0,
To: &to,
Gas: 123457,
GasPrice: big.NewInt(10),
AccessList: types.AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}},
})
sig2 := common.Hex2Bytes("3dbacc8d0259f2508625e97fdfc57cd85fdd16e5821bc2c10bdd1a52649e8335476e10695b183a87b0aa292a7f4b78ef0c3fbe62aa2c42c84e1d9c3da159ef1401")
tx2, _ = tx2.WithSignature(types.NewEIP2930Signer(big.NewInt(1)), sig2)
check("len(Transactions)", len(block.Transactions()), 2)
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash())
check("Transactions[1].Type()", block.Transactions()[1].Type(), uint8(types.AccessListTxType))
ourBlockEnc, err := rlp.EncodeToBytes(&block)
if err != nil {
t.Fatal("encode error: ", err)
}
if !bytes.Equal(ourBlockEnc, blockEnc) {
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
}
}
func TestUncleHash(t *testing.T) {
uncles := make([]*types.Header, 0)
h := types.CalcUncleHash(uncles)
exp := common.HexToHash("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")
if h != exp {
t.Fatalf("empty uncle hash is wrong, got %x != %x", h, exp)
}
}
var benchBuffer = bytes.NewBuffer(make([]byte, 0, 32000))
func BenchmarkEncodeBlock(b *testing.B) {
block := makeBenchBlock()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchBuffer.Reset()
if err := rlp.Encode(benchBuffer, block); err != nil {
b.Fatal(err)
}
}
}
func makeBenchBlock() *types.Block {
var (
key, _ = crypto.GenerateKey()
txs = make([]*types.Transaction, 70)
receipts = make([]*types.Receipt, len(txs))
signer = types.LatestSigner(params.TestChainConfig)
uncles = make([]*types.Header, 3)
)
header := &types.Header{
Difficulty: math.BigPow(11, 11),
Number: math.BigPow(2, 9),
GasLimit: 12345678,
GasUsed: 1476322,
Time: 9876543,
Extra: []byte("coolest block on chain"),
}
for i := range txs {
amount := math.BigPow(2, int64(i))
price := big.NewInt(300000)
data := make([]byte, 100)
tx := types.NewTransaction(uint64(i), common.Address{}, amount, 123457, price, data)
signedTx, err := types.SignTx(tx, signer, key)
if err != nil {
panic(err)
}
txs[i] = signedTx
receipts[i] = types.NewReceipt(make([]byte, 32), false, tx.Gas())
}
for i := range uncles {
uncles[i] = &types.Header{
Difficulty: math.BigPow(11, 11),
Number: math.BigPow(2, 9),
GasLimit: 12345678,
GasUsed: 1476322,
Time: 9876543,
Extra: []byte("benchmark uncle"),
}
}
body := &types.Body{
Transactions: txs,
Uncles: uncles,
}
return types.NewBlock(header, body, receipts, blocktest.NewHasher())
}
func TestSubnetEVMBlockEncoding(t *testing.T) {
blockEnc := common.FromHex("f9030ff90202a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4843b9aca00830186a0f90106f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b8a302f8a0018080843b9aca008301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8c0")
var block types.Block
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
t.Fatal("decode error: ", err)
}
check := func(f string, got, want interface{}) {
if !reflect.DeepEqual(got, want) {
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
}
}
check("Difficulty", block.Difficulty(), big.NewInt(131072))
check("GasLimit", block.GasLimit(), uint64(3141592))
check("GasUsed", block.GasUsed(), uint64(21000))
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("Hash", block.Hash(), common.HexToHash("0x06206d4ff804e93b36a8447a12a47653b07fd18115a05956c5ed8817f0b11eb9"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), uint64(len(blockEnc)))
check("BaseFee", block.BaseFee(), big.NewInt(1_000_000_000))
check("BlockGasCost", BlockGasCost(&block), big.NewInt(100_000))
tx1 := types.NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil)
tx1, _ = tx1.WithSignature(types.HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
addr := common.HexToAddress("0x0000000000000000000000000000000000000001")
accesses := types.AccessList{types.AccessTuple{
Address: addr,
StorageKeys: []common.Hash{
{0},
},
}}
to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
txdata := &types.DynamicFeeTx{
ChainID: big.NewInt(1),
Nonce: 0,
To: &to,
Gas: 123457,
GasFeeCap: new(big.Int).Set(block.BaseFee()),
GasTipCap: big.NewInt(0),
AccessList: accesses,
Data: []byte{},
}
tx2 := types.NewTx(txdata)
tx2, err := tx2.WithSignature(types.LatestSignerForChainID(big.NewInt(1)), common.Hex2Bytes("fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a800"))
if err != nil {
t.Fatal("invalid signature error: ", err)
}
check("len(Transactions)", len(block.Transactions()), 2)
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash())
check("Transactions[1].Type", block.Transactions()[1].Type(), tx2.Type())
ourBlockEnc, err := rlp.EncodeToBytes(&block)
if err != nil {
t.Fatal("encode error: ", err)
}
if !bytes.Equal(ourBlockEnc, blockEnc) {
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
}
}
@@ -1,169 +0,0 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package customtypes
import (
"encoding/json"
"errors"
"math/big"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/common/hexutil"
"github.com/luxfi/geth/core/types"
)
var _ = (*headerMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (h HeaderSerializable) MarshalJSON() ([]byte, error) {
type HeaderSerializable struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"`
Root common.Hash `json:"stateRoot" gencodec:"required"`
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
Number *hexutil.Big `json:"number" gencodec:"required"`
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Time hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash"`
Nonce types.BlockNonce `json:"nonce"`
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
BlockGasCost *hexutil.Big `json:"blockGasCost" rlp:"optional"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
Hash common.Hash `json:"hash"`
}
var enc HeaderSerializable
enc.ParentHash = h.ParentHash
enc.UncleHash = h.UncleHash
enc.Coinbase = h.Coinbase
enc.Root = h.Root
enc.TxHash = h.TxHash
enc.ReceiptHash = h.ReceiptHash
enc.Bloom = h.Bloom
enc.Difficulty = (*hexutil.Big)(h.Difficulty)
enc.Number = (*hexutil.Big)(h.Number)
enc.GasLimit = hexutil.Uint64(h.GasLimit)
enc.GasUsed = hexutil.Uint64(h.GasUsed)
enc.Time = hexutil.Uint64(h.Time)
enc.Extra = h.Extra
enc.MixDigest = h.MixDigest
enc.Nonce = h.Nonce
enc.BaseFee = (*hexutil.Big)(h.BaseFee)
enc.BlockGasCost = (*hexutil.Big)(h.BlockGasCost)
enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed)
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
enc.ParentBeaconRoot = h.ParentBeaconRoot
enc.Hash = h.Hash()
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (h *HeaderSerializable) UnmarshalJSON(input []byte) error {
type HeaderSerializable struct {
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase *common.Address `json:"miner" gencodec:"required"`
Root *common.Hash `json:"stateRoot" gencodec:"required"`
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom *types.Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
Number *hexutil.Big `json:"number" gencodec:"required"`
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest *common.Hash `json:"mixHash"`
Nonce *types.BlockNonce `json:"nonce"`
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
BlockGasCost *hexutil.Big `json:"blockGasCost" rlp:"optional"`
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
}
var dec HeaderSerializable
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.ParentHash == nil {
return errors.New("missing required field 'parentHash' for HeaderSerializable")
}
h.ParentHash = *dec.ParentHash
if dec.UncleHash == nil {
return errors.New("missing required field 'sha3Uncles' for HeaderSerializable")
}
h.UncleHash = *dec.UncleHash
if dec.Coinbase == nil {
return errors.New("missing required field 'miner' for HeaderSerializable")
}
h.Coinbase = *dec.Coinbase
if dec.Root == nil {
return errors.New("missing required field 'stateRoot' for HeaderSerializable")
}
h.Root = *dec.Root
if dec.TxHash == nil {
return errors.New("missing required field 'transactionsRoot' for HeaderSerializable")
}
h.TxHash = *dec.TxHash
if dec.ReceiptHash == nil {
return errors.New("missing required field 'receiptsRoot' for HeaderSerializable")
}
h.ReceiptHash = *dec.ReceiptHash
if dec.Bloom == nil {
return errors.New("missing required field 'logsBloom' for HeaderSerializable")
}
h.Bloom = *dec.Bloom
if dec.Difficulty == nil {
return errors.New("missing required field 'difficulty' for HeaderSerializable")
}
h.Difficulty = (*big.Int)(dec.Difficulty)
if dec.Number == nil {
return errors.New("missing required field 'number' for HeaderSerializable")
}
h.Number = (*big.Int)(dec.Number)
if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for HeaderSerializable")
}
h.GasLimit = uint64(*dec.GasLimit)
if dec.GasUsed == nil {
return errors.New("missing required field 'gasUsed' for HeaderSerializable")
}
h.GasUsed = uint64(*dec.GasUsed)
if dec.Time == nil {
return errors.New("missing required field 'timestamp' for HeaderSerializable")
}
h.Time = uint64(*dec.Time)
if dec.Extra == nil {
return errors.New("missing required field 'extraData' for HeaderSerializable")
}
h.Extra = *dec.Extra
if dec.MixDigest != nil {
h.MixDigest = *dec.MixDigest
}
if dec.Nonce != nil {
h.Nonce = *dec.Nonce
}
if dec.BaseFee != nil {
h.BaseFee = (*big.Int)(dec.BaseFee)
}
if dec.BlockGasCost != nil {
h.BlockGasCost = (*big.Int)(dec.BlockGasCost)
}
if dec.BlobGasUsed != nil {
h.BlobGasUsed = (*uint64)(dec.BlobGasUsed)
}
if dec.ExcessBlobGas != nil {
h.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
}
if dec.ParentBeaconRoot != nil {
h.ParentBeaconRoot = dec.ParentBeaconRoot
}
return nil
}
@@ -1,91 +0,0 @@
// Code generated by rlpgen. DO NOT EDIT.
package customtypes
import (
"io"
"github.com/luxfi/geth/rlp"
)
func (obj *HeaderSerializable) EncodeRLP(_w io.Writer) error {
w := rlp.NewEncoderBuffer(_w)
_tmp0 := w.List()
w.WriteBytes(obj.ParentHash[:])
w.WriteBytes(obj.UncleHash[:])
w.WriteBytes(obj.Coinbase[:])
w.WriteBytes(obj.Root[:])
w.WriteBytes(obj.TxHash[:])
w.WriteBytes(obj.ReceiptHash[:])
w.WriteBytes(obj.Bloom[:])
if obj.Difficulty == nil {
_, _ = w.Write(rlp.EmptyString)
} else {
if obj.Difficulty.Sign() == -1 {
return rlp.ErrNegativeBigInt
}
w.WriteBigInt(obj.Difficulty)
}
if obj.Number == nil {
_, _ = w.Write(rlp.EmptyString)
} else {
if obj.Number.Sign() == -1 {
return rlp.ErrNegativeBigInt
}
w.WriteBigInt(obj.Number)
}
w.WriteUint64(obj.GasLimit)
w.WriteUint64(obj.GasUsed)
w.WriteUint64(obj.Time)
w.WriteBytes(obj.Extra)
w.WriteBytes(obj.MixDigest[:])
w.WriteBytes(obj.Nonce[:])
_tmp1 := obj.BaseFee != nil
_tmp2 := obj.BlockGasCost != nil
_tmp3 := obj.BlobGasUsed != nil
_tmp4 := obj.ExcessBlobGas != nil
_tmp5 := obj.ParentBeaconRoot != nil
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 {
if obj.BaseFee == nil {
_, _ = w.Write(rlp.EmptyString)
} else {
if obj.BaseFee.Sign() == -1 {
return rlp.ErrNegativeBigInt
}
w.WriteBigInt(obj.BaseFee)
}
}
if _tmp2 || _tmp3 || _tmp4 || _tmp5 {
if obj.BlockGasCost == nil {
_, _ = w.Write(rlp.EmptyString)
} else {
if obj.BlockGasCost.Sign() == -1 {
return rlp.ErrNegativeBigInt
}
w.WriteBigInt(obj.BlockGasCost)
}
}
if _tmp3 || _tmp4 || _tmp5 {
if obj.BlobGasUsed == nil {
_, _ = w.Write([]byte{0x80})
} else {
w.WriteUint64((*obj.BlobGasUsed))
}
}
if _tmp4 || _tmp5 {
if obj.ExcessBlobGas == nil {
_, _ = w.Write([]byte{0x80})
} else {
w.WriteUint64((*obj.ExcessBlobGas))
}
}
if _tmp5 {
if obj.ParentBeaconRoot == nil {
_, _ = w.Write([]byte{0x80})
} else {
w.WriteBytes(obj.ParentBeaconRoot[:])
}
}
w.ListEnd(_tmp0)
return w.Flush()
}
-244
View File
@@ -1,244 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//
// This file is a derived work, based on the go-ethereum library whose original
// notices appear below.
//
// It is distributed under a license compatible with the licensing terms of the
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package customtypes_test
import (
"bytes"
"fmt"
"io"
"math/big"
mrand "math/rand"
"testing"
"github.com/luxfi/crypto"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/common/hexutil"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/rlp"
"github.com/luxfi/geth/trie"
"github.com/luxfi/geth/triedb"
)
func TestDeriveSha(t *testing.T) {
txs, err := genTxs(0)
if err != nil {
t.Fatal(err)
}
for len(txs) < 1000 {
exp := types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
got := types.DeriveSha(txs, trie.NewStackTrie(nil))
if !bytes.Equal(got[:], exp[:]) {
t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp)
}
newTxs, err := genTxs(uint64(len(txs) + 1))
if err != nil {
t.Fatal(err)
}
txs = append(txs, newTxs...)
}
}
// TestEIP2718DeriveSha tests that the input to the DeriveSha function is correct.
func TestEIP2718DeriveSha(t *testing.T) {
for _, tc := range []struct {
rlpData string
exp string
}{
{
rlpData: "0xb8a701f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9",
exp: "01 01f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9\n80 01f8a486796f6c6f763380843b9aca008262d4948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0775101f92dcca278a56bfe4d613428624a1ebfc3cd9e0bcc1de80c41455b9021a06c9deac205afe7b124907d4ba54a9f46161498bd3990b90d175aac12c9a40ee9\n",
},
} {
d := &hashToHumanReadable{}
var t1, t2 types.Transaction
rlp.DecodeBytes(common.FromHex(tc.rlpData), &t1)
rlp.DecodeBytes(common.FromHex(tc.rlpData), &t2)
txs := types.Transactions{&t1, &t2}
types.DeriveSha(txs, d)
if tc.exp != string(d.data) {
t.Fatalf("Want\n%v\nhave:\n%v", tc.exp, string(d.data))
}
}
}
func BenchmarkDeriveSha200(b *testing.B) {
txs, err := genTxs(200)
if err != nil {
b.Fatal(err)
}
var exp common.Hash
var got common.Hash
b.Run("std_trie", func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
exp = types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
}
})
b.Run("stack_trie", func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
got = types.DeriveSha(txs, trie.NewStackTrie(nil))
}
})
if got != exp {
b.Errorf("got %x exp %x", got, exp)
}
}
func TestFuzzDeriveSha(t *testing.T) {
// increase this for longer runs -- it's set to quite low for travis
rndSeed := mrand.Int()
for i := 0; i < 10; i++ {
seed := rndSeed + i
exp := types.DeriveSha(newDummy(i), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil))
if !bytes.Equal(got[:], exp[:]) {
printList(newDummy(seed))
t.Fatalf("seed %d: got %x exp %x", seed, got, exp)
}
}
}
// TestDerivableList contains testcases found via fuzzing
func TestDerivableList(t *testing.T) {
type tcase []string
tcs := []tcase{
{
"0xc041",
},
{
"0xf04cf757812428b0763112efb33b6f4fad7deb445e",
"0xf04cf757812428b0763112efb33b6f4fad7deb445e",
},
{
"0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d",
"0x6cd850eca0a7ac46bb1748d7b9cb88aa3bd21c57d852c28198ad8fa422c4595032e88a4494b4778b36b944fe47a52b8c5cd312910139dfcb4147ab8e972cc456bcb063f25dd78f54c4d34679e03142c42c662af52947d45bdb6e555751334ace76a5080ab5a0256a1d259855dfc5c0b8023b25befbb13fd3684f9f755cbd3d63544c78ee2001452dd54633a7593ade0b183891a0a4e9c7844e1254005fbe592b1b89149a502c24b6e1dca44c158aebedf01beae9c30cabe16a",
"0x14abd5c47c0be87b0454596baad2",
"0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d",
},
}
for i, tc := range tcs[1:] {
exp := types.DeriveSha(flatList(tc), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil))
if !bytes.Equal(got[:], exp[:]) {
t.Fatalf("case %d: got %x exp %x", i, got, exp)
}
}
}
func genTxs(num uint64) (types.Transactions, error) {
key, err := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
if err != nil {
return nil, err
}
cryptoAddr := crypto.PubkeyToAddress(key.PublicKey)
var addr common.Address
copy(addr[:], cryptoAddr[:])
newTx := func(i uint64) (*types.Transaction, error) {
signer := types.NewEIP155Signer(big.NewInt(18))
utx := types.NewTransaction(i, addr, new(big.Int), 0, new(big.Int).SetUint64(10000000), nil)
tx, err := types.SignTx(utx, signer, key)
return tx, err
}
var txs types.Transactions
for i := uint64(0); i < num; i++ {
tx, err := newTx(i)
if err != nil {
return nil, err
}
txs = append(txs, tx)
}
return txs, nil
}
type dummyDerivableList struct {
len int
seed int
}
func newDummy(seed int) *dummyDerivableList {
d := &dummyDerivableList{}
src := mrand.NewSource(int64(seed))
// don't use lists longer than 4K items
d.len = int(src.Int63() & 0x0FFF)
d.seed = seed
return d
}
func (d *dummyDerivableList) Len() int {
return d.len
}
func (d *dummyDerivableList) EncodeIndex(i int, w *bytes.Buffer) {
src := mrand.NewSource(int64(d.seed + i))
// max item size 256, at least 1 byte per item
size := 1 + src.Int63()&0x00FF
io.CopyN(w, mrand.New(src), size)
}
func printList(l types.DerivableList) {
fmt.Printf("list length: %d\n", l.Len())
fmt.Printf("{\n")
for i := 0; i < l.Len(); i++ {
var buf bytes.Buffer
l.EncodeIndex(i, &buf)
fmt.Printf("\"%#x\",\n", buf.Bytes())
}
fmt.Printf("},\n")
}
type flatList []string
func (f flatList) Len() int {
return len(f)
}
func (f flatList) EncodeIndex(i int, w *bytes.Buffer) {
_, _ = w.Write(hexutil.MustDecode(f[i]))
}
type hashToHumanReadable struct {
data []byte
}
func (d *hashToHumanReadable) Reset() {
d.data = make([]byte, 0)
}
func (d *hashToHumanReadable) Update(i []byte, i2 []byte) error {
l := fmt.Sprintf("%x %x\n", i, i2)
d.data = append(d.data, []byte(l)...)
return nil
}
func (d *hashToHumanReadable) Hash() common.Hash {
return common.Hash{}
}
-244
View File
@@ -1,244 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customtypes
import (
"io"
"math/big"
"sync"
"github.com/luxfi/crypto"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/common/hexutil"
ethtypes "github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/rlp"
)
// Map-based storage for header extras until extras package is available
var (
headerExtras = make(map[*ethtypes.Header]*HeaderExtra)
headerExtrasMutex sync.RWMutex
)
// GetHeaderExtra returns the [HeaderExtra] from the given [Header].
func GetHeaderExtra(h *ethtypes.Header) *HeaderExtra {
if h == nil {
return nil
}
headerExtrasMutex.RLock()
defer headerExtrasMutex.RUnlock()
extra := headerExtras[h]
if extra == nil {
// Return a default HeaderExtra with BlockGasCost set to nil
// This matches the expected behavior for blocks without gas cost set
return &HeaderExtra{BlockGasCost: nil}
}
return extra
}
// SetHeaderExtra sets the given [HeaderExtra] on the [Header].
func SetHeaderExtra(h *ethtypes.Header, extra *HeaderExtra) {
if h == nil {
return
}
headerExtrasMutex.Lock()
defer headerExtrasMutex.Unlock()
headerExtras[h] = extra
}
// WithHeaderExtra sets the given [HeaderExtra] on the [Header]
// and returns the [Header] for chaining.
func WithHeaderExtra(h *ethtypes.Header, extra *HeaderExtra) *ethtypes.Header {
SetHeaderExtra(h, extra)
return h
}
// HeaderExtra is a struct that contains extra fields used by Subnet-EVM
// in the block header.
// This type uses [HeaderSerializable] to encode and decode the extra fields
// along with the upstream type for compatibility with existing network blocks.
type HeaderExtra struct {
BlockGasCost *big.Int
}
// EncodeRLP RLP encodes the given [ethtypes.Header] and [HeaderExtra] together
// to the `writer`. It does merge both structs into a single [HeaderSerializable].
func (h *HeaderExtra) EncodeRLP(eth *ethtypes.Header, writer io.Writer) error {
temp := new(HeaderSerializable)
temp.updateFromEth(eth)
temp.updateFromExtras(h)
return rlp.Encode(writer, temp)
}
// DecodeRLP RLP decodes from the [*rlp.Stream] and writes the output to both the
// [ethtypes.Header] passed as argument and to the receiver [HeaderExtra].
func (h *HeaderExtra) DecodeRLP(eth *ethtypes.Header, stream *rlp.Stream) error {
temp := new(HeaderSerializable)
if err := stream.Decode(temp); err != nil {
return err
}
temp.updateToEth(eth)
temp.updateToExtras(h)
return nil
}
// EncodeJSON JSON encodes the given [ethtypes.Header] and [HeaderExtra] together
// to the `writer`. It does merge both structs into a single [HeaderSerializable].
func (h *HeaderExtra) EncodeJSON(eth *ethtypes.Header) ([]byte, error) {
temp := new(HeaderSerializable)
temp.updateFromEth(eth)
temp.updateFromExtras(h)
return temp.MarshalJSON()
}
// DecodeJSON JSON decodes from the `input` bytes and writes the output to both the
// [ethtypes.Header] passed as argument and to the receiver [HeaderExtra].
func (h *HeaderExtra) DecodeJSON(eth *ethtypes.Header, input []byte) error {
temp := new(HeaderSerializable)
if err := temp.UnmarshalJSON(input); err != nil {
return err
}
temp.updateToEth(eth)
temp.updateToExtras(h)
return nil
}
func (h *HeaderExtra) PostCopy(dst *ethtypes.Header) {
cp := &HeaderExtra{}
if h.BlockGasCost != nil {
cp.BlockGasCost = new(big.Int).Set(h.BlockGasCost)
}
SetHeaderExtra(dst, cp)
}
func (h *HeaderSerializable) updateFromEth(eth *ethtypes.Header) {
h.ParentHash = eth.ParentHash
h.UncleHash = eth.UncleHash
h.Coinbase = eth.Coinbase
h.Root = eth.Root
h.TxHash = eth.TxHash
h.ReceiptHash = eth.ReceiptHash
h.Bloom = eth.Bloom
h.Difficulty = eth.Difficulty
h.Number = eth.Number
h.GasLimit = eth.GasLimit
h.GasUsed = eth.GasUsed
h.Time = eth.Time
h.Extra = eth.Extra
h.MixDigest = eth.MixDigest
h.Nonce = eth.Nonce
h.BaseFee = eth.BaseFee
h.BlobGasUsed = eth.BlobGasUsed
h.ExcessBlobGas = eth.ExcessBlobGas
h.ParentBeaconRoot = eth.ParentBeaconRoot
}
func (h *HeaderSerializable) updateToEth(eth *ethtypes.Header) {
eth.ParentHash = h.ParentHash
eth.UncleHash = h.UncleHash
eth.Coinbase = h.Coinbase
eth.Root = h.Root
eth.TxHash = h.TxHash
eth.ReceiptHash = h.ReceiptHash
eth.Bloom = h.Bloom
eth.Difficulty = h.Difficulty
eth.Number = h.Number
eth.GasLimit = h.GasLimit
eth.GasUsed = h.GasUsed
eth.Time = h.Time
eth.Extra = h.Extra
eth.MixDigest = h.MixDigest
eth.Nonce = h.Nonce
eth.BaseFee = h.BaseFee
eth.BlobGasUsed = h.BlobGasUsed
eth.ExcessBlobGas = h.ExcessBlobGas
eth.ParentBeaconRoot = h.ParentBeaconRoot
}
func (h *HeaderSerializable) updateFromExtras(extras *HeaderExtra) {
h.BlockGasCost = extras.BlockGasCost
}
func (h *HeaderSerializable) updateToExtras(extras *HeaderExtra) {
extras.BlockGasCost = h.BlockGasCost
}
//go:generate go run github.com/fjl/gencodec -type HeaderSerializable -field-override headerMarshaling -out gen_header_serializable_json.go
//go:generate go run github.com/luxfi/geth/rlp/rlpgen@739ba847f6f407f63fd6a24175b24e56fea583a1 -type HeaderSerializable -out gen_header_serializable_rlp.go
// HeaderSerializable defines the header of a block in the Ethereum blockchain,
// as it is to be serialized into RLP and JSON. Note it must be exported so that
// rlpgen can generate the serialization code from it.
type HeaderSerializable struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"`
Root common.Hash `json:"stateRoot" gencodec:"required"`
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom ethtypes.Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
Number *big.Int `json:"number" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
Time uint64 `json:"timestamp" gencodec:"required"`
Extra []byte `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash"`
Nonce ethtypes.BlockNonce `json:"nonce"`
// BaseFee was added by EIP-1559 and is ignored in legacy headers.
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
// BlockGasCost was added by SubnetEVM and is ignored in legacy
// headers.
BlockGasCost *big.Int `json:"blockGasCost" rlp:"optional"`
// BlobGasUsed was added by EIP-4844 and is ignored in legacy headers.
BlobGasUsed *uint64 `json:"blobGasUsed" rlp:"optional"`
// ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers.
ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"`
// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
}
// field type overrides for gencodec
type headerMarshaling struct {
Difficulty *hexutil.Big
Number *hexutil.Big
GasLimit hexutil.Uint64
GasUsed hexutil.Uint64
Time hexutil.Uint64
Extra hexutil.Bytes
BaseFee *hexutil.Big
BlockGasCost *hexutil.Big
Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
BlobGasUsed *hexutil.Uint64
ExcessBlobGas *hexutil.Uint64
}
// Hash returns the block hash of the header, which is simply the keccak256 hash of its
// RLP encoding.
// This function MUST be exported and is used in [HeaderSerializable.EncodeJSON] which is
// generated to the file gen_header_json.go.
func (h *HeaderSerializable) Hash() common.Hash {
return rlpHash(h)
}
// rlpHash encodes x and hashes the encoded bytes.
func rlpHash(x interface{}) (h common.Hash) {
hw := crypto.NewKeccakState()
rlp.Encode(hw, x)
hw.Sum(h[:0])
return h
}
-189
View File
@@ -1,189 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customtypes
import (
"encoding/json"
"math/big"
"reflect"
"slices"
"testing"
"unsafe"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/rlp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
// package so assume the presence of identifiers. A dot-import reduces PR
// noise during the refactoring.
. "github.com/luxfi/geth/core/types"
)
func TestHeaderRLP(t *testing.T) {
// Test header RLP encoding with current Lux header structure
t.Parallel()
got := testHeaderEncodeDecode(t, rlp.EncodeToBytes, rlp.DecodeBytes)
// Test current header structure - don't check against fixed values
// since the header structure has evolved
if len(got) == 0 {
t.Fatal("Header RLP encoding returned empty bytes")
}
// Test that we can round-trip encode/decode
header, _ := headerWithNonZeroFields()
gotHashHex := header.Hash().Hex()
// Just verify the hash is valid (not empty)
if gotHashHex == "0x0000000000000000000000000000000000000000000000000000000000000000" {
t.Error("Header hash should not be empty")
}
t.Logf("Header RLP length: %d, Hash: %s", len(got), gotHashHex)
}
func TestHeaderJSON(t *testing.T) {
// Test with current Lux header structure
t.Parallel()
// Note we ignore the returned encoded bytes because we don't
// need to compare them to a JSON gold standard.
_ = testHeaderEncodeDecode(t, json.Marshal, json.Unmarshal)
}
func testHeaderEncodeDecode(
t *testing.T,
encode func(any) ([]byte, error),
decode func([]byte, any) error,
) (encoded []byte) {
t.Helper()
input, _ := headerWithNonZeroFields() // the Header carries the HeaderExtra so we can ignore it
encoded, err := encode(input)
require.NoError(t, err, "encode")
gotHeader := new(Header)
err = decode(encoded, gotHeader)
require.NoError(t, err, "decode")
gotExtra := GetHeaderExtra(gotHeader)
wantHeader, wantExtra := headerWithNonZeroFields()
wantHeader.WithdrawalsHash = nil
assert.Equal(t, wantHeader, gotHeader)
assert.Equal(t, wantExtra, gotExtra)
return encoded
}
func TestHeaderWithNonZeroFields(t *testing.T) {
// Test with current Lux header structure
t.Parallel()
header, extra := headerWithNonZeroFields()
t.Run("Header", func(t *testing.T) { allFieldsSet(t, header, "extra") })
t.Run("HeaderExtra", func(t *testing.T) { allFieldsSet(t, extra) })
}
// headerWithNonZeroFields returns a [Header] and a [HeaderExtra],
// each with all fields set to non-zero values.
// The [HeaderExtra] extra payload is set in the [Header] via [WithHeaderExtra].
//
// NOTE: They can be used to demonstrate that RLP and JSON round-trip encoding
// can recover all fields, but not that the encoded format is correct. This is
// very important as the RLP encoding of a [Header] defines its hash.
func headerWithNonZeroFields() (*Header, *HeaderExtra) {
header := &Header{
ParentHash: common.Hash{1},
UncleHash: common.Hash{2},
Coinbase: common.Address{3},
Root: common.Hash{4},
TxHash: common.Hash{5},
ReceiptHash: common.Hash{6},
Bloom: Bloom{7},
Difficulty: big.NewInt(8),
Number: big.NewInt(9),
GasLimit: 10,
GasUsed: 11,
Time: 12,
Extra: []byte{13},
MixDigest: common.Hash{14},
Nonce: BlockNonce{15},
BaseFee: big.NewInt(16),
WithdrawalsHash: &common.Hash{17},
BlobGasUsed: ptrTo(uint64(18)),
ExcessBlobGas: ptrTo(uint64(19)),
ParentBeaconRoot: &common.Hash{20},
}
extra := &HeaderExtra{
BlockGasCost: big.NewInt(21),
}
return WithHeaderExtra(header, extra), extra
}
func allFieldsSet[T interface {
Header | HeaderExtra
}](t *testing.T, x *T, ignoredFields ...string) {
// We don't test for nil pointers because we're only confirming that
// test-input data is well-formed. A panic due to a dereference will be
// reported anyway.
v := reflect.ValueOf(x).Elem()
for i := range v.Type().NumField() {
field := v.Type().Field(i)
if slices.Contains(ignoredFields, field.Name) {
continue
}
t.Run(field.Name, func(t *testing.T) {
fieldValue := v.Field(i)
if !field.IsExported() {
// Note: we need to check unexported fields especially for [Block].
if fieldValue.Kind() == reflect.Ptr {
require.Falsef(t, fieldValue.IsNil(), "field %q is nil", field.Name)
}
fieldValue = reflect.NewAt(fieldValue.Type(), unsafe.Pointer(fieldValue.UnsafeAddr())).Elem() //nolint:gosec
}
switch f := fieldValue.Interface().(type) {
case common.Hash:
assertNonZero(t, f)
case common.Address:
assertNonZero(t, f)
case BlockNonce:
assertNonZero(t, f)
case Bloom:
assertNonZero(t, f)
case uint64:
assertNonZero(t, f)
case *big.Int:
assertNonZero(t, f)
case *common.Hash:
assertNonZero(t, f)
case *uint64:
assertNonZero(t, f)
case []uint8:
assert.NotEmpty(t, f)
default:
t.Errorf("Field %q has unsupported type %T", field.Name, f)
}
})
}
}
func assertNonZero[T interface {
common.Hash | common.Address | BlockNonce | uint64 | Bloom |
*big.Int | *common.Hash | *uint64
}](t *testing.T, v T) {
t.Helper()
var zero T
if v == zero {
t.Errorf("must not be zero value for %T", v)
}
}
// Note [TestCopyHeader] tests the [HeaderExtra.PostCopy] method.
func ptrTo[T any](x T) *T { return &x }
-39
View File
@@ -1,39 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package customtypes
import (
"io"
"github.com/luxfi/geth/rlp"
)
// TODO: RegisterExtras not available in current geth version
// var extras = ethtypes.RegisterExtras[
// HeaderExtra, *HeaderExtra,
// ethtypes.NOOPBlockBodyHooks, *ethtypes.NOOPBlockBodyHooks,
// noopStateAccountExtras,
// ]()
// Mock extras struct for compatibility
type mockExtras struct {
Header mockHeaderExtras
}
type mockHeaderExtras struct{}
func (m mockHeaderExtras) Set(header interface{}, extra *HeaderExtra) {}
func (m mockHeaderExtras) Get(header interface{}) *HeaderExtra { return nil }
var extras = mockExtras{
Header: mockHeaderExtras{},
}
type noopStateAccountExtras struct{}
// EncodeRLP implements the [rlp.Encoder] interface.
func (noopStateAccountExtras) EncodeRLP(w io.Writer) error { return nil }
// DecodeRLP implements the [rlp.Decoder] interface.
func (*noopStateAccountExtras) DecodeRLP(s *rlp.Stream) error { return nil }
-162
View File
@@ -1,162 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//
// This file is a derived work, based on the go-ethereum library whose original
// notices appear below.
//
// It is distributed under a license compatible with the licensing terms of the
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package customtypes
import (
"bytes"
"fmt"
"math/big"
"testing"
"github.com/holiman/uint256"
"github.com/luxfi/geth/rlp"
// package so assume the presence of identifiers. A dot-import reduces PR
// noise during the refactoring.
. "github.com/luxfi/geth/core/types"
)
func decodeEncode(input []byte, val interface{}) error {
if err := rlp.DecodeBytes(input, val); err != nil {
// not valid rlp, nothing to do
return nil
}
// If it _were_ valid rlp, we can encode it again
output, err := rlp.EncodeToBytes(val)
if err != nil {
return err
}
if !bytes.Equal(input, output) {
return fmt.Errorf("encode-decode is not equal, \ninput : %x\noutput: %x", input, output)
}
return nil
}
func FuzzRLP(f *testing.F) {
f.Fuzz(fuzzRlp)
}
func fuzzRlp(t *testing.T, input []byte) {
if len(input) == 0 || len(input) > 500*1024 {
return
}
rlp.Split(input)
if elems, _, err := rlp.SplitList(input); err == nil {
rlp.CountValues(elems)
}
rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{}))
if err := decodeEncode(input, new(interface{})); err != nil {
t.Fatal(err)
}
{
var v struct {
Int uint
String string
Bytes []byte
}
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
{
type Types struct {
Bool bool
Raw rlp.RawValue
Slice []*Types
Iface []interface{}
}
var v Types
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
{
type AllTypes struct {
Int uint
String string
Bytes []byte
Bool bool
Raw rlp.RawValue
Slice []*AllTypes
Array [3]*AllTypes
Iface []interface{}
}
var v AllTypes
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
{
if err := decodeEncode(input, [10]byte{}); err != nil {
t.Fatal(err)
}
}
{
var v struct {
Byte [10]byte
Rool [10]bool
}
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
{
var h Header
if err := decodeEncode(input, &h); err != nil {
t.Fatal(err)
}
var b Block
if err := decodeEncode(input, &b); err != nil {
t.Fatal(err)
}
var tx Transaction
if err := decodeEncode(input, &tx); err != nil {
t.Fatal(err)
}
var txs Transactions
if err := decodeEncode(input, &txs); err != nil {
t.Fatal(err)
}
var rs Receipts
if err := decodeEncode(input, &rs); err != nil {
t.Fatal(err)
}
}
{
var v struct {
AnIntPtr *big.Int
AnInt big.Int
AnU256Ptr *uint256.Int
AnU256 uint256.Int
NotAnU256 [4]uint64
}
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
}
-163
View File
@@ -1,163 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//
// This file is a derived work, based on the go-ethereum library whose original
// notices appear below.
//
// It is distributed under a license compatible with the licensing terms of the
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package customtypes
import (
"math/big"
"testing"
"github.com/luxfi/crypto"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/rlp"
// package so assume the presence of identifiers. A dot-import reduces PR
// noise during the refactoring.
. "github.com/luxfi/geth/core/types"
)
type devnull struct{ len int }
func (d *devnull) Write(p []byte) (int, error) {
d.len += len(p)
return len(p), nil
}
func BenchmarkEncodeRLP(b *testing.B) {
benchRLP(b, true)
}
func BenchmarkDecodeRLP(b *testing.B) {
benchRLP(b, false)
}
func benchRLP(b *testing.B, encode bool) {
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
to := common.HexToAddress("0x00000000000000000000000000000000deadbeef")
signer := NewLondonSigner(big.NewInt(1337))
for _, tc := range []struct {
name string
obj interface{}
}{
{
"legacy-header",
&Header{
Difficulty: big.NewInt(10000000000),
Number: big.NewInt(1000),
GasLimit: 8_000_000,
GasUsed: 8_000_000,
Time: 555,
Extra: make([]byte, 32),
},
},
{
"london-header",
&Header{
Difficulty: big.NewInt(10000000000),
Number: big.NewInt(1000),
GasLimit: 8_000_000,
GasUsed: 8_000_000,
Time: 555,
Extra: make([]byte, 32),
BaseFee: big.NewInt(10000000000),
},
},
{
"receipt-for-storage",
&ReceiptForStorage{
Status: ReceiptStatusSuccessful,
CumulativeGasUsed: 0x888888888,
Logs: make([]*Log, 0),
},
},
{
"receipt-full",
&Receipt{
Status: ReceiptStatusSuccessful,
CumulativeGasUsed: 0x888888888,
Logs: make([]*Log, 0),
},
},
{
"legacy-transaction",
MustSignNewTx(key, signer,
&LegacyTx{
Nonce: 1,
GasPrice: big.NewInt(500),
Gas: 1000000,
To: &to,
Value: big.NewInt(1),
}),
},
{
"access-transaction",
MustSignNewTx(key, signer,
&AccessListTx{
Nonce: 1,
GasPrice: big.NewInt(500),
Gas: 1000000,
To: &to,
Value: big.NewInt(1),
}),
},
{
"1559-transaction",
MustSignNewTx(key, signer,
&DynamicFeeTx{
Nonce: 1,
Gas: 1000000,
To: &to,
Value: big.NewInt(1),
GasTipCap: big.NewInt(500),
GasFeeCap: big.NewInt(500),
}),
},
} {
if encode {
b.Run(tc.name, func(b *testing.B) {
b.ReportAllocs()
var null = &devnull{}
for i := 0; i < b.N; i++ {
rlp.Encode(null, tc.obj)
}
b.SetBytes(int64(null.len / b.N))
})
} else {
data, _ := rlp.EncodeToBytes(tc.obj)
// Test decoding
b.Run(tc.name, func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if err := rlp.DecodeBytes(data, tc.obj); err != nil {
b.Fatal(err)
}
}
b.SetBytes(int64(len(data)))
})
}
}
}
-100
View File
@@ -1,100 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package database
import (
"errors"
"github.com/luxfi/database"
"github.com/luxfi/geth/ethdb"
)
var (
_ ethdb.KeyValueStore = (*ethDbWrapper)(nil)
ErrSnapshotNotSupported = errors.New("snapshot is not supported")
)
// ethDbWrapper implements ethdb.Database
type ethDbWrapper struct{ database.Database }
func WrapDatabase(db database.Database) ethdb.KeyValueStore { return ethDbWrapper{db} }
// Stat implements ethdb.Database
func (db ethDbWrapper) Stat() (string, error) { return "", errors.New("stat not supported") }
// NewBatch implements ethdb.Database
func (db ethDbWrapper) NewBatch() ethdb.Batch { return &wrappedBatch{db.Database.NewBatch()} }
// NewBatchWithSize implements ethdb.Database
// TODO: propagate size through luxd Database interface
func (db ethDbWrapper) NewBatchWithSize(size int) ethdb.Batch {
return &wrappedBatch{db.Database.NewBatch()}
}
func (db ethDbWrapper) NewSnapshot() (interface{}, error) {
return nil, ErrSnapshotNotSupported
}
// DeleteRange implements ethdb.KeyValueRangeDeleter
func (db ethDbWrapper) DeleteRange(start, end []byte) error {
return errors.New("DeleteRange not supported")
}
// SyncKeyValue implements ethdb.KeyValueStore
func (db ethDbWrapper) SyncKeyValue() error {
return nil
}
// NewIterator implements ethdb.Database
//
// Note: This method assumes that the prefix is NOT part of the start, so there's
// no need for the caller to prepend the prefix to the start.
func (db ethDbWrapper) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
// luxd's database implementation assumes that the prefix is part of the
// start, so it is added here (if it is provided).
if len(prefix) > 0 {
newStart := make([]byte, len(prefix)+len(start))
copy(newStart, prefix)
copy(newStart[len(prefix):], start)
start = newStart
}
return db.NewIteratorWithStartAndPrefix(start, prefix)
}
// NewIteratorWithStart implements ethdb.Database
func (db ethDbWrapper) NewIteratorWithStart(start []byte) ethdb.Iterator {
return db.Database.NewIteratorWithStart(start)
}
// wrappedBatch implements ethdb.wrappedBatch
type wrappedBatch struct{ batch database.Batch }
// ValueSize implements ethdb.Batch
func (batch wrappedBatch) ValueSize() int { return batch.batch.Size() }
// Replay implements ethdb.Batch
func (batch wrappedBatch) Replay(w ethdb.KeyValueWriter) error { return batch.batch.Replay(w) }
// DeleteRange implements ethdb.KeyValueRangeDeleter for the batch
func (batch wrappedBatch) DeleteRange(start, end []byte) error {
return errors.New("DeleteRange not supported in batch")
}
// Implement missing methods from database.Batch
func (batch wrappedBatch) Put(key []byte, value []byte) error {
return batch.batch.Put(key, value)
}
func (batch wrappedBatch) Delete(key []byte) error {
return batch.batch.Delete(key)
}
func (batch wrappedBatch) Write() error {
return batch.batch.Write()
}
func (batch wrappedBatch) Reset() {
batch.batch.Reset()
}
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
MANIFEST-000342
View File
Binary file not shown.
Binary file not shown.
-108
View File
@@ -1,108 +0,0 @@
[Version]
pebble_version=0.1
[Options]
bytes_per_sync=524288
cache_size=562949953421312
cleaner=delete
compaction_debt_concurrency=1073741824
comparer=leveldb.BytewiseComparator
disable_wal=false
flush_delay_delete_range=0s
flush_delay_range_key=0s
flush_split_bytes=4194304
format_major_version=1
l0_compaction_concurrency=10
l0_compaction_file_threshold=500
l0_compaction_threshold=2
l0_stop_writes_threshold=1000
lbase_max_bytes=67108864
max_concurrent_compactions=56
max_manifest_file_size=134217728
max_open_files=256
mem_table_size=4194304
mem_table_stop_writes_threshold=2
min_deletion_rate=0
merger=pebble.concatenate
read_compaction_rate=16000
read_sampling_multiplier=16
strict_wal_tail=true
table_cache_shards=56
table_property_collectors=[]
validate_on_ingest=false
wal_dir=
wal_bytes_per_sync=0
max_writer_concurrency=0
force_writer_parallelism=false
secondary_cache_size_bytes=0
create_on_shared=0
[Level "0"]
block_restart_interval=16
block_size=32768
block_size_threshold=90
compression=Snappy
filter_policy=rocksdb.BuiltinBloomFilter
filter_type=table
index_block_size=262144
target_file_size=2097152
[Level "1"]
block_restart_interval=16
block_size=32768
block_size_threshold=90
compression=Snappy
filter_policy=rocksdb.BuiltinBloomFilter
filter_type=table
index_block_size=262144
target_file_size=2097152
[Level "2"]
block_restart_interval=16
block_size=32768
block_size_threshold=90
compression=Snappy
filter_policy=rocksdb.BuiltinBloomFilter
filter_type=table
index_block_size=262144
target_file_size=2097152
[Level "3"]
block_restart_interval=16
block_size=32768
block_size_threshold=90
compression=Snappy
filter_policy=rocksdb.BuiltinBloomFilter
filter_type=table
index_block_size=262144
target_file_size=2097152
[Level "4"]
block_restart_interval=16
block_size=32768
block_size_threshold=90
compression=Snappy
filter_policy=rocksdb.BuiltinBloomFilter
filter_type=table
index_block_size=262144
target_file_size=2097152
[Level "5"]
block_restart_interval=16
block_size=32768
block_size_threshold=90
compression=Snappy
filter_policy=rocksdb.BuiltinBloomFilter
filter_type=table
index_block_size=262144
target_file_size=2097152
[Level "6"]
block_restart_interval=16
block_size=32768
block_size_threshold=90
compression=Snappy
filter_policy=rocksdb.BuiltinBloomFilter
filter_type=table
index_block_size=262144
target_file_size=2097152
-178
View File
@@ -1,178 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// TODO: move to network
package evm
import (
"context"
"fmt"
"sync"
"sync/atomic"
"github.com/prometheus/client_golang/prometheus"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/node/network/p2p/gossip"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/txpool"
"github.com/luxfi/evm/eth"
"github.com/luxfi/evm/plugin/evm/config"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
)
const pendingTxsBuffer = 10
var (
_ gossip.Gossipable = (*GossipEthTx)(nil)
_ gossip.Marshaller[*GossipEthTx] = (*GossipEthTxMarshaller)(nil)
_ gossip.Set[*GossipEthTx] = (*GossipEthTxPool)(nil)
_ eth.PushGossiper = (*EthPushGossiper)(nil)
)
func NewGossipEthTxPool(mempool *txpool.TxPool, registerer prometheus.Registerer) (*GossipEthTxPool, error) {
bloom, err := gossip.NewBloomFilter(
registerer,
"eth_tx_bloom_filter",
config.TxGossipBloomMinTargetElements,
config.TxGossipBloomTargetFalsePositiveRate,
config.TxGossipBloomResetFalsePositiveRate,
)
if err != nil {
return nil, fmt.Errorf("failed to initialize bloom filter: %w", err)
}
return &GossipEthTxPool{
mempool: mempool,
pendingTxs: make(chan core.NewTxsEvent, pendingTxsBuffer),
bloom: bloom,
}, nil
}
type GossipEthTxPool struct {
mempool *txpool.TxPool
pendingTxs chan core.NewTxsEvent
bloom *gossip.BloomFilter
lock sync.RWMutex
// subscribed is set to true when the gossip subscription is active
// mostly used for testing
subscribed atomic.Bool
}
// IsSubscribed returns whether or not the gossip subscription is active.
func (g *GossipEthTxPool) IsSubscribed() bool {
return g.subscribed.Load()
}
func (g *GossipEthTxPool) Subscribe(ctx context.Context) {
sub := g.mempool.SubscribeTransactions(g.pendingTxs, false)
if sub == nil {
log.Warn("failed to subscribe to new txs event")
return
}
g.subscribed.CompareAndSwap(false, true)
defer func() {
sub.Unsubscribe()
g.subscribed.CompareAndSwap(true, false)
}()
for {
select {
case <-ctx.Done():
log.Debug("shutting down subscription")
return
case pendingTxs := <-g.pendingTxs:
g.lock.Lock()
optimalElements := (g.mempool.PendingSize(txpool.PendingFilter{}) + len(pendingTxs.Txs)) * config.TxGossipBloomChurnMultiplier
for _, pendingTx := range pendingTxs.Txs {
tx := &GossipEthTx{Tx: pendingTx}
g.bloom.Add(tx)
reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, optimalElements)
if err != nil {
log.Error("failed to reset bloom filter", "err", err)
continue
}
if reset {
log.Debug("resetting bloom filter", "reason", "reached max filled ratio")
g.mempool.IteratePending(func(tx *types.Transaction) bool {
g.bloom.Add(&GossipEthTx{Tx: tx})
return true
})
}
}
g.lock.Unlock()
}
}
}
// Add enqueues the transaction to the mempool. Subscribe should be called
// to receive an event if tx is actually added to the mempool or not.
func (g *GossipEthTxPool) Add(tx *GossipEthTx) error {
return g.mempool.Add([]*types.Transaction{tx.Tx}, false, false)[0]
}
// Has should just return whether or not the [txID] is still in the mempool,
// not whether it is in the mempool AND pending.
func (g *GossipEthTxPool) Has(txID ids.ID) bool {
return g.mempool.Has(common.Hash(txID))
}
func (g *GossipEthTxPool) Iterate(f func(tx *GossipEthTx) bool) {
g.mempool.IteratePending(func(tx *types.Transaction) bool {
return f(&GossipEthTx{Tx: tx})
})
}
func (g *GossipEthTxPool) GetFilter() ([]byte, []byte) {
g.lock.RLock()
defer g.lock.RUnlock()
return g.bloom.Marshal()
}
type GossipEthTxMarshaller struct{}
func (GossipEthTxMarshaller) MarshalGossip(tx *GossipEthTx) ([]byte, error) {
return tx.Tx.MarshalBinary()
}
func (GossipEthTxMarshaller) UnmarshalGossip(bytes []byte) (*GossipEthTx, error) {
tx := &GossipEthTx{
Tx: &types.Transaction{},
}
return tx, tx.Tx.UnmarshalBinary(bytes)
}
type GossipEthTx struct {
Tx *types.Transaction
}
func (tx *GossipEthTx) GossipID() ids.ID {
return ids.ID(tx.Tx.Hash())
}
// EthPushGossiper is used by the ETH backend to push transactions issued over
// the RPC and added to the mempool to peers.
type EthPushGossiper struct {
vm *VM
}
func (e *EthPushGossiper) Add(tx *types.Transaction) {
// eth.Backend is initialized before the [ethTxPushGossiper] is created, so
// we just ignore any gossip requests until it is set.
ethTxPushGossiper := e.vm.ethTxPushGossiper.Get()
if ethTxPushGossiper == nil {
return
}
ethTxPushGossiper.Add(&GossipEthTx{tx})
}
-24
View File
@@ -1,24 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/node/vms"
)
var (
// ID this VM should be referenced by
IDStr = "subnetevm"
ID = ids.ID{'s', 'u', 'b', 'n', 'e', 't', 'e', 'v', 'm'}
_ vms.Factory = (*Factory)(nil)
)
type Factory struct{}
func (*Factory) New(log.Logger) (interface{}, error) {
return &VM{}, nil
}
-86
View File
@@ -1,86 +0,0 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package gossip
import (
"context"
"time"
"github.com/luxfi/consensus/engine/core"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/node/network/p2p"
"github.com/luxfi/node/network/p2p/gossip"
"github.com/prometheus/client_golang/prometheus"
)
var _ p2p.Handler = (*txGossipHandler)(nil)
func NewTxGossipHandler[T gossip.Gossipable](
logger log.Logger,
marshaller gossip.Marshaller[T],
mempool gossip.Set[T],
metrics gossip.Metrics,
maxMessageSize int,
throttlingPeriod time.Duration,
requestsPerPeer float64,
validators p2p.ValidatorSet,
registerer prometheus.Registerer,
namespace string,
) (*txGossipHandler, error) {
// Create logger adapter for node's logging interface
nodeLogger := NewLoggerAdapter(logger)
// push gossip messages can be handled from any peer
handler := gossip.NewHandler(
nodeLogger,
marshaller,
mempool,
metrics,
maxMessageSize,
)
// Create a sliding window throttler for rate limiting
throttler := p2p.NewSlidingWindowThrottler(
throttlingPeriod,
int(requestsPerPeer), // Convert float64 to int for limit
)
throttledHandler := p2p.NewThrottlerHandler(
handler,
throttler,
nodeLogger,
)
// pull gossip requests are filtered by validators and are throttled
// to prevent spamming
validatorHandler := p2p.NewValidatorHandler(
throttledHandler,
validators,
nodeLogger,
)
return &txGossipHandler{
appGossipHandler: handler,
appRequestHandler: validatorHandler,
}, nil
}
type txGossipHandler struct {
appGossipHandler p2p.Handler
appRequestHandler p2p.Handler
}
func (t *txGossipHandler) AppGossip(ctx context.Context, nodeID ids.NodeID, gossipBytes []byte) {
t.appGossipHandler.AppGossip(ctx, nodeID, gossipBytes)
}
func (t *txGossipHandler) AppRequest(ctx context.Context, nodeID ids.NodeID, deadline time.Time, requestBytes []byte) ([]byte, *core.AppError) {
return t.appRequestHandler.AppRequest(ctx, nodeID, deadline, requestBytes)
}
func (t *txGossipHandler) CrossChainAppRequest(ctx context.Context, chainID ids.ID, deadline time.Time, requestBytes []byte) ([]byte, error) {
// Cross-chain requests are not supported for transaction gossip
return nil, nil
}
-124
View File
@@ -1,124 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package gossip
import (
"context"
"log/slog"
"github.com/luxfi/log"
)
// loggerAdapter adapts luxfi/log.Logger
type loggerAdapter struct {
logger log.Logger
}
// NewLoggerAdapter creates a new logger adapter
func NewLoggerAdapter(logger log.Logger) log.Logger {
return &loggerAdapter{logger: logger}
}
// Write implements io.Writer
func (l *loggerAdapter) Write(p []byte) (n int, err error) {
l.logger.Info(string(p))
return len(p), nil
}
// Stop implements log.Logger
func (l *loggerAdapter) Stop() {
l.logger.Stop()
}
// StopOnPanic implements log.Logger
func (l *loggerAdapter) StopOnPanic() {
l.logger.StopOnPanic()
}
// RecoverAndPanic implements log.Logger
func (l *loggerAdapter) RecoverAndPanic(f func()) {
l.logger.RecoverAndPanic(f)
}
// RecoverAndExit implements log.Logger
func (l *loggerAdapter) RecoverAndExit(f func(), exit func()) {
l.logger.RecoverAndExit(f, exit)
}
// Delegate all other methods to the underlying logger
func (l *loggerAdapter) With(ctx ...interface{}) log.Logger {
return &loggerAdapter{logger: l.logger.With(ctx...)}
}
func (l *loggerAdapter) New(ctx ...interface{}) log.Logger {
return &loggerAdapter{logger: l.logger.New(ctx...)}
}
func (l *loggerAdapter) Log(level slog.Level, msg string, ctx ...interface{}) {
l.logger.Log(level, msg, ctx...)
}
func (l *loggerAdapter) Trace(msg string, ctx ...interface{}) {
l.logger.Trace(msg, ctx...)
}
func (l *loggerAdapter) Debug(msg string, ctx ...interface{}) {
l.logger.Debug(msg, ctx...)
}
func (l *loggerAdapter) Info(msg string, ctx ...interface{}) {
l.logger.Info(msg, ctx...)
}
func (l *loggerAdapter) Warn(msg string, ctx ...interface{}) {
l.logger.Warn(msg, ctx...)
}
func (l *loggerAdapter) Error(msg string, ctx ...interface{}) {
l.logger.Error(msg, ctx...)
}
func (l *loggerAdapter) Crit(msg string, ctx ...interface{}) {
l.logger.Crit(msg, ctx...)
}
func (l *loggerAdapter) Fatal(msg string, fields ...log.Field) {
l.logger.Fatal(msg, fields...)
}
func (l *loggerAdapter) Verbo(msg string, fields ...log.Field) {
l.logger.Verbo(msg, fields...)
}
func (l *loggerAdapter) WithFields(fields ...log.Field) log.Logger {
return &loggerAdapter{logger: l.logger.WithFields(fields...)}
}
func (l *loggerAdapter) WithOptions(opts ...log.Option) log.Logger {
return &loggerAdapter{logger: l.logger.WithOptions(opts...)}
}
func (l *loggerAdapter) SetLevel(level slog.Level) {
l.logger.SetLevel(level)
}
func (l *loggerAdapter) GetLevel() slog.Level {
return l.logger.GetLevel()
}
func (l *loggerAdapter) EnabledLevel(lvl slog.Level) bool {
return l.logger.EnabledLevel(lvl)
}
func (l *loggerAdapter) WriteLog(level slog.Level, msg string, attrs ...any) {
l.logger.WriteLog(level, msg, attrs...)
}
func (l *loggerAdapter) Enabled(ctx context.Context, level slog.Level) bool {
return l.logger.Enabled(ctx, level)
}
func (l *loggerAdapter) Handler() slog.Handler {
return l.logger.Handler()
}
-7
View File
@@ -1,7 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package gossip
// Placeholder for gossip package
type NoOpGossip struct{}
-112
View File
@@ -1,112 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"context"
"math/big"
"testing"
"time"
"github.com/luxfi/crypto"
"github.com/luxfi/evm/consensus/dummy"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/core/txpool"
"github.com/luxfi/evm/core/txpool/legacypool"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/utils"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/core/vm"
"github.com/luxfi/node/network/p2p/gossip"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGossipEthTxMarshaller(t *testing.T) {
require := require.New(t)
blobTx := &types.BlobTx{}
want := &GossipEthTx{Tx: types.NewTx(blobTx)}
marshaller := GossipEthTxMarshaller{}
bytes, err := marshaller.MarshalGossip(want)
require.NoError(err)
got, err := marshaller.UnmarshalGossip(bytes)
require.NoError(err)
require.Equal(want.GossipID(), got.GossipID())
}
func TestGossipSubscribe(t *testing.T) {
require := require.New(t)
key, err := crypto.GenerateKey()
require.NoError(err)
addr := crypto.PubkeyToAddress(key.PublicKey)
require.NoError(err)
txPool := setupPoolWithConfig(t, params.TestChainConfig, common.Address(addr))
defer txPool.Close()
txPool.SetGasTip(common.Big1)
txPool.SetMinFee(common.Big0)
gossipTxPool, err := NewGossipEthTxPool(txPool, prometheus.NewRegistry())
require.NoError(err)
// use a custom bloom filter to test the bloom filter reset
gossipTxPool.bloom, err = gossip.NewBloomFilter(prometheus.NewRegistry(), "", 1, 0.01, 0.0000000000000001) // maxCount =1
require.NoError(err)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go gossipTxPool.Subscribe(ctx)
require.Eventually(func() bool {
return gossipTxPool.IsSubscribed()
}, 10*time.Second, 500*time.Millisecond, "expected gossipTxPool to be subscribed")
// create eth txs
ethTxs := getValidEthTxs(key, 10, big.NewInt(226*utils.GWei))
// Notify mempool about txs
errs := txPool.AddRemotesSync(ethTxs)
for _, err := range errs {
require.NoError(err, "failed adding evm tx to remote mempool")
}
require.EventuallyWithTf(
func(c *assert.CollectT) {
gossipTxPool.lock.RLock()
defer gossipTxPool.lock.RUnlock()
for i, tx := range ethTxs {
assert.Truef(c, gossipTxPool.bloom.Has(&GossipEthTx{Tx: tx}), "expected tx[%d] to be in bloom filter", i)
}
},
30*time.Second,
500*time.Millisecond,
"expected all transactions to eventually be in the bloom filter",
)
}
func setupPoolWithConfig(t *testing.T, config *params.ChainConfig, fundedAddress common.Address) *txpool.TxPool {
diskdb := rawdb.NewMemoryDatabase()
engine := dummy.NewETHFaker()
gspec := &core.Genesis{
Config: config,
Alloc: types.GenesisAlloc{fundedAddress: {Balance: big.NewInt(1000000000000000000)}},
}
chain, err := core.NewBlockChain(diskdb, core.DefaultCacheConfig, gspec, engine, vm.Config{}, common.Hash{}, false)
require.NoError(t, err)
testTxPoolConfig := legacypool.DefaultConfig
legacyPool := legacypool.New(testTxPoolConfig, chain)
txPool, err := txpool.New(testTxPoolConfig.PriceLimit, chain, []txpool.SubPool{legacyPool})
require.NoError(t, err)
return txPool
}
-141
View File
@@ -1,141 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"context"
"crypto/ecdsa"
"encoding/json"
"math/big"
"strings"
"sync"
"testing"
"time"
"github.com/luxfi/consensus/utils/set"
"github.com/luxfi/ids"
"github.com/luxfi/crypto"
"github.com/luxfi/geth/common"
"github.com/stretchr/testify/assert"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/params"
"github.com/luxfi/geth/core/types"
)
func fundAddressByGenesis(addrs []common.Address) (string, error) {
balance := big.NewInt(0xffffffffffffff)
genesis := &core.Genesis{
Difficulty: common.Big0,
GasLimit: params.GetExtra(params.TestChainConfig).FeeConfig.GasLimit.Uint64(),
}
funds := make(map[common.Address]types.Account)
for _, addr := range addrs {
funds[addr] = types.Account{
Balance: balance,
}
}
genesis.Alloc = funds
genesis.Config = params.TestChainConfig
bytes, err := json.Marshal(genesis)
return string(bytes), err
}
func getValidEthTxs(key *ecdsa.PrivateKey, count int, gasPrice *big.Int) []*types.Transaction {
res := make([]*types.Transaction, count)
to := common.Address{}
amount := big.NewInt(0)
gasLimit := uint64(37000)
for i := 0; i < count; i++ {
tx, _ := types.SignTx(
types.NewTransaction(
uint64(i),
to,
amount,
gasLimit,
gasPrice,
[]byte(strings.Repeat("aaaaaaaaaa", 100))),
types.HomesteadSigner{}, key)
tx.SetTime(time.Now().Add(-1 * time.Minute))
res[i] = tx
}
return res
}
// show that a geth tx discovered from gossip is requested to the same node that
// gossiped it
func TestMempoolEthTxsAppGossipHandling(t *testing.T) {
// Fixed database lock issues by using proper test isolation
t.Parallel() // Run in parallel to avoid database lock conflicts
assert := assert.New(t)
key, err := crypto.GenerateKey()
assert.NoError(err)
addr := crypto.PubkeyToAddress(key.PublicKey)
genesisJSON, err := fundAddressByGenesis([]common.Address{common.Address(addr)})
assert.NoError(err)
tvm := newVM(t, testVMConfig{
genesisJSON: genesisJSON,
})
defer func() {
err := tvm.vm.Shutdown(context.Background())
assert.NoError(err)
}()
tvm.vm.txPool.SetGasTip(common.Big1)
tvm.vm.txPool.SetMinFee(common.Big0)
var (
wg sync.WaitGroup
txRequested bool
)
tvm.appSender.CantSendAppGossip = false
tvm.appSender.SendAppRequestF = func(context.Context, set.Set[ids.NodeID], uint32, []byte) error {
txRequested = true
return nil
}
wg.Add(1)
tvm.appSender.SendAppGossipF = func(context.Context, set.Set[ids.NodeID], []byte) error {
wg.Done()
return nil
}
// prepare a tx
tx := getValidEthTxs(key, 1, common.Big1)[0]
// Txs must be submitted over the API to be included in push gossip.
// (i.e., txs received via p2p are not included in push gossip)
err = tvm.vm.eth.APIBackend.SendTx(context.Background(), tx)
assert.NoError(err)
assert.False(txRequested, "tx should not be requested")
// wait for transaction to be re-gossiped
attemptAwait(t, &wg, 5*time.Second)
}
func attemptAwait(t *testing.T, wg *sync.WaitGroup, delay time.Duration) {
ticker := make(chan struct{})
// Wait for [wg] and then close [ticket] to indicate that
// the wait group has finished.
go func() {
wg.Wait()
close(ticker)
}()
select {
case <-time.After(delay):
t.Fatal("Timed out waiting for wait group to complete")
case <-ticker:
// The wait group completed without issue
}
}
-56
View File
@@ -1,56 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"errors"
"math/big"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/geth/core/types"
)
var errEstimateBaseFeeWithoutActivation = errors.New("cannot estimate base fee for chain without activation scheduled")
// BaseFee takes the previous header and the timestamp of its child block and
// calculates the expected base fee for the child block.
//
// Prior to SubnetEVM, the returned base fee will be nil.
func BaseFee(
config *extras.ChainConfig,
feeConfig commontype.FeeConfig,
parent *types.Header,
timestamp uint64,
) (*big.Int, error) {
switch {
case config.IsSubnetEVM(timestamp):
return baseFeeFromWindow(config, feeConfig, parent, timestamp)
default:
// Prior to SubnetEVM the expected base fee is nil.
return nil, nil
}
}
// EstimateNextBaseFee attempts to estimate the base fee of a block built at
// `timestamp` on top of `parent`.
//
// If timestamp is before parent.Time or the SubnetEVM activation time, then timestamp
// is set to the maximum of parent.Time and the SubnetEVM activation time.
//
// Warning: This function should only be used in estimation and should not be
// used when calculating the canonical base fee for a block.
func EstimateNextBaseFee(
config *extras.ChainConfig,
feeConfig commontype.FeeConfig,
parent *types.Header,
timestamp uint64,
) (*big.Int, error) {
if config.SubnetEVMTimestamp == nil {
return nil, errEstimateBaseFeeWithoutActivation
}
timestamp = max(timestamp, parent.Time, *config.SubnetEVMTimestamp)
return BaseFee(config, feeConfig, parent, timestamp)
}
-260
View File
@@ -1,260 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"math/big"
"testing"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/plugin/evm/upgrade/subnetevm"
"github.com/luxfi/evm/utils"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
"github.com/stretchr/testify/require"
)
const (
maxBaseFee = 225 * utils.GWei
)
func TestBaseFee(t *testing.T) {
t.Run("normal", func(t *testing.T) {
BaseFeeTest(t, testFeeConfig)
})
t.Run("double", func(t *testing.T) {
BaseFeeTest(t, testFeeConfigDouble)
})
}
func BaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig) {
tests := []struct {
name string
upgrades extras.NetworkUpgrades
parent *types.Header
timestamp uint64
want *big.Int
wantErr error
}{
{
name: "pre_subnet_evm",
upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades,
want: nil,
wantErr: nil,
},
{
name: "subnet_evm_first_block",
upgrades: extras.NetworkUpgrades{
SubnetEVMTimestamp: utils.NewUint64(1),
},
parent: &types.Header{
Number: big.NewInt(1),
},
timestamp: 1,
want: big.NewInt(feeConfig.MinBaseFee.Int64()),
},
{
name: "subnet_evm_genesis_block",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(0),
},
want: big.NewInt(feeConfig.MinBaseFee.Int64()),
},
{
name: "subnet_evm_invalid_fee_window",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
},
wantErr: subnetevm.ErrWindowInsufficientLength,
},
{
name: "subnet_evm_invalid_timestamp",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
Time: 1,
Extra: (&subnetevm.Window{}).Bytes(),
},
timestamp: 0,
wantErr: errInvalidTimestamp,
},
{
name: "subnet_evm_no_change",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
GasUsed: feeConfig.TargetGas.Uint64(),
Time: 1,
Extra: (&subnetevm.Window{}).Bytes(),
BaseFee: big.NewInt(feeConfig.MinBaseFee.Int64() + 1),
},
timestamp: 1,
want: big.NewInt(feeConfig.MinBaseFee.Int64() + 1),
},
{
name: "subnet_evm_small_decrease",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
Extra: (&subnetevm.Window{}).Bytes(),
BaseFee: big.NewInt(maxBaseFee),
},
timestamp: 1,
want: func() *big.Int {
var (
gasTarget = feeConfig.TargetGas.Int64()
gasUsed = int64(0)
amountUnderTarget = gasTarget - gasUsed
parentBaseFee = int64(maxBaseFee)
smoothingFactor = feeConfig.BaseFeeChangeDenominator.Int64()
baseFeeFractionUnderTarget = amountUnderTarget * parentBaseFee / gasTarget
delta = baseFeeFractionUnderTarget / smoothingFactor
baseFee = parentBaseFee - delta
)
return big.NewInt(baseFee)
}(),
},
{
name: "subnet_evm_large_decrease",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
Extra: (&subnetevm.Window{}).Bytes(),
BaseFee: big.NewInt(maxBaseFee),
},
timestamp: 2 * subnetevm.WindowLen,
want: func() *big.Int {
var (
gasTarget = feeConfig.TargetGas.Int64()
gasUsed = int64(0)
amountUnderTarget = gasTarget - gasUsed
parentBaseFee = int64(maxBaseFee)
smoothingFactor = feeConfig.BaseFeeChangeDenominator.Int64()
baseFeeFractionUnderTarget = amountUnderTarget * parentBaseFee / gasTarget
windowsElapsed = int64(2)
delta = windowsElapsed * baseFeeFractionUnderTarget / smoothingFactor
baseFee = parentBaseFee - delta
)
return big.NewInt(baseFee)
}(),
},
{
name: "subnet_evm_increase",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
GasUsed: 2 * feeConfig.TargetGas.Uint64(),
Extra: (&subnetevm.Window{}).Bytes(),
BaseFee: big.NewInt(feeConfig.MinBaseFee.Int64()),
},
timestamp: 1,
want: func() *big.Int {
var (
gasTarget = feeConfig.TargetGas.Int64()
gasUsed = 2 * gasTarget
amountOverTarget = gasUsed - gasTarget
parentBaseFee = feeConfig.MinBaseFee.Int64()
smoothingFactor = feeConfig.BaseFeeChangeDenominator.Int64()
baseFeeFractionOverTarget = amountOverTarget * parentBaseFee / gasTarget
delta = baseFeeFractionOverTarget / smoothingFactor
baseFee = parentBaseFee + delta
)
return big.NewInt(baseFee)
}(),
},
{
name: "subnet_evm_big_1_not_modified",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
GasUsed: 1,
Extra: (&subnetevm.Window{}).Bytes(),
BaseFee: big.NewInt(1),
},
timestamp: 2 * subnetevm.WindowLen,
want: big.NewInt(feeConfig.MinBaseFee.Int64()),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require := require.New(t)
config := &extras.ChainConfig{
NetworkUpgrades: test.upgrades,
}
got, err := BaseFee(config, feeConfig, test.parent, test.timestamp)
require.ErrorIs(err, test.wantErr)
require.Equal(test.want, got)
// Verify that [common.Big1] is not modified by [BaseFee].
require.Equal(big.NewInt(1), common.Big1)
})
}
}
func TestEstimateNextBaseFee(t *testing.T) {
t.Run("normal", func(t *testing.T) {
EstimateNextBaseFeeTest(t, testFeeConfig)
})
t.Run("double", func(t *testing.T) {
EstimateNextBaseFeeTest(t, testFeeConfigDouble)
})
}
func EstimateNextBaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig) {
testBaseFee := uint64(225 * utils.GWei)
nilUpgrade := extras.NetworkUpgrades{}
tests := []struct {
name string
upgrades extras.NetworkUpgrades
parent *types.Header
timestamp uint64
want *big.Int
wantErr error
}{
{
name: "activated",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
Extra: (&subnetevm.Window{}).Bytes(),
BaseFee: new(big.Int).SetUint64(testBaseFee),
},
timestamp: 1,
want: func() *big.Int {
var (
gasTarget = feeConfig.TargetGas.Uint64()
gasUsed = uint64(0)
amountUnderTarget = gasTarget - gasUsed
parentBaseFee = testBaseFee
smoothingFactor = feeConfig.BaseFeeChangeDenominator.Uint64()
baseFeeFractionUnderTarget = amountUnderTarget * parentBaseFee / gasTarget
delta = baseFeeFractionUnderTarget / smoothingFactor
baseFee = parentBaseFee - delta
)
return new(big.Int).SetUint64(baseFee)
}(),
},
{
name: "not_scheduled",
upgrades: nilUpgrade,
wantErr: errEstimateBaseFeeWithoutActivation,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require := require.New(t)
config := &extras.ChainConfig{
NetworkUpgrades: test.upgrades,
}
got, err := EstimateNextBaseFee(config, feeConfig, test.parent, test.timestamp)
require.ErrorIs(err, test.wantErr)
require.Equal(test.want, got)
})
}
}
-118
View File
@@ -1,118 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"errors"
"math/big"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/plugin/evm/blockgascost"
"github.com/luxfi/evm/plugin/evm/customtypes"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
)
var (
errBaseFeeNil = errors.New("base fee is nil")
errBlockGasCostNil = errors.New("block gas cost is nil")
errNoGasUsed = errors.New("no gas used")
)
// BlockGasCost calculates the required block gas cost based on the parent
// header and the timestamp of the new block.
// Prior to Subnet-EVM, the returned block gas cost will be nil.
func BlockGasCost(
config *extras.ChainConfig,
feeConfig commontype.FeeConfig,
parent *types.Header,
timestamp uint64,
) *big.Int {
if !config.IsSubnetEVM(timestamp) {
return nil
}
step := feeConfig.BlockGasCostStep.Uint64()
// Treat an invalid parent/current time combination as 0 elapsed time.
//
// TODO: Does it even make sense to handle this? The timestamp should be
// verified to ensure this never happens.
var timeElapsed uint64
if parent.Time <= timestamp {
timeElapsed = timestamp - parent.Time
}
return new(big.Int).SetUint64(BlockGasCostWithStep(
feeConfig,
customtypes.GetHeaderExtra(parent).BlockGasCost,
step,
timeElapsed,
))
}
// BlockGasCostWithStep calculates the required block gas cost based on the
// parent cost and the time difference between the parent block and new block.
//
// This is a helper function that allows the caller to manually specify the step
// value to use.
func BlockGasCostWithStep(
feeConfig commontype.FeeConfig,
parentCost *big.Int,
step uint64,
timeElapsed uint64,
) uint64 {
if parentCost == nil {
return feeConfig.MinBlockGasCost.Uint64()
}
// [feeConfig.MaxBlockGasCost] is <= MaxUint64, so we know that parentCost is
// always going to be a valid uint64.
return blockgascost.BlockGasCost(
feeConfig,
parentCost.Uint64(),
step,
timeElapsed,
)
}
// EstimateRequiredTip is the estimated tip a transaction would have needed to
// pay to be included in a given block (assuming it paid a tip proportional to
// its gas usage).
//
// In reality, the consensus engine does not enforce a minimum tip on individual
// transactions. The only correctness check performed is that the sum of all
// tips is >= the required block fee.
//
// This function will return nil for all return values prior to SubnetEVM.
func EstimateRequiredTip(
config *extras.ChainConfig,
header *types.Header,
) (*big.Int, error) {
extra := customtypes.GetHeaderExtra(header)
switch {
case !config.IsSubnetEVM(header.Time):
return nil, nil
case header.BaseFee == nil:
return nil, errBaseFeeNil
case extra.BlockGasCost == nil:
return nil, errBlockGasCostNil
}
totalGasUsed := new(big.Int).SetUint64(header.GasUsed)
if totalGasUsed.Sign() == 0 {
return nil, errNoGasUsed
}
// totalRequiredTips = blockGasCost * baseFee + totalGasUsed - 1
//
// We add totalGasUsed - 1 to ensure that the total required tips
// calculation rounds up.
totalRequiredTips := new(big.Int)
totalRequiredTips.Mul(extra.BlockGasCost, header.BaseFee)
totalRequiredTips.Add(totalRequiredTips, totalGasUsed)
totalRequiredTips.Sub(totalRequiredTips, common.Big1)
// estimatedTip = totalRequiredTips / totalGasUsed
estimatedTip := totalRequiredTips.Div(totalRequiredTips, totalGasUsed)
return estimatedTip, nil
}
-312
View File
@@ -1,312 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"math/big"
"testing"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/plugin/evm/customtypes"
"github.com/luxfi/evm/utils"
"github.com/luxfi/geth/core/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testFeeConfig = commontype.FeeConfig{
MinBlockGasCost: big.NewInt(0),
MaxBlockGasCost: big.NewInt(1_000_000),
TargetBlockRate: 2,
BlockGasCostStep: big.NewInt(50_000),
TargetGas: big.NewInt(10_000_000),
BaseFeeChangeDenominator: big.NewInt(12),
MinBaseFee: big.NewInt(25 * utils.GWei),
GasLimit: big.NewInt(12_000_000),
}
testFeeConfigDouble = commontype.FeeConfig{
MinBlockGasCost: big.NewInt(2),
MaxBlockGasCost: big.NewInt(2_000_000),
TargetBlockRate: 4,
BlockGasCostStep: big.NewInt(100_000),
TargetGas: big.NewInt(20_000_000),
BaseFeeChangeDenominator: big.NewInt(24),
MinBaseFee: big.NewInt(50 * utils.GWei),
GasLimit: big.NewInt(24_000_000),
}
)
func TestBlockGasCost(t *testing.T) {
t.Run("normal", func(t *testing.T) {
BlockGasCostTest(t, testFeeConfig)
})
t.Run("double", func(t *testing.T) {
BlockGasCostTest(t, testFeeConfigDouble)
})
}
func BlockGasCostTest(t *testing.T, feeConfig commontype.FeeConfig) {
maxBlockGasCostBig := feeConfig.MaxBlockGasCost
maxBlockGasCost := feeConfig.MaxBlockGasCost.Uint64()
blockGasCostStep := feeConfig.BlockGasCostStep.Uint64()
minBlockGasCost := feeConfig.MinBlockGasCost.Uint64()
targetBlockRate := feeConfig.TargetBlockRate
tests := []struct {
name string
upgrades extras.NetworkUpgrades
parentTime uint64
parentCost *big.Int
timestamp uint64
expected *big.Int
}{
{
name: "before_subnet_evm",
parentTime: 10,
upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades,
parentCost: maxBlockGasCostBig,
timestamp: 10 + targetBlockRate + 1,
expected: nil,
},
{
name: "normal",
upgrades: extras.TestChainConfig.NetworkUpgrades,
parentTime: 10,
parentCost: maxBlockGasCostBig,
timestamp: 10 + targetBlockRate + 1,
expected: new(big.Int).SetUint64(maxBlockGasCost - blockGasCostStep),
},
{
name: "negative_time_elapsed",
upgrades: extras.TestChainConfig.NetworkUpgrades,
parentTime: 10,
parentCost: feeConfig.MinBlockGasCost,
timestamp: 9,
expected: new(big.Int).SetUint64(minBlockGasCost + blockGasCostStep*targetBlockRate),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
config := &extras.ChainConfig{
NetworkUpgrades: test.upgrades,
}
parent := customtypes.WithHeaderExtra(
&types.Header{
Time: test.parentTime,
},
&customtypes.HeaderExtra{
BlockGasCost: test.parentCost,
},
)
assert.Equal(t, test.expected, BlockGasCost(
config,
feeConfig,
parent,
test.timestamp,
))
})
}
}
func TestBlockGasCostWithStep(t *testing.T) {
t.Run("normal", func(t *testing.T) {
BlockGasCostWithStepTest(t, testFeeConfig)
})
t.Run("double", func(t *testing.T) {
BlockGasCostWithStepTest(t, testFeeConfigDouble)
})
}
func BlockGasCostWithStepTest(t *testing.T, feeConfig commontype.FeeConfig) {
minBlockGasCost := feeConfig.MinBlockGasCost.Uint64()
blockGasCostStep := feeConfig.BlockGasCostStep.Uint64()
targetBlockRate := feeConfig.TargetBlockRate
bigMaxBlockGasCost := feeConfig.MaxBlockGasCost
maxBlockGasCost := bigMaxBlockGasCost.Uint64()
tests := []struct {
name string
parentCost *big.Int
timeElapsed uint64
expected uint64
}{
{
name: "nil_parentCost",
parentCost: nil,
timeElapsed: 0,
expected: minBlockGasCost,
},
{
name: "timeElapsed_0",
parentCost: big.NewInt(0),
timeElapsed: 0,
expected: targetBlockRate * blockGasCostStep,
},
{
name: "timeElapsed_1",
parentCost: big.NewInt(0),
timeElapsed: 1,
expected: (targetBlockRate - 1) * blockGasCostStep,
},
{
name: "timeElapsed_0_with_parentCost",
parentCost: big.NewInt(50_000),
timeElapsed: 0,
expected: 50_000 + targetBlockRate*blockGasCostStep,
},
{
name: "timeElapsed_0_with_max_parentCost",
parentCost: bigMaxBlockGasCost,
timeElapsed: 0,
expected: maxBlockGasCost,
},
{
name: "timeElapsed_1_with_max_parentCost",
parentCost: bigMaxBlockGasCost,
timeElapsed: 1,
expected: maxBlockGasCost,
},
{
name: "timeElapsed_at_target",
parentCost: big.NewInt(900_000),
timeElapsed: targetBlockRate,
expected: 900_000,
},
{
name: "timeElapsed_over_target_1",
parentCost: bigMaxBlockGasCost,
timeElapsed: targetBlockRate + 1,
expected: maxBlockGasCost - blockGasCostStep,
},
{
name: "timeElapsed_over_target_10",
parentCost: bigMaxBlockGasCost,
timeElapsed: targetBlockRate + 10,
expected: maxBlockGasCost - 10*blockGasCostStep,
},
{
name: "timeElapsed_over_target_15",
parentCost: bigMaxBlockGasCost,
timeElapsed: targetBlockRate + 15,
expected: maxBlockGasCost - 15*blockGasCostStep,
},
{
name: "timeElapsed_large_clamped_to_0",
parentCost: bigMaxBlockGasCost,
timeElapsed: targetBlockRate + 100,
expected: minBlockGasCost,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.expected, BlockGasCostWithStep(
feeConfig,
test.parentCost,
blockGasCostStep,
test.timeElapsed,
))
})
}
}
func TestEstimateRequiredTip(t *testing.T) {
tests := []struct {
name string
subnetEVMTimestamp *uint64
header *types.Header
want *big.Int
wantErr error
}{
{
name: "not_subnet_evm",
subnetEVMTimestamp: utils.NewUint64(1),
header: &types.Header{},
},
{
name: "nil_base_fee",
subnetEVMTimestamp: utils.NewUint64(0),
header: customtypes.WithHeaderExtra(
&types.Header{},
&customtypes.HeaderExtra{
BlockGasCost: big.NewInt(1),
},
),
wantErr: errBaseFeeNil,
},
{
name: "nil_block_gas_cost",
subnetEVMTimestamp: utils.NewUint64(0),
header: &types.Header{
BaseFee: big.NewInt(1),
},
wantErr: errBlockGasCostNil,
},
{
name: "no_gas_used",
subnetEVMTimestamp: utils.NewUint64(0),
header: customtypes.WithHeaderExtra(
&types.Header{
GasUsed: 0,
BaseFee: big.NewInt(1),
},
&customtypes.HeaderExtra{
BlockGasCost: big.NewInt(1),
},
),
wantErr: errNoGasUsed,
},
{
name: "success",
subnetEVMTimestamp: utils.NewUint64(0),
header: customtypes.WithHeaderExtra(
&types.Header{
GasUsed: 912,
BaseFee: big.NewInt(456),
},
&customtypes.HeaderExtra{
BlockGasCost: big.NewInt(101112),
},
),
// totalRequiredTips = BlockGasCost * BaseFee
// estimatedTip = totalRequiredTips / GasUsed
want: big.NewInt((101112 * 456) / (912)),
},
{
name: "success_rounds_up",
subnetEVMTimestamp: utils.NewUint64(0),
header: customtypes.WithHeaderExtra(
&types.Header{
GasUsed: 124,
BaseFee: big.NewInt(456),
},
&customtypes.HeaderExtra{
BlockGasCost: big.NewInt(101112),
},
),
// totalGasUsed = GasUsed + ExtDataGasUsed
// totalRequiredTips = BlockGasCost * BaseFee
// estimatedTip = totalRequiredTips / totalGasUsed
want: big.NewInt((101112*456)/(124) + 1), // +1 to round up
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require := require.New(t)
config := &extras.ChainConfig{
NetworkUpgrades: extras.NetworkUpgrades{
SubnetEVMTimestamp: test.subnetEVMTimestamp,
},
}
requiredTip, err := EstimateRequiredTip(config, test.header)
require.ErrorIs(err, test.wantErr)
require.Equal(test.want, requiredTip)
})
}
}
-170
View File
@@ -1,170 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"errors"
"fmt"
"math/big"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/plugin/evm/upgrade/subnetevm"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/types"
)
var (
maxUint256Plus1 = new(big.Int).Lsh(common.Big1, 256)
maxUint256 = new(big.Int).Sub(maxUint256Plus1, common.Big1)
errInvalidTimestamp = errors.New("invalid timestamp")
)
// bigMax returns the larger of x or y.
func bigMax(x, y *big.Int) *big.Int {
if x.Cmp(y) > 0 {
return x
}
return y
}
// baseFeeFromWindow should only be called if `timestamp` >= `config.SubnetEVMTimestamp`
func baseFeeFromWindow(config *extras.ChainConfig, feeConfig commontype.FeeConfig, parent *types.Header, timestamp uint64) (*big.Int, error) {
// If the current block is the first EIP-1559 block, or it is the genesis block
// return the initial slice and initial base fee.
if !config.IsSubnetEVM(parent.Time) || parent.Number.Cmp(common.Big0) == 0 {
return big.NewInt(feeConfig.MinBaseFee.Int64()), nil
}
dynamicFeeWindow, err := feeWindow(config, parent, timestamp)
if err != nil {
return nil, err
}
// Calculate the amount of gas consumed within the rollup window.
var (
baseFee = new(big.Int).Set(parent.BaseFee)
baseFeeChangeDenominator = feeConfig.BaseFeeChangeDenominator
parentGasTarget = feeConfig.TargetGas.Uint64()
totalGas = dynamicFeeWindow.Sum()
)
if totalGas == parentGasTarget {
// If the parent block used exactly its target gas, the baseFee stays
// the same.
//
// For legacy reasons, this is true even if the baseFee would have
// otherwise been clamped to a different range.
return baseFee, nil
}
var (
num = new(big.Int)
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
)
if totalGas > parentGasTarget {
// If the parent block used more gas than its target, the baseFee should increase.
num.SetUint64(totalGas - parentGasTarget)
num.Mul(num, parent.BaseFee)
num.Div(num, parentGasTargetBig)
num.Div(num, baseFeeChangeDenominator)
baseFeeDelta := bigMax(num, common.Big1)
baseFee.Add(baseFee, baseFeeDelta)
} else {
// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
num.SetUint64(parentGasTarget - totalGas)
num.Mul(num, parent.BaseFee)
num.Div(num, parentGasTargetBig)
num.Div(num, baseFeeChangeDenominator)
baseFeeDelta := bigMax(num, common.Big1)
if timestamp < parent.Time {
// This should never happen as the fee window calculations should
// have already failed, but it is kept for clarity.
return nil, fmt.Errorf("cannot calculate base fee for timestamp %d prior to parent timestamp %d",
timestamp,
parent.Time,
)
}
// If timeElapsed is greater than [subnetevm.WindowLen], apply the state
// transition to the base fee to account for the interval during which
// no blocks were produced.
//
// We use timeElapsed/[subnetevm.WindowLen], so that the transition is applied
// for every [subnetevm.WindowLen] seconds that has elapsed between the parent
// and this block.
var (
timeElapsed = timestamp - parent.Time
windowsElapsed = timeElapsed / subnetevm.WindowLen
)
if windowsElapsed > 1 {
bigWindowsElapsed := new(big.Int).SetUint64(windowsElapsed)
// Because baseFeeDelta could actually be [common.Big1], we must not
// modify the existing value of `baseFeeDelta` but instead allocate
// a new one.
baseFeeDelta = new(big.Int).Mul(baseFeeDelta, bigWindowsElapsed)
}
baseFee.Sub(baseFee, baseFeeDelta)
}
// Ensure that the base fee does not increase/decrease outside of the bounds
baseFee = selectBigWithinBounds(feeConfig.MinBaseFee, baseFee, maxUint256)
return baseFee, nil
}
// feeWindow takes the previous header and the timestamp of its child block and
// calculates the expected fee window.
//
// feeWindow should only be called if timestamp >= config.SubnetEVMTimestamp
func feeWindow(
config *extras.ChainConfig,
parent *types.Header,
timestamp uint64,
) (subnetevm.Window, error) {
// If the current block is the first EIP-1559 block, or it is the genesis block
// return the initial window.
if !config.IsSubnetEVM(parent.Time) || parent.Number.Cmp(common.Big0) == 0 {
return subnetevm.Window{}, nil
}
dynamicFeeWindow, err := subnetevm.ParseWindow(parent.Extra)
if err != nil {
return subnetevm.Window{}, err
}
if timestamp < parent.Time {
return subnetevm.Window{}, fmt.Errorf("%w: timestamp %d prior to parent timestamp %d",
errInvalidTimestamp,
timestamp,
parent.Time,
)
}
timeElapsed := timestamp - parent.Time
// Compute the new state of the gas rolling window.
dynamicFeeWindow.Add(parent.GasUsed)
// roll the window over by the timeElapsed to generate the new rollup
// window.
dynamicFeeWindow.Shift(timeElapsed)
return dynamicFeeWindow, nil
}
// selectBigWithinBounds returns [value] if it is within the bounds:
// lowerBound <= value <= upperBound or the bound at either end if [value]
// is outside of the defined boundaries.
func selectBigWithinBounds(lowerBound, value, upperBound *big.Int) *big.Int {
switch {
case lowerBound != nil && value.Cmp(lowerBound) < 0:
return new(big.Int).Set(lowerBound)
case upperBound != nil && value.Cmp(upperBound) > 0:
return new(big.Int).Set(upperBound)
default:
return value
}
}
@@ -1,57 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"math/big"
"testing"
)
func TestSelectBigWithinBounds(t *testing.T) {
type test struct {
lower, value, upper, expected *big.Int
}
tests := map[string]test{
"value within bounds": {
lower: big.NewInt(0),
value: big.NewInt(5),
upper: big.NewInt(10),
expected: big.NewInt(5),
},
"value below lower bound": {
lower: big.NewInt(0),
value: big.NewInt(-1),
upper: big.NewInt(10),
expected: big.NewInt(0),
},
"value above upper bound": {
lower: big.NewInt(0),
value: big.NewInt(11),
upper: big.NewInt(10),
expected: big.NewInt(10),
},
"value matches lower bound": {
lower: big.NewInt(0),
value: big.NewInt(0),
upper: big.NewInt(10),
expected: big.NewInt(0),
},
"value matches upper bound": {
lower: big.NewInt(0),
value: big.NewInt(10),
upper: big.NewInt(10),
expected: big.NewInt(10),
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
v := selectBigWithinBounds(test.lower, test.value, test.upper)
if v.Cmp(test.expected) != 0 {
t.Fatalf("Expected (%d), found (%d)", test.expected, v)
}
})
}
}
-135
View File
@@ -1,135 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"bytes"
"errors"
"fmt"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/plugin/evm/upgrade/subnetevm"
"github.com/luxfi/geth/core/types"
)
const (
maximumExtraDataSize = 64 // Maximum size extra data may be after Genesis.
)
var (
errInvalidExtraPrefix = errors.New("invalid header.Extra prefix")
errInvalidExtraLength = errors.New("invalid header.Extra length")
)
// ExtraPrefix takes the previous header and the timestamp of its child
// block and calculates the expected extra prefix for the child block.
func ExtraPrefix(
config *extras.ChainConfig,
parent *types.Header,
header *types.Header,
) ([]byte, error) {
switch {
case config.IsSubnetEVM(header.Time):
window, err := feeWindow(config, parent, header.Time)
if err != nil {
return nil, fmt.Errorf("failed to calculate fee window: %w", err)
}
return window.Bytes(), nil
default:
// Prior to SubnetEVM there was no expected extra prefix.
return nil, nil
}
}
// VerifyExtraPrefix verifies that the header's Extra field is correctly
// formatted.
func VerifyExtraPrefix(
config *extras.ChainConfig,
parent *types.Header,
header *types.Header,
) error {
switch {
case config.IsSubnetEVM(header.Time):
feeWindow, err := feeWindow(config, parent, header.Time)
if err != nil {
return fmt.Errorf("calculating expected fee window: %w", err)
}
feeWindowBytes := feeWindow.Bytes()
if !bytes.HasPrefix(header.Extra, feeWindowBytes) {
return fmt.Errorf("%w: expected %x as prefix, found %x",
errInvalidExtraPrefix,
feeWindowBytes,
header.Extra,
)
}
}
return nil
}
// VerifyExtra verifies that the header's Extra field is correctly formatted for
// rules.
//
// TODO: Should this be merged with VerifyExtraPrefix?
func VerifyExtra(rules extras.LuxRules, extra []byte) error {
extraLen := len(extra)
switch {
case rules.IsDurango:
if extraLen < subnetevm.WindowSize {
return fmt.Errorf(
"%w: expected >= %d but got %d",
errInvalidExtraLength,
subnetevm.WindowSize,
extraLen,
)
}
case rules.IsSubnetEVM:
if extraLen != subnetevm.WindowSize {
return fmt.Errorf(
"%w: expected %d but got %d",
errInvalidExtraLength,
subnetevm.WindowSize,
extraLen,
)
}
default:
if uint64(extraLen) > maximumExtraDataSize {
return fmt.Errorf(
"%w: expected <= %d but got %d",
errInvalidExtraLength,
maximumExtraDataSize,
extraLen,
)
}
}
return nil
}
// PredicateBytesFromExtra returns the predicate result bytes from the header's
// extra data. If the extra data is not long enough, an empty slice is returned.
func PredicateBytesFromExtra(extra []byte) []byte {
offset := subnetevm.WindowSize
// Prior to Durango, the VM enforces the extra data is smaller than or equal
// to `offset`.
// After Durango, the VM pre-verifies the extra data past `offset` is valid.
if len(extra) <= offset {
return nil
}
return extra[offset:]
}
// SetPredicateBytesInExtra sets the predicate result bytes in the header's extra
// data. If the extra data is not long enough (i.e., an incomplete header.Extra
// as built in the miner), it is padded with zeros.
func SetPredicateBytesInExtra(extra []byte, predicateBytes []byte) []byte {
offset := subnetevm.WindowSize
if len(extra) < offset {
// pad extra with zeros
extra = append(extra, make([]byte, offset-len(extra))...)
} else {
// truncate extra to the offset
extra = extra[:offset]
}
extra = append(extra, predicateBytes...)
return extra
}
-369
View File
@@ -1,369 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"math/big"
"testing"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/plugin/evm/customtypes"
"github.com/luxfi/evm/plugin/evm/upgrade/subnetevm"
"github.com/luxfi/evm/utils"
"github.com/luxfi/geth/core/types"
"github.com/stretchr/testify/require"
)
const (
targetGas = 10_000_000
blockGas = 1_000_000
)
func TestExtraPrefix(t *testing.T) {
tests := []struct {
name string
upgrades extras.NetworkUpgrades
parent *types.Header
header *types.Header
want []byte
wantErr error
}{
{
name: "pre_subnet_evm",
upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades,
header: &types.Header{},
want: nil,
wantErr: nil,
},
{
name: "subnet_evm_first_block",
upgrades: extras.NetworkUpgrades{
SubnetEVMTimestamp: utils.NewUint64(1),
},
parent: &types.Header{
Number: big.NewInt(1),
},
header: &types.Header{
Time: 1,
},
want: (&subnetevm.Window{}).Bytes(),
},
{
name: "subnet_evm_genesis_block",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(0),
},
header: &types.Header{},
want: (&subnetevm.Window{}).Bytes(),
},
{
name: "subnet_evm_invalid_fee_window",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
},
header: &types.Header{},
wantErr: subnetevm.ErrWindowInsufficientLength,
},
{
name: "subnet_evm_invalid_timestamp",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
Time: 1,
Extra: (&subnetevm.Window{}).Bytes(),
},
header: &types.Header{
Time: 0,
},
wantErr: errInvalidTimestamp,
},
{
name: "subnet_evm_normal",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: customtypes.WithHeaderExtra(
&types.Header{
Number: big.NewInt(1),
GasUsed: targetGas,
Extra: (&subnetevm.Window{
1, 2, 3, 4,
}).Bytes(),
},
&customtypes.HeaderExtra{
BlockGasCost: big.NewInt(blockGas),
},
),
header: &types.Header{
Time: 1,
},
want: func() []byte {
window := subnetevm.Window{
1, 2, 3, 4,
}
window.Add(targetGas)
window.Shift(1)
return window.Bytes()
}(),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require := require.New(t)
config := &extras.ChainConfig{
NetworkUpgrades: test.upgrades,
}
got, err := ExtraPrefix(config, test.parent, test.header)
require.ErrorIs(err, test.wantErr)
require.Equal(test.want, got)
})
}
}
func TestVerifyExtraPrefix(t *testing.T) {
tests := []struct {
name string
upgrades extras.NetworkUpgrades
parent *types.Header
header *types.Header
wantErr error
}{
{
name: "pre_subnet_evm",
upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades,
header: &types.Header{},
wantErr: nil,
},
{
name: "subnet_evm_invalid_parent_header",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(1),
},
header: &types.Header{},
wantErr: subnetevm.ErrWindowInsufficientLength,
},
{
name: "subnet_evm_invalid_header",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(0),
},
header: &types.Header{},
wantErr: errInvalidExtraPrefix,
},
{
name: "subnet_evm_valid",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
Number: big.NewInt(0),
},
header: &types.Header{
Extra: (&subnetevm.Window{}).Bytes(),
},
wantErr: nil,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
config := &extras.ChainConfig{
NetworkUpgrades: test.upgrades,
}
err := VerifyExtraPrefix(config, test.parent, test.header)
require.ErrorIs(t, err, test.wantErr)
})
}
}
func TestVerifyExtra(t *testing.T) {
tests := []struct {
name string
rules extras.LuxRules
extra []byte
expected error
}{
{
name: "initial_valid",
rules: extras.LuxRules{},
extra: make([]byte, maximumExtraDataSize),
expected: nil,
},
{
name: "initial_invalid",
rules: extras.LuxRules{},
extra: make([]byte, maximumExtraDataSize+1),
expected: errInvalidExtraLength,
},
{
name: "subnet_evm_valid",
rules: extras.LuxRules{
IsSubnetEVM: true,
},
extra: make([]byte, subnetevm.WindowSize),
expected: nil,
},
{
name: "subnet_evm_invalid_less",
rules: extras.LuxRules{
IsSubnetEVM: true,
},
extra: make([]byte, subnetevm.WindowSize-1),
expected: errInvalidExtraLength,
},
{
name: "subnet_evm_invalid_more",
rules: extras.LuxRules{
IsSubnetEVM: true,
},
extra: make([]byte, subnetevm.WindowSize+1),
expected: errInvalidExtraLength,
},
{
name: "durango_valid_min",
rules: extras.LuxRules{
IsDurango: true,
},
extra: make([]byte, subnetevm.WindowSize),
expected: nil,
},
{
name: "durango_valid_extra",
rules: extras.LuxRules{
IsDurango: true,
},
extra: make([]byte, subnetevm.WindowSize+1),
expected: nil,
},
{
name: "durango_invalid",
rules: extras.LuxRules{
IsDurango: true,
},
extra: make([]byte, subnetevm.WindowSize-1),
expected: errInvalidExtraLength,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := VerifyExtra(test.rules, test.extra)
require.ErrorIs(t, err, test.expected)
})
}
}
func TestPredicateBytesFromExtra(t *testing.T) {
tests := []struct {
name string
extra []byte
expected []byte
}{
{
name: "empty_extra",
extra: nil,
expected: nil,
},
{
name: "too_short",
extra: make([]byte, subnetevm.WindowSize-1),
expected: nil,
},
{
name: "empty_predicate",
extra: make([]byte, subnetevm.WindowSize),
expected: nil,
},
{
name: "non_empty_predicate",
extra: []byte{
subnetevm.WindowSize: 5,
},
expected: []byte{5},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := PredicateBytesFromExtra(test.extra)
require.Equal(t, test.expected, got)
})
}
}
func TestSetPredicateBytesInExtra(t *testing.T) {
tests := []struct {
name string
extra []byte
predicate []byte
want []byte
}{
{
name: "empty_extra_predicate",
want: make([]byte, subnetevm.WindowSize),
},
{
name: "extra_too_short",
extra: []byte{1},
predicate: []byte{2},
want: []byte{
0: 1,
subnetevm.WindowSize: 2,
},
},
{
name: "extra_too_long",
extra: []byte{
subnetevm.WindowSize: 1,
},
predicate: []byte{2},
want: []byte{
subnetevm.WindowSize: 2,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := SetPredicateBytesInExtra(test.extra, test.predicate)
require.Equal(t, test.want, got)
})
}
}
func TestPredicateBytesExtra(t *testing.T) {
tests := []struct {
name string
extra []byte
predicate []byte
wantExtraWithPredicate []byte
wantPredicateBytes []byte
}{
{
name: "empty_extra_predicate",
extra: nil,
predicate: nil,
wantExtraWithPredicate: make([]byte, subnetevm.WindowSize),
wantPredicateBytes: nil,
},
{
name: "extra_too_short",
extra: []byte{
0: 1,
subnetevm.WindowSize - 1: 0,
},
predicate: []byte{2},
wantExtraWithPredicate: []byte{
0: 1,
subnetevm.WindowSize: 2,
},
wantPredicateBytes: []byte{2},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
gotExtra := SetPredicateBytesInExtra(test.extra, test.predicate)
require.Equal(t, test.wantExtraWithPredicate, gotExtra)
gotPredicateBytes := PredicateBytesFromExtra(gotExtra)
require.Equal(t, test.wantPredicateBytes, gotPredicateBytes)
})
}
}
-118
View File
@@ -1,118 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"errors"
"fmt"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/geth/core/types"
ethparams "github.com/luxfi/geth/params"
"github.com/luxfi/node/utils/math"
)
var (
errInvalidGasUsed = errors.New("invalid gas used")
errInvalidGasLimit = errors.New("invalid gas limit")
)
type CalculateGasLimitFunc func(parentGasUsed, parentGasLimit, gasFloor, gasCeil uint64) uint64
// GasLimit takes the previous header and the timestamp of its child block and
// calculates the gas limit for the child block.
func GasLimit(
config *extras.ChainConfig,
feeConfig commontype.FeeConfig,
parent *types.Header,
timestamp uint64,
) (uint64, error) {
switch {
case config.IsSubnetEVM(timestamp):
return feeConfig.GasLimit.Uint64(), nil
default:
// since all chains have activated Subnet-EVM,
// this code is not used in production. To avoid a dependency on the
// `core` package, this code is modified to just return the parent gas
// limit; which was valid to do prior to Subnet-EVM.
return parent.GasLimit, nil
}
}
// VerifyGasUsed verifies that the gas used is less than or equal to the gas
// limit.
func VerifyGasUsed(
config *extras.ChainConfig,
feeConfig commontype.FeeConfig,
parent *types.Header,
header *types.Header,
) error {
gasUsed := header.GasUsed
capacity, err := GasCapacity(config, feeConfig, parent, header.Time)
if err != nil {
return fmt.Errorf("calculating gas capacity: %w", err)
}
if gasUsed > capacity {
return fmt.Errorf("%w: have %d, capacity %d",
errInvalidGasUsed,
gasUsed,
capacity,
)
}
return nil
}
// VerifyGasLimit verifies that the gas limit for the header is valid.
func VerifyGasLimit(
config *extras.ChainConfig,
feeConfig commontype.FeeConfig,
parent *types.Header,
header *types.Header,
) error {
switch {
case config.IsSubnetEVM(header.Time):
expectedGasLimit := feeConfig.GasLimit.Uint64()
if header.GasLimit != expectedGasLimit {
return fmt.Errorf("%w: expected to be %d in Subnet-EVM, but found %d",
errInvalidGasLimit,
expectedGasLimit,
header.GasLimit,
)
}
default:
if header.GasLimit < ethparams.MinGasLimit || header.GasLimit > ethparams.MaxGasLimit {
return fmt.Errorf("%w: %d not in range [%d, %d]",
errInvalidGasLimit,
header.GasLimit,
ethparams.MinGasLimit,
ethparams.MaxGasLimit,
)
}
// Verify that the gas limit remains within allowed bounds
diff := math.AbsDiff(parent.GasLimit, header.GasLimit)
limit := parent.GasLimit / ethparams.GasLimitBoundDivisor
if diff >= limit {
return fmt.Errorf("%w: have %d, want %d += %d",
errInvalidGasLimit,
header.GasLimit,
parent.GasLimit,
limit,
)
}
}
return nil
}
// GasCapacity takes the previous header and the timestamp of its child block
// and calculates the available gas that can be consumed in the child block.
func GasCapacity(
config *extras.ChainConfig,
feeConfig commontype.FeeConfig,
parent *types.Header,
timestamp uint64,
) (uint64, error) {
return GasLimit(config, feeConfig, parent, timestamp)
}
-185
View File
@@ -1,185 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"testing"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/geth/core/types"
ethparams "github.com/luxfi/geth/params"
"github.com/stretchr/testify/require"
)
func TestGasLimit(t *testing.T) {
t.Run("normal", func(t *testing.T) {
GasLimitTest(t, testFeeConfig)
})
t.Run("double", func(t *testing.T) {
GasLimitTest(t, testFeeConfigDouble)
})
}
func GasLimitTest(t *testing.T, feeConfig commontype.FeeConfig) {
tests := []struct {
name string
upgrades extras.NetworkUpgrades
parent *types.Header
timestamp uint64
want uint64
wantErr error
}{
{
name: "subnet_evm",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
want: feeConfig.GasLimit.Uint64(),
},
{
name: "pre_subnet_evm",
upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
GasLimit: 1,
},
want: 1, // Same as parent
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require := require.New(t)
config := &extras.ChainConfig{
NetworkUpgrades: test.upgrades,
}
got, err := GasLimit(config, feeConfig, test.parent, test.timestamp)
require.ErrorIs(err, test.wantErr)
require.Equal(test.want, got)
})
}
}
func TestVerifyGasLimit(t *testing.T) {
t.Run("normal", func(t *testing.T) {
VerifyGasLimitTest(t, testFeeConfig)
})
t.Run("double", func(t *testing.T) {
VerifyGasLimitTest(t, testFeeConfigDouble)
})
}
func VerifyGasLimitTest(t *testing.T, feeConfig commontype.FeeConfig) {
tests := []struct {
name string
upgrades extras.NetworkUpgrades
parent *types.Header
header *types.Header
want error
}{
{
name: "subnet_evm_valid",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
header: &types.Header{
GasLimit: feeConfig.GasLimit.Uint64(),
},
},
{
name: "subnet_evm_invalid",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
header: &types.Header{
GasLimit: feeConfig.GasLimit.Uint64() + 1,
},
want: errInvalidGasLimit,
},
{
name: "pre_subnet_evm_valid",
upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
GasLimit: 50_000,
},
header: &types.Header{
GasLimit: 50_001, // Gas limit is allowed to change by 1/1024
},
},
{
name: "pre_subnet_evm_too_low",
upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
GasLimit: ethparams.MinGasLimit,
},
header: &types.Header{
GasLimit: ethparams.MinGasLimit - 1,
},
want: errInvalidGasLimit,
},
{
name: "pre_subnet_evm_too_high",
upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
GasLimit: ethparams.MaxGasLimit,
},
header: &types.Header{
GasLimit: ethparams.MaxGasLimit + 1,
},
want: errInvalidGasLimit,
},
{
name: "pre_subnet_evm_too_large",
upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades,
parent: &types.Header{
GasLimit: ethparams.MinGasLimit,
},
header: &types.Header{
GasLimit: ethparams.MaxGasLimit,
},
want: errInvalidGasLimit,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
config := &extras.ChainConfig{
NetworkUpgrades: test.upgrades,
}
err := VerifyGasLimit(config, feeConfig, test.parent, test.header)
require.ErrorIs(t, err, test.want)
})
}
}
func TestGasCapacity(t *testing.T) {
t.Run("normal", func(t *testing.T) {
GasCapacityTest(t, testFeeConfig)
})
t.Run("double", func(t *testing.T) {
GasCapacityTest(t, testFeeConfigDouble)
})
}
func GasCapacityTest(t *testing.T, feeConfig commontype.FeeConfig) {
tests := []struct {
name string
upgrades extras.NetworkUpgrades
parent *types.Header
timestamp uint64
want uint64
wantErr error
}{
{
name: "subnet_evm",
upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades,
want: feeConfig.GasLimit.Uint64(),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require := require.New(t)
config := &extras.ChainConfig{
NetworkUpgrades: test.upgrades,
}
got, err := GasCapacity(config, feeConfig, test.parent, test.timestamp)
require.ErrorIs(err, test.wantErr)
require.Equal(test.want, got)
})
}
}
-14
View File
@@ -1,14 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import "context"
// Health returns nil if this chain is healthy.
// Also returns details, which should be one of:
// string, []byte, map[string]string
func (vm *VM) HealthCheck(context.Context) (interface{}, error) {
// TODO perform actual health check
return nil, nil
}
-76
View File
@@ -1,76 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/tools/go/packages"
)
// getDependencies takes a fully qualified package name and returns a map of all
// its recursive package imports (including itself) in the same format.
func getDependencies(packageName string) (map[string]struct{}, error) {
// Configure the load mode to include dependencies
cfg := &packages.Config{Mode: packages.NeedImports | packages.NeedName}
pkgs, err := packages.Load(cfg, packageName)
if err != nil {
return nil, fmt.Errorf("failed to load package: %v", err)
}
if len(pkgs) == 0 || pkgs[0].Errors != nil {
return nil, fmt.Errorf("failed to load package %s", packageName)
}
deps := make(map[string]struct{})
var collectDeps func(pkg *packages.Package)
collectDeps = func(pkg *packages.Package) {
if _, ok := deps[pkg.PkgPath]; ok {
return // Avoid re-processing the same dependency
}
deps[pkg.PkgPath] = struct{}{}
for _, dep := range pkg.Imports {
collectDeps(dep)
}
}
// Start collecting dependencies
for _, pkg := range pkgs {
collectDeps(pkg)
}
return deps, nil
}
func TestMustNotImport(t *testing.T) {
withRepo := func(pkg string) string {
const repo = "github.com/luxfi/evm"
return fmt.Sprintf("%s/%s", repo, pkg)
}
mustNotImport := map[string][]string{
// The following sub-packages of plugin/evm must not import core, core/vm
// so clients (e.g., wallets, e2e tests) can import them without pulling in
// the entire VM logic.
// Importing these packages configures geth globally and it is not
// possible to do so for both coreth and evm, where the client may
// wish to connect to multiple chains.
"plugin/evm/client": {"core", "plugin/evm/customtypes", "params"},
"plugin/evm/config": {"core", "params", "plugin/evm/customtypes"},
"plugin/evm/header": {"core", "core/vm", "params"},
// "ethclient": {"plugin/evm/customtypes", "params"},
"warp": {"plugin/evm/customtypes", "params"},
}
for packageName, forbiddenImports := range mustNotImport {
imports, err := getDependencies(withRepo(packageName))
require.NoError(t, err)
for _, forbiddenImport := range forbiddenImports {
fullForbiddenImport := withRepo(forbiddenImport)
_, found := imports[fullForbiddenImport]
require.False(t, found, "package %s must not import %s, check output of go list -f '{{ .Deps }}' \"%s\" ", packageName, fullForbiddenImport, withRepo(packageName))
}
}
}
-102
View File
@@ -1,102 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package log
import (
"fmt"
"io"
"log/slog"
"strings"
"github.com/luxfi/log"
)
type Logger struct {
log.Logger
}
// InitLogger initializes logger with alias and sets the log level and format with the original [os.StdErr] interface
// along with the context logger.
func InitLogger(alias string, level string, jsonFormat bool, writer io.Writer) (Logger, error) {
// Parse log level
logLevel, err := ParseLogLevel(level)
if err != nil {
return Logger{}, err
}
var handler slog.Handler
if jsonFormat {
handler = log.JSONHandlerWithLevel(writer, logLevel)
// Add context to the handler
handler = handler.WithAttrs([]slog.Attr{
slog.String("logger", fmt.Sprintf("%s Chain", alias)),
})
} else {
useColor := false
chainStr := fmt.Sprintf("<%s Chain> ", alias)
termHandler := log.NewTerminalHandlerWithLevel(writer, logLevel, useColor)
// TODO: Add chain prefix to terminal handler when API is available
handler = termHandler
_ = chainStr
}
// Create logger
logger := log.Root().With("chain", alias)
c := Logger{
Logger: logger,
}
log.SetDefault(c.Logger)
return c, nil
}
// SetLogLevel sets the log level of initialized log handler.
func (l *Logger) SetLogLevel(level string) error {
// Parse and set new log level
logLevel, err := ParseLogLevel(level)
if err != nil {
return err
}
// Set the level on the logger
l.SetLevel(logLevel)
return nil
}
// ParseLogLevel parses a string log level
func ParseLogLevel(level string) (slog.Level, error) {
switch strings.ToLower(level) {
case "trace":
return log.LevelTrace, nil
case "debug":
return log.LevelDebug, nil
case "info":
return log.LevelInfo, nil
case "warn", "warning":
return log.LevelWarn, nil
case "error":
return log.LevelError, nil
case "crit", "critical":
return log.LevelCrit, nil
default:
return log.LevelInfo, fmt.Errorf("unknown log level: %s", level)
}
}
// locationTrims are trimmed for display to avoid unwieldy log lines.
var locationTrims = []string{
"evm/",
}
func trimPrefixes(s string) string {
for _, prefix := range locationTrims {
idx := strings.LastIndex(s, prefix)
if idx >= 0 {
s = s[idx+len(prefix):]
}
}
return s
}
-37
View File
@@ -1,37 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package log
import (
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestInitLogger(t *testing.T) {
tests := []struct {
logLevel string
expectedErr bool
}{
{
logLevel: "info",
},
{
logLevel: "cchain", // invalid
expectedErr: true,
},
}
for _, test := range tests {
t.Run(test.logLevel, func(t *testing.T) {
require := require.New(t)
_, err := InitLogger("alias", test.logLevel, true, os.Stderr)
if test.expectedErr {
require.Error(err)
} else {
require.NoError(err)
}
})
}
}
-26
View File
@@ -1,26 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"io"
)
// loggerWriter wraps a consensus.Logger to implement io.Writer
type loggerWriter struct {
logger interface{}
}
// Write implements io.Writer
func (w *loggerWriter) Write(p []byte) (n int, err error) {
// Since the logger is an interface{} from GetLogger, we can't call methods on it
// This is a placeholder that just returns success
// In practice, the logger will be set differently during initialization
return len(p), nil
}
// newLoggerWriter creates a new loggerWriter
func newLoggerWriter(logger interface{}) io.Writer {
return &loggerWriter{logger: logger}
}
-43
View File
@@ -1,43 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"context"
"fmt"
"github.com/luxfi/ids"
"github.com/luxfi/geth/common"
)
var (
_ Request = BlockRequest{}
)
// BlockRequest is a request to retrieve Parents number of blocks starting from Hash from newest-oldest manner
type BlockRequest struct {
Hash common.Hash `serialize:"true"`
Height uint64 `serialize:"true"`
Parents uint16 `serialize:"true"`
}
func (b BlockRequest) String() string {
return fmt.Sprintf(
"BlockRequest(Hash=%s, Height=%d, Parents=%d)",
b.Hash, b.Height, b.Parents,
)
}
func (b BlockRequest) Handle(ctx context.Context, nodeID ids.NodeID, requestID uint32, handler RequestHandler) ([]byte, error) {
return handler.HandleBlockRequest(ctx, nodeID, requestID, b)
}
// BlockResponse is a response to a BlockRequest
// Blocks is slice of RLP encoded blocks starting with the block
// requested in BlockRequest.Hash. The next block is the parent, etc.
// handler: handlers.BlockRequestHandler
type BlockResponse struct {
Blocks [][]byte `serialize:"true"`
}
-65
View File
@@ -1,65 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"encoding/base64"
"math/rand"
"testing"
"github.com/luxfi/geth/common"
"github.com/stretchr/testify/assert"
)
// TestMarshalBlockRequest asserts that the structure or serialization logic hasn't changed, primarily to
// ensure compatibility with the network.
func TestMarshalBlockRequest(t *testing.T) {
blockRequest := BlockRequest{
Hash: common.BytesToHash([]byte("some hash is here yo")),
Height: 1337,
Parents: 64,
}
base64BlockRequest := "AAAAAAAAAAAAAAAAAABzb21lIGhhc2ggaXMgaGVyZSB5bwAAAAAAAAU5AEA="
blockRequestBytes, err := Codec.Marshal(Version, blockRequest)
assert.NoError(t, err)
assert.Equal(t, base64BlockRequest, base64.StdEncoding.EncodeToString(blockRequestBytes))
var b BlockRequest
_, err = Codec.Unmarshal(blockRequestBytes, &b)
assert.NoError(t, err)
assert.Equal(t, blockRequest.Hash, b.Hash)
assert.Equal(t, blockRequest.Height, b.Height)
assert.Equal(t, blockRequest.Parents, b.Parents)
}
// TestMarshalBlockResponse asserts that the structure or serialization logic hasn't changed, primarily to
// ensure compatibility with the network.
func TestMarshalBlockResponse(t *testing.T) {
// create some random bytes
// use deterministic random source for testing
r := rand.New(rand.NewSource(1))
blocksBytes := make([][]byte, 32)
for i := range blocksBytes {
blocksBytes[i] = make([]byte, r.Intn(32)+32) // min 32 length, max 64
_, err := r.Read(blocksBytes[i])
assert.NoError(t, err)
}
blockResponse := BlockResponse{
Blocks: blocksBytes,
}
base64BlockResponse := "AAAAAAAgAAAAIU8WP18PmmIdcpVmx00QA3xNe7sEB9HixkmBhVrYaB0NhgAAADnR6ZTSxCKs0gigByk5SH9pmeudGKRHhARdh/PGfPInRumVr1olNnlRuqL/bNRxxIPxX7kLrbN8WCEAAAA6tmgLTnyLdjobHUnUlVyEhiFjJSU/7HON16nii/khEZwWDwcCRIYVu9oIMT9qjrZo0gv1BZh1kh5migAAACtb3yx/xIRo0tbFL1BU4tCDa/hMcXTLdHY2TMPb2Wiw9xcu2FeUuzWLDDtSAAAAO12heG+f69ehnQ97usvgJVqlt9RL7ED4TIkrm//UNimwIjvupfT3Q5H0RdFa/UKUBAN09pJLmMv4cT+NAAAAMpYtJOLK/Mrjph+1hrFDI6a8j5598dkpMz/5k5M76m9bOvbeA3Q2bEcZ5DobBn2JvH8BAAAAOfHxekxyFaO1OeseWEnGB327VyL1cXoomiZvl2R5gZmOvqicC0s3OXARXoLtb0ElyPpzEeTX3vqSLQAAACc2zU8kq/ffhmuqVgODZ61hRd4e6PSosJk+vfiIOgrYvpw5eLBIg+UAAAAkahVqnexqQOmh0AfwM8KCMGG90Oqln45NpkMBBSINCyloi3NLAAAAKI6gENd8luqAp6Zl9gb2pjt/Pf0lZ8GJeeTWDyZobZvy+ybJAf81TN4AAAA8FgfuKbpk+Eq0PKDG5rkcH9O+iZBDQXnTr0SRo2kBLbktGE/DnRc0/1cWQolTu2hl/PkrDDoXyQKL6ZFOAAAAMwl50YMDVvKlTD3qsqS0R11jr76PtWmHx39YGFJvGBS+gjNQ6rE5NfMdhEhFF+kkrveK4QAAADhRwAdVkgww7CmjcDk0v1CijaECl13tp351hXnqPf5BNqv3UrO4Jx0D6USzyds2a3UEX479adIq5QAAADpBGUfLVbzqQGsy1hCL1oWE9X43yqxuM/6qMmOjmUNwJLqcmxRniidPAakQrilfbvv+X1q/RMzeJjtWAAAAKAZjPn05Bp8BojnENlhUw69/a0HWMfkrmo0S9BJXMl//My91drBiBVYAAAAqMEo+Pq6QGlJyDahcoeSzjq8/RMbG74Ni8vVPwA4J1vwlZAhUwV38rKqKAAAAOyzszlo6lLTTOKUUPmNAjYcksM8/rhej95vhBy+2PDXWBCxBYPOO6eKp8/tP+wAZtFTVIrX/oXYEGT+4AAAAMpZnz1PD9SDIibeb9QTPtXx2ASMtWJuszqnW4mPiXCd0HT9sYsu7FdmvvL9/faQasECOAAAALzk4vxd0rOdwmk8JHpqD/erg7FXrIzqbU5TLPHhWtUbTE8ijtMHA4FRH9Lo3DrNtAAAAPLz97PUi4qbx7Qr+wfjiD6q+32sWLnF9OnSKWGd6DFY0j4khomaxHQ8zTGL+UrpTrxl3nLKUi2Vw/6C3cwAAADqWPBMK15dRJSEPDvHDFAkPB8eab1ccJG8+msC3QT7xEL1YsAznO/9wb3/0tvRAkKMnEfMgjk5LictRAAAAJ2XOZAA98kaJKNWiO5ynQPgMk4LZxgNK0pYMeWUD4c4iFyX1DK8fvwAAADtcR6U9v459yvyeE4ZHpLRO1LzpZO1H90qllEaM7TI8t28NP6xHbJ+wP8kij7roj9WAZjoEVLaDEiB/CgAAADc7WExi1QJ84VpPClglDY+1Dnfyv08BUuXUlDWAf51Ll75vt3lwRmpWJv4zQIz56I4seXQIoy0pAAAAKkFrryBqmDIJgsharXA4SFnAWksTodWy9b/vWm7ZLaSCyqlWjltv6dip3QAAAC7Z6wkne1AJRMvoAKCxUn6mRymoYdL2SXoyNcN/QZJ3nsHZazscVCT84LcnsDByAAAAI+ZAq8lEj93rIZHZRcBHZ6+Eev0O212IV7eZrLGOSv+r4wN/AAAAL/7MQW5zTTc8Xr68nNzFlbzOPHvT2N+T+rfhJd3rr+ZaMb1dQeLSzpwrF4kvD+oZAAAAMTGikNy/poQG6HcHP/CINOGXpANKpIr6P4W4picIyuu6yIC1uJuT2lOBAWRAIQTmSLYAAAA1ImobDzE6id38RUxfj3KsibOLGfU3hMGem+rAPIdaJ9sCneN643pCMYgTSHaFkpNZyoxeuU4AAAA9FS3Br0LquOKSXG2u5N5e+fnc8I38vQK4CAk5hYWSig995QvhptwdV2joU3mI/dzlYum5SMkYu6PpM+XEAAAAAC3Nrne6HSWbGIpLIchvvCPXKLRTR+raZQryTFbQgAqGkTMgiKgFvVXERuJesHU="
blockResponseBytes, err := Codec.Marshal(Version, blockResponse)
assert.NoError(t, err)
assert.Equal(t, base64BlockResponse, base64.StdEncoding.EncodeToString(blockResponseBytes))
var b BlockResponse
_, err = Codec.Unmarshal(blockResponseBytes, &b)
assert.NoError(t, err)
assert.Equal(t, blockResponse.Blocks, b.Blocks)
}
-47
View File
@@ -1,47 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"context"
"fmt"
"strings"
"github.com/luxfi/geth/common"
"github.com/luxfi/ids"
)
var _ Request = CodeRequest{}
// CodeRequest is a request to retrieve a contract code with specified Hash
type CodeRequest struct {
// Hashes is a list of contract code hashes
Hashes []common.Hash `serialize:"true"`
}
func (c CodeRequest) String() string {
hashStrs := make([]string, len(c.Hashes))
for i, hash := range c.Hashes {
hashStrs[i] = hash.String()
}
return fmt.Sprintf("CodeRequest(Hashes=%s)", strings.Join(hashStrs, ", "))
}
func (c CodeRequest) Handle(ctx context.Context, nodeID ids.NodeID, requestID uint32, handler RequestHandler) ([]byte, error) {
return handler.HandleCodeRequest(ctx, nodeID, requestID, c)
}
func NewCodeRequest(hashes []common.Hash) CodeRequest {
return CodeRequest{
Hashes: hashes,
}
}
// CodeResponse is a response to a CodeRequest
// crypto.Keccak256Hash of each element in Data is expected to equal
// the corresponding element in CodeRequest.Hashes
// handler: handlers.CodeRequestHandler
type CodeResponse struct {
Data [][]byte `serialize:"true"`
}
-58
View File
@@ -1,58 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"encoding/base64"
"math/rand"
"testing"
"github.com/luxfi/geth/common"
"github.com/stretchr/testify/assert"
)
// TestMarshalCodeRequest asserts that the structure or serialization logic hasn't changed, primarily to
// ensure compatibility with the network.
func TestMarshalCodeRequest(t *testing.T) {
codeRequest := CodeRequest{
Hashes: []common.Hash{common.BytesToHash([]byte("some code pls"))},
}
base64CodeRequest := "AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAHNvbWUgY29kZSBwbHM="
codeRequestBytes, err := Codec.Marshal(Version, codeRequest)
assert.NoError(t, err)
assert.Equal(t, base64CodeRequest, base64.StdEncoding.EncodeToString(codeRequestBytes))
var c CodeRequest
_, err = Codec.Unmarshal(codeRequestBytes, &c)
assert.NoError(t, err)
assert.Equal(t, codeRequest.Hashes, c.Hashes)
}
// TestMarshalCodeResponse asserts that the structure or serialization logic hasn't changed, primarily to
// ensure compatibility with the network.
func TestMarshalCodeResponse(t *testing.T) {
// generate some random code data
// use deterministic random source for testing
r := rand.New(rand.NewSource(1))
codeData := make([]byte, 50)
_, err := r.Read(codeData)
assert.NoError(t, err)
codeResponse := CodeResponse{
Data: [][]byte{codeData},
}
base64CodeResponse := "AAAAAAABAAAAMlL9/AchgmVPFj9fD5piHXKVZsdNEAN8TXu7BAfR4sZJgYVa2GgdDYbR6R4AFnk5y2aU"
codeResponseBytes, err := Codec.Marshal(Version, codeResponse)
assert.NoError(t, err)
assert.Equal(t, base64CodeResponse, base64.StdEncoding.EncodeToString(codeResponseBytes))
var c CodeResponse
_, err = Codec.Unmarshal(codeResponseBytes, &c)
assert.NoError(t, err)
assert.Equal(t, codeResponse.Data, c.Data)
}
-52
View File
@@ -1,52 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"github.com/luxfi/node/codec"
"github.com/luxfi/node/codec/linearcodec"
"github.com/luxfi/node/utils/units"
"github.com/luxfi/node/utils/wrappers"
)
const (
Version = uint16(0)
maxMessageSize = 2*units.MiB - 64*units.KiB // Subtract 64 KiB from p2p network cap to leave room for encoding overhead from Luxd
)
var (
Codec codec.Manager
)
func init() {
Codec = codec.NewManager(maxMessageSize)
c := linearcodec.NewDefault()
// Skip registration to keep registeredTypes unchanged after legacy gossip deprecation
c.SkipRegistrations(1)
errs := wrappers.Errs{}
errs.Add(
// Types for state sync frontier consensus
c.RegisterType(SyncSummary{}),
// state sync types
c.RegisterType(BlockRequest{}),
c.RegisterType(BlockResponse{}),
c.RegisterType(LeafsRequest{}),
c.RegisterType(LeafsResponse{}),
c.RegisterType(CodeRequest{}),
c.RegisterType(CodeResponse{}),
)
// Deprecated Warp request/responde types are skipped
// See https://github.com/luxfi/coreth/pull/999
c.SkipRegistrations(3)
Codec.RegisterCodec(Version, c)
if errs.Errored() {
panic(errs.Err)
}
}
-48
View File
@@ -1,48 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"context"
"github.com/luxfi/ids"
)
var (
_ RequestHandler = NoopRequestHandler{}
)
// RequestHandler interface handles incoming requests from peers
// Must have methods in format of handleType(context.Context, ids.NodeID, uint32, request Type) error
// so that the Request object of relevant Type can invoke its respective handle method
// on this struct.
// Also see GossipHandler for implementation style.
type RequestHandler interface {
HandleStateTrieLeafsRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, leafsRequest LeafsRequest) ([]byte, error)
HandleBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, request BlockRequest) ([]byte, error)
HandleCodeRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, codeRequest CodeRequest) ([]byte, error)
}
// ResponseHandler handles response for a sent request
// Only one of OnResponse or OnFailure is called for a given requestID, not both
type ResponseHandler interface {
// OnResponse is invoked when the peer responded to a request
OnResponse(response []byte) error
// OnFailure is invoked when there was a failure in processing a request
OnFailure() error
}
type NoopRequestHandler struct{}
func (NoopRequestHandler) HandleStateTrieLeafsRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, leafsRequest LeafsRequest) ([]byte, error) {
return nil, nil
}
func (NoopRequestHandler) HandleBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, request BlockRequest) ([]byte, error) {
return nil, nil
}
func (NoopRequestHandler) HandleCodeRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, codeRequest CodeRequest) ([]byte, error) {
return nil, nil
}
-64
View File
@@ -1,64 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"context"
"fmt"
"github.com/luxfi/geth/common"
"github.com/luxfi/ids"
)
const MaxCodeHashesPerRequest = 5
var _ Request = LeafsRequest{}
// LeafsRequest is a request to receive trie leaves at specified Root within Start and End byte range
// Limit outlines maximum number of leaves to returns starting at Start
type LeafsRequest struct {
Root common.Hash `serialize:"true"`
Account common.Hash `serialize:"true"`
Start []byte `serialize:"true"`
End []byte `serialize:"true"`
Limit uint16 `serialize:"true"`
}
func (l LeafsRequest) String() string {
return fmt.Sprintf(
"LeafsRequest(Root=%s, Account=%s, Start=%s, End %s, Limit=%d)",
l.Root, l.Account, common.Bytes2Hex(l.Start), common.Bytes2Hex(l.End), l.Limit,
)
}
func (l LeafsRequest) Handle(ctx context.Context, nodeID ids.NodeID, requestID uint32, handler RequestHandler) ([]byte, error) {
return handler.HandleStateTrieLeafsRequest(ctx, nodeID, requestID, l)
}
// LeafsResponse is a response to a LeafsRequest
// Keys must be within LeafsRequest.Start and LeafsRequest.End and sorted in lexicographical order.
//
// ProofVals must be non-empty and contain a valid range proof unless the key-value pairs in the
// response are the entire trie.
// If the key-value pairs make up the entire trie, ProofVals should be empty since the root will be
// sufficient to prove that the leaves are included in the trie.
//
// More is a flag set in the client after verifying the response, which indicates if the last key-value
// pair in the response has any more elements to its right within the trie.
type LeafsResponse struct {
// Keys and Vals provides the key-value pairs in the trie in the response.
Keys [][]byte `serialize:"true"`
Vals [][]byte `serialize:"true"`
// More indicates if there are more leaves to the right of the last value in this response.
//
// This is not serialized since it is set in the client after verifying the response via
// VerifyRangeProof and determining if there are in fact more leaves to the right of the
// last value in this response.
More bool
// ProofVals contain the edge merkle-proofs for the range of keys included in the response.
// The keys for the proof are simply the keccak256 hashes of the values, so they are not included in the response to save bandwidth.
ProofVals [][]byte `serialize:"true"`
}
-167
View File
@@ -1,167 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"bytes"
"context"
"encoding/base64"
"math/rand"
"testing"
"github.com/luxfi/ids"
"github.com/luxfi/geth/common"
"github.com/stretchr/testify/assert"
)
// TestMarshalLeafsRequest asserts that the structure or serialization logic hasn't changed, primarily to
// ensure compatibility with the network.
func TestMarshalLeafsRequest(t *testing.T) {
// generate some random code data
// use deterministic random source for testing
r := rand.New(rand.NewSource(1))
startBytes := make([]byte, common.HashLength)
endBytes := make([]byte, common.HashLength)
_, err := r.Read(startBytes)
assert.NoError(t, err)
_, err = r.Read(endBytes)
assert.NoError(t, err)
leafsRequest := LeafsRequest{
Root: common.BytesToHash([]byte("im ROOTing for ya")),
Start: startBytes,
End: endBytes,
Limit: 1024,
}
base64LeafsRequest := "AAAAAAAAAAAAAAAAAAAAAABpbSBST09UaW5nIGZvciB5YQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFL9/AchgmVPFj9fD5piHXKVZsdNEAN8TXu7BAfR4sZJAAAAIIGFWthoHQ2G0ekeABZ5OctmlNLEIqzSCKAHKTlIf2mZBAA="
leafsRequestBytes, err := Codec.Marshal(Version, leafsRequest)
assert.NoError(t, err)
assert.Equal(t, base64LeafsRequest, base64.StdEncoding.EncodeToString(leafsRequestBytes))
var l LeafsRequest
_, err = Codec.Unmarshal(leafsRequestBytes, &l)
assert.NoError(t, err)
assert.Equal(t, leafsRequest.Root, l.Root)
assert.Equal(t, leafsRequest.Start, l.Start)
assert.Equal(t, leafsRequest.End, l.End)
assert.Equal(t, leafsRequest.Limit, l.Limit)
}
// TestMarshalLeafsResponse asserts that the structure or serialization logic hasn't changed, primarily to
// ensure compatibility with the network.
func TestMarshalLeafsResponse(t *testing.T) {
// generate some random code data
// use deterministic random source for testing
r := rand.New(rand.NewSource(1))
keysBytes := make([][]byte, 16)
valsBytes := make([][]byte, 16)
for i := range keysBytes {
keysBytes[i] = make([]byte, common.HashLength)
valsBytes[i] = make([]byte, r.Intn(8)+8) // min 8 bytes, max 16 bytes
_, err := r.Read(keysBytes[i])
assert.NoError(t, err)
_, err = r.Read(valsBytes[i])
assert.NoError(t, err)
}
nextKey := make([]byte, common.HashLength)
_, err := r.Read(nextKey)
assert.NoError(t, err)
proofVals := make([][]byte, 4)
for i := range proofVals {
proofVals[i] = make([]byte, r.Intn(8)+8) // min 8 bytes, max 16 bytes
_, err = r.Read(proofVals[i])
assert.NoError(t, err)
}
leafsResponse := LeafsResponse{
Keys: keysBytes,
Vals: valsBytes,
More: true,
ProofVals: proofVals,
}
base64LeafsResponse := "AAAAAAAQAAAAIE8WP18PmmIdcpVmx00QA3xNe7sEB9HixkmBhVrYaB0NAAAAIGagByk5SH9pmeudGKRHhARdh/PGfPInRumVr1olNnlRAAAAIK2zfFghtmgLTnyLdjobHUnUlVyEhiFjJSU/7HON16niAAAAIIYVu9oIMfUFmHWSHmaKW98sf8SERZLSVyvNBmjS1sUvAAAAIHHb2Wiw9xcu2FeUuzWLDDtSXaF4b5//CUJ52xlE69ehAAAAIPhMiSs77qX090OR9EXRWv1ClAQDdPaSS5jL+HE/jZYtAAAAIMr8yuOmvI+effHZKTM/+ZOTO+pvWzr23gN0NmxHGeQ6AAAAIBZZpE856x5YScYHfbtXIvVxeiiaJm+XZHmBmY6+qJwLAAAAIHOq53hmZ/fpNs1PJKv334ZrqlYDg2etYUXeHuj0qLCZAAAAIHiN5WOvpGfUnexqQOmh0AfwM8KCMGG90Oqln45NpkMBAAAAIKAQ13yW6oCnpmX2BvamO389/SVnwYl55NYPJmhtm/L7AAAAIAfuKbpk+Eq0PKDG5rkcH9O+iZBDQXnTr0SRo2kBLbktAAAAILsXyQKL6ZFOt2ScbJNHgAl50YMDVvKlTD3qsqS0R11jAAAAIOqxOTXzHYRIRRfpJK73iuFRwAdVklg2twdYhWUMMOwpAAAAIHnqPf5BNqv3UrO4Jx0D6USzyds2a3UEX479adIq5UEZAAAAIDLWEMqsbjP+qjJjo5lDcCS6nJsUZ4onTwGpEK4pX277AAAAEAAAAAmG0ekeABZ5OcsAAAAMuqL/bNRxxIPxX7kLAAAACov5IRGcFg8HAkQAAAAIUFTi0INr+EwAAAAOnQ97usvgJVqlt9RL7EAAAAAJfI0BkZLCQiTiAAAACxsGfYm8fwHx9XOYAAAADUs3OXARXoLtb0ElyPoAAAAKPr34iDoK2L6cOQAAAAoFIg0LKWiLc0uOAAAACCbJAf81TN4WAAAADBhPw50XNP9XFkKJUwAAAAuvvo+1aYfHf1gYUgAAAAqjcDk0v1CijaECAAAADkfLVT12lCZ670686kBrAAAADf5fWr9EzN4mO1YGYz4AAAAEAAAADlcyXwVWMEo+Pq4Uwo0MAAAADeo50qHks46vP0TGxu8AAAAOg2Ly9WQIVMFd/KyqiiwAAAAL7M5aOpS00zilFD4="
leafsResponseBytes, err := Codec.Marshal(Version, leafsResponse)
assert.NoError(t, err)
assert.Equal(t, base64LeafsResponse, base64.StdEncoding.EncodeToString(leafsResponseBytes))
var l LeafsResponse
_, err = Codec.Unmarshal(leafsResponseBytes, &l)
assert.NoError(t, err)
assert.Equal(t, leafsResponse.Keys, l.Keys)
assert.Equal(t, leafsResponse.Vals, l.Vals)
assert.False(t, l.More) // make sure it is not serialized
assert.Equal(t, leafsResponse.ProofVals, l.ProofVals)
}
func TestLeafsRequestValidation(t *testing.T) {
mockRequestHandler := &mockHandler{}
tests := map[string]struct {
request LeafsRequest
assertResponse func(t *testing.T)
}{
"node type StateTrieNode": {
request: LeafsRequest{
Root: common.BytesToHash([]byte("some hash goes here")),
Start: bytes.Repeat([]byte{0x00}, common.HashLength),
End: bytes.Repeat([]byte{0xff}, common.HashLength),
Limit: 10,
},
assertResponse: func(t *testing.T) {
assert.True(t, mockRequestHandler.handleStateTrieCalled)
assert.False(t, mockRequestHandler.handleBlockRequestCalled)
assert.False(t, mockRequestHandler.handleCodeRequestCalled)
},
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
_, _ = test.request.Handle(context.Background(), ids.GenerateTestNodeID(), 1, mockRequestHandler)
test.assertResponse(t)
mockRequestHandler.reset()
})
}
}
var _ RequestHandler = (*mockHandler)(nil)
type mockHandler struct {
handleStateTrieCalled,
handleBlockRequestCalled,
handleCodeRequestCalled bool
}
func (m *mockHandler) HandleStateTrieLeafsRequest(context.Context, ids.NodeID, uint32, LeafsRequest) ([]byte, error) {
m.handleStateTrieCalled = true
return nil, nil
}
func (m *mockHandler) HandleBlockRequest(context.Context, ids.NodeID, uint32, BlockRequest) ([]byte, error) {
m.handleBlockRequestCalled = true
return nil, nil
}
func (m *mockHandler) HandleCodeRequest(context.Context, ids.NodeID, uint32, CodeRequest) ([]byte, error) {
m.handleCodeRequestCalled = true
return nil, nil
}
func (m *mockHandler) reset() {
m.handleStateTrieCalled = false
m.handleBlockRequestCalled = false
m.handleCodeRequestCalled = false
}
-27
View File
@@ -1,27 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"context"
"fmt"
"github.com/luxfi/ids"
"github.com/luxfi/node/codec"
)
// Request represents a Network request type
type Request interface {
// Requests should implement String() for logging.
fmt.Stringer
// Handle allows `Request` to call respective methods on handler to handle
// this particular request type
Handle(ctx context.Context, nodeID ids.NodeID, requestID uint32, handler RequestHandler) ([]byte, error)
}
// RequestToBytes marshals the given request object into bytes
func RequestToBytes(codec codec.Manager, request Request) ([]byte, error) {
return codec.Marshal(Version, &request)
}
-91
View File
@@ -1,91 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package message
import (
"context"
"fmt"
"github.com/luxfi/consensus/engine/chain/block"
"github.com/luxfi/crypto"
"github.com/luxfi/geth/common"
"github.com/luxfi/ids"
)
// var _ block.StateSummary = (*SyncSummary)(nil)
// Note: StateSummary interface seems to be removed from node/consensus/engine/chain/block
// SyncSummary provides the information necessary to sync a node starting
// at the given block.
type SyncSummary struct {
BlockNumber uint64 `serialize:"true"`
BlockHash common.Hash `serialize:"true"`
BlockRoot common.Hash `serialize:"true"`
summaryID ids.ID
bytes []byte
acceptImpl func(SyncSummary) (block.StateSyncMode, error)
}
func NewSyncSummaryFromBytes(summaryBytes []byte, acceptImpl func(SyncSummary) (block.StateSyncMode, error)) (SyncSummary, error) {
summary := SyncSummary{}
if codecVersion, err := Codec.Unmarshal(summaryBytes, &summary); err != nil {
return SyncSummary{}, err
} else if codecVersion != Version {
return SyncSummary{}, fmt.Errorf("failed to parse syncable summary due to unexpected codec version (%d != %d)", codecVersion, Version)
}
summary.bytes = summaryBytes
summaryID, err := ids.ToID(crypto.Keccak256(summaryBytes))
if err != nil {
return SyncSummary{}, err
}
summary.summaryID = summaryID
summary.acceptImpl = acceptImpl
return summary, nil
}
func NewSyncSummary(blockHash common.Hash, blockNumber uint64, blockRoot common.Hash) (SyncSummary, error) {
summary := SyncSummary{
BlockNumber: blockNumber,
BlockHash: blockHash,
BlockRoot: blockRoot,
}
bytes, err := Codec.Marshal(Version, &summary)
if err != nil {
return SyncSummary{}, err
}
summary.bytes = bytes
summaryID, err := ids.ToID(crypto.Keccak256(bytes))
if err != nil {
return SyncSummary{}, err
}
summary.summaryID = summaryID
return summary, nil
}
func (s SyncSummary) Bytes() []byte {
return s.bytes
}
func (s SyncSummary) Height() uint64 {
return s.BlockNumber
}
func (s SyncSummary) ID() ids.ID {
return s.summaryID
}
func (s SyncSummary) String() string {
return fmt.Sprintf("SyncSummary(BlockHash=%s, BlockNumber=%d, BlockRoot=%s)", s.BlockHash, s.BlockNumber, s.BlockRoot)
}
func (s SyncSummary) Accept(context.Context) (block.StateSyncMode, error) {
if s.acceptImpl == nil {
return block.StateSyncSkipped, fmt.Errorf("accept implementation not specified for summary: %s", s)
}
return s.acceptImpl(s)
}
-54
View File
@@ -1,54 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"context"
"github.com/luxfi/evm/plugin/evm/message"
syncHandlers "github.com/luxfi/evm/sync/handlers"
syncStats "github.com/luxfi/evm/sync/handlers/stats"
"github.com/luxfi/evm/warp"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/geth/metrics"
"github.com/luxfi/geth/triedb"
"github.com/luxfi/ids"
"github.com/luxfi/node/codec"
)
var _ message.RequestHandler = (*networkHandler)(nil)
type networkHandler struct {
leafRequestHandler *syncHandlers.LeafsRequestHandler
blockRequestHandler *syncHandlers.BlockRequestHandler
codeRequestHandler *syncHandlers.CodeRequestHandler
}
// newNetworkHandler constructs the handler for serving network requests.
func newNetworkHandler(
provider syncHandlers.SyncDataProvider,
diskDB ethdb.KeyValueReader,
evmTrieDB *triedb.Database,
warpBackend warp.Backend,
networkCodec codec.Manager,
) message.RequestHandler {
syncStats := syncStats.NewHandlerStats(metrics.Enabled())
return &networkHandler{
leafRequestHandler: syncHandlers.NewLeafsRequestHandler(evmTrieDB, nil, networkCodec, syncStats),
blockRequestHandler: syncHandlers.NewBlockRequestHandler(provider, networkCodec, syncStats),
codeRequestHandler: syncHandlers.NewCodeRequestHandler(diskDB, networkCodec, syncStats),
}
}
func (n networkHandler) HandleStateTrieLeafsRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, leafsRequest message.LeafsRequest) ([]byte, error) {
return n.leafRequestHandler.OnLeafsRequest(ctx, nodeID, requestID, leafsRequest)
}
func (n networkHandler) HandleBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, blockRequest message.BlockRequest) ([]byte, error) {
return n.blockRequestHandler.OnBlockRequest(ctx, nodeID, requestID, blockRequest)
}
func (n networkHandler) HandleCodeRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, codeRequest message.CodeRequest) ([]byte, error) {
return n.codeRequestHandler.OnCodeRequest(ctx, nodeID, requestID, codeRequest)
}
-82
View File
@@ -1,82 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"fmt"
"net/http"
"time"
"github.com/luxfi/evm/plugin/evm/client"
"github.com/luxfi/ids"
"github.com/luxfi/math/set"
)
type ValidatorsAPI struct {
vm *VM
}
func (api *ValidatorsAPI) GetCurrentValidators(_ *http.Request, req *client.GetCurrentValidatorsRequest, reply *client.GetCurrentValidatorsResponse) error {
api.vm.vmLock.RLock()
defer api.vm.vmLock.RUnlock()
var vIDs set.Set[ids.ID]
if len(req.NodeIDs) > 0 {
vIDs = set.NewSet[ids.ID](len(req.NodeIDs))
for _, nodeID := range req.NodeIDs {
vID, err := api.vm.validatorsManager.GetValidationID(nodeID)
if err != nil {
return fmt.Errorf("couldn't find validator with node ID %s", nodeID)
}
vIDs.Add(vID)
}
} else {
vIDs = api.vm.validatorsManager.GetValidationIDs()
}
reply.Validators = make([]client.CurrentValidator, 0, vIDs.Len())
for _, vID := range vIDs.List() {
validator, err := api.vm.validatorsManager.GetValidator(vID)
if err != nil {
return fmt.Errorf("couldn't find validator with validation ID %s", vID)
}
// TODO: Fix IsConnected with new validator manager interface
isConnected := false // api.vm.validatorsManager.IsConnected(validator.NodeID)
// TODO: Fix CalculateUptime with new validator manager interface
upDuration := time.Duration(0)
_ = time.Now() // lastUpdated
// upDuration, lastUpdated, err := api.vm.validatorsManager.CalculateUptime(validator.NodeID)
// if err != nil {
// return err
// }
var uptimeFloat float64
startTime := time.Unix(int64(validator.StartTimestamp), 0)
bestPossibleUpDuration := time.Since(startTime)
if bestPossibleUpDuration == 0 {
uptimeFloat = 1
} else {
uptimeFloat = float64(upDuration) / float64(bestPossibleUpDuration)
}
// Transform this to a percentage (0-100) to make it consistent
// with currentValidators in PlatformVM API
uptimePercentage := float32(uptimeFloat * 100)
reply.Validators = append(reply.Validators, client.CurrentValidator{
ValidationID: validator.ValidationID,
NodeID: validator.NodeID,
StartTimestamp: validator.StartTimestamp,
Weight: validator.Weight,
IsActive: validator.IsActive,
IsL1Validator: validator.IsL1Validator,
IsConnected: isConnected,
UptimePercentage: uptimePercentage,
UptimeSeconds: uint64(upDuration.Seconds()),
})
}
return nil
}
-321
View File
@@ -1,321 +0,0 @@
---
title: Subnet-EVM API
---
[Subnet-EVM](https://github.com/luxfi/evm) APIs are identical to
[Coreth](https://build.lux.network/docs/api-reference/c-chain/api) C-Chain APIs, except Lux Specific APIs
starting with `lux`. Subnet-EVM also supports standard Ethereum APIs as well. For more
information about Coreth APIs see [GitHub](https://github.com/luxfi/coreth).
Subnet-EVM has some additional APIs that are not available in Coreth.
## `eth_feeConfig`
Subnet-EVM comes with an API request for getting fee config at a specific block. You can use this
API to check your activated fee config.
**Signature:**
```bash
eth_feeConfig([blk BlkNrOrHash]) -> {feeConfig: json}
```
- `blk` is the block number or hash at which to retrieve the fee config. Defaults to the latest block if omitted.
**Example Call:**
```bash
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "eth_feeConfig",
"params": [
"latest"
],
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt/rpc
```
**Example Response:**
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"feeConfig": {
"gasLimit": 15000000,
"targetBlockRate": 2,
"minBaseFee": 33000000000,
"targetGas": 15000000,
"baseFeeChangeDenominator": 36,
"minBlockGasCost": 0,
"maxBlockGasCost": 1000000,
"blockGasCostStep": 200000
},
"lastChangedAt": 0
}
}
```
## `eth_getChainConfig`
`eth_getChainConfig` returns the Chain Config of the blockchain. This API is enabled by default with
`internal-blockchain` namespace.
This API exists on the C-Chain as well, but in addition to the normal Chain Config returned by the
C-Chain `eth_getChainConfig` on `evm` additionally returns the upgrade config, which specifies
network upgrades activated after the genesis. **Signature:**
```bash
eth_getChainConfig({}) -> {chainConfig: json}
```
**Example Call:**
```bash
curl -X POST --data '{
"jsonrpc":"2.0",
"id" :1,
"method" :"eth_getChainConfig",
"params" :[]
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/Nvqcm33CX2XABS62iZsAcVUkavfnzp1Sc5k413wn5Nrf7Qjt7/rpc
```
**Example Response:**
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"chainId": 43214,
"feeConfig": {
"gasLimit": 8000000,
"targetBlockRate": 2,
"minBaseFee": 33000000000,
"targetGas": 15000000,
"baseFeeChangeDenominator": 36,
"minBlockGasCost": 0,
"maxBlockGasCost": 1000000,
"blockGasCostStep": 200000
},
"allowFeeRecipients": true,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"subnetEVMTimestamp": 0,
"contractDeployerAllowListConfig": {
"adminAddresses": ["0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc"],
"blockTimestamp": 0
},
"contractNativeMinterConfig": {
"adminAddresses": ["0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc"],
"blockTimestamp": 0
},
"feeManagerConfig": {
"adminAddresses": ["0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc"],
"blockTimestamp": 0
},
"upgrades": {
"precompileUpgrades": [
{
"feeManagerConfig": {
"adminAddresses": null,
"blockTimestamp": 1661541259,
"disable": true
}
},
{
"feeManagerConfig": {
"adminAddresses": null,
"blockTimestamp": 1661541269
}
}
]
}
}
}
```
## `eth_getActivePrecompilesAt`
**DEPRECATED—instead use** [`eth_getActiveRulesAt`](#eth_getactiveprecompilesat).
`eth_getActivePrecompilesAt` returns activated precompiles at a specific timestamp. If no
timestamp is provided it returns the latest block timestamp. This API is enabled by default with
`internal-blockchain` namespace.
**Signature:**
```bash
eth_getActivePrecompilesAt([timestamp uint]) -> {precompiles: []Precompile}
```
- `timestamp` specifies the timestamp to show the precompiles active at this time. If omitted it shows precompiles activated at the latest block timestamp.
**Example Call:**
```bash
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "eth_getActivePrecompilesAt",
"params": [],
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/Nvqcm33CX2XABS62iZsAcVUkavfnzp1Sc5k413wn5Nrf7Qjt7/rpc
```
**Example Response:**
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"contractDeployerAllowListConfig": {
"adminAddresses": ["0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc"],
"blockTimestamp": 0
},
"contractNativeMinterConfig": {
"adminAddresses": ["0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc"],
"blockTimestamp": 0
},
"feeManagerConfig": {
"adminAddresses": ["0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc"],
"blockTimestamp": 0
}
}
}
```
## `eth_getActiveRulesAt`
`eth_getActiveRulesAt` returns activated rules (precompiles, upgrades) at a specific timestamp. If no
timestamp is provided it returns the latest block timestamp. This API is enabled by default with
`internal-blockchain` namespace.
**Signature:**
```bash
eth_getActiveRulesAt([timestamp uint]) -> {rules: json}
```
- `timestamp` specifies the timestamp to show the rules active at this time. If omitted it shows rules activated at the latest block timestamp.
**Example Call:**
```bash
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "eth_getActiveRulesAt",
"params": [],
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/Nvqcm33CX2XABS62iZsAcVUkavfnzp1Sc5k413wn5Nrf7Qjt7/rpc
```
**Example Response:**
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"ethRules": {
"IsHomestead": true,
"IsEIP150": true,
"IsEIP155": true,
"IsEIP158": true,
"IsByzantium": true,
"IsConstantinople": true,
"IsPetersburg": true,
"IsIstanbul": true,
"IsCancun": true
},
"luxRules": {
"IsSubnetEVM": true,
"IsDurango": true,
"IsEUpgrade": true
},
"precompiles": {
"contractNativeMinterConfig": {
"timestamp": 0
},
"rewardManagerConfig": {
"timestamp": 1712918700
},
"warpConfig": {
"timestamp": 1714158045
}
}
}
}
```
## `validators.getCurrentValidators`
This API retrieves the list of current validators for the Subnet/L1. It provides detailed information about each validator, including their ID, status, weight, connection, and uptime.
URL: `http://<server-uri>/ext/bc/<blockchainID>/validators`
**Signature:**
```bash
validators.getCurrentValidators({nodeIDs: []string}) -> {validators: []Validator}
```
- `nodeIDs` is an optional parameter that specifies the node IDs of the validators to retrieve. If omitted, all validators are returned.
**Example Call:**
```bash
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "validators.getCurrentValidators",
"params": {
"nodeIDs": []
},
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/C49rHzk3vLr1w9Z8sY7scrZ69TU4WcD2pRS6ZyzaSn9xA2U9F/validators
```
**Example Response:**
```json
{
"jsonrpc": "2.0",
"result": {
"validators": [
{
"validationID": "nESqWkcNXihfdZESS2idWbFETMzatmkoTCktjxG1qryaQXfS6",
"nodeID": "NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5",
"weight": 20,
"startTimestamp": 1732025492,
"isActive": true,
"isL1Validator": false,
"isConnected": true,
"uptimeSeconds": 36,
"uptimePercentage": 100
}
]
},
"id": 1
}
```
**Response Fields:**
- `validationID`: (string) Unique identifier for the validation. This returns validation ID for L1s, AddSubnetValidator txID for Subnets.
- `nodeID`: (string) Node identifier for the validator.
- `weight`: (integer) The weight of the validator, often representing stake.
- `startTimestamp`: (integer) UNIX timestamp for when validation started.
- `isActive`: (boolean) Indicates if the validator is active. This returns true if this is L1 validator and has enough continuous subnet staking fees in P-Chain. It always returns true for subnet validators.
- `isL1Validator`: (boolean) Indicates if the validator is a L1 validator or a subnet validator.
- `isConnected`: (boolean) Indicates if the validator node is currently connected to the callee node.
- `uptimeSeconds`: (integer) The number of seconds the validator has been online.
- `uptimePercentage`: (float) The percentage of time the validator has been online.
-82
View File
@@ -1,82 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"errors"
"fmt"
)
var (
errUnknownStatus = errors.New("unknown status")
)
// Status ...
type Status uint32
// List of possible status values
// [Unknown] Zero value, means the status is not known
// [Dropped] means the transaction was in the mempool, but was dropped because it failed verification
// [Processing] means the transaction is in the mempool
// [Accepted] means the transaction was accepted
const (
Unknown Status = iota
Dropped
Processing
Accepted
)
// MarshalJSON ...
func (s Status) MarshalJSON() ([]byte, error) {
if err := s.Valid(); err != nil {
return nil, err
}
return []byte(fmt.Sprintf("%q", s)), nil
}
// UnmarshalJSON ...
func (s *Status) UnmarshalJSON(b []byte) error {
str := string(b)
if str == "null" {
return nil
}
switch str {
case `"Unknown"`:
*s = Unknown
case `"Dropped"`:
*s = Dropped
case `"Processing"`:
*s = Processing
case `"Accepted"`:
*s = Accepted
default:
return errUnknownStatus
}
return nil
}
// Valid returns nil if the status is a valid status.
func (s Status) Valid() error {
switch s {
case Unknown, Dropped, Processing, Accepted:
return nil
default:
return errUnknownStatus
}
}
func (s Status) String() string {
switch s {
case Unknown:
return "Unknown"
case Dropped:
return "Dropped"
case Processing:
return "Processing"
case Accepted:
return "Accepted"
default:
return "Invalid status"
}
}
-365
View File
@@ -1,365 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"context"
"fmt"
"sync"
"github.com/luxfi/consensus/engine/chain/block"
"github.com/luxfi/database"
"github.com/luxfi/database/versiondb"
"github.com/luxfi/evm/core/state/snapshot"
"github.com/luxfi/evm/eth"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/plugin/evm/message"
syncclient "github.com/luxfi/evm/sync/client"
"github.com/luxfi/evm/sync/statesync"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/ethdb"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/node/vms/components/chain"
)
const (
// State sync fetches [parentsToGet] parents of the block it syncs to.
// The last 256 block hashes are necessary to support the BLOCKHASH opcode.
parentsToGet = 256
)
var stateSyncSummaryKey = []byte("stateSyncSummary")
// stateSyncClientConfig defines the options and dependencies needed to construct a StateSyncerClient
type stateSyncClientConfig struct {
enabled bool
skipResume bool
// Specifies the number of blocks behind the latest state summary that the chain must be
// in order to prefer performing state sync over falling back to the normal bootstrapping
// algorithm.
stateSyncMinBlocks uint64
stateSyncRequestSize uint16 // number of key/value pairs to ask peers for per request
lastAcceptedHeight uint64
chain *eth.Ethereum
state *chain.State
chaindb ethdb.Database
metadataDB database.Database
acceptedBlockDB database.Database
db *versiondb.Database
client syncclient.Client
stateSyncDone chan struct{}
}
type stateSyncerClient struct {
*stateSyncClientConfig
resumableSummary message.SyncSummary
cancel context.CancelFunc
wg sync.WaitGroup
// State Sync results
syncSummary message.SyncSummary
stateSyncErr error
stateSyncDone chan struct{}
}
func NewStateSyncClient(config *stateSyncClientConfig) StateSyncClient {
return &stateSyncerClient{
stateSyncDone: config.stateSyncDone,
stateSyncClientConfig: config,
}
}
type StateSyncClient interface {
// methods that implement the client side of [block.StateSyncableVM]
StateSyncEnabled(context.Context) (bool, error)
GetOngoingSyncStateSummary(context.Context) (block.StateSummary, error)
ParseStateSummary(ctx context.Context, summaryBytes []byte) (block.StateSummary, error)
// additional methods required by the evm package
ClearOngoingSummary() error
Shutdown() error
Error() error
}
// Syncer represents a step in state sync,
// along with Start/Done methods to control
// and monitor progress.
// Error returns an error if any was encountered.
type Syncer interface {
Start(ctx context.Context) error
Wait(ctx context.Context) error
}
// StateSyncEnabled returns [client.enabled], which is set in the chain's config file.
func (client *stateSyncerClient) StateSyncEnabled(context.Context) (bool, error) {
return client.enabled, nil
}
// GetOngoingSyncStateSummary returns a state summary that was previously started
// and not finished, and sets [resumableSummary] if one was found.
// Returns [database.ErrNotFound] if no ongoing summary is found or if [client.skipResume] is true.
func (client *stateSyncerClient) GetOngoingSyncStateSummary(context.Context) (block.StateSummary, error) {
if client.skipResume {
return nil, database.ErrNotFound
}
summaryBytes, err := client.metadataDB.Get(stateSyncSummaryKey)
if err != nil {
return nil, err // includes the [database.ErrNotFound] case
}
summary, err := message.NewSyncSummaryFromBytes(summaryBytes, client.acceptSyncSummary)
if err != nil {
return nil, fmt.Errorf("failed to parse saved state sync summary to SyncSummary: %w", err)
}
client.resumableSummary = summary
return summary, nil
}
// ClearOngoingSummary clears any marker of an ongoing state sync summary
func (client *stateSyncerClient) ClearOngoingSummary() error {
if err := client.metadataDB.Delete(stateSyncSummaryKey); err != nil {
return fmt.Errorf("failed to clear ongoing summary: %w", err)
}
if err := client.db.Commit(); err != nil {
return fmt.Errorf("failed to commit db while clearing ongoing summary: %w", err)
}
return nil
}
// ParseStateSummary parses [summaryBytes] to [commonEng.Summary]
func (client *stateSyncerClient) ParseStateSummary(_ context.Context, summaryBytes []byte) (block.StateSummary, error) {
return message.NewSyncSummaryFromBytes(summaryBytes, client.acceptSyncSummary)
}
// stateSync blockingly performs the state sync for the EVM state and the atomic state
// to [client.syncSummary]. returns an error if one occurred.
func (client *stateSyncerClient) stateSync(ctx context.Context) error {
if err := client.syncBlocks(ctx, client.syncSummary.BlockHash, client.syncSummary.BlockNumber, parentsToGet); err != nil {
return err
}
// Sync the EVM trie.
return client.syncStateTrie(ctx)
}
// acceptSyncSummary returns true if sync will be performed and launches the state sync process
// in a goroutine.
func (client *stateSyncerClient) acceptSyncSummary(proposedSummary message.SyncSummary) (block.StateSyncMode, error) {
isResume := proposedSummary.BlockHash == client.resumableSummary.BlockHash
if !isResume {
// Skip syncing if the blockchain is not significantly ahead of local state,
// since bootstrapping would be faster.
// (Also ensures we don't sync to a height prior to local state.)
if client.lastAcceptedHeight+client.stateSyncMinBlocks > proposedSummary.Height() {
log.Info(
"last accepted too close to most recent syncable block, skipping state sync",
"lastAccepted", client.lastAcceptedHeight,
"syncableHeight", proposedSummary.Height(),
)
return block.StateSyncSkipped, nil
}
// Wipe the snapshot completely if we are not resuming from an existing sync, so that we do not
// use a corrupted snapshot.
// Note: this assumes that when the node is started with state sync disabled, the in-progress state
// sync marker will be wiped, so we do not accidentally resume progress from an incorrect version
// of the snapshot. (if switching between versions that come before this change and back this could
// lead to the snapshot not being cleaned up correctly)
<-snapshot.WipeSnapshot(client.chaindb, true)
// Reset the snapshot generator here so that when state sync completes, snapshots will not attempt to read an
// invalid generator.
// Note: this must be called after WipeSnapshot is called so that we do not invalidate a partially generated snapshot.
snapshot.ResetSnapshotGeneration(client.chaindb)
}
client.syncSummary = proposedSummary
// Update the current state sync summary key in the database
// Note: this must be performed after WipeSnapshot finishes so that we do not start a state sync
// session from a partially wiped snapshot.
if err := client.metadataDB.Put(stateSyncSummaryKey, proposedSummary.Bytes()); err != nil {
return block.StateSyncSkipped, fmt.Errorf("failed to write state sync summary key to disk: %w", err)
}
if err := client.db.Commit(); err != nil {
return block.StateSyncSkipped, fmt.Errorf("failed to commit db: %w", err)
}
log.Info("Starting state sync", "summary", proposedSummary)
// create a cancellable ctx for the state sync goroutine
ctx, cancel := context.WithCancel(context.Background())
client.cancel = cancel
client.wg.Add(1) // track the state sync goroutine so we can wait for it on shutdown
go func() {
defer client.wg.Done()
defer cancel()
if err := client.stateSync(ctx); err != nil {
client.stateSyncErr = err
} else {
client.stateSyncErr = client.finishSync()
}
// notify engine regardless of whether err == nil,
// this error will be propagated to the engine when it calls
// vm.SetState(consensus.Bootstrapping)
log.Info("stateSync completed, notifying engine", "err", client.stateSyncErr)
close(client.stateSyncDone)
}()
return block.StateSyncStatic, nil
}
// syncBlocks fetches (up to) [parentsToGet] blocks from peers
// using [client] and writes them to disk.
// the process begins with [fromHash] and it fetches parents recursively.
// fetching starts from the first ancestor not found on disk
func (client *stateSyncerClient) syncBlocks(ctx context.Context, fromHash common.Hash, fromHeight uint64, parentsToGet int) error {
nextHash := fromHash
nextHeight := fromHeight
parentsPerRequest := uint16(32)
// first, check for blocks already available on disk so we don't
// request them from peers.
for parentsToGet >= 0 {
blk := rawdb.ReadBlock(client.chaindb, nextHash, nextHeight)
if blk != nil {
// block exists
nextHash = blk.ParentHash()
nextHeight--
parentsToGet--
continue
}
// block was not found
break
}
// get any blocks we couldn't find on disk from peers and write
// them to disk.
batch := client.chaindb.NewBatch()
for i := parentsToGet - 1; i >= 0 && (nextHash != common.Hash{}); {
if err := ctx.Err(); err != nil {
return err
}
blocks, err := client.client.GetBlocks(ctx, nextHash, nextHeight, parentsPerRequest)
if err != nil {
log.Error("could not get blocks from peer", "err", err, "nextHash", nextHash, "remaining", i+1)
return err
}
for _, block := range blocks {
rawdb.WriteBlock(batch, block)
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
i--
nextHash = block.ParentHash()
nextHeight--
}
log.Info("fetching blocks from peer", "remaining", i+1, "total", parentsToGet)
}
log.Info("fetched blocks from peer", "total", parentsToGet)
return batch.Write()
}
func (client *stateSyncerClient) syncStateTrie(ctx context.Context) error {
log.Info("state sync: sync starting", "root", client.syncSummary.BlockRoot)
evmSyncer, err := statesync.NewStateSyncer(&statesync.StateSyncerConfig{
Client: client.client,
Root: client.syncSummary.BlockRoot,
BatchSize: ethdb.IdealBatchSize,
DB: client.chaindb,
MaxOutstandingCodeHashes: statesync.DefaultMaxOutstandingCodeHashes,
NumCodeFetchingWorkers: statesync.DefaultNumCodeFetchingWorkers,
RequestSize: client.stateSyncRequestSize,
})
if err != nil {
return err
}
if err := evmSyncer.Start(ctx); err != nil {
return err
}
err = evmSyncer.Wait(ctx)
log.Info("state sync: sync finished", "root", client.syncSummary.BlockRoot, "err", err)
return err
}
func (client *stateSyncerClient) Shutdown() error {
if client.cancel != nil {
client.cancel()
}
client.wg.Wait() // wait for the background goroutine to exit
return nil
}
// finishSync is responsible for updating disk and memory pointers so the VM is prepared
// for bootstrapping.
func (client *stateSyncerClient) finishSync() error {
stateBlock, err := client.state.GetBlock(context.TODO(), ids.ID(client.syncSummary.BlockHash))
if err != nil {
return fmt.Errorf("could not get block by hash from client state: %s", client.syncSummary.BlockHash)
}
wrapper, ok := stateBlock.(*chain.BlockWrapper)
if !ok {
return fmt.Errorf("could not convert block(%T) to *chain.BlockWrapper", wrapper)
}
evmBlock, ok := wrapper.Block.(*Block)
if !ok {
return fmt.Errorf("could not convert block(%T) to evm.Block", stateBlock)
}
block := evmBlock.ethBlock
if block.Hash() != client.syncSummary.BlockHash {
return fmt.Errorf("attempted to set last summary block to unexpected block hash: (%s != %s)", block.Hash(), client.syncSummary.BlockHash)
}
if block.NumberU64() != client.syncSummary.BlockNumber {
return fmt.Errorf("attempted to set last summary block to unexpected block number: (%d != %d)", block.NumberU64(), client.syncSummary.BlockNumber)
}
// BloomIndexer needs to know that some parts of the chain are not available
// and cannot be indexed. This is done by calling [AddCheckpoint] here.
// Since the indexer uses sections of size [params.BloomBitsBlocks] (= 4096),
// each block is indexed in section number [blockNumber/params.BloomBitsBlocks].
// To allow the indexer to start with the block we just synced to,
// we create a checkpoint for its parent.
// Note: This requires assuming the synced block height is divisible
// by [params.BloomBitsBlocks].
parentHeight := block.NumberU64() - 1
parentHash := block.ParentHash()
client.chain.BloomIndexer().AddCheckpoint(parentHeight/params.BloomBitsBlocks, parentHash)
if err := client.chain.BlockChain().ResetToStateSyncedBlock(block); err != nil {
return err
}
if err := client.updateVMMarkers(); err != nil {
return fmt.Errorf("error updating vm markers, height=%d, hash=%s, err=%w", block.NumberU64(), block.Hash(), err)
}
return client.state.SetLastAcceptedBlock(evmBlock)
}
// updateVMMarkers updates the following markers in the VM's database
// and commits them atomically:
// - updates lastAcceptedKey
// - removes state sync progress markers
func (client *stateSyncerClient) updateVMMarkers() error {
if err := client.acceptedBlockDB.Put(lastAcceptedKey, client.syncSummary.BlockHash[:]); err != nil {
return err
}
if err := client.metadataDB.Delete(stateSyncSummaryKey); err != nil {
return err
}
return client.db.Commit()
}
// Error returns a non-nil error if one occurred during the sync.
func (client *stateSyncerClient) Error() error { return client.stateSyncErr }
-97
View File
@@ -1,97 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"context"
"fmt"
"github.com/luxfi/consensus/engine/chain/block"
"github.com/luxfi/database"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/plugin/evm/message"
"github.com/luxfi/log"
)
type stateSyncServerConfig struct {
Chain *core.BlockChain
// SyncableInterval is the interval at which blocks are eligible to provide syncable block summaries.
SyncableInterval uint64
}
type stateSyncServer struct {
chain *core.BlockChain
syncableInterval uint64
}
type StateSyncServer interface {
GetLastStateSummary(context.Context) (block.StateSummary, error)
GetStateSummary(context.Context, uint64) (block.StateSummary, error)
}
func NewStateSyncServer(config *stateSyncServerConfig) StateSyncServer {
return &stateSyncServer{
chain: config.Chain,
syncableInterval: config.SyncableInterval,
}
}
// stateSummaryAtHeight returns the SyncSummary at [height] if valid and available.
func (server *stateSyncServer) stateSummaryAtHeight(height uint64) (message.SyncSummary, error) {
blk := server.chain.GetBlockByNumber(height)
if blk == nil {
return message.SyncSummary{}, fmt.Errorf("block not found for height (%d)", height)
}
if !server.chain.HasState(blk.Root()) {
return message.SyncSummary{}, fmt.Errorf("block root does not exist for height (%d), root (%s)", height, blk.Root())
}
summary, err := message.NewSyncSummary(blk.Hash(), height, blk.Root())
if err != nil {
return message.SyncSummary{}, fmt.Errorf("failed to construct syncable block at height %d: %w", height, err)
}
return summary, nil
}
// GetLastStateSummary returns the latest state summary.
// State summary is calculated by the block nearest to last accepted
// that is divisible by [syncableInterval]
// If no summary is available, [database.ErrNotFound] must be returned.
func (server *stateSyncServer) GetLastStateSummary(context.Context) (block.StateSummary, error) {
lastHeight := server.chain.LastAcceptedBlock().NumberU64()
lastSyncSummaryNumber := lastHeight - lastHeight%server.syncableInterval
summary, err := server.stateSummaryAtHeight(lastSyncSummaryNumber)
if err != nil {
log.Debug("could not get latest state summary", "err", err)
return nil, database.ErrNotFound
}
log.Debug("Serving syncable block at latest height", "summary", summary)
return summary, nil
}
// GetStateSummary implements StateSyncableVM and returns a summary corresponding
// to the provided [height] if the node can serve state sync data for that key.
// If not, [database.ErrNotFound] must be returned.
func (server *stateSyncServer) GetStateSummary(_ context.Context, height uint64) (block.StateSummary, error) {
summaryBlock := server.chain.GetBlockByNumber(height)
if summaryBlock == nil ||
summaryBlock.NumberU64() > server.chain.LastAcceptedBlock().NumberU64() ||
summaryBlock.NumberU64()%server.syncableInterval != 0 {
return nil, database.ErrNotFound
}
summary, err := server.stateSummaryAtHeight(summaryBlock.NumberU64())
if err != nil {
log.Debug("could not get state summary", "height", height, "err", err)
return nil, database.ErrNotFound
}
log.Debug("Serving syncable block at requested height", "height", height, "summary", summary)
return summary, nil
}

Some files were not shown because too many files have changed in this diff Show More