mirror of
https://github.com/luxfi/geth.git
synced 2026-07-27 01:59:25 +00:00
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:
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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.")
|
||||
}
|
||||
@@ -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 := ¶ms.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!")
|
||||
}
|
||||
@@ -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 ¶ms.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: ¶ms.CliqueConfig{
|
||||
Period: 1,
|
||||
Epoch: 30000,
|
||||
},
|
||||
}
|
||||
case 96368:
|
||||
// LUX Testnet configuration
|
||||
config := getChainConfig(96369)
|
||||
config.ChainID = big.NewInt(96368)
|
||||
return config
|
||||
default:
|
||||
// Default configuration
|
||||
return ¶ms.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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 := ¶ms.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!")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
@@ -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.
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()...)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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{}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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 }
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 +0,0 @@
|
||||
MANIFEST-000342
|
||||
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
@@ -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})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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{}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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
Reference in New Issue
Block a user