From 0978604196e5949cf83b45d1a08d175f0cbe4f73 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 1 Sep 2025 17:19:26 +0200 Subject: [PATCH 001/119] version: release v1.16.3 (#32528) --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 30c81b804b..2695c43d43 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 16 // Minor version component of the current release - Patch = 3 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 16 // Minor version component of the current release + Patch = 3 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string ) From 1263f3dfc14cbf1af7a3b051b0e8a416f0baec10 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 1 Sep 2025 20:38:37 +0200 Subject: [PATCH 002/119] version: begin v1.16.4 release cycle (#32529) --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 2695c43d43..092735ff1f 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 16 // Minor version component of the current release - Patch = 3 // Patch version component of the current release - Meta = "stable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 16 // Minor version component of the current release + Patch = 4 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string ) From c36f7bec7f580a34fc6a55b8ea72734be44ba42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Irmak?= Date: Tue, 2 Sep 2025 13:43:26 +0300 Subject: [PATCH 003/119] core/stateless: track number of leaf nodes at each trie depth (#32533) Switches to using counters so that the gauges don't cause any information to be lost. Counters can be used to calculate all sorts of metrics on Grafana. Which is also why min/avg/max logic is removed to make things simple and small here. --- core/stateless/stats.go | 71 +++++-------------- core/stateless/stats_test.go | 128 +++++++++++------------------------ 2 files changed, 57 insertions(+), 142 deletions(-) diff --git a/core/stateless/stats.go b/core/stateless/stats.go index adc898929b..1a6389284c 100644 --- a/core/stateless/stats.go +++ b/core/stateless/stats.go @@ -20,73 +20,32 @@ import ( "maps" "slices" "sort" + "strconv" "strings" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/metrics" ) -var ( - accountTrieDepthAvg = metrics.NewRegisteredGauge("witness/trie/account/depth/avg", nil) - accountTrieDepthMin = metrics.NewRegisteredGauge("witness/trie/account/depth/min", nil) - accountTrieDepthMax = metrics.NewRegisteredGauge("witness/trie/account/depth/max", nil) +var accountTrieLeavesAtDepth [16]*metrics.Counter +var storageTrieLeavesAtDepth [16]*metrics.Counter - storageTrieDepthAvg = metrics.NewRegisteredGauge("witness/trie/storage/depth/avg", nil) - storageTrieDepthMin = metrics.NewRegisteredGauge("witness/trie/storage/depth/min", nil) - storageTrieDepthMax = metrics.NewRegisteredGauge("witness/trie/storage/depth/max", nil) -) - -// depthStats tracks min/avg/max statistics for trie access depths. -type depthStats struct { - totalDepth int64 - samples int64 - minDepth int64 - maxDepth int64 -} - -// newDepthStats creates a new depthStats with default values. -func newDepthStats() *depthStats { - return &depthStats{minDepth: -1} -} - -// add records a new depth sample. -func (d *depthStats) add(n int64) { - if n < 0 { - return +func init() { + for i := 0; i < 16; i++ { + accountTrieLeavesAtDepth[i] = metrics.NewRegisteredCounter("witness/trie/account/leaves/depth_"+strconv.Itoa(i), nil) + storageTrieLeavesAtDepth[i] = metrics.NewRegisteredCounter("witness/trie/storage/leaves/depth_"+strconv.Itoa(i), nil) } - d.totalDepth += n - d.samples++ - - if d.minDepth == -1 || n < d.minDepth { - d.minDepth = n - } - if n > d.maxDepth { - d.maxDepth = n - } -} - -// report uploads the collected statistics into the provided gauges. -func (d *depthStats) report(maxGauge, minGauge, avgGauge *metrics.Gauge) { - if d.samples == 0 { - return - } - maxGauge.Update(d.maxDepth) - minGauge.Update(d.minDepth) - avgGauge.Update(d.totalDepth / d.samples) } // WitnessStats aggregates statistics for account and storage trie accesses. type WitnessStats struct { - accountTrie *depthStats - storageTrie *depthStats + accountTrieLeaves [16]int64 + storageTrieLeaves [16]int64 } // NewWitnessStats creates a new WitnessStats collector. func NewWitnessStats() *WitnessStats { - return &WitnessStats{ - accountTrie: newDepthStats(), - storageTrie: newDepthStats(), - } + return &WitnessStats{} } // Add records trie access depths from the given node paths. @@ -102,9 +61,9 @@ func (s *WitnessStats) Add(nodes map[string][]byte, owner common.Hash) { // The last path is always a leaf. if i == len(paths)-1 || !strings.HasPrefix(paths[i+1], paths[i]) { if owner == (common.Hash{}) { - s.accountTrie.add(int64(len(path))) + s.accountTrieLeaves[len(path)] += 1 } else { - s.storageTrie.add(int64(len(path))) + s.storageTrieLeaves[len(path)] += 1 } } } @@ -112,6 +71,8 @@ func (s *WitnessStats) Add(nodes map[string][]byte, owner common.Hash) { // ReportMetrics reports the collected statistics to the global metrics registry. func (s *WitnessStats) ReportMetrics() { - s.accountTrie.report(accountTrieDepthMax, accountTrieDepthMin, accountTrieDepthAvg) - s.storageTrie.report(storageTrieDepthMax, storageTrieDepthMin, storageTrieDepthAvg) + for i := 0; i < 16; i++ { + accountTrieLeavesAtDepth[i].Inc(s.accountTrieLeaves[i]) + storageTrieLeavesAtDepth[i].Inc(s.storageTrieLeaves[i]) + } } diff --git a/core/stateless/stats_test.go b/core/stateless/stats_test.go index 51c78cc9c9..6219e622f5 100644 --- a/core/stateless/stats_test.go +++ b/core/stateless/stats_test.go @@ -24,27 +24,32 @@ import ( func TestWitnessStatsAdd(t *testing.T) { tests := []struct { - name string - nodes map[string][]byte - owner common.Hash - expectedAccountDepth int64 - expectedStorageDepth int64 + name string + nodes map[string][]byte + owner common.Hash + expectedAccountLeaves map[int64]int64 + expectedStorageLeaves map[int64]int64 }{ { - name: "empty nodes", - nodes: map[string][]byte{}, - owner: common.Hash{}, - expectedAccountDepth: 0, - expectedStorageDepth: 0, + name: "empty nodes", + nodes: map[string][]byte{}, + owner: common.Hash{}, + }, + { + name: "single account trie leaf at depth 0", + nodes: map[string][]byte{ + "": []byte("data"), + }, + owner: common.Hash{}, + expectedAccountLeaves: map[int64]int64{0: 1}, }, { name: "single account trie leaf", nodes: map[string][]byte{ "abc": []byte("data"), }, - owner: common.Hash{}, - expectedAccountDepth: 3, - expectedStorageDepth: 0, + owner: common.Hash{}, + expectedAccountLeaves: map[int64]int64{3: 1}, }, { name: "account trie with internal nodes", @@ -53,9 +58,8 @@ func TestWitnessStatsAdd(t *testing.T) { "ab": []byte("data2"), "abc": []byte("data3"), }, - owner: common.Hash{}, - expectedAccountDepth: 3, // Only "abc" is a leaf - expectedStorageDepth: 0, + owner: common.Hash{}, + expectedAccountLeaves: map[int64]int64{3: 1}, // Only "abc" is a leaf }, { name: "multiple account trie branches", @@ -67,9 +71,8 @@ func TestWitnessStatsAdd(t *testing.T) { "bc": []byte("data5"), "bcd": []byte("data6"), }, - owner: common.Hash{}, - expectedAccountDepth: 6, // "abc" (3) + "bcd" (3) = 6 - expectedStorageDepth: 0, + owner: common.Hash{}, + expectedAccountLeaves: map[int64]int64{3: 2}, // "abc" (3) + "bcd" (3) }, { name: "siblings are all leaves", @@ -78,9 +81,8 @@ func TestWitnessStatsAdd(t *testing.T) { "ab": []byte("data2"), "ac": []byte("data3"), }, - owner: common.Hash{}, - expectedAccountDepth: 6, // 2 + 2 + 2 = 6 - expectedStorageDepth: 0, + owner: common.Hash{}, + expectedAccountLeaves: map[int64]int64{2: 3}, }, { name: "storage trie leaves", @@ -90,9 +92,8 @@ func TestWitnessStatsAdd(t *testing.T) { "123": []byte("data3"), "124": []byte("data4"), }, - owner: common.HexToHash("0x1234"), - expectedAccountDepth: 0, - expectedStorageDepth: 6, // "123" (3) + "124" (3) = 6 + owner: common.HexToHash("0x1234"), + expectedStorageLeaves: map[int64]int64{3: 2}, // "123" (3) + "124" (3) }, { name: "complex trie structure", @@ -107,9 +108,8 @@ func TestWitnessStatsAdd(t *testing.T) { "235": []byte("data8"), "3": []byte("data9"), }, - owner: common.Hash{}, - expectedAccountDepth: 13, // "123"(3) + "124"(3) + "234"(3) + "235"(3) + "3"(1) = 13 - expectedStorageDepth: 0, + owner: common.Hash{}, + expectedAccountLeaves: map[int64]int64{1: 1, 3: 4}, // "123"(3) + "124"(3) + "234"(3) + "235"(3) + "3"(1) }, } @@ -118,74 +118,28 @@ func TestWitnessStatsAdd(t *testing.T) { stats := NewWitnessStats() stats.Add(tt.nodes, tt.owner) + var expectedAccountTrieLeaves [16]int64 + for depth, count := range tt.expectedAccountLeaves { + expectedAccountTrieLeaves[depth] = count + } + var expectedStorageTrieLeaves [16]int64 + for depth, count := range tt.expectedStorageLeaves { + expectedStorageTrieLeaves[depth] = count + } + // Check account trie depth - if stats.accountTrie.totalDepth != tt.expectedAccountDepth { - t.Errorf("Account trie total depth = %d, want %d", stats.accountTrie.totalDepth, tt.expectedAccountDepth) + if stats.accountTrieLeaves != expectedAccountTrieLeaves { + t.Errorf("Account trie total depth = %v, want %v", stats.accountTrieLeaves, expectedAccountTrieLeaves) } // Check storage trie depth - if stats.storageTrie.totalDepth != tt.expectedStorageDepth { - t.Errorf("Storage trie total depth = %d, want %d", stats.storageTrie.totalDepth, tt.expectedStorageDepth) + if stats.storageTrieLeaves != expectedStorageTrieLeaves { + t.Errorf("Storage trie total depth = %v, want %v", stats.storageTrieLeaves, expectedStorageTrieLeaves) } }) } } -func TestWitnessStatsMinMax(t *testing.T) { - stats := NewWitnessStats() - - // Add some account trie nodes with varying depths - stats.Add(map[string][]byte{ - "a": []byte("data1"), - "ab": []byte("data2"), - "abc": []byte("data3"), - "abcd": []byte("data4"), - "abcde": []byte("data5"), - }, common.Hash{}) - - // Only "abcde" is a leaf (depth 5) - if stats.accountTrie.minDepth != 5 { - t.Errorf("Account trie min depth = %d, want %d", stats.accountTrie.minDepth, 5) - } - if stats.accountTrie.maxDepth != 5 { - t.Errorf("Account trie max depth = %d, want %d", stats.accountTrie.maxDepth, 5) - } - - // Add more leaves with different depths - stats.Add(map[string][]byte{ - "x": []byte("data6"), - "yz": []byte("data7"), - }, common.Hash{}) - - // Now we have leaves at depths 1, 2, and 5 - if stats.accountTrie.minDepth != 1 { - t.Errorf("Account trie min depth after update = %d, want %d", stats.accountTrie.minDepth, 1) - } - if stats.accountTrie.maxDepth != 5 { - t.Errorf("Account trie max depth after update = %d, want %d", stats.accountTrie.maxDepth, 5) - } -} - -func TestWitnessStatsAverage(t *testing.T) { - stats := NewWitnessStats() - - // Add nodes that will create leaves at depths 2, 3, and 4 - stats.Add(map[string][]byte{ - "aa": []byte("data1"), - "bb": []byte("data2"), - "ccc": []byte("data3"), - "dddd": []byte("data4"), - }, common.Hash{}) - - // All are leaves: 2 + 2 + 3 + 4 = 11 total, 4 samples - expectedAvg := int64(11) / int64(4) - actualAvg := stats.accountTrie.totalDepth / stats.accountTrie.samples - - if actualAvg != expectedAvg { - t.Errorf("Account trie average depth = %d, want %d", actualAvg, expectedAvg) - } -} - func BenchmarkWitnessStatsAdd(b *testing.B) { // Create a realistic trie node structure nodes := make(map[string][]byte) From 6f08b3a7254e97c7f3b64d3a26c1fba3bd116982 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Tue, 2 Sep 2025 12:44:09 +0200 Subject: [PATCH 004/119] core/tracing: fix selfdestruct 6780 balance change emit (#32526) Noticed in #32376 --- core/state/statedb_hooked.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index 3d1ef15031..556a3e6c49 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -242,7 +242,7 @@ func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, b prev, changed := s.inner.SelfDestruct6780(address) - if s.hooks.OnBalanceChange != nil && changed && !prev.IsZero() { + if s.hooks.OnBalanceChange != nil && !prev.IsZero() { s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) } From 328add2a5aa54f9700b8a27d2d86f3d3bdddb57b Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Tue, 2 Sep 2025 12:44:47 +0200 Subject: [PATCH 005/119] core/tracing: add code change reason (#32525) Closes #32376 --- cmd/evm/internal/t8ntool/execution.go | 2 +- cmd/evm/runner.go | 2 +- core/genesis.go | 4 +-- core/state/statedb.go | 2 +- core/state/statedb_fuzz_test.go | 2 +- core/state/statedb_hooked.go | 30 ++++++++++++----- core/state/statedb_hooked_test.go | 2 +- core/state/statedb_test.go | 18 +++++------ core/state/trie_prefetcher_test.go | 4 +-- core/state_transition.go | 4 +-- .../gen_code_change_reason_stringer.go | 29 +++++++++++++++++ core/tracing/hooks.go | 32 +++++++++++++++++++ core/tracing/journal.go | 25 +++++++++++++-- core/tracing/journal_test.go | 30 ++++++++++++++++- core/txpool/legacypool/legacypool_test.go | 10 +++--- core/verkle_witness_test.go | 2 +- core/vm/evm.go | 2 +- core/vm/gas_table_test.go | 5 +-- core/vm/interface.go | 2 +- core/vm/interpreter_test.go | 3 +- core/vm/runtime/runtime.go | 3 +- core/vm/runtime/runtime_test.go | 28 ++++++++-------- internal/ethapi/override/override.go | 2 +- tests/state_test_util.go | 2 +- 24 files changed, 186 insertions(+), 59 deletions(-) create mode 100644 core/tracing/gen_code_change_reason_stringer.go diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index da1fb3701f..7ea6e578cc 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -423,7 +423,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB sdb := state.NewDatabase(tdb, nil) statedb, _ := state.New(types.EmptyRootHash, sdb) for addr, a := range accounts { - statedb.SetCode(addr, a.Code) + statedb.SetCode(addr, a.Code, tracing.CodeChangeUnspecified) statedb.SetNonce(addr, a.Nonce, tracing.NonceChangeGenesis) statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance) for k, v := range a.Storage { diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index b2cf28353b..ebb3e04461 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -322,7 +322,7 @@ func runCmd(ctx *cli.Context) error { } } else { if len(code) > 0 { - prestate.SetCode(receiver, code) + prestate.SetCode(receiver, code, tracing.CodeChangeUnspecified) } execFunc = func() ([]byte, uint64, error) { // don't mutate the state! diff --git a/core/genesis.go b/core/genesis.go index f1a620da57..2673334e9e 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -153,7 +153,7 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) { if account.Balance != nil { statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), tracing.BalanceIncreaseGenesisBalance) } - statedb.SetCode(addr, account.Code) + statedb.SetCode(addr, account.Code, tracing.CodeChangeGenesis) statedb.SetNonce(addr, account.Nonce, tracing.NonceChangeGenesis) for key, value := range account.Storage { statedb.SetState(addr, key, value) @@ -179,7 +179,7 @@ func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database) (common.Hash, e // already captures the allocations. statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), tracing.BalanceIncreaseGenesisBalance) } - statedb.SetCode(addr, account.Code) + statedb.SetCode(addr, account.Code, tracing.CodeChangeGenesis) statedb.SetNonce(addr, account.Nonce, tracing.NonceChangeGenesis) for key, value := range account.Storage { statedb.SetState(addr, key, value) diff --git a/core/state/statedb.go b/core/state/statedb.go index 6474d3a2fa..cdfd638221 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -458,7 +458,7 @@ func (s *StateDB) SetNonce(addr common.Address, nonce uint64, reason tracing.Non } } -func (s *StateDB) SetCode(addr common.Address, code []byte) (prev []byte) { +func (s *StateDB) SetCode(addr common.Address, code []byte, reason tracing.CodeChangeReason) (prev []byte) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { return stateObject.SetCode(crypto.Keccak256Hash(code), code) diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index 7dada63d45..f4761bd10c 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -89,7 +89,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction code := make([]byte, 16) binary.BigEndian.PutUint64(code, uint64(a.args[0])) binary.BigEndian.PutUint64(code[8:], uint64(a.args[1])) - s.SetCode(addr, code) + s.SetCode(addr, code, tracing.CodeChangeUnspecified) }, args: make([]int64, 2), }, diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index 556a3e6c49..d2595bcefe 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -189,14 +189,20 @@ func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64, reason tr } } -func (s *hookedStateDB) SetCode(address common.Address, code []byte) []byte { - prev := s.inner.SetCode(address, code) - if s.hooks.OnCodeChange != nil { +func (s *hookedStateDB) SetCode(address common.Address, code []byte, reason tracing.CodeChangeReason) []byte { + prev := s.inner.SetCode(address, code, reason) + if s.hooks.OnCodeChangeV2 != nil || s.hooks.OnCodeChange != nil { prevHash := types.EmptyCodeHash if len(prev) != 0 { prevHash = crypto.Keccak256Hash(prev) } - s.hooks.OnCodeChange(address, prevHash, prev, crypto.Keccak256Hash(code), code) + codeHash := crypto.Keccak256Hash(code) + + if s.hooks.OnCodeChangeV2 != nil { + s.hooks.OnCodeChangeV2(address, prevHash, prev, codeHash, code, reason) + } else if s.hooks.OnCodeChange != nil { + s.hooks.OnCodeChange(address, prevHash, prev, codeHash, code) + } } return prev } @@ -224,8 +230,12 @@ func (s *hookedStateDB) SelfDestruct(address common.Address) uint256.Int { s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) } - if s.hooks.OnCodeChange != nil && len(prevCode) > 0 { - s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil) + if len(prevCode) > 0 { + if s.hooks.OnCodeChangeV2 != nil { + s.hooks.OnCodeChangeV2(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct) + } else if s.hooks.OnCodeChange != nil { + s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil) + } } return prev @@ -246,8 +256,12 @@ func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, b s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) } - if s.hooks.OnCodeChange != nil && changed && len(prevCode) > 0 { - s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil) + if changed && len(prevCode) > 0 { + if s.hooks.OnCodeChangeV2 != nil { + s.hooks.OnCodeChangeV2(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct) + } else if s.hooks.OnCodeChange != nil { + s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil) + } } return prev, changed diff --git a/core/state/statedb_hooked_test.go b/core/state/statedb_hooked_test.go index f319b0e63c..bacb7baee1 100644 --- a/core/state/statedb_hooked_test.go +++ b/core/state/statedb_hooked_test.go @@ -114,7 +114,7 @@ func TestHooks(t *testing.T) { sdb.AddBalance(common.Address{0xaa}, uint256.NewInt(100), tracing.BalanceChangeUnspecified) sdb.SubBalance(common.Address{0xaa}, uint256.NewInt(50), tracing.BalanceChangeTransfer) sdb.SetNonce(common.Address{0xaa}, 1337, tracing.NonceChangeGenesis) - sdb.SetCode(common.Address{0xaa}, []byte{0x13, 37}) + sdb.SetCode(common.Address{0xaa}, []byte{0x13, 37}, tracing.CodeChangeUnspecified) sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x11")) sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x22")) sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x01")) diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 67e27cc832..147546a3c7 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -65,7 +65,7 @@ func TestUpdateLeaks(t *testing.T) { state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) } if i%3 == 0 { - state.SetCode(addr, []byte{i, i, i, i, i}) + state.SetCode(addr, []byte{i, i, i, i, i}, tracing.CodeChangeUnspecified) } } @@ -101,7 +101,7 @@ func TestIntermediateLeaks(t *testing.T) { state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak}) } if i%3 == 0 { - state.SetCode(addr, []byte{i, i, i, i, i, tweak}) + state.SetCode(addr, []byte{i, i, i, i, i, tweak}, tracing.CodeChangeUnspecified) } } @@ -374,7 +374,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { code := make([]byte, 16) binary.BigEndian.PutUint64(code, uint64(a.args[0])) binary.BigEndian.PutUint64(code[8:], uint64(a.args[1])) - s.SetCode(addr, code) + s.SetCode(addr, code, tracing.CodeChangeUnspecified) }, args: make([]int64, 2), }, @@ -403,7 +403,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { // which would cause a difference in state when unrolling // the journal. (CreateContact assumes created was false prior to // invocation, and the journal rollback sets it to false). - s.SetCode(addr, []byte{1}) + s.SetCode(addr, []byte{1}, tracing.CodeChangeUnspecified) } }, }, @@ -731,7 +731,7 @@ func TestCopyCommitCopy(t *testing.T) { sval := common.HexToHash("bbb") state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { @@ -804,7 +804,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { sval := common.HexToHash("bbb") state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { @@ -874,7 +874,7 @@ func TestCommitCopy(t *testing.T) { sval1, sval2 := common.HexToHash("b1"), common.HexToHash("b2") state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata state.SetState(addr, skey1, sval1) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { @@ -987,10 +987,10 @@ func testMissingTrieNodes(t *testing.T, scheme string) { addr := common.BytesToAddress([]byte("so")) { state.SetBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified) - state.SetCode(addr, []byte{1, 2, 3}) + state.SetCode(addr, []byte{1, 2, 3}, tracing.CodeChangeUnspecified) a2 := common.BytesToAddress([]byte("another")) state.SetBalance(a2, uint256.NewInt(100), tracing.BalanceChangeUnspecified) - state.SetCode(a2, []byte{1, 2, 4}) + state.SetCode(a2, []byte{1, 2, 4}, tracing.CodeChangeUnspecified) root, _ = state.Commit(0, false, false) t.Logf("root: %x", root) // force-flush diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go index 4d1b627c4d..41349c0c0e 100644 --- a/core/state/trie_prefetcher_test.go +++ b/core/state/trie_prefetcher_test.go @@ -39,7 +39,7 @@ func filledStateDB() *StateDB { sval := common.HexToHash("bbb") state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata state.SetState(addr, skey, sval) // Change the storage trie for i := 0; i < 100; i++ { sk := common.BigToHash(big.NewInt(int64(i))) @@ -81,7 +81,7 @@ func TestVerklePrefetcher(t *testing.T) { sval := testrand.Hash() state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata state.SetState(addr, skey, sval) // Change the storage trie root, _ := state.Commit(0, true, false) diff --git a/core/state_transition.go b/core/state_transition.go index 681c300696..2cafe4865f 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -617,12 +617,12 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization) st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization) if auth.Address == (common.Address{}) { // Delegation to zero address means clear. - st.state.SetCode(authority, nil) + st.state.SetCode(authority, nil, tracing.CodeChangeAuthorizationClear) return nil } // Otherwise install delegation to auth.Address. - st.state.SetCode(authority, types.AddressToDelegation(auth.Address)) + st.state.SetCode(authority, types.AddressToDelegation(auth.Address), tracing.CodeChangeAuthorization) return nil } diff --git a/core/tracing/gen_code_change_reason_stringer.go b/core/tracing/gen_code_change_reason_stringer.go new file mode 100644 index 0000000000..9372954063 --- /dev/null +++ b/core/tracing/gen_code_change_reason_stringer.go @@ -0,0 +1,29 @@ +// Code generated by "stringer -type=CodeChangeReason -trimprefix=CodeChange -output gen_code_change_reason_stringer.go"; DO NOT EDIT. + +package tracing + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[CodeChangeUnspecified-0] + _ = x[CodeChangeContractCreation-1] + _ = x[CodeChangeGenesis-2] + _ = x[CodeChangeAuthorization-3] + _ = x[CodeChangeAuthorizationClear-4] + _ = x[CodeChangeSelfDestruct-5] + _ = x[CodeChangeRevert-6] +} + +const _CodeChangeReason_name = "UnspecifiedContractCreationGenesisAuthorizationAuthorizationClearSelfDestructRevert" + +var _CodeChangeReason_index = [...]uint8{0, 11, 27, 34, 47, 65, 77, 83} + +func (i CodeChangeReason) String() string { + if i >= CodeChangeReason(len(_CodeChangeReason_index)-1) { + return "CodeChangeReason(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _CodeChangeReason_name[_CodeChangeReason_index[i]:_CodeChangeReason_index[i+1]] +} diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 0485f7a3eb..8e50dc3d8f 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -177,6 +177,9 @@ type ( // CodeChangeHook is called when the code of an account changes. CodeChangeHook = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) + // CodeChangeHookV2 is called when the code of an account changes. + CodeChangeHookV2 = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason CodeChangeReason) + // StorageChangeHook is called when the storage of an account changes. StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash) @@ -211,6 +214,7 @@ type Hooks struct { OnNonceChange NonceChangeHook OnNonceChangeV2 NonceChangeHookV2 OnCodeChange CodeChangeHook + OnCodeChangeV2 CodeChangeHookV2 OnStorageChange StorageChangeHook OnLog LogHook // Block hash read @@ -372,3 +376,31 @@ const ( // It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal). NonceChangeRevert NonceChangeReason = 6 ) + +// CodeChangeReason is used to indicate the reason for a code change. +type CodeChangeReason byte + +//go:generate go run golang.org/x/tools/cmd/stringer -type=CodeChangeReason -trimprefix=CodeChange -output gen_code_change_reason_stringer.go + +const ( + CodeChangeUnspecified CodeChangeReason = 0 + + // CodeChangeContractCreation is when a new contract is deployed via CREATE/CREATE2 operations. + CodeChangeContractCreation CodeChangeReason = 1 + + // CodeChangeGenesis is when contract code is set during blockchain genesis or initial setup. + CodeChangeGenesis CodeChangeReason = 2 + + // CodeChangeAuthorization is when code is set via EIP-7702 Set Code Authorization. + CodeChangeAuthorization CodeChangeReason = 3 + + // CodeChangeAuthorizationClear is when EIP-7702 delegation is cleared by setting to zero address. + CodeChangeAuthorizationClear CodeChangeReason = 4 + + // CodeChangeSelfDestruct is when contract code is cleared due to self-destruct. + CodeChangeSelfDestruct CodeChangeReason = 5 + + // CodeChangeRevert is emitted when the code is reverted back to a previous value due to call failure. + // It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal). + CodeChangeRevert CodeChangeReason = 6 +) diff --git a/core/tracing/journal.go b/core/tracing/journal.go index a402f1ac09..62a70d6c27 100644 --- a/core/tracing/journal.go +++ b/core/tracing/journal.go @@ -42,12 +42,15 @@ func WrapWithJournal(hooks *Hooks) (*Hooks, error) { return nil, errors.New("wrapping nil tracer") } // No state change to journal, return the wrapped hooks as is - if hooks.OnBalanceChange == nil && hooks.OnNonceChange == nil && hooks.OnNonceChangeV2 == nil && hooks.OnCodeChange == nil && hooks.OnStorageChange == nil { + if hooks.OnBalanceChange == nil && hooks.OnNonceChange == nil && hooks.OnNonceChangeV2 == nil && hooks.OnCodeChange == nil && hooks.OnCodeChangeV2 == nil && hooks.OnStorageChange == nil { return hooks, nil } if hooks.OnNonceChange != nil && hooks.OnNonceChangeV2 != nil { return nil, errors.New("cannot have both OnNonceChange and OnNonceChangeV2") } + if hooks.OnCodeChange != nil && hooks.OnCodeChangeV2 != nil { + return nil, errors.New("cannot have both OnCodeChange and OnCodeChangeV2") + } // Create a new Hooks instance and copy all hooks wrapped := *hooks @@ -72,6 +75,9 @@ func WrapWithJournal(hooks *Hooks) (*Hooks, error) { if hooks.OnCodeChange != nil { wrapped.OnCodeChange = j.OnCodeChange } + if hooks.OnCodeChangeV2 != nil { + wrapped.OnCodeChangeV2 = j.OnCodeChangeV2 + } if hooks.OnStorageChange != nil { wrapped.OnStorageChange = j.OnStorageChange } @@ -174,6 +180,19 @@ func (j *journal) OnCodeChange(addr common.Address, prevCodeHash common.Hash, pr } } +func (j *journal) OnCodeChangeV2(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason CodeChangeReason) { + j.entries = append(j.entries, codeChange{ + addr: addr, + prevCodeHash: prevCodeHash, + prevCode: prevCode, + newCodeHash: codeHash, + newCode: code, + }) + if j.hooks.OnCodeChangeV2 != nil { + j.hooks.OnCodeChangeV2(addr, prevCodeHash, prevCode, codeHash, code, reason) + } +} + func (j *journal) OnStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash) { j.entries = append(j.entries, storageChange{addr: addr, slot: slot, prev: prev, new: new}) if j.hooks.OnStorageChange != nil { @@ -225,7 +244,9 @@ func (n nonceChange) revert(hooks *Hooks) { } func (c codeChange) revert(hooks *Hooks) { - if hooks.OnCodeChange != nil { + if hooks.OnCodeChangeV2 != nil { + hooks.OnCodeChangeV2(c.addr, c.newCodeHash, c.newCode, c.prevCodeHash, c.prevCode, CodeChangeRevert) + } else if hooks.OnCodeChange != nil { hooks.OnCodeChange(c.addr, c.newCodeHash, c.newCode, c.prevCodeHash, c.prevCode) } } diff --git a/core/tracing/journal_test.go b/core/tracing/journal_test.go index 99447e1e1d..cf74d83483 100644 --- a/core/tracing/journal_test.go +++ b/core/tracing/journal_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" ) type testTracer struct { @@ -56,6 +57,11 @@ func (t *testTracer) OnCodeChange(addr common.Address, prevCodeHash common.Hash, t.code = code } +func (t *testTracer) OnCodeChangeV2(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason CodeChangeReason) { + t.t.Logf("OnCodeChangeV2(%v, %v -> %v, %v)", addr, prevCodeHash, codeHash, reason) + t.code = code +} + func (t *testTracer) OnStorageChange(addr common.Address, slot common.Hash, prev common.Hash, new common.Hash) { t.t.Logf("OnStorageCodeChange(%v, %v, %v -> %v)", addr, slot, prev, new) if t.storage == nil { @@ -232,6 +238,27 @@ func TestOnNonceChangeV2(t *testing.T) { } } +func TestOnCodeChangeV2(t *testing.T) { + tr := &testTracer{t: t} + wr, err := WrapWithJournal(&Hooks{OnCodeChangeV2: tr.OnCodeChangeV2}) + if err != nil { + t.Fatalf("failed to wrap test tracer: %v", err) + } + + addr := common.HexToAddress("0x1234") + code := []byte{1, 2, 3} + { + wr.OnEnter(2, 0, addr, addr, nil, 1000, big.NewInt(0)) + wr.OnCodeChangeV2(addr, common.Hash{}, nil, crypto.Keccak256Hash(code), code, CodeChangeContractCreation) + wr.OnExit(2, nil, 100, nil, true) + } + + // After revert, code should be nil + if tr.code != nil { + t.Fatalf("unexpected code after revert: %v", tr.code) + } +} + func TestAllHooksCalled(t *testing.T) { tracer := newTracerAllHooks() hooks := tracer.hooks() @@ -298,6 +325,7 @@ func newTracerAllHooks() *tracerAllHooks { t.hooksCalled[hooksType.Field(i).Name] = false } delete(t.hooksCalled, "OnNonceChange") + delete(t.hooksCalled, "OnCodeChange") return t } @@ -322,7 +350,7 @@ func (t *tracerAllHooks) hooks() *Hooks { hooksValue := reflect.ValueOf(h).Elem() for i := 0; i < hooksValue.NumField(); i++ { field := hooksValue.Type().Field(i) - if field.Name == "OnNonceChange" { + if field.Name == "OnNonceChange" || field.Name == "OnCodeChange" { continue } hookMethod := reflect.MakeFunc(field.Type, func(args []reflect.Value) []reflect.Value { diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index 1ba080b749..0c8642659d 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -2292,8 +2292,8 @@ func TestSetCodeTransactions(t *testing.T) { pending: 1, run: func(name string) { aa := common.Address{0xaa, 0xaa} - statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...)) - statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)}) + statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...), tracing.CodeChangeUnspecified) + statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)}, tracing.CodeChangeUnspecified) // Send gapped transaction, it should be rejected. if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrOutOfOrderTxFromDelegated) { @@ -2317,7 +2317,7 @@ func TestSetCodeTransactions(t *testing.T) { } // Reset the delegation, avoid leaking state into the other tests - statedb.SetCode(addrA, nil) + statedb.SetCode(addrA, nil, tracing.CodeChangeUnspecified) }, }, { @@ -2583,7 +2583,7 @@ func TestSetCodeTransactionsReorg(t *testing.T) { } // Simulate the chain moving blockchain.statedb.SetNonce(addrA, 1, tracing.NonceChangeAuthorization) - blockchain.statedb.SetCode(addrA, types.AddressToDelegation(auth.Address)) + blockchain.statedb.SetCode(addrA, types.AddressToDelegation(auth.Address), tracing.CodeChangeUnspecified) <-pool.requestReset(nil, nil) // Set an authorization for 0x00 auth, _ = types.SignSetCode(keyA, types.SetCodeAuthorization{ @@ -2601,7 +2601,7 @@ func TestSetCodeTransactionsReorg(t *testing.T) { } // Simulate the chain moving blockchain.statedb.SetNonce(addrA, 2, tracing.NonceChangeAuthorization) - blockchain.statedb.SetCode(addrA, nil) + blockchain.statedb.SetCode(addrA, nil, tracing.CodeChangeUnspecified) <-pool.requestReset(nil, nil) // Now send two transactions from addrA if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); err != nil { diff --git a/core/verkle_witness_test.go b/core/verkle_witness_test.go index ed85d1555f..ca0c928c3c 100644 --- a/core/verkle_witness_test.go +++ b/core/verkle_witness_test.go @@ -232,7 +232,7 @@ func TestProcessParentBlockHash(t *testing.T) { // etc checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) { statedb.SetNonce(params.HistoryStorageAddress, 1, tracing.NonceChangeUnspecified) - statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode) + statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode, tracing.CodeChangeUnspecified) // Process n blocks, from 1 .. num var num = 2 for i := 1; i <= num; i++ { diff --git a/core/vm/evm.go b/core/vm/evm.go index e360187f7b..88ef1cf121 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -601,7 +601,7 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b } } - evm.StateDB.SetCode(address, ret) + evm.StateDB.SetCode(address, ret, tracing.CodeChangeContractCreation) return ret, nil } diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index cb6143c0b5..7fe76b0a63 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -87,7 +88,7 @@ func TestEIP2200(t *testing.T) { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) statedb.CreateAccount(address) - statedb.SetCode(address, hexutil.MustDecode(tt.input)) + statedb.SetCode(address, hexutil.MustDecode(tt.input), tracing.CodeChangeUnspecified) statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original})) statedb.Finalise(true) // Push the state into the "original" slot @@ -139,7 +140,7 @@ func TestCreateGas(t *testing.T) { address := common.BytesToAddress([]byte("contract")) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) statedb.CreateAccount(address) - statedb.SetCode(address, hexutil.MustDecode(tt.code)) + statedb.SetCode(address, hexutil.MustDecode(tt.code), tracing.CodeChangeUnspecified) statedb.Finalise(true) vmctx := BlockContext{ CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true }, diff --git a/core/vm/interface.go b/core/vm/interface.go index 42a72db482..d7f4c10e1f 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -43,7 +43,7 @@ type StateDB interface { GetCode(common.Address) []byte // SetCode sets the new code for the address, and returns the previous code, if any. - SetCode(common.Address, []byte) []byte + SetCode(common.Address, []byte, tracing.CodeChangeReason) []byte GetCodeSize(common.Address) int AddRefund(uint64) diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index 8ed512316b..90eeda34e6 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -45,7 +46,7 @@ func TestLoopInterrupt(t *testing.T) { for i, tt := range loopInterruptTests { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) statedb.CreateAccount(address) - statedb.SetCode(address, common.Hex2Bytes(tt)) + statedb.SetCode(address, common.Hex2Bytes(tt), tracing.CodeChangeUnspecified) statedb.Finalise(true) evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{}) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 9d984291f2..b40e99d047 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -139,7 +140,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil) cfg.State.CreateAccount(address) // set the receiver's (the executing contract) code for execution. - cfg.State.SetCode(address, code) + cfg.State.SetCode(address, code, tracing.CodeChangeUnspecified) // Call the code with the given configuration. ret, leftOverGas, err := vmenv.Call( cfg.Origin, diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index d75a5b0459..cabc57d1fb 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -114,7 +114,7 @@ func TestCall(t *testing.T) { byte(vm.PUSH1), 32, byte(vm.PUSH1), 0, byte(vm.RETURN), - }) + }, tracing.CodeChangeUnspecified) ret, _, err := Call(address, nil, &Config{State: state}) if err != nil { @@ -167,7 +167,7 @@ func benchmarkEVM_Create(bench *testing.B, code string) { ) statedb.CreateAccount(sender) - statedb.SetCode(receiver, common.FromHex(code)) + statedb.SetCode(receiver, common.FromHex(code), tracing.CodeChangeUnspecified) runtimeConfig := Config{ Origin: sender, State: statedb, @@ -232,7 +232,7 @@ func BenchmarkEVM_SWAP1(b *testing.B) { b.Run("10k", func(b *testing.B) { contractCode := swapContract(10_000) - state.SetCode(contractAddr, contractCode) + state.SetCode(contractAddr, contractCode, tracing.CodeChangeUnspecified) for i := 0; i < b.N; i++ { _, _, err := Call(contractAddr, []byte{}, &Config{State: state}) @@ -263,7 +263,7 @@ func BenchmarkEVM_RETURN(b *testing.B) { b.ReportAllocs() contractCode := returnContract(n) - state.SetCode(contractAddr, contractCode) + state.SetCode(contractAddr, contractCode, tracing.CodeChangeUnspecified) for i := 0; i < b.N; i++ { ret, _, err := Call(contractAddr, []byte{}, &Config{State: state}) @@ -422,12 +422,12 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, byte(vm.REVERT), - }) + }, tracing.CodeChangeUnspecified) } //cfg.State.CreateAccount(cfg.Origin) // set the receiver's (the executing contract) code for execution. - cfg.State.SetCode(destination, code) + cfg.State.SetCode(destination, code, tracing.CodeChangeUnspecified) Call(destination, nil, cfg) b.Run(name, func(b *testing.B) { @@ -772,12 +772,12 @@ func TestRuntimeJSTracer(t *testing.T) { for i, jsTracer := range jsTracers { for j, tc := range tests { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) - statedb.SetCode(main, tc.code) - statedb.SetCode(common.HexToAddress("0xbb"), calleeCode) - statedb.SetCode(common.HexToAddress("0xcc"), calleeCode) - statedb.SetCode(common.HexToAddress("0xdd"), calleeCode) - statedb.SetCode(common.HexToAddress("0xee"), calleeCode) - statedb.SetCode(common.HexToAddress("0xff"), suicideCode) + statedb.SetCode(main, tc.code, tracing.CodeChangeUnspecified) + statedb.SetCode(common.HexToAddress("0xbb"), calleeCode, tracing.CodeChangeUnspecified) + statedb.SetCode(common.HexToAddress("0xcc"), calleeCode, tracing.CodeChangeUnspecified) + statedb.SetCode(common.HexToAddress("0xdd"), calleeCode, tracing.CodeChangeUnspecified) + statedb.SetCode(common.HexToAddress("0xee"), calleeCode, tracing.CodeChangeUnspecified) + statedb.SetCode(common.HexToAddress("0xff"), suicideCode, tracing.CodeChangeUnspecified) tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil, params.MergedTestChainConfig) if err != nil { @@ -862,8 +862,8 @@ func BenchmarkTracerStepVsCallFrame(b *testing.B) { // delegation designator incurs the correct amount of gas based on the tracer. func TestDelegatedAccountAccessCost(t *testing.T) { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) - statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa"))) - statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes()) + statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa")), tracing.CodeChangeUnspecified) + statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes(), tracing.CodeChangeUnspecified) for i, tc := range []struct { code []byte diff --git a/internal/ethapi/override/override.go b/internal/ethapi/override/override.go index 0bcf3c444d..9d57a78651 100644 --- a/internal/ethapi/override/override.go +++ b/internal/ethapi/override/override.go @@ -91,7 +91,7 @@ func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.Precompi } // Override account(contract) code. if account.Code != nil { - statedb.SetCode(addr, *account.Code) + statedb.SetCode(addr, *account.Code, tracing.CodeChangeUnspecified) } // Override account balance. if account.Balance != nil { diff --git a/tests/state_test_util.go b/tests/state_test_util.go index a22e470ad8..ec7eec1f39 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -511,7 +511,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bo sdb := state.NewDatabase(triedb, nil) statedb, _ := state.New(types.EmptyRootHash, sdb) for addr, a := range accounts { - statedb.SetCode(addr, a.Code) + statedb.SetCode(addr, a.Code, tracing.CodeChangeUnspecified) statedb.SetNonce(addr, a.Nonce, tracing.NonceChangeUnspecified) statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceChangeUnspecified) for k, v := range a.Storage { From 0e82b6be631d1257fd9848f4039d4230aeaaba42 Mon Sep 17 00:00:00 2001 From: keeghcet Date: Tue, 2 Sep 2025 20:01:33 +0800 Subject: [PATCH 006/119] core/txpool: fix duplicate function comment (#32524) --- core/txpool/blobpool/blobpool_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 51ab27eb01..c246928974 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -262,8 +262,8 @@ func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap return makeUnsignedTxWithTestBlob(nonce, gasTipCap, gasFeeCap, blobFeeCap, rnd.Intn(len(testBlobs))) } -// makeUnsignedTx is a utility method to construct a random blob transaction -// without signing it. +// makeUnsignedTxWithTestBlob is a utility method to construct a random blob transaction +// with a specific test blob without signing it. func makeUnsignedTxWithTestBlob(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, blobIdx int) *types.BlobTx { return &types.BlobTx{ ChainID: uint256.MustFromBig(params.MainnetChainConfig.ChainID), From 00516c71fbc4781f345806cdc684c6c223159029 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Wed, 3 Sep 2025 15:44:00 +0800 Subject: [PATCH 007/119] core/txpool/blobpool, eth/catalyst: place null for missing blob (#32536) This pull request fixes a regression, introduced in #32190 Specifically, in GetBlobsV1 engine API, if any blob is missing, the null should be placed in response, unfortunately a behavioral change was introduced in #32190, returning an error instead. What's more, a more comprehensive test suite is added to cover both `GetBlobsV1` and `GetBlobsV2` endpoints. --- core/txpool/blobpool/blobpool.go | 12 +- core/txpool/blobpool/blobpool_test.go | 127 +++++++---- eth/catalyst/api.go | 57 +++++ eth/catalyst/api_test.go | 300 ++++++++++++++++++++++++-- 4 files changed, 431 insertions(+), 65 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 64ee3fcd9a..68ea557633 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1298,6 +1298,13 @@ func (p *BlobPool) GetMetadata(hash common.Hash) *txpool.TxMetadata { } // GetBlobs returns a number of blobs and proofs for the given versioned hashes. +// Blobpool must place responses in the order given in the request, using null +// for any missing blobs. +// +// For instance, if the request is [A_versioned_hash, B_versioned_hash, +// C_versioned_hash] and blobpool has data for blobs A and C, but doesn't have +// data for B, the response MUST be [A, null, C]. +// // This is a utility method for the engine API, enabling consensus clients to // retrieve blobs from the pools directly instead of the network. func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blob, []kzg4844.Commitment, [][]kzg4844.Proof, error) { @@ -1317,12 +1324,13 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blo if _, ok := filled[vhash]; ok { continue } - // Retrieve the corresponding blob tx with the vhash + // Retrieve the corresponding blob tx with the vhash, skip blob resolution + // if it's not found locally and place the null instead. p.lock.RLock() txID, exists := p.lookup.storeidOfBlob(vhash) p.lock.RUnlock() if !exists { - return nil, nil, nil, fmt.Errorf("blob with vhash %x is not found", vhash) + continue } data, err := p.store.Get(txID) if err != nil { diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index c246928974..8171ae294a 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -24,6 +24,7 @@ import ( "fmt" "math" "math/big" + "math/rand" "os" "path/filepath" "reflect" @@ -41,6 +42,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/billy" @@ -1814,10 +1816,10 @@ func TestGetBlobs(t *testing.T) { } cases := []struct { - start int - limit int - version byte - expErr bool + start int + limit int + fillRandom bool + version byte }{ { start: 0, limit: 6, @@ -1827,6 +1829,14 @@ func TestGetBlobs(t *testing.T) { start: 0, limit: 6, version: types.BlobSidecarVersion1, }, + { + start: 0, limit: 6, fillRandom: true, + version: types.BlobSidecarVersion0, + }, + { + start: 0, limit: 6, fillRandom: true, + version: types.BlobSidecarVersion1, + }, { start: 3, limit: 9, version: types.BlobSidecarVersion0, @@ -1835,6 +1845,14 @@ func TestGetBlobs(t *testing.T) { start: 3, limit: 9, version: types.BlobSidecarVersion1, }, + { + start: 3, limit: 9, fillRandom: true, + version: types.BlobSidecarVersion0, + }, + { + start: 3, limit: 9, fillRandom: true, + version: types.BlobSidecarVersion1, + }, { start: 3, limit: 15, version: types.BlobSidecarVersion0, @@ -1843,6 +1861,14 @@ func TestGetBlobs(t *testing.T) { start: 3, limit: 15, version: types.BlobSidecarVersion1, }, + { + start: 3, limit: 15, fillRandom: true, + version: types.BlobSidecarVersion0, + }, + { + start: 3, limit: 15, fillRandom: true, + version: types.BlobSidecarVersion1, + }, { start: 0, limit: 18, version: types.BlobSidecarVersion0, @@ -1852,58 +1878,79 @@ func TestGetBlobs(t *testing.T) { version: types.BlobSidecarVersion1, }, { - start: 18, limit: 20, + start: 0, limit: 18, fillRandom: true, version: types.BlobSidecarVersion0, - expErr: true, + }, + { + start: 0, limit: 18, fillRandom: true, + version: types.BlobSidecarVersion1, }, } for i, c := range cases { - var vhashes []common.Hash + var ( + vhashes []common.Hash + filled = make(map[int]struct{}) + ) + if c.fillRandom { + filled[len(vhashes)] = struct{}{} + vhashes = append(vhashes, testrand.Hash()) + } for j := c.start; j < c.limit; j++ { vhashes = append(vhashes, testBlobVHashes[j]) + if c.fillRandom && rand.Intn(2) == 0 { + filled[len(vhashes)] = struct{}{} + vhashes = append(vhashes, testrand.Hash()) + } + } + if c.fillRandom { + filled[len(vhashes)] = struct{}{} + vhashes = append(vhashes, testrand.Hash()) } blobs, _, proofs, err := pool.GetBlobs(vhashes, c.version) + if err != nil { + t.Errorf("Unexpected error for case %d, %v", i, err) + } - if c.expErr { - if err == nil { - t.Errorf("Unexpected return, want error for case %d", i) - } - } else { - if err != nil { - t.Errorf("Unexpected error for case %d, %v", i, err) - } - // Cross validate what we received vs what we wanted - length := c.limit - c.start - if len(blobs) != length || len(proofs) != length { - t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), length) + // Cross validate what we received vs what we wanted + length := c.limit - c.start + wantLen := length + len(filled) + if len(blobs) != wantLen || len(proofs) != wantLen { + t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), wantLen) + continue + } + + var unknown int + for j := 0; j < len(blobs); j++ { + if _, exist := filled[j]; exist { + if blobs[j] != nil || proofs[j] != nil { + t.Errorf("Unexpected blob and proof, item %d", j) + } + unknown++ continue } - for j := 0; j < len(blobs); j++ { - // If an item is missing, but shouldn't, error - if blobs[j] == nil || proofs[j] == nil { - t.Errorf("tracked blob retrieval failed: item %d, hash %x", j, vhashes[j]) - continue + // If an item is missing, but shouldn't, error + if blobs[j] == nil || proofs[j] == nil { + t.Errorf("tracked blob retrieval failed: item %d, hash %x", j, vhashes[j]) + continue + } + // Item retrieved, make sure the blob matches the expectation + if *blobs[j] != *testBlobs[c.start+j-unknown] { + t.Errorf("retrieved blob mismatch: item %d, hash %x", j, vhashes[j]) + continue + } + // Item retrieved, make sure the proof matches the expectation + if c.version == types.BlobSidecarVersion0 { + if proofs[j][0] != testBlobProofs[c.start+j-unknown] { + t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j]) } - // Item retrieved, make sure the blob matches the expectation - if *blobs[j] != *testBlobs[c.start+j] { - t.Errorf("retrieved blob mismatch: item %d, hash %x", j, vhashes[j]) - continue - } - // Item retrieved, make sure the proof matches the expectation - if c.version == types.BlobSidecarVersion0 { - if proofs[j][0] != testBlobProofs[c.start+j] { - t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j]) - } - } else { - want, _ := kzg4844.ComputeCellProofs(blobs[j]) - if !reflect.DeepEqual(want, proofs[j]) { - t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j]) - } + } else { + want, _ := kzg4844.ComputeCellProofs(blobs[j]) + if !reflect.DeepEqual(want, proofs[j]) { + t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j]) } } } } - pool.Close() } diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 7f6dd40907..07ce523462 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -458,6 +458,26 @@ func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*eng } // GetBlobsV1 returns a blob from the transaction pool. +// +// Specification: +// +// Given an array of blob versioned hashes client software MUST respond with an +// array of BlobAndProofV1 objects with matching versioned hashes, respecting the +// order of versioned hashes in the input array. +// +// Client software MUST place responses in the order given in the request, using +// null for any missing blobs. For instance: +// +// if the request is [A_versioned_hash, B_versioned_hash, C_versioned_hash] and +// client software has data for blobs A and C, but doesn't have data for B, the +// response MUST be [A, null, C]. +// +// Client software MUST support request sizes of at least 128 blob versioned hashes. +// The client MUST return -38004: Too large request error if the number of requested +// blobs is too large. +// +// Client software MAY return an array of all null entries if syncing or otherwise +// unable to serve blob pool data. func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProofV1, error) { if len(hashes) > 128 { return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))) @@ -468,6 +488,10 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo } res := make([]*engine.BlobAndProofV1, len(hashes)) for i := 0; i < len(blobs); i++ { + // Skip the non-existing blob + if blobs[i] == nil { + continue + } res[i] = &engine.BlobAndProofV1{ Blob: blobs[i][:], Proof: proofs[i][0][:], @@ -477,6 +501,33 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo } // GetBlobsV2 returns a blob from the transaction pool. +// +// Specification: +// Refer to the specification for engine_getBlobsV1 with changes of the following: +// +// Given an array of blob versioned hashes client software MUST respond with an +// array of BlobAndProofV2 objects with matching versioned hashes, respecting +// the order of versioned hashes in the input array. +// +// Client software MUST return null in case of any missing or older version blobs. +// For instance, +// +// - if the request is [A_versioned_hash, B_versioned_hash, C_versioned_hash] and +// client software has data for blobs A and C, but doesn't have data for B, the +// response MUST be null. +// +// - if the request is [A_versioned_hash_for_blob_with_blob_proof], the response +// MUST be null as well. +// +// Note, geth internally make the conversion from old version to new one, so the +// data will be returned normally. +// +// Client software MUST support request sizes of at least 128 blob versioned +// hashes. The client MUST return -38004: Too large request error if the number +// of requested blobs is too large. +// +// Client software MUST return null if syncing or otherwise unable to serve +// blob pool data. func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProofV2, error) { if len(hashes) > 128 { return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))) @@ -498,6 +549,12 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo } res := make([]*engine.BlobAndProofV2, len(hashes)) for i := 0; i < len(blobs); i++ { + // the blob is missing, return null as response. It should + // be caught by `AvailableBlobs` though, perhaps data race + // occurs between two calls. + if blobs[i] == nil { + return nil, nil + } var cellProofs []hexutil.Bytes for _, proof := range proofs[i] { cellProofs = append(cellProofs, proof[:]) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index ad377113b5..659280bf3b 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -19,7 +19,9 @@ package catalyst import ( "bytes" "context" + "crypto/ecdsa" crand "crypto/rand" + "crypto/sha256" "errors" "fmt" "math/big" @@ -40,6 +42,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" @@ -47,6 +50,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" + "github.com/holiman/uint256" ) var ( @@ -112,7 +116,7 @@ func TestEth2AssembleBlock(t *testing.T) { n, ethservice := startEthService(t, genesis, blocks) defer n.Close() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey) if err != nil { @@ -151,7 +155,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { n, ethservice := startEthService(t, genesis, blocks[:9]) defer n.Close() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) // Put the 10th block's tx in the pool and produce a new block txs := blocks[9].Transactions() @@ -173,7 +177,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { n, ethservice := startEthService(t, genesis, blocks[:9]) defer n.Close() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) // Put the 10th block's tx in the pool and produce a new block txs := blocks[9].Transactions() @@ -238,8 +242,9 @@ func TestInvalidPayloadTimestamp(t *testing.T) { genesis, preMergeBlocks := generateMergeChain(10, false) n, ethservice := startEthService(t, genesis, preMergeBlocks) defer n.Close() + var ( - api = NewConsensusAPI(ethservice) + api = newConsensusAPIWithoutHeartbeat(ethservice) parent = ethservice.BlockChain().CurrentBlock() ) tests := []struct { @@ -281,7 +286,7 @@ func TestEth2NewBlock(t *testing.T) { defer n.Close() var ( - api = NewConsensusAPI(ethservice) + api = newConsensusAPIWithoutHeartbeat(ethservice) parent = preMergeBlocks[len(preMergeBlocks)-1] // This EVM code generates a log when the contract is created. @@ -434,8 +439,14 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) t.Fatal("can't create node:", err) } - mcfg := miner.DefaultConfig - ethcfg := ðconfig.Config{Genesis: genesis, SyncMode: ethconfig.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg} + ethcfg := ðconfig.Config{ + Genesis: genesis, + SyncMode: ethconfig.FullSync, + TrieTimeout: time.Minute, + TrieDirtyCache: 256, + TrieCleanCache: 256, + Miner: miner.DefaultConfig, + } ethservice, err := eth.New(n, ethcfg) if err != nil { t.Fatal("can't create eth service:", err) @@ -459,6 +470,7 @@ func TestFullAPI(t *testing.T) { genesis, preMergeBlocks := generateMergeChain(10, false) n, ethservice := startEthService(t, genesis, preMergeBlocks) defer n.Close() + var ( parent = ethservice.BlockChain().CurrentBlock() // This EVM code generates a log when the contract is created. @@ -476,7 +488,7 @@ func TestFullAPI(t *testing.T) { } func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Header, callback func(parent *types.Header), withdrawals [][]*types.Withdrawal, beaconRoots []common.Hash) []*types.Header { - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) var blocks []*types.Header for i := 0; i < n; i++ { callback(parent) @@ -524,7 +536,7 @@ func TestExchangeTransitionConfig(t *testing.T) { defer n.Close() // invalid ttd - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) config := engine.TransitionConfigurationV1{ TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)), TerminalBlockHash: common.Hash{}, @@ -585,7 +597,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { defer n.Close() var ( - api = NewConsensusAPI(ethservice) + api = newConsensusAPIWithoutHeartbeat(ethservice) parent = ethservice.BlockChain().CurrentBlock() signer = types.LatestSigner(ethservice.BlockChain().Config()) // This EVM code generates a log when the contract is created. @@ -688,7 +700,7 @@ func TestEmptyBlocks(t *testing.T) { defer n.Close() commonAncestor := ethservice.BlockChain().CurrentBlock() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) // Setup 10 blocks on the canonical chain setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Header) {}, nil, nil) @@ -814,8 +826,8 @@ func TestTrickRemoteBlockCache(t *testing.T) { } nodeA.Server().AddPeer(nodeB.Server().Self()) nodeB.Server().AddPeer(nodeA.Server().Self()) - apiA := NewConsensusAPI(ethserviceA) - apiB := NewConsensusAPI(ethserviceB) + apiA := newConsensusAPIWithoutHeartbeat(ethserviceA) + apiB := newConsensusAPIWithoutHeartbeat(ethserviceB) commonAncestor := ethserviceA.BlockChain().CurrentBlock() @@ -872,7 +884,7 @@ func TestInvalidBloom(t *testing.T) { defer n.Close() commonAncestor := ethservice.BlockChain().CurrentBlock() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) // Setup 10 blocks on the canonical chain setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Header) {}, nil, nil) @@ -898,7 +910,7 @@ func TestSimultaneousNewBlock(t *testing.T) { defer n.Close() var ( - api = NewConsensusAPI(ethservice) + api = newConsensusAPIWithoutHeartbeat(ethservice) parent = preMergeBlocks[len(preMergeBlocks)-1] ) for i := 0; i < 10; i++ { @@ -988,7 +1000,7 @@ func TestWithdrawals(t *testing.T) { n, ethservice := startEthService(t, genesis, blocks) defer n.Close() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) // 10: Build Shanghai block with no withdrawals. parent := ethservice.BlockChain().CurrentHeader() @@ -1105,7 +1117,7 @@ func TestNilWithdrawals(t *testing.T) { n, ethservice := startEthService(t, genesis, blocks) defer n.Close() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) parent := ethservice.BlockChain().CurrentHeader() aa := common.Address{0xaa} @@ -1301,7 +1313,7 @@ func allBodies(blocks []*types.Block) []*types.Body { func TestGetBlockBodiesByHash(t *testing.T) { node, eth, blocks := setupBodies(t) - api := NewConsensusAPI(eth) + api := newConsensusAPIWithoutHeartbeat(eth) defer node.Close() tests := []struct { @@ -1357,7 +1369,7 @@ func TestGetBlockBodiesByHash(t *testing.T) { func TestGetBlockBodiesByRange(t *testing.T) { node, eth, blocks := setupBodies(t) - api := NewConsensusAPI(eth) + api := newConsensusAPIWithoutHeartbeat(eth) defer node.Close() tests := []struct { @@ -1438,7 +1450,7 @@ func TestGetBlockBodiesByRange(t *testing.T) { func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) { node, eth, _ := setupBodies(t) - api := NewConsensusAPI(eth) + api := newConsensusAPIWithoutHeartbeat(eth) defer node.Close() tests := []struct { start hexutil.Uint64 @@ -1550,7 +1562,7 @@ func TestParentBeaconBlockRoot(t *testing.T) { n, ethservice := startEthService(t, genesis, blocks) defer n.Close() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) // 11: Build Shanghai block with no withdrawals. parent := ethservice.BlockChain().CurrentHeader() @@ -1633,7 +1645,7 @@ func TestWitnessCreationAndConsumption(t *testing.T) { n, ethservice := startEthService(t, genesis, blocks[:9]) defer n.Close() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) // Put the 10th block's tx in the pool and produce a new block txs := blocks[9].Transactions() @@ -1725,7 +1737,7 @@ func TestGetClientVersion(t *testing.T) { n, ethservice := startEthService(t, genesis, preMergeBlocks) defer n.Close() - api := NewConsensusAPI(ethservice) + api := newConsensusAPIWithoutHeartbeat(ethservice) info := engine.ClientVersionV1{ Code: "TT", Name: "test", @@ -1799,3 +1811,245 @@ func TestValidateRequests(t *testing.T) { }) } } + +var ( + testBlobs []*kzg4844.Blob + testBlobCommits []kzg4844.Commitment + testBlobProofs []kzg4844.Proof + testBlobCellProofs [][]kzg4844.Proof + testBlobVHashes [][32]byte +) + +func init() { + for i := 0; i < 6; i++ { + testBlob := &kzg4844.Blob{byte(i)} + testBlobs = append(testBlobs, testBlob) + + testBlobCommit, _ := kzg4844.BlobToCommitment(testBlob) + testBlobCommits = append(testBlobCommits, testBlobCommit) + + testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit) + testBlobProofs = append(testBlobProofs, testBlobProof) + + testBlobCellProof, _ := kzg4844.ComputeCellProofs(testBlob) + testBlobCellProofs = append(testBlobCellProofs, testBlobCellProof) + + testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit) + testBlobVHashes = append(testBlobVHashes, testBlobVHash) + } +} + +// makeMultiBlobTx is a utility method to construct a random blob tx with +// certain number of blobs in its sidecar. +func makeMultiBlobTx(chainConfig *params.ChainConfig, nonce uint64, blobCount int, blobOffset int, key *ecdsa.PrivateKey, version byte) *types.Transaction { + var ( + blobs []kzg4844.Blob + blobHashes []common.Hash + commitments []kzg4844.Commitment + proofs []kzg4844.Proof + ) + for i := 0; i < blobCount; i++ { + blobs = append(blobs, *testBlobs[blobOffset+i]) + commitments = append(commitments, testBlobCommits[blobOffset+i]) + if version == types.BlobSidecarVersion0 { + proofs = append(proofs, testBlobProofs[blobOffset+i]) + } else { + cellProofs, _ := kzg4844.ComputeCellProofs(testBlobs[blobOffset+i]) + proofs = append(proofs, cellProofs...) + } + blobHashes = append(blobHashes, testBlobVHashes[blobOffset+i]) + } + blobtx := &types.BlobTx{ + ChainID: uint256.MustFromBig(chainConfig.ChainID), + Nonce: nonce, + GasTipCap: uint256.NewInt(1), + GasFeeCap: uint256.NewInt(1000), + Gas: 21000, + BlobFeeCap: uint256.NewInt(1000), + BlobHashes: blobHashes, + Value: uint256.NewInt(100), + Sidecar: types.NewBlobTxSidecar(version, blobs, commitments, proofs), + } + return types.MustSignNewTx(key, types.LatestSigner(chainConfig), blobtx) +} + +func newGetBlobEnv(t *testing.T, version byte) (*node.Node, *ConsensusAPI) { + var ( + // Create a database pre-initialize with a genesis block + config = *params.MergedTestChainConfig + + key1, _ = crypto.GenerateKey() + key2, _ = crypto.GenerateKey() + key3, _ = crypto.GenerateKey() + + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + addr3 = crypto.PubkeyToAddress(key3.PublicKey) + ) + // Disable Osaka fork for GetBlobsV1 + if version == 0 { + config.OsakaTime = nil + } + gspec := &core.Genesis{ + Config: &config, + Alloc: types.GenesisAlloc{ + testAddr: {Balance: testBalance}, + addr1: {Balance: testBalance}, + addr2: {Balance: testBalance}, + addr3: {Balance: testBalance}, + }, + Difficulty: common.Big0, + } + n, ethServ := startEthService(t, gspec, nil) + + // fill blob txs into the pool + tx1 := makeMultiBlobTx(&config, 0, 2, 0, key1, version) // blob[0, 2) + tx2 := makeMultiBlobTx(&config, 0, 2, 2, key2, version) // blob[2, 4) + tx3 := makeMultiBlobTx(&config, 0, 2, 4, key3, version) // blob[4, 6) + ethServ.TxPool().Add([]*types.Transaction{tx1, tx2, tx3}, true) + + api := newConsensusAPIWithoutHeartbeat(ethServ) + return n, api +} + +func TestGetBlobsV1(t *testing.T) { + n, api := newGetBlobEnv(t, 0) + defer n.Close() + + suites := []struct { + start int + limit int + fillRandom bool + }{ + { + start: 0, limit: 1, + }, + { + start: 0, limit: 1, fillRandom: true, + }, + { + start: 0, limit: 2, + }, + { + start: 0, limit: 2, fillRandom: true, + }, + { + start: 1, limit: 3, + }, + { + start: 1, limit: 3, fillRandom: true, + }, + { + start: 0, limit: 6, + }, + { + start: 0, limit: 6, fillRandom: true, + }, + { + start: 1, limit: 5, + }, + { + start: 1, limit: 5, fillRandom: true, + }, + } + for i, suite := range suites { + // Fill the request for retrieving blobs + var ( + vhashes []common.Hash + expect []*engine.BlobAndProofV1 + ) + // fill missing blob at the beginning + if suite.fillRandom { + vhashes = append(vhashes, testrand.Hash()) + expect = append(expect, nil) + } + for j := suite.start; j < suite.limit; j++ { + vhashes = append(vhashes, testBlobVHashes[j]) + expect = append(expect, &engine.BlobAndProofV1{ + Blob: testBlobs[j][:], + Proof: testBlobProofs[j][:], + }) + + // fill missing blobs in the middle + if suite.fillRandom && rand.Intn(2) == 0 { + vhashes = append(vhashes, testrand.Hash()) + expect = append(expect, nil) + } + } + // fill missing blobs at the end + if suite.fillRandom { + vhashes = append(vhashes, testrand.Hash()) + expect = append(expect, nil) + } + result, err := api.GetBlobsV1(vhashes) + if err != nil { + t.Errorf("Unexpected error for case %d, %v", i, err) + } + if !reflect.DeepEqual(result, expect) { + t.Fatalf("Unexpected result for case %d", i) + } + } +} + +func TestGetBlobsV2(t *testing.T) { + n, api := newGetBlobEnv(t, 1) + defer n.Close() + + suites := []struct { + start int + limit int + fillRandom bool + }{ + { + start: 0, limit: 1, + }, + { + start: 0, limit: 2, + }, + { + start: 1, limit: 3, + }, + { + start: 0, limit: 6, + }, + { + start: 1, limit: 5, + }, + { + start: 0, limit: 6, fillRandom: true, + }, + } + for i, suite := range suites { + // Fill the request for retrieving blobs + var ( + vhashes []common.Hash + expect []*engine.BlobAndProofV2 + ) + // fill missing blob + if suite.fillRandom { + vhashes = append(vhashes, testrand.Hash()) + } + for j := suite.start; j < suite.limit; j++ { + vhashes = append(vhashes, testBlobVHashes[j]) + var cellProofs []hexutil.Bytes + for _, proof := range testBlobCellProofs[j] { + cellProofs = append(cellProofs, proof[:]) + } + expect = append(expect, &engine.BlobAndProofV2{ + Blob: testBlobs[j][:], + CellProofs: cellProofs, + }) + } + result, err := api.GetBlobsV2(vhashes) + if err != nil { + t.Errorf("Unexpected error for case %d, %v", i, err) + } + // null is responded if any blob is missing + if suite.fillRandom { + expect = nil + } + if !reflect.DeepEqual(result, expect) { + t.Fatalf("Unexpected result for case %d", i) + } + } +} From e6884ccccfac51f9528953275769ba8a72bf18c6 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Wed, 3 Sep 2025 09:45:58 +0200 Subject: [PATCH 008/119] core/tracing: update changelog (#32535) Update all the accumulated changes --- core/tracing/CHANGELOG.md | 66 ++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/core/tracing/CHANGELOG.md b/core/tracing/CHANGELOG.md index a14e123d99..a94fa81b55 100644 --- a/core/tracing/CHANGELOG.md +++ b/core/tracing/CHANGELOG.md @@ -4,6 +4,27 @@ All notable changes to the tracing interface will be documented in this file. ## [Unreleased] +### Deprecated methods + +- `OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte)`: This hook is deprecated in favor of `OnCodeChangeV2` which includes a reason parameter ([#32525](https://github.com/ethereum/go-ethereum/pull/32525)). + +### New methods + +- `OnCodeChangeV2(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason CodeChangeReason)`: This hook is called when a code change occurs. It is a successor to `OnCodeChange` with an additional reason parameter ([#32525](https://github.com/ethereum/go-ethereum/pull/32525)). + +### New types + +- `CodeChangeReason` is a new type used to provide a reason for code changes. It includes various reasons such as contract creation, genesis initialization, EIP-7702 authorization, self-destruct, and revert operations ([#32525](https://github.com/ethereum/go-ethereum/pull/32525)). + +## [v1.15.4](https://github.com/ethereum/go-ethereum/releases/tag/v1.15.4) + +### Modified types + +- `GasChangeReason` has been extended with auto-generated String() methods for better debugging and logging ([#31234](https://github.com/ethereum/go-ethereum/pull/31234)). +- `NonceChangeReason` has been extended with auto-generated String() methods for better debugging and logging ([#31234](https://github.com/ethereum/go-ethereum/pull/31234)). + +## [v1.15.0](https://github.com/ethereum/go-ethereum/releases/tag/v1.15.0) + The tracing interface has been extended with backwards-compatible changes to support more use-cases and simplify tracer code. The most notable change is a state journaling library which emits reverse events when a call is reverted. ### Deprecated methods @@ -23,8 +44,13 @@ The tracing interface has been extended with backwards-compatible changes to sup ### Modified types -- `VMContext.StateDB` has been extended with `GetCodeHash(addr common.Address) common.Hash` method used to retrieve the code hash an account. +- `VMContext.StateDB` has been extended with the following method: + - `GetCodeHash(addr common.Address) common.Hash` method used to retrieve the code hash of an account. +- `BlockEvent` has been modified: + - The `TD` (Total Difficulty) field has been removed ([#30744](https://github.com/ethereum/go-ethereum/pull/30744)). - `BalanceChangeReason` has been extended with the `BalanceChangeRevert` reason. More on that below. +- `GasChangeReason` has been extended with the following reason: + - `GasChangeTxDataFloor` is the amount of extra gas the transaction has to pay to reach the minimum gas requirement for the transaction data. This change will always be a negative change. ### State journaling @@ -49,6 +75,26 @@ The state changes that are covered by the journaling library are: - `OnCodeChange` - `OnStorageChange` +## [v1.14.12](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.12) + +This release contains a change in behavior for `OnCodeChange` hook and an extension to the StateDB interface. + +### Modified types + +- `VMContext.StateDB` has been extended with the following method: + - `GetTransientState(addr common.Address, slot common.Hash) common.Hash` method used to access contract transient storage ([#30531](https://github.com/ethereum/go-ethereum/pull/30531)). + +### `OnCodeChange` change + +The `OnCodeChange` hook is now called when the code of a contract is removed due to a selfdestruct. Previously, no code change was emitted on such occasions. + +## [v1.14.10](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.10) + +### Modified types + +- `OpContext` has been extended with the following method: + - `ContractCode() []byte` provides access to the contract bytecode within the OpContext interface ([#30466](https://github.com/ethereum/go-ethereum/pull/30466)). + ## [v1.14.9](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.9) ### Modified types @@ -56,13 +102,6 @@ The state changes that are covered by the journaling library are: - `GasChangeReason` has been extended with the following reasons which will be enabled only post-Verkle. There shouldn't be any gas changes with those reasons prior to the fork. - `GasChangeWitnessContractCollisionCheck` flags the event of adding to the witness when checking for contract address collision. -## [v1.14.12] - -This release contains a change in behavior for `OnCodeChange` hook. - -### `OnCodeChange` change - -The `OnCodeChange` hook is now called when the code of a contract is removed due to a selfdestruct. Previously, no code change was emitted on such occasions. ## [v1.14.4] @@ -148,7 +187,12 @@ The hooks `CaptureStart` and `CaptureEnd` have been removed. These hooks signale - `CaptureState` -> `OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error)`. `op` is of type `byte` which can be cast to `vm.OpCode` when necessary. A `*vm.ScopeContext` is not passed anymore. It is replaced by `tracing.OpContext` which offers access to the memory, stack and current contract. - `CaptureFault` -> `OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error)`. Similar to above. -[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.14.8...master -[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0 -[v1.14.3]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.3 +[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.16.3...master +[v1.15.4]: https://github.com/ethereum/go-ethereum/releases/tag/v1.15.4 +[v1.15.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.15.0 +[v1.14.12]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.12 +[v1.14.10]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.10 +[v1.14.9]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.9 [v1.14.4]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.4 +[v1.14.3]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.3 +[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0 From 70f177a527360412c68a4ef9cfb451702749e952 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Thu, 4 Sep 2025 09:42:03 +0200 Subject: [PATCH 009/119] core/txpool/blobpool: fix getblobs error handling (#32538) Another getBlobs PR on top of https://github.com/ethereum/go-ethereum/pull/32190 to avoid some minor regressions. - bring back more log messages from before - continue processing also on some internal errors - ensure v2 complies with spec even if there are internal errors --------- Signed-off-by: Csaba Kiraly Co-authored-by: rjl493456442 --- core/txpool/blobpool/blobpool.go | 9 ++++++--- eth/catalyst/api.go | 17 ++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 68ea557633..edc8eb3e55 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1334,17 +1334,20 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blo } data, err := p.store.Get(txID) if err != nil { - return nil, nil, nil, err + log.Error("Tracked blob transaction missing from store", "id", txID, "err", err) + continue } // Decode the blob transaction tx := new(types.Transaction) if err := rlp.DecodeBytes(data, tx); err != nil { - return nil, nil, nil, err + log.Error("Blobs corrupted for traced transaction", "id", txID, "err", err) + continue } sidecar := tx.BlobTxSidecar() if sidecar == nil { - return nil, nil, nil, fmt.Errorf("blob tx without sidecar %x", tx.Hash()) + log.Error("Blob tx without sidecar", "hash", tx.Hash(), "id", txID) + continue } // Traverse the blobs in the transaction for i, hash := range tx.BlobHashes() { diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 07ce523462..b40698b999 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -541,20 +541,23 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo getBlobsV2RequestMiss.Inc(1) return nil, nil } - getBlobsV2RequestHit.Inc(1) blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion1) if err != nil { return nil, engine.InvalidParams.With(err) } - res := make([]*engine.BlobAndProofV2, len(hashes)) - for i := 0; i < len(blobs); i++ { - // the blob is missing, return null as response. It should - // be caught by `AvailableBlobs` though, perhaps data race - // occurs between two calls. - if blobs[i] == nil { + + // To comply with API spec, check again that we really got all data needed + for _, blob := range blobs { + if blob == nil { + getBlobsV2RequestMiss.Inc(1) return nil, nil } + } + getBlobsV2RequestHit.Inc(1) + + res := make([]*engine.BlobAndProofV2, len(hashes)) + for i := 0; i < len(blobs); i++ { var cellProofs []hexutil.Bytes for _, proof := range proofs[i] { cellProofs = append(cellProofs, proof[:]) From f5fcfb2fbe47f15ccf10fc363943d800443ee62a Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 4 Sep 2025 17:25:33 +0800 Subject: [PATCH 010/119] core/rawdb: remove outdated functions (#32542) --- core/rawdb/accessors_metadata.go | 13 ------------- core/rawdb/schema.go | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go index 859566f722..6996031be2 100644 --- a/core/rawdb/accessors_metadata.go +++ b/core/rawdb/accessors_metadata.go @@ -174,16 +174,3 @@ func UpdateUncleanShutdownMarker(db ethdb.KeyValueStore) { log.Warn("Failed to write unclean-shutdown marker", "err", err) } } - -// ReadTransitionStatus retrieves the eth2 transition status from the database -func ReadTransitionStatus(db ethdb.KeyValueReader) []byte { - data, _ := db.Get(transitionStatusKey) - return data -} - -// WriteTransitionStatus stores the eth2 transition status to the database -func WriteTransitionStatus(db ethdb.KeyValueWriter, data []byte) { - if err := db.Put(transitionStatusKey, data); err != nil { - log.Crit("Failed to store the eth2 transition status", "err", err) - } -} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 3588063468..9a17e1c173 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -95,7 +95,7 @@ var ( uncleanShutdownKey = []byte("unclean-shutdown") // config prefix for the db // transitionStatusKey tracks the eth2 transition status. - transitionStatusKey = []byte("eth2-transition") + transitionStatusKey = []byte("eth2-transition") // deprecated // snapSyncStatusFlagKey flags that status of snap sync. snapSyncStatusFlagKey = []byte("SnapSyncStatus") From 902ec5baae3581993b26239c5226d890aec6367c Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 5 Sep 2025 10:37:05 +0800 Subject: [PATCH 011/119] cmd, core, eth, triedb/pathdb: track node origins in the path database (#32418) This PR is the first step in the trienode history series. It introduces the `nodeWithOrigin` struct in the path database, which tracks the original values of dirty nodes to support trienode history construction. Note, the original value is always empty in this PR, so it won't break the existing journal for encoding and decoding. The compatibility of journal should be handled in the following PR. --- core/blockchain.go | 11 +- trie/trienode/node.go | 17 ++- triedb/pathdb/config.go | 118 +++++++++++++++++ triedb/pathdb/database.go | 94 +------------- triedb/pathdb/database_test.go | 181 +++++++++++++++++++++++---- triedb/pathdb/difflayer.go | 6 +- triedb/pathdb/difflayer_test.go | 6 +- triedb/pathdb/disklayer.go | 4 +- triedb/pathdb/execute.go | 26 ++-- triedb/pathdb/history_reader_test.go | 6 +- triedb/pathdb/journal.go | 2 +- triedb/pathdb/layertree.go | 5 +- triedb/pathdb/layertree_test.go | 131 ++++++++++--------- triedb/pathdb/nodes.go | 123 ++++++++++++++++++ triedb/pathdb/nodes_test.go | 128 +++++++++++++++++++ 15 files changed, 648 insertions(+), 210 deletions(-) create mode 100644 triedb/pathdb/config.go create mode 100644 triedb/pathdb/nodes_test.go diff --git a/core/blockchain.go b/core/blockchain.go index 5205483af9..dc6e7e9040 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -168,10 +168,13 @@ type BlockChainConfig struct { TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed TrieJournalDirectory string // Directory path to the journal used for persisting trie data across node restarts - Preimages bool // Whether to store preimage of trie key to the disk - StateHistory uint64 // Number of blocks from head whose state histories are reserved. - StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top - ArchiveMode bool // Whether to enable the archive mode + Preimages bool // Whether to store preimage of trie key to the disk + StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top + ArchiveMode bool // Whether to enable the archive mode + + // Number of blocks from the chain head for which state histories are retained. + // If set to 0, all state histories across the entire chain will be retained; + StateHistory uint64 // State snapshot related options SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory diff --git a/trie/trienode/node.go b/trie/trienode/node.go index c83dc27cef..228a64f04c 100644 --- a/trie/trienode/node.go +++ b/trie/trienode/node.go @@ -259,11 +259,24 @@ func (set *MergedNodeSet) Merge(other *NodeSet) error { return nil } -// Flatten returns a two-dimensional map for internal nodes. -func (set *MergedNodeSet) Flatten() map[common.Hash]map[string]*Node { +// Nodes returns a two-dimensional map for internal nodes. +func (set *MergedNodeSet) Nodes() map[common.Hash]map[string]*Node { nodes := make(map[common.Hash]map[string]*Node, len(set.Sets)) for owner, set := range set.Sets { nodes[owner] = set.Nodes } return nodes } + +// NodeAndOrigins returns a two-dimensional map for internal nodes along with +// their original values. +func (set *MergedNodeSet) NodeAndOrigins() (map[common.Hash]map[string]*Node, map[common.Hash]map[string][]byte) { + var ( + nodes = make(map[common.Hash]map[string]*Node, len(set.Sets)) + origins = make(map[common.Hash]map[string][]byte, len(set.Sets)) + ) + for owner, set := range set.Sets { + nodes[owner], origins[owner] = set.Nodes, set.Origins + } + return nodes, origins +} diff --git a/triedb/pathdb/config.go b/triedb/pathdb/config.go new file mode 100644 index 0000000000..3745a63edd --- /dev/null +++ b/triedb/pathdb/config.go @@ -0,0 +1,118 @@ +// Copyright 2025 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 . + +package pathdb + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" +) + +const ( + // defaultTrieCleanSize is the default memory allowance of clean trie cache. + defaultTrieCleanSize = 16 * 1024 * 1024 + + // defaultStateCleanSize is the default memory allowance of clean state cache. + defaultStateCleanSize = 16 * 1024 * 1024 + + // maxBufferSize is the maximum memory allowance of node buffer. + // Too large buffer will cause the system to pause for a long + // time when write happens. Also, the largest batch that pebble can + // support is 4GB, node will panic if batch size exceeds this limit. + maxBufferSize = 256 * 1024 * 1024 + + // defaultBufferSize is the default memory allowance of node buffer + // that aggregates the writes from above until it's flushed into the + // disk. It's meant to be used once the initial sync is finished. + // Do not increase the buffer size arbitrarily, otherwise the system + // pause time will increase when the database writes happen. + defaultBufferSize = 64 * 1024 * 1024 +) + +var ( + // maxDiffLayers is the maximum diff layers allowed in the layer tree. + maxDiffLayers = 128 +) + +// Defaults contains default settings for Ethereum mainnet. +var Defaults = &Config{ + StateHistory: params.FullImmutabilityThreshold, + EnableStateIndexing: false, + TrieCleanSize: defaultTrieCleanSize, + StateCleanSize: defaultStateCleanSize, + WriteBufferSize: defaultBufferSize, +} + +// ReadOnly is the config in order to open database in read only mode. +var ReadOnly = &Config{ + ReadOnly: true, + TrieCleanSize: defaultTrieCleanSize, + StateCleanSize: defaultStateCleanSize, +} + +// Config contains the settings for database. +type Config struct { + StateHistory uint64 // Number of recent blocks to maintain state history for, 0: full chain + EnableStateIndexing bool // Whether to enable state history indexing for external state access + TrieCleanSize int // Maximum memory allowance (in bytes) for caching clean trie data + StateCleanSize int // Maximum memory allowance (in bytes) for caching clean state data + WriteBufferSize int // Maximum memory allowance (in bytes) for write buffer + ReadOnly bool // Flag whether the database is opened in read only mode + JournalDirectory string // Absolute path of journal directory (null means the journal data is persisted in key-value store) + + // Testing configurations + SnapshotNoBuild bool // Flag Whether the state generation is disabled + NoAsyncFlush bool // Flag whether the background buffer flushing is disabled + NoAsyncGeneration bool // Flag whether the background generation is disabled +} + +// sanitize checks the provided user configurations and changes anything that's +// unreasonable or unworkable. +func (c *Config) sanitize() *Config { + conf := *c + if conf.WriteBufferSize > maxBufferSize { + log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.WriteBufferSize), "updated", common.StorageSize(maxBufferSize)) + conf.WriteBufferSize = maxBufferSize + } + return &conf +} + +// fields returns a list of attributes of config for printing. +func (c *Config) fields() []interface{} { + var list []interface{} + if c.ReadOnly { + list = append(list, "readonly", true) + } + list = append(list, "triecache", common.StorageSize(c.TrieCleanSize)) + list = append(list, "statecache", common.StorageSize(c.StateCleanSize)) + list = append(list, "buffer", common.StorageSize(c.WriteBufferSize)) + + if c.StateHistory == 0 { + list = append(list, "state-history", "entire chain") + } else { + list = append(list, "state-history", fmt.Sprintf("last %d blocks", c.StateHistory)) + } + if c.EnableStateIndexing { + list = append(list, "index-history", true) + } + if c.JournalDirectory != "" { + list = append(list, "journal-dir", c.JournalDirectory) + } + return list +} diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index f438c64aa2..423b921d47 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -31,37 +31,10 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-verkle" ) -const ( - // defaultTrieCleanSize is the default memory allowance of clean trie cache. - defaultTrieCleanSize = 16 * 1024 * 1024 - - // defaultStateCleanSize is the default memory allowance of clean state cache. - defaultStateCleanSize = 16 * 1024 * 1024 - - // maxBufferSize is the maximum memory allowance of node buffer. - // Too large buffer will cause the system to pause for a long - // time when write happens. Also, the largest batch that pebble can - // support is 4GB, node will panic if batch size exceeds this limit. - maxBufferSize = 256 * 1024 * 1024 - - // defaultBufferSize is the default memory allowance of node buffer - // that aggregates the writes from above until it's flushed into the - // disk. It's meant to be used once the initial sync is finished. - // Do not increase the buffer size arbitrarily, otherwise the system - // pause time will increase when the database writes happen. - defaultBufferSize = 64 * 1024 * 1024 -) - -var ( - // maxDiffLayers is the maximum diff layers allowed in the layer tree. - maxDiffLayers = 128 -) - // layer is the interface implemented by all state layers which includes some // public methods and some additional methods for internal usage. type layer interface { @@ -105,7 +78,7 @@ type layer interface { // the provided dirty trie nodes along with the state change set. // // Note, the maps are retained by the method to avoid copying everything. - update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer + update(root common.Hash, id uint64, block uint64, nodes *nodeSetWithOrigin, states *StateSetWithOrigin) *diffLayer // journal commits an entire diff hierarchy to disk into a single journal entry. // This is meant to be used during shutdown to persist the layer without @@ -113,68 +86,6 @@ type layer interface { journal(w io.Writer) error } -// Config contains the settings for database. -type Config struct { - StateHistory uint64 // Number of recent blocks to maintain state history for - EnableStateIndexing bool // Whether to enable state history indexing for external state access - TrieCleanSize int // Maximum memory allowance (in bytes) for caching clean trie nodes - StateCleanSize int // Maximum memory allowance (in bytes) for caching clean state data - WriteBufferSize int // Maximum memory allowance (in bytes) for write buffer - ReadOnly bool // Flag whether the database is opened in read only mode - JournalDirectory string // Absolute path of journal directory (null means the journal data is persisted in key-value store) - - // Testing configurations - SnapshotNoBuild bool // Flag Whether the state generation is allowed - NoAsyncFlush bool // Flag whether the background buffer flushing is allowed - NoAsyncGeneration bool // Flag whether the background generation is allowed -} - -// sanitize checks the provided user configurations and changes anything that's -// unreasonable or unworkable. -func (c *Config) sanitize() *Config { - conf := *c - if conf.WriteBufferSize > maxBufferSize { - log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.WriteBufferSize), "updated", common.StorageSize(maxBufferSize)) - conf.WriteBufferSize = maxBufferSize - } - return &conf -} - -// fields returns a list of attributes of config for printing. -func (c *Config) fields() []interface{} { - var list []interface{} - if c.ReadOnly { - list = append(list, "readonly", true) - } - if c.SnapshotNoBuild { - list = append(list, "snapshot", false) - } - list = append(list, "triecache", common.StorageSize(c.TrieCleanSize)) - list = append(list, "statecache", common.StorageSize(c.StateCleanSize)) - list = append(list, "buffer", common.StorageSize(c.WriteBufferSize)) - - if c.StateHistory == 0 { - list = append(list, "history", "entire chain") - } else { - list = append(list, "history", fmt.Sprintf("last %d blocks", c.StateHistory)) - } - if c.JournalDirectory != "" { - list = append(list, "journal-dir", c.JournalDirectory) - } - return list -} - -// Defaults contains default settings for Ethereum mainnet. -var Defaults = &Config{ - StateHistory: params.FullImmutabilityThreshold, - TrieCleanSize: defaultTrieCleanSize, - StateCleanSize: defaultStateCleanSize, - WriteBufferSize: defaultBufferSize, -} - -// ReadOnly is the config in order to open database in read only mode. -var ReadOnly = &Config{ReadOnly: true} - // nodeHasher is the function to compute the hash of supplied node blob. type nodeHasher func([]byte) (common.Hash, error) @@ -422,7 +333,8 @@ func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint6 if err := db.modifyAllowed(); err != nil { return err } - if err := db.tree.add(root, parentRoot, block, nodes, states); err != nil { + // TODO(rjl493456442) tracking the origins in the following PRs. + if err := db.tree.add(root, parentRoot, block, NewNodeSetWithOrigin(nodes.Nodes(), nil), states); err != nil { return err } // Keep 128 diff layers in the memory, persistent layer is 129th. diff --git a/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go index 47d13e54a4..99de4380bf 100644 --- a/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -36,9 +36,10 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/holiman/uint256" + "golang.org/x/exp/maps" ) -func updateTrie(db *Database, stateRoot common.Hash, addrHash common.Hash, root common.Hash, dirties map[common.Hash][]byte) (common.Hash, *trienode.NodeSet) { +func updateTrie(db *Database, stateRoot common.Hash, addrHash common.Hash, root common.Hash, entries map[common.Hash][]byte) (common.Hash, *trienode.NodeSet) { var id *trie.ID if addrHash == (common.Hash{}) { id = trie.StateTrieID(stateRoot) @@ -49,13 +50,17 @@ func updateTrie(db *Database, stateRoot common.Hash, addrHash common.Hash, root if err != nil { panic(fmt.Errorf("failed to load trie, err: %w", err)) } - for key, val := range dirties { + var deletes []common.Hash + for key, val := range entries { if len(val) == 0 { - tr.Delete(key.Bytes()) + deletes = append(deletes, key) } else { tr.Update(key.Bytes(), val) } } + for _, key := range deletes { + tr.Delete(key.Bytes()) + } return tr.Commit(false) } @@ -72,16 +77,18 @@ const ( createAccountOp int = iota modifyAccountOp deleteAccountOp + resurrectAccountOp opLen ) +// genctx carries the generation context used within a single state transition. type genctx struct { stateRoot common.Hash accounts map[common.Hash][]byte // Keyed by the hash of account address storages map[common.Hash]map[common.Hash][]byte // Keyed by the hash of account address and the hash of storage key accountOrigin map[common.Address][]byte // Keyed by the account address storageOrigin map[common.Address]map[common.Hash][]byte // Keyed by the account address and the hash of storage key - nodes *trienode.MergedNodeSet + nodes *trienode.MergedNodeSet // Trie nodes produced from the state transition } func newCtx(stateRoot common.Hash) *genctx { @@ -123,20 +130,31 @@ type tester struct { // state snapshots snapAccounts map[common.Hash]map[common.Hash][]byte // Keyed by the hash of account address snapStorages map[common.Hash]map[common.Hash]map[common.Hash][]byte // Keyed by the hash of account address and the hash of storage key + + // trienode snapshots + snapNodes map[common.Hash]*trienode.MergedNodeSet } -func newTester(t *testing.T, historyLimit uint64, isVerkle bool, layers int, enableIndex bool, journalDir string) *tester { +type testerConfig struct { + stateHistory uint64 + isVerkle bool + layers int + enableIndex bool + journalDir string +} + +func newTester(t *testing.T, config *testerConfig) *tester { var ( disk, _ = rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{Ancient: t.TempDir()}) db = New(disk, &Config{ - StateHistory: historyLimit, - EnableStateIndexing: enableIndex, + StateHistory: config.stateHistory, + EnableStateIndexing: config.enableIndex, TrieCleanSize: 256 * 1024, StateCleanSize: 256 * 1024, WriteBufferSize: 256 * 1024, NoAsyncFlush: true, - JournalDirectory: journalDir, - }, isVerkle) + JournalDirectory: config.journalDir, + }, config.isVerkle) obj = &tester{ db: db, @@ -145,9 +163,10 @@ func newTester(t *testing.T, historyLimit uint64, isVerkle bool, layers int, ena storages: make(map[common.Hash]map[common.Hash][]byte), snapAccounts: make(map[common.Hash]map[common.Hash][]byte), snapStorages: make(map[common.Hash]map[common.Hash]map[common.Hash][]byte), + snapNodes: make(map[common.Hash]*trienode.MergedNodeSet), } ) - for i := 0; i < layers; i++ { + for i := 0; i < config.layers; i++ { var parent = types.EmptyRootHash if len(obj.roots) != 0 { parent = obj.roots[len(obj.roots)-1] @@ -270,10 +289,53 @@ func (t *tester) clearStorage(ctx *genctx, addr common.Address, root common.Hash return root } +func (t *tester) resurrectStorage(ctx *genctx, addr common.Address, old map[common.Hash][]byte) common.Hash { + var ( + addrHash = crypto.Keccak256Hash(addr.Bytes()) + storage = make(map[common.Hash][]byte) + origin = make(map[common.Hash][]byte) + ) + for i := 0; i < 3; i++ { + v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32))) + key := testrand.Bytes(32) + hash := crypto.Keccak256Hash(key) + t.preimages[hash] = key + + storage[hash] = v + origin[hash] = nil + } + var cnt int + for khash := range old { + cnt += 1 + v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32))) + + storage[khash] = v + origin[khash] = old[khash] + if cnt >= 3 { + break + } + } + root, set := updateTrie(t.db, ctx.stateRoot, addrHash, types.EmptyRootHash, storage) + + maps.Copy(ctx.storages[addrHash], storage) + if ctx.storageOrigin[addr] == nil { + ctx.storageOrigin[addr] = make(map[common.Hash][]byte) + } + for k, v := range origin { + if _, exists := ctx.storageOrigin[addr][k]; !exists { + ctx.storageOrigin[addr][k] = v + } + } + ctx.nodes.Merge(set) + return root +} + func (t *tester) generate(parent common.Hash, rawStorageKey bool) (common.Hash, *trienode.MergedNodeSet, *StateSetWithOrigin) { var ( - ctx = newCtx(parent) - dirties = make(map[common.Hash]struct{}) + ctx = newCtx(parent) + dirties = make(map[common.Hash]struct{}) + deleted = make(map[common.Address]struct{}) + resurrect = make(map[common.Address]struct{}) ) for i := 0; i < 20; i++ { // Start with account creation always @@ -336,6 +398,7 @@ func (t *tester) generate(parent common.Hash, rawStorageKey bool) (common.Hash, continue } dirties[addrHash] = struct{}{} + deleted[addr] = struct{}{} acct, _ := types.FullAccount(account) if acct.Root != types.EmptyRootHash { @@ -343,6 +406,25 @@ func (t *tester) generate(parent common.Hash, rawStorageKey bool) (common.Hash, } ctx.accounts[addrHash] = nil ctx.accountOrigin[addr] = account + + case resurrectAccountOp: + if len(deleted) == 0 { + continue + } + addresses := maps.Keys(deleted) + addr := addresses[rand.Intn(len(addresses))] + if _, exist := resurrect[addr]; exist { + continue + } + resurrect[addr] = struct{}{} + + addrHash := crypto.Keccak256Hash(addr.Bytes()) + root := t.resurrectStorage(ctx, addr, t.storages[addrHash]) + ctx.accounts[addrHash] = types.SlimAccountRLP(generateAccount(root)) + if _, exist := ctx.accountOrigin[addr]; !exist { + ctx.accountOrigin[addr] = nil + } + t.preimages[addrHash] = addr.Bytes() } } root, set := updateTrie(t.db, parent, common.Hash{}, parent, ctx.accounts) @@ -351,6 +433,7 @@ func (t *tester) generate(parent common.Hash, rawStorageKey bool) (common.Hash, // Save state snapshot before commit t.snapAccounts[parent] = copyAccounts(t.accounts) t.snapStorages[parent] = copyStorages(t.storages) + t.snapNodes[parent] = ctx.nodes // Commit all changes to live state set for addrHash, account := range ctx.accounts { @@ -470,8 +553,7 @@ func TestDatabaseRollback(t *testing.T) { maxDiffLayers = 128 }() - // Verify state histories - tester := newTester(t, 0, false, 32, false, "") + tester := newTester(t, &testerConfig{layers: 32}) defer tester.release() if err := tester.verifyHistory(); err != nil { @@ -505,7 +587,7 @@ func TestDatabaseRecoverable(t *testing.T) { }() var ( - tester = newTester(t, 0, false, 12, false, "") + tester = newTester(t, &testerConfig{layers: 12}) index = tester.bottomIndex() ) defer tester.release() @@ -526,7 +608,7 @@ func TestDatabaseRecoverable(t *testing.T) { // Layers below current disk layer are recoverable {tester.roots[index-1], true}, - // Disklayer itself is not recoverable, since it's + // Disk layer itself is not recoverable, since it's // available for accessing. {tester.roots[index], false}, @@ -542,6 +624,59 @@ func TestDatabaseRecoverable(t *testing.T) { } } +func TestExecuteRollback(t *testing.T) { + // Redefine the diff layer depth allowance for faster testing. + maxDiffLayers = 4 + defer func() { + maxDiffLayers = 128 + }() + + tester := newTester(t, &testerConfig{layers: 32}) + defer tester.release() + + // Revert database from top to bottom + for i := tester.bottomIndex(); i >= 0; i-- { + dl := tester.db.tree.bottom() + h, err := readStateHistory(tester.db.stateFreezer, dl.stateID()) + if err != nil { + t.Fatalf("Failed to read history, err: %v", err) + } + nodes, err := apply(tester.db, h.meta.parent, h.meta.root, h.meta.version == stateHistoryV1, h.accounts, h.storages) + if err != nil { + t.Fatalf("Failed to apply history, err: %v", err) + } + + // Verify the produced node set, ensuring they are aligned with the + // tracked dirty nodes. + want := tester.snapNodes[h.meta.parent] + if len(nodes) != len(want.Sets) { + t.Fatalf("Unexpected node sets, want: %d, got: %d", len(want.Sets), len(nodes)) + } + for owner, setA := range nodes { + setB, ok := want.Sets[owner] + if !ok { + t.Fatalf("Excessive nodeset, %x", owner) + } + if len(setA) != len(setB.Origins) { + t.Fatalf("Unexpected origins, want: %d, got: %d", len(setA), len(setB.Origins)) + } + for k, nA := range setA { + nB, ok := setB.Origins[k] + if !ok { + t.Fatalf("Excessive node, %v", []byte(k)) + } + if !bytes.Equal(nA.Blob, nB) { + t.Fatalf("Unexpected node value, want: %v, got: %v", nA.Blob, nB) + } + } + } + + if err := tester.db.Recover(h.meta.parent); err != nil { + t.Fatalf("Failed to recover db, err: %v", err) + } + } +} + func TestDisable(t *testing.T) { // Redefine the diff layer depth allowance for faster testing. maxDiffLayers = 4 @@ -549,7 +684,7 @@ func TestDisable(t *testing.T) { maxDiffLayers = 128 }() - tester := newTester(t, 0, false, 32, false, "") + tester := newTester(t, &testerConfig{layers: 32}) defer tester.release() stored := crypto.Keccak256Hash(rawdb.ReadAccountTrieNode(tester.db.diskdb, nil)) @@ -563,10 +698,6 @@ func TestDisable(t *testing.T) { t.Fatalf("Failed to activate database: %v", err) } - // Ensure journal is deleted from disk - if blob := rawdb.ReadTrieJournal(tester.db.diskdb); len(blob) != 0 { - t.Fatal("Failed to clean journal") - } // Ensure all trie histories are removed n, err := tester.db.stateFreezer.Ancients() if err != nil { @@ -591,7 +722,7 @@ func TestCommit(t *testing.T) { maxDiffLayers = 128 }() - tester := newTester(t, 0, false, 12, false, "") + tester := newTester(t, &testerConfig{layers: 12}) defer tester.release() if err := tester.db.Commit(tester.lastHash(), false); err != nil { @@ -626,7 +757,7 @@ func testJournal(t *testing.T, journalDir string) { maxDiffLayers = 128 }() - tester := newTester(t, 0, false, 12, false, journalDir) + tester := newTester(t, &testerConfig{layers: 12, journalDir: journalDir}) defer tester.release() if err := tester.db.Journal(tester.lastHash()); err != nil { @@ -673,7 +804,7 @@ func testCorruptedJournal(t *testing.T, journalDir string, modifyFn func(databas maxDiffLayers = 128 }() - tester := newTester(t, 0, false, 12, false, journalDir) + tester := newTester(t, &testerConfig{layers: 12, journalDir: journalDir}) defer tester.release() if err := tester.db.Journal(tester.lastHash()); err != nil { @@ -718,7 +849,7 @@ func TestTailTruncateHistory(t *testing.T) { maxDiffLayers = 128 }() - tester := newTester(t, 10, false, 12, false, "") + tester := newTester(t, &testerConfig{layers: 12, stateHistory: 10}) defer tester.release() tester.db.Close() diff --git a/triedb/pathdb/difflayer.go b/triedb/pathdb/difflayer.go index ac05b4a0fb..ae523c979c 100644 --- a/triedb/pathdb/difflayer.go +++ b/triedb/pathdb/difflayer.go @@ -34,7 +34,7 @@ type diffLayer struct { root common.Hash // Root hash to which this layer diff belongs to id uint64 // Corresponding state id block uint64 // Associated block number - nodes *nodeSet // Cached trie nodes indexed by owner and path + nodes *nodeSetWithOrigin // Cached trie nodes indexed by owner and path states *StateSetWithOrigin // Associated state changes along with origin value parent layer // Parent layer modified by this one, never nil, **can be changed** @@ -42,7 +42,7 @@ type diffLayer struct { } // newDiffLayer creates a new diff layer on top of an existing layer. -func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { +func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes *nodeSetWithOrigin, states *StateSetWithOrigin) *diffLayer { dl := &diffLayer{ root: root, id: id, @@ -151,7 +151,7 @@ func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([ // update implements the layer interface, creating a new layer on top of the // existing layer tree with the specified data items. -func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { +func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSetWithOrigin, states *StateSetWithOrigin) *diffLayer { return newDiffLayer(dl, root, id, block, nodes, states) } diff --git a/triedb/pathdb/difflayer_test.go b/triedb/pathdb/difflayer_test.go index 83ed833486..0484fd9644 100644 --- a/triedb/pathdb/difflayer_test.go +++ b/triedb/pathdb/difflayer_test.go @@ -76,7 +76,7 @@ func benchmarkSearch(b *testing.B, depth int, total int) { nblob = common.CopyBytes(blob) } } - return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return newDiffLayer(parent, common.Hash{}, 0, 0, NewNodeSetWithOrigin(nodes, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) } var layer layer layer = emptyLayer() @@ -118,7 +118,7 @@ func BenchmarkPersist(b *testing.B) { ) nodes[common.Hash{}][string(path)] = node } - return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return newDiffLayer(parent, common.Hash{}, 0, 0, NewNodeSetWithOrigin(nodes, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) } for i := 0; i < b.N; i++ { b.StopTimer() @@ -156,7 +156,7 @@ func BenchmarkJournal(b *testing.B) { ) nodes[common.Hash{}][string(path)] = node } - return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return newDiffLayer(parent, common.Hash{}, 0, 0, NewNodeSetWithOrigin(nodes, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) } var layer layer layer = emptyLayer() diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index 13df6251e8..2042e91611 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -319,7 +319,7 @@ func (dl *diskLayer) storage(accountHash, storageHash common.Hash, depth int) ([ // update implements the layer interface, returning a new diff layer on top // with the given state set. -func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { +func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSetWithOrigin, states *StateSetWithOrigin) *diffLayer { return newDiffLayer(dl, root, id, block, nodes, states) } @@ -413,7 +413,7 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { // Merge the trie nodes and flat states of the bottom-most diff layer into the // buffer as the combined layer. - combined := dl.buffer.commit(bottom.nodes, bottom.states.stateSet) + combined := dl.buffer.commit(bottom.nodes.nodeSet, bottom.states.stateSet) // Terminate the background state snapshot generation before mutating the // persistent state. diff --git a/triedb/pathdb/execute.go b/triedb/pathdb/execute.go index aa4bd8b44b..4c1cafec12 100644 --- a/triedb/pathdb/execute.go +++ b/triedb/pathdb/execute.go @@ -59,13 +59,19 @@ func apply(db database.NodeDatabase, prevRoot common.Hash, postRoot common.Hash, rawStorageKey: rawStorageKey, nodes: trienode.NewMergedNodeSet(), } + var deletes []common.Address for addr, account := range accounts { - var err error if len(account) == 0 { - err = deleteAccount(ctx, db, addr) + deletes = append(deletes, addr) } else { - err = updateAccount(ctx, db, addr) + err := updateAccount(ctx, db, addr) + if err != nil { + return nil, fmt.Errorf("failed to revert state, err: %w", err) + } } + } + for _, addr := range deletes { + err := deleteAccount(ctx, db, addr) if err != nil { return nil, fmt.Errorf("failed to revert state, err: %w", err) } @@ -77,7 +83,7 @@ func apply(db database.NodeDatabase, prevRoot common.Hash, postRoot common.Hash, if err := ctx.nodes.Merge(result); err != nil { return nil, err } - return ctx.nodes.Flatten(), nil + return ctx.nodes.Nodes(), nil } // updateAccount the account was present in prev-state, and may or may not @@ -108,17 +114,23 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) if err != nil { return err } + var deletes []common.Hash for key, val := range ctx.storages[addr] { tkey := key if ctx.rawStorageKey { tkey = crypto.Keccak256Hash(key.Bytes()) } - var err error if len(val) == 0 { - err = st.Delete(tkey.Bytes()) + deletes = append(deletes, tkey) } else { - err = st.Update(tkey.Bytes(), val) + err := st.Update(tkey.Bytes(), val) + if err != nil { + return err + } } + } + for _, tkey := range deletes { + err := st.Delete(tkey.Bytes()) if err != nil { return err } diff --git a/triedb/pathdb/history_reader_test.go b/triedb/pathdb/history_reader_test.go index 9028a886ce..2ae1cfdd29 100644 --- a/triedb/pathdb/history_reader_test.go +++ b/triedb/pathdb/history_reader_test.go @@ -144,8 +144,7 @@ func testHistoryReader(t *testing.T, historyLimit uint64) { maxDiffLayers = 128 }() - // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) - env := newTester(t, historyLimit, false, 64, true, "") + env := newTester(t, &testerConfig{stateHistory: historyLimit, layers: 64, enableIndex: true}) defer env.release() waitIndexing(env.db) @@ -184,7 +183,8 @@ func TestHistoricalStateReader(t *testing.T) { }() //log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) - env := newTester(t, 0, false, 64, true, "") + config := &testerConfig{stateHistory: 0, layers: 64, enableIndex: true} + env := newTester(t, config) defer env.release() waitIndexing(env.db) diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 4639932763..bd9081a28f 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -229,7 +229,7 @@ func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) { return nil, fmt.Errorf("load block number: %v", err) } // Read in-memory trie nodes from journal - var nodes nodeSet + var nodes nodeSetWithOrigin if err := nodes.decode(r); err != nil { return nil, err } diff --git a/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go index b2f3f7f37d..ec45257db5 100644 --- a/triedb/pathdb/layertree.go +++ b/triedb/pathdb/layertree.go @@ -22,7 +22,6 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/trie/trienode" ) // layerTree is a group of state layers identified by the state root. @@ -142,7 +141,7 @@ func (tree *layerTree) len() int { } // add inserts a new layer into the tree if it can be linked to an existing old parent. -func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *StateSetWithOrigin) error { +func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint64, nodes *nodeSetWithOrigin, states *StateSetWithOrigin) error { // Reject noop updates to avoid self-loops. This is a special case that can // happen for clique networks and proof-of-stake networks where empty blocks // don't modify the state (0 block subsidy). @@ -156,7 +155,7 @@ func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint6 if parent == nil { return fmt.Errorf("triedb parent [%#x] layer missing", parentRoot) } - l := parent.update(root, parent.stateID()+1, block, newNodeSet(nodes.Flatten()), states) + l := parent.update(root, parent.stateID()+1, block, nodes, states) tree.lock.Lock() defer tree.lock.Unlock() diff --git a/triedb/pathdb/layertree_test.go b/triedb/pathdb/layertree_test.go index a76d60ba5b..a74c6eb045 100644 --- a/triedb/pathdb/layertree_test.go +++ b/triedb/pathdb/layertree_test.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/trie/trienode" ) func newTestLayerTree() *layerTree { @@ -45,9 +44,9 @@ func TestLayerCap(t *testing.T) { // C1->C2->C3->C4 (HEAD) init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, // Chain: @@ -66,9 +65,9 @@ func TestLayerCap(t *testing.T) { // C1->C2->C3->C4 (HEAD) init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, // Chain: @@ -86,9 +85,9 @@ func TestLayerCap(t *testing.T) { // C1->C2->C3->C4 (HEAD) init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, // Chain: @@ -106,12 +105,12 @@ func TestLayerCap(t *testing.T) { // ->C2'->C3'->C4' init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, // Chain: @@ -131,12 +130,12 @@ func TestLayerCap(t *testing.T) { // ->C2'->C3'->C4' init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, // Chain: @@ -155,11 +154,11 @@ func TestLayerCap(t *testing.T) { // ->C3'->C4' init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3a}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3b}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, // Chain: @@ -213,8 +212,8 @@ func TestBaseLayer(t *testing.T) { // C1->C2->C3 (HEAD) { func() { - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) }, common.Hash{0x1}, }, @@ -230,9 +229,9 @@ func TestBaseLayer(t *testing.T) { // C4->C5->C6 (HEAD) { func() { - tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x5}, common.Hash{0x4}, 4, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x6}, common.Hash{0x5}, 5, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x5}, common.Hash{0x4}, 4, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x6}, common.Hash{0x5}, 5, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) tr.cap(common.Hash{0x6}, 2) }, common.Hash{0x4}, @@ -258,7 +257,7 @@ func TestDescendant(t *testing.T) { // C1->C2 (HEAD) init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, snapshotA: map[common.Hash]map[common.Hash]struct{}{ @@ -269,7 +268,7 @@ func TestDescendant(t *testing.T) { // Chain: // C1->C2->C3 (HEAD) op: func(tr *layerTree) { - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) }, snapshotB: map[common.Hash]map[common.Hash]struct{}{ common.Hash{0x1}: { @@ -286,9 +285,9 @@ func TestDescendant(t *testing.T) { // C1->C2->C3->C4 (HEAD) init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, snapshotA: map[common.Hash]map[common.Hash]struct{}{ @@ -325,9 +324,9 @@ func TestDescendant(t *testing.T) { // C1->C2->C3->C4 (HEAD) init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, snapshotA: map[common.Hash]map[common.Hash]struct{}{ @@ -360,9 +359,9 @@ func TestDescendant(t *testing.T) { // C1->C2->C3->C4 (HEAD) init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, snapshotA: map[common.Hash]map[common.Hash]struct{}{ @@ -392,12 +391,12 @@ func TestDescendant(t *testing.T) { // ->C2'->C3'->C4' init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, snapshotA: map[common.Hash]map[common.Hash]struct{}{ @@ -445,12 +444,12 @@ func TestDescendant(t *testing.T) { // ->C2'->C3'->C4' init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, snapshotA: map[common.Hash]map[common.Hash]struct{}{ @@ -494,11 +493,11 @@ func TestDescendant(t *testing.T) { // ->C3'->C4' init: func() *layerTree { tr := newTestLayerTree() - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3a}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x3b}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) - tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(nil, nil, nil, nil, false)) return tr }, snapshotA: map[common.Hash]map[common.Hash]struct{}{ @@ -580,11 +579,11 @@ func TestAccountLookup(t *testing.T) { // Chain: // C1->C2->C3->C4 (HEAD) tr := newTestLayerTree() // base = 0x1 - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(randomAccountSet("0xa"), nil, nil, nil, false)) - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(randomAccountSet("0xb"), nil, nil, nil, false)) - tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(randomAccountSet("0xa", "0xc"), nil, nil, nil, false)) var cases = []struct { @@ -734,11 +733,11 @@ func TestStorageLookup(t *testing.T) { // Chain: // C1->C2->C3->C4 (HEAD) tr := newTestLayerTree() // base = 0x1 - tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(randomAccountSet("0xa"), randomStorageSet([]string{"0xa"}, [][]string{{"0x1"}}, nil), nil, nil, false)) - tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(randomAccountSet("0xa"), randomStorageSet([]string{"0xa"}, [][]string{{"0x2"}}, nil), nil, nil, false)) - tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, NewNodeSetWithOrigin(nil, nil), NewStateSetWithOrigin(randomAccountSet("0xa"), randomStorageSet([]string{"0xa"}, [][]string{{"0x1", "0x3"}}, nil), nil, nil, false)) var cases = []struct { diff --git a/triedb/pathdb/nodes.go b/triedb/pathdb/nodes.go index f90bd0f01c..c6f9e7aece 100644 --- a/triedb/pathdb/nodes.go +++ b/triedb/pathdb/nodes.go @@ -18,6 +18,7 @@ package pathdb import ( "bytes" + "errors" "fmt" "io" "maps" @@ -301,3 +302,125 @@ func (s *nodeSet) dbsize() int { } return m + int(s.size) } + +// nodeSetWithOrigin wraps the node set with additional original values of the +// mutated trie nodes. +type nodeSetWithOrigin struct { + *nodeSet + + // nodeOrigin represents the trie nodes before the state transition. It's keyed + // by the account address hash and node path. The nil value means the trie node + // was not present. + nodeOrigin map[common.Hash]map[string][]byte + + // memory size of the state data (accountNodeOrigin and storageNodeOrigin) + size uint64 +} + +// NewNodeSetWithOrigin constructs the state set with the provided data. +func NewNodeSetWithOrigin(nodes map[common.Hash]map[string]*trienode.Node, origins map[common.Hash]map[string][]byte) *nodeSetWithOrigin { + // Don't panic for the lazy callers, initialize the nil maps instead. + if origins == nil { + origins = make(map[common.Hash]map[string][]byte) + } + set := &nodeSetWithOrigin{ + nodeSet: newNodeSet(nodes), + nodeOrigin: origins, + } + set.computeSize() + return set +} + +// computeSize calculates the database size of the held trie nodes. +func (s *nodeSetWithOrigin) computeSize() { + var size int + for owner, slots := range s.nodeOrigin { + prefixLen := common.HashLength + if owner == (common.Hash{}) { + prefixLen = 0 + } + for path, data := range slots { + size += prefixLen + len(path) + len(data) + } + } + s.size = s.nodeSet.size + uint64(size) +} + +// encode serializes the content of node set into the provided writer. +func (s *nodeSetWithOrigin) encode(w io.Writer) error { + // Encode node set + if err := s.nodeSet.encode(w); err != nil { + return err + } + // Short circuit if the origins are not tracked + if len(s.nodeOrigin) == 0 { + return nil + } + + // Encode node origins + nodes := make([]journalNodes, 0, len(s.nodeOrigin)) + for owner, subset := range s.nodeOrigin { + entry := journalNodes{ + Owner: owner, + Nodes: make([]journalNode, 0, len(subset)), + } + for path, node := range subset { + entry.Nodes = append(entry.Nodes, journalNode{ + Path: []byte(path), + Blob: node, + }) + } + nodes = append(nodes, entry) + } + return rlp.Encode(w, nodes) +} + +// hasOrigin returns whether the origin data set exists in the rlp stream. +// It's a workaround for backward compatibility. +func (s *nodeSetWithOrigin) hasOrigin(r *rlp.Stream) (bool, error) { + kind, _, err := r.Kind() + if err != nil { + if errors.Is(err, io.EOF) { + return false, nil + } + return false, err + } + // If the type of next element in the RLP stream is: + // - `rlp.List`: represents the original value of trienodes; + // - others, like `boolean`: represent a field in the following state data set; + return kind == rlp.List, nil +} + +// decode deserializes the content from the rlp stream into the node set. +func (s *nodeSetWithOrigin) decode(r *rlp.Stream) error { + if s.nodeSet == nil { + s.nodeSet = &nodeSet{} + } + if err := s.nodeSet.decode(r); err != nil { + return err + } + + // Decode node origins + s.nodeOrigin = make(map[common.Hash]map[string][]byte) + if hasOrigin, err := s.hasOrigin(r); err != nil { + return err + } else if hasOrigin { + var encoded []journalNodes + if err := r.Decode(&encoded); err != nil { + return fmt.Errorf("load nodes: %v", err) + } + for _, entry := range encoded { + subset := make(map[string][]byte, len(entry.Nodes)) + for _, n := range entry.Nodes { + if len(n.Blob) > 0 { + subset[string(n.Path)] = n.Blob + } else { + subset[string(n.Path)] = nil + } + } + s.nodeOrigin[entry.Owner] = subset + } + } + s.computeSize() + return nil +} diff --git a/triedb/pathdb/nodes_test.go b/triedb/pathdb/nodes_test.go new file mode 100644 index 0000000000..483dc4b1a6 --- /dev/null +++ b/triedb/pathdb/nodes_test.go @@ -0,0 +1,128 @@ +// Copyright 2025 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 . + +package pathdb + +import ( + "bytes" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie/trienode" +) + +func TestNodeSetEncode(t *testing.T) { + nodes := make(map[common.Hash]map[string]*trienode.Node) + nodes[common.Hash{}] = map[string]*trienode.Node{ + "": trienode.New(crypto.Keccak256Hash([]byte{0x0}), []byte{0x0}), + "1": trienode.New(crypto.Keccak256Hash([]byte{0x1}), []byte{0x1}), + "2": trienode.New(crypto.Keccak256Hash([]byte{0x2}), []byte{0x2}), + } + nodes[common.Hash{0x1}] = map[string]*trienode.Node{ + "": trienode.New(crypto.Keccak256Hash([]byte{0x0}), []byte{0x0}), + "1": trienode.New(crypto.Keccak256Hash([]byte{0x1}), []byte{0x1}), + "2": trienode.New(crypto.Keccak256Hash([]byte{0x2}), []byte{0x2}), + } + s := newNodeSet(nodes) + + buf := bytes.NewBuffer(nil) + if err := s.encode(buf); err != nil { + t.Fatalf("Failed to encode states, %v", err) + } + var dec nodeSet + if err := dec.decode(rlp.NewStream(buf, 0)); err != nil { + t.Fatalf("Failed to decode states, %v", err) + } + if !reflect.DeepEqual(s.accountNodes, dec.accountNodes) { + t.Fatal("Unexpected account data") + } + if !reflect.DeepEqual(s.storageNodes, dec.storageNodes) { + t.Fatal("Unexpected storage data") + } +} + +func TestNodeSetWithOriginEncode(t *testing.T) { + nodes := make(map[common.Hash]map[string]*trienode.Node) + nodes[common.Hash{}] = map[string]*trienode.Node{ + "": trienode.New(crypto.Keccak256Hash([]byte{0x0}), []byte{0x0}), + "1": trienode.New(crypto.Keccak256Hash([]byte{0x1}), []byte{0x1}), + "2": trienode.New(crypto.Keccak256Hash([]byte{0x2}), []byte{0x2}), + } + nodes[common.Hash{0x1}] = map[string]*trienode.Node{ + "": trienode.New(crypto.Keccak256Hash([]byte{0x0}), []byte{0x0}), + "1": trienode.New(crypto.Keccak256Hash([]byte{0x1}), []byte{0x1}), + "2": trienode.New(crypto.Keccak256Hash([]byte{0x2}), []byte{0x2}), + } + origins := make(map[common.Hash]map[string][]byte) + origins[common.Hash{}] = map[string][]byte{ + "": nil, + "1": {0x1}, + "2": {0x2}, + } + origins[common.Hash{0x1}] = map[string][]byte{ + "": nil, + "1": {0x1}, + "2": {0x2}, + } + + // Encode with origin set + s := NewNodeSetWithOrigin(nodes, origins) + + buf := bytes.NewBuffer(nil) + if err := s.encode(buf); err != nil { + t.Fatalf("Failed to encode states, %v", err) + } + var dec nodeSetWithOrigin + if err := dec.decode(rlp.NewStream(buf, 0)); err != nil { + t.Fatalf("Failed to decode states, %v", err) + } + if !reflect.DeepEqual(s.accountNodes, dec.accountNodes) { + t.Fatal("Unexpected account data") + } + if !reflect.DeepEqual(s.storageNodes, dec.storageNodes) { + t.Fatal("Unexpected storage data") + } + if !reflect.DeepEqual(s.nodeOrigin, dec.nodeOrigin) { + t.Fatal("Unexpected node origin data") + } + + // Encode without origin set + s = NewNodeSetWithOrigin(nodes, nil) + + buf = bytes.NewBuffer(nil) + if err := s.encode(buf); err != nil { + t.Fatalf("Failed to encode states, %v", err) + } + var dec2 nodeSetWithOrigin + if err := dec2.decode(rlp.NewStream(buf, 0)); err != nil { + t.Fatalf("Failed to decode states, %v", err) + } + if !reflect.DeepEqual(s.accountNodes, dec2.accountNodes) { + t.Fatal("Unexpected account data") + } + if !reflect.DeepEqual(s.storageNodes, dec2.storageNodes) { + t.Fatal("Unexpected storage data") + } + if len(dec2.nodeOrigin) != 0 { + t.Fatal("unexpected node origin data") + } + if dec2.size != s.size { + t.Fatalf("Unexpected data size, got: %d, want: %d", dec2.size, s.size) + } +} From 8ce204734879580a0a38e13708c8f473967eac83 Mon Sep 17 00:00:00 2001 From: Delweng Date: Fri, 5 Sep 2025 10:59:11 +0800 Subject: [PATCH 012/119] cmd/geth: set trie journal for pathdb (#32531) As in #32060 we introduced the file based journal path, for the other sub command(eg: snapshot, db), we should also pass the directory to the triedb, else the subcommand(eg: `geth snapshot`) failed to run: ```bash geth snapshot verify-state --datadir /geth-data ... INFO [09-02|02:12:29.493] Allocated cache and file handles database=/geth-data/geth/chaindata cache=512.00MiB handles=524,288 INFO [09-02|02:12:32.746] Opened ancient database database=/geth-data/geth/chaindata/ancient/chain readonly=true INFO [09-02|02:12:32.746] Opened Era store datadir=/geth-data/geth/chaindata/ancient/chain/era INFO [09-02|02:12:32.758] State scheme set to already existing scheme=path INFO [09-02|02:12:32.760] Load database journal from disk INFO [09-02|02:12:32.764] Failed to load journal, discard it err="journal not found" INFO [09-02|02:12:32.789] Opened ancient database database=/geth-data/geth/chaindata/ancient/state readonly=true INFO [09-02|02:12:32.790] Initialized path database readonly=true triecache=0.00B statecache=0.00B buffer=0.00B history="entire chain" ERROR[09-02|02:12:32.791] Failed to verify state root=c5458d..4cc785 err="unknown layer: c5458d476da0136a67ef24a93b909aa5c29efa5c5b885dbd1fbaed4e784cc785" ``` --- cmd/geth/chaincmd.go | 4 ++-- cmd/geth/dbcmd.go | 4 ++-- cmd/geth/snapshot.go | 10 +++++----- cmd/utils/flags.go | 9 ++++++--- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index d0f4d6f81d..9868142f53 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -281,7 +281,7 @@ func initGenesis(ctx *cli.Context) error { chaindb := utils.MakeChainDatabase(ctx, stack, false) defer chaindb.Close() - triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle()) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle()) defer triedb.Close() _, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides) @@ -635,7 +635,7 @@ func dump(ctx *cli.Context) error { if err != nil { return err } - triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup + triedb := utils.MakeTrieDatabase(ctx, stack, db, true, true, false) // always enable preimage lookup defer triedb.Close() state, err := state.New(root, state.NewDatabase(triedb, nil)) diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 44a52521f0..c57add0656 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -524,7 +524,7 @@ func dbDumpTrie(ctx *cli.Context) error { db := utils.MakeChainDatabase(ctx, stack, true) defer db.Close() - triedb := utils.MakeTrieDatabase(ctx, db, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false) defer triedb.Close() var ( @@ -859,7 +859,7 @@ func inspectHistory(ctx *cli.Context) error { db := utils.MakeChainDatabase(ctx, stack, true) defer db.Close() - triedb := utils.MakeTrieDatabase(ctx, db, false, false, false) + triedb := utils.MakeTrieDatabase(ctx, stack, db, false, false, false) defer triedb.Close() var ( diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index aa9ae7087f..994cb149ce 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -217,7 +217,7 @@ func verifyState(ctx *cli.Context) error { log.Error("Failed to load head block") return errors.New("no head block") } - triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false) defer triedb.Close() var ( @@ -282,7 +282,7 @@ func traverseState(ctx *cli.Context) error { chaindb := utils.MakeChainDatabase(ctx, stack, true) defer chaindb.Close() - triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false) defer triedb.Close() headBlock := rawdb.ReadHeadBlock(chaindb) @@ -391,7 +391,7 @@ func traverseRawState(ctx *cli.Context) error { chaindb := utils.MakeChainDatabase(ctx, stack, true) defer chaindb.Close() - triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false) defer triedb.Close() headBlock := rawdb.ReadHeadBlock(chaindb) @@ -558,7 +558,7 @@ func dumpState(ctx *cli.Context) error { if err != nil { return err } - triedb := utils.MakeTrieDatabase(ctx, db, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false) defer triedb.Close() snapConfig := snapshot.Config{ @@ -640,7 +640,7 @@ func snapshotExportPreimages(ctx *cli.Context) error { chaindb := utils.MakeChainDatabase(ctx, stack, true) defer chaindb.Close() - triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false) defer triedb.Close() var root common.Hash diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index cbc1d925e4..bfc1ff0983 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2269,7 +2269,7 @@ func MakeConsolePreloads(ctx *cli.Context) []string { } // MakeTrieDatabase constructs a trie database based on the configured scheme. -func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database { +func MakeTrieDatabase(ctx *cli.Context, stack *node.Node, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database { config := &triedb.Config{ Preimages: preimage, IsVerkle: isVerkle, @@ -2285,10 +2285,13 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read config.HashDB = hashdb.Defaults return triedb.NewDatabase(disk, config) } + var pathConfig pathdb.Config if readOnly { - config.PathDB = pathdb.ReadOnly + pathConfig = *pathdb.ReadOnly } else { - config.PathDB = pathdb.Defaults + pathConfig = *pathdb.Defaults } + pathConfig.JournalDirectory = stack.ResolvePath("triedb") + config.PathDB = &pathConfig return triedb.NewDatabase(disk, config) } From c4ec4504bbb300278874715ca9527aa074f5bc4a Mon Sep 17 00:00:00 2001 From: Delweng Date: Mon, 8 Sep 2025 14:00:23 +0800 Subject: [PATCH 013/119] core/state: state size tracking (#32362) Add state size tracking and retrieve api, start geth with `--state.size-tracking`, the initial bootstrap is required (around 1h on mainnet), after the bootstrap, use `debug_stateSize()` RPC to retrieve the state size: ``` > debug.stateSize() { accountBytes: "0x39681967b", accountTrienodeBytes: "0xc57939f0c", accountTrienodes: "0x198b36ac", accounts: "0x129da14a", blockNumber: "0x1635e90", contractCodeBytes: "0x2b63ef481", contractCodes: "0x1c7b45", stateRoot: "0x9c36a3ec3745d72eea8700bd27b90dcaa66de0494b187c5600750044151e620a", storageBytes: "0x18a6e7d3f1", storageTrienodeBytes: "0x2e7f53fae6", storageTrienodes: "0x6e49a234", storages: "0x517859c5" } ``` --------- Signed-off-by: jsvisa Co-authored-by: Gary Rong --- cmd/geth/chaincmd.go | 1 + cmd/geth/config.go | 2 +- cmd/geth/main.go | 1 + cmd/utils/flags.go | 12 + core/blockchain.go | 30 +- core/state/state_sizer.go | 638 +++++++++++++++++++++++++++++++++ core/state/state_sizer_test.go | 231 ++++++++++++ core/state/statedb.go | 16 +- core/state/stateupdate.go | 9 +- eth/api_debug.go | 48 +++ eth/backend.go | 1 + eth/ethconfig/config.go | 3 + eth/ethconfig/gen_config.go | 6 + internal/web3ext/web3ext.go | 6 + triedb/database.go | 9 + triedb/pathdb/database.go | 11 + 16 files changed, 1016 insertions(+), 8 deletions(-) create mode 100644 core/state/state_sizer.go create mode 100644 core/state/state_sizer_test.go diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 9868142f53..71ff821bb9 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -108,6 +108,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`, utils.MetricsInfluxDBTokenFlag, utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBOrganizationFlag, + utils.StateSizeTrackingFlag, utils.TxLookupLimitFlag, utils.VMTraceFlag, utils.VMTraceJsonConfigFlag, diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 96bd715e88..33a3eadea8 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -209,7 +209,7 @@ func constructDevModeBanner(ctx *cli.Context, cfg gethConfig) string { 0x%x (10^49 ETH) `, cfg.Eth.Miner.PendingFeeRecipient) if cfg.Eth.Miner.PendingFeeRecipient == utils.DeveloperAddr { - devModeBanner += fmt.Sprintf(` + devModeBanner += fmt.Sprintf(` Private Key ------------------ 0x%x diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 2da5c43216..750bf55927 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -200,6 +200,7 @@ var ( utils.MetricsInfluxDBTokenFlag, utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBOrganizationFlag, + utils.StateSizeTrackingFlag, } ) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index bfc1ff0983..a134ea4308 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -270,6 +270,12 @@ var ( Usage: "Scheme to use for storing ethereum state ('hash' or 'path')", Category: flags.StateCategory, } + StateSizeTrackingFlag = &cli.BoolFlag{ + Name: "state.size-tracking", + Usage: "Enable state size tracking, retrieve state size with debug_stateSize.", + Value: ethconfig.Defaults.EnableStateSizeTracking, + Category: flags.StateCategory, + } StateHistoryFlag = &cli.Uint64Flag{ Name: "history.state", Usage: "Number of recent blocks to retain state history for, only relevant in state.scheme=path (default = 90,000 blocks, 0 = entire chain)", @@ -1726,6 +1732,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.EthDiscoveryURLs = SplitAndTrim(urls) } } + if ctx.Bool(StateSizeTrackingFlag.Name) { + cfg.EnableStateSizeTracking = true + } // Override any default configs for hard coded networks. switch { case ctx.Bool(MainnetFlag.Name): @@ -2208,6 +2217,9 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh // - DATADIR/triedb/merkle.journal // - DATADIR/triedb/verkle.journal TrieJournalDirectory: stack.ResolvePath("triedb"), + + // Enable state size tracking if enabled + StateSizeTracking: ctx.Bool(StateSizeTrackingFlag.Name), } if options.ArchiveMode && !options.Preimages { options.Preimages = true diff --git a/core/blockchain.go b/core/blockchain.go index dc6e7e9040..c97897cd70 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -196,6 +196,9 @@ type BlockChainConfig struct { // If the value is zero, all transactions of the entire chain will be indexed. // If the value is -1, indexing is disabled. TxLookupLimit int64 + + // StateSizeTracking indicates whether the state size tracking is enabled. + StateSizeTracking bool } // DefaultConfig returns the default config. @@ -333,6 +336,7 @@ type BlockChain struct { prefetcher Prefetcher processor Processor // Block transaction processor interface logger *tracing.Hooks + stateSizer *state.SizeTracker // State size tracking lastForkReadyAlert time.Time // Last time there was a fork readiness print out } @@ -526,6 +530,17 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine, if bc.cfg.TxLookupLimit >= 0 { bc.txIndexer = newTxIndexer(uint64(bc.cfg.TxLookupLimit), bc) } + + // Start state size tracker + if bc.cfg.StateSizeTracking { + stateSizer, err := state.NewSizeTracker(bc.db, bc.triedb) + if err == nil { + bc.stateSizer = stateSizer + log.Info("Enabled state size metrics") + } else { + log.Info("Failed to setup size tracker", "err", err) + } + } return bc, nil } @@ -1252,6 +1267,10 @@ func (bc *BlockChain) stopWithoutSaving() { // Signal shutdown to all goroutines. bc.InterruptInsert(true) + // Stop state size tracker + if bc.stateSizer != nil { + bc.stateSizer.Stop() + } // Now wait for all chain modifications to end and persistent goroutines to exit. // // Note: Close waits for the mutex to become available, i.e. any running chain @@ -1586,10 +1605,14 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. log.Crit("Failed to write block into disk", "err", err) } // Commit all cached state changes into underlying memory database. - root, err := statedb.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()), bc.chainConfig.IsCancun(block.Number(), block.Time())) + root, stateUpdate, err := statedb.CommitWithUpdate(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()), bc.chainConfig.IsCancun(block.Number(), block.Time())) if err != nil { return err } + // Emit the state update to the state sizestats if it's active + if bc.stateSizer != nil { + bc.stateSizer.Notify(stateUpdate) + } // If node is running in path mode, skip explicit gc operation // which is unnecessary in this mode. if bc.triedb.Scheme() == rawdb.PathScheme { @@ -2791,3 +2814,8 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) { func (bc *BlockChain) GetTrieFlushInterval() time.Duration { return time.Duration(bc.flushInterval.Load()) } + +// StateSizer returns the state size tracker, or nil if it's not initialized +func (bc *BlockChain) StateSizer() *state.SizeTracker { + return bc.stateSizer +} diff --git a/core/state/state_sizer.go b/core/state/state_sizer.go new file mode 100644 index 0000000000..2066c94845 --- /dev/null +++ b/core/state/state_sizer.go @@ -0,0 +1,638 @@ +// Copyright 2025 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 . + +package state + +import ( + "container/heap" + "errors" + "fmt" + "maps" + "runtime" + "slices" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/triedb" + "golang.org/x/sync/errgroup" +) + +const ( + statEvictThreshold = 128 // the depth of statistic to be preserved +) + +// Database key scheme for states. +var ( + accountKeySize = int64(len(rawdb.SnapshotAccountPrefix) + common.HashLength) + storageKeySize = int64(len(rawdb.SnapshotStoragePrefix) + common.HashLength*2) + accountTrienodePrefixSize = int64(len(rawdb.TrieNodeAccountPrefix)) + storageTrienodePrefixSize = int64(len(rawdb.TrieNodeStoragePrefix) + common.HashLength) + codeKeySize = int64(len(rawdb.CodePrefix) + common.HashLength) +) + +// SizeStats represents either the current state size statistics or the size +// differences resulting from a state transition. +type SizeStats struct { + StateRoot common.Hash // State root hash at the time of measurement + BlockNumber uint64 // Associated block number at the time of measurement + + Accounts int64 // Total number of accounts in the state + AccountBytes int64 // Total storage size used by all account data (in bytes) + Storages int64 // Total number of storage slots across all accounts + StorageBytes int64 // Total storage size used by all storage slot data (in bytes) + AccountTrienodes int64 // Total number of account trie nodes in the state + AccountTrienodeBytes int64 // Total storage size occupied by account trie nodes (in bytes) + StorageTrienodes int64 // Total number of storage trie nodes in the state + StorageTrienodeBytes int64 // Total storage size occupied by storage trie nodes (in bytes) + ContractCodes int64 // Total number of contract codes in the state + ContractCodeBytes int64 // Total size of all contract code (in bytes) +} + +func (s SizeStats) String() string { + return fmt.Sprintf("Accounts: %d(%s), Storages: %d(%s), AccountTrienodes: %d(%s), StorageTrienodes: %d(%s), Codes: %d(%s)", + s.Accounts, common.StorageSize(s.AccountBytes), + s.Storages, common.StorageSize(s.StorageBytes), + s.AccountTrienodes, common.StorageSize(s.AccountTrienodeBytes), + s.StorageTrienodes, common.StorageSize(s.StorageTrienodeBytes), + s.ContractCodes, common.StorageSize(s.ContractCodeBytes), + ) +} + +// add applies the given state diffs and produces a new version of the statistics. +func (s SizeStats) add(diff SizeStats) SizeStats { + s.StateRoot = diff.StateRoot + s.BlockNumber = diff.BlockNumber + + s.Accounts += diff.Accounts + s.AccountBytes += diff.AccountBytes + s.Storages += diff.Storages + s.StorageBytes += diff.StorageBytes + s.AccountTrienodes += diff.AccountTrienodes + s.AccountTrienodeBytes += diff.AccountTrienodeBytes + s.StorageTrienodes += diff.StorageTrienodes + s.StorageTrienodeBytes += diff.StorageTrienodeBytes + s.ContractCodes += diff.ContractCodes + s.ContractCodeBytes += diff.ContractCodeBytes + return s +} + +// calSizeStats measures the state size changes of the provided state update. +func calSizeStats(update *stateUpdate) (SizeStats, error) { + stats := SizeStats{ + BlockNumber: update.blockNumber, + StateRoot: update.root, + } + + // Measure the account changes + for addr, oldValue := range update.accountsOrigin { + addrHash := crypto.Keccak256Hash(addr.Bytes()) + newValue, exists := update.accounts[addrHash] + if !exists { + return SizeStats{}, fmt.Errorf("account %x not found", addr) + } + oldLen, newLen := len(oldValue), len(newValue) + + switch { + case oldLen > 0 && newLen == 0: + // Account deletion + stats.Accounts -= 1 + stats.AccountBytes -= accountKeySize + int64(oldLen) + case oldLen == 0 && newLen > 0: + // Account creation + stats.Accounts += 1 + stats.AccountBytes += accountKeySize + int64(newLen) + default: + // Account update + stats.AccountBytes += int64(newLen - oldLen) + } + } + + // Measure storage changes + for addr, slots := range update.storagesOrigin { + addrHash := crypto.Keccak256Hash(addr.Bytes()) + subset, exists := update.storages[addrHash] + if !exists { + return SizeStats{}, fmt.Errorf("storage %x not found", addr) + } + for key, oldValue := range slots { + var ( + exists bool + newValue []byte + ) + if update.rawStorageKey { + newValue, exists = subset[crypto.Keccak256Hash(key.Bytes())] + } else { + newValue, exists = subset[key] + } + if !exists { + return SizeStats{}, fmt.Errorf("storage slot %x-%x not found", addr, key) + } + oldLen, newLen := len(oldValue), len(newValue) + + switch { + case oldLen > 0 && newLen == 0: + // Storage deletion + stats.Storages -= 1 + stats.StorageBytes -= storageKeySize + int64(oldLen) + case oldLen == 0 && newLen > 0: + // Storage creation + stats.Storages += 1 + stats.StorageBytes += storageKeySize + int64(newLen) + default: + // Storage update + stats.StorageBytes += int64(newLen - oldLen) + } + } + } + + // Measure trienode changes + for owner, subset := range update.nodes.Sets { + var ( + keyPrefix int64 + isAccount = owner == (common.Hash{}) + ) + if isAccount { + keyPrefix = accountTrienodePrefixSize + } else { + keyPrefix = storageTrienodePrefixSize + } + + // Iterate over Origins since every modified node has an origin entry + for path, oldNode := range subset.Origins { + newNode, exists := subset.Nodes[path] + if !exists { + return SizeStats{}, fmt.Errorf("node %x-%v not found", owner, path) + } + keySize := keyPrefix + int64(len(path)) + + switch { + case len(oldNode) > 0 && len(newNode.Blob) == 0: + // Node deletion + if isAccount { + stats.AccountTrienodes -= 1 + stats.AccountTrienodeBytes -= keySize + int64(len(oldNode)) + } else { + stats.StorageTrienodes -= 1 + stats.StorageTrienodeBytes -= keySize + int64(len(oldNode)) + } + case len(oldNode) == 0 && len(newNode.Blob) > 0: + // Node creation + if isAccount { + stats.AccountTrienodes += 1 + stats.AccountTrienodeBytes += keySize + int64(len(newNode.Blob)) + } else { + stats.StorageTrienodes += 1 + stats.StorageTrienodeBytes += keySize + int64(len(newNode.Blob)) + } + default: + // Node update + if isAccount { + stats.AccountTrienodeBytes += int64(len(newNode.Blob) - len(oldNode)) + } else { + stats.StorageTrienodeBytes += int64(len(newNode.Blob) - len(oldNode)) + } + } + } + } + + // Measure code changes. Note that the reported contract code size may be slightly + // inaccurate due to database deduplication (code is stored by its hash). However, + // this deviation is negligible and acceptable for measurement purposes. + for _, code := range update.codes { + stats.ContractCodes += 1 + stats.ContractCodeBytes += codeKeySize + int64(len(code.blob)) + } + return stats, nil +} + +type stateSizeQuery struct { + root *common.Hash // nil means latest + err error // non-nil if the state size is not yet initialized + result chan *SizeStats // nil means the state is unknown +} + +// SizeTracker handles the state size initialization and tracks of state size metrics. +type SizeTracker struct { + db ethdb.KeyValueStore + triedb *triedb.Database + abort chan struct{} + aborted chan struct{} + updateCh chan *stateUpdate + queryCh chan *stateSizeQuery +} + +// NewSizeTracker creates a new state size tracker and starts it automatically +func NewSizeTracker(db ethdb.KeyValueStore, triedb *triedb.Database) (*SizeTracker, error) { + if triedb.Scheme() != rawdb.PathScheme { + return nil, errors.New("state size tracker is not compatible with hash mode") + } + t := &SizeTracker{ + db: db, + triedb: triedb, + abort: make(chan struct{}), + aborted: make(chan struct{}), + updateCh: make(chan *stateUpdate), + queryCh: make(chan *stateSizeQuery), + } + go t.run() + return t, nil +} + +func (t *SizeTracker) Stop() { + close(t.abort) + <-t.aborted +} + +// sizeStatsHeap is a heap.Interface implementation over statesize statistics for +// retrieving the oldest statistics for eviction. +type sizeStatsHeap []SizeStats + +func (h sizeStatsHeap) Len() int { return len(h) } +func (h sizeStatsHeap) Less(i, j int) bool { return h[i].BlockNumber < h[j].BlockNumber } +func (h sizeStatsHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *sizeStatsHeap) Push(x any) { + *h = append(*h, x.(SizeStats)) +} + +func (h *sizeStatsHeap) Pop() any { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +// run performs the state size initialization and handles updates +func (t *SizeTracker) run() { + defer close(t.aborted) + + var last common.Hash + stats, err := t.init() // launch background thread for state size init + if err != nil { + return + } + h := sizeStatsHeap(slices.Collect(maps.Values(stats))) + heap.Init(&h) + + for { + select { + case u := <-t.updateCh: + base, found := stats[u.originRoot] + if !found { + log.Debug("Ignored the state size without parent", "parent", u.originRoot, "root", u.root, "number", u.blockNumber) + continue + } + diff, err := calSizeStats(u) + if err != nil { + continue + } + stat := base.add(diff) + stats[u.root] = stat + last = u.root + + heap.Push(&h, stats[u.root]) + for u.blockNumber-h[0].BlockNumber > statEvictThreshold { + delete(stats, h[0].StateRoot) + heap.Pop(&h) + } + log.Debug("Update state size", "number", stat.BlockNumber, "root", stat.StateRoot, "stat", stat) + + case r := <-t.queryCh: + var root common.Hash + if r.root != nil { + root = *r.root + } else { + root = last + } + if s, ok := stats[root]; ok { + r.result <- &s + } else { + r.result <- nil + } + + case <-t.abort: + return + } + } +} + +type buildResult struct { + stat SizeStats + root common.Hash + blockNumber uint64 + elapsed time.Duration + err error +} + +func (t *SizeTracker) init() (map[common.Hash]SizeStats, error) { + // Wait for snapshot completion and then init + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + +wait: + for { + select { + case <-ticker.C: + if t.triedb.SnapshotCompleted() { + break wait + } + case <-t.updateCh: + continue + case r := <-t.queryCh: + r.err = errors.New("state size is not initialized yet") + r.result <- nil + case <-t.abort: + return nil, errors.New("size tracker closed") + } + } + + var ( + updates = make(map[common.Hash]*stateUpdate) + children = make(map[common.Hash][]common.Hash) + done chan buildResult + ) + + for { + select { + case u := <-t.updateCh: + updates[u.root] = u + children[u.originRoot] = append(children[u.originRoot], u.root) + log.Debug("Received state update", "root", u.root, "blockNumber", u.blockNumber) + + case r := <-t.queryCh: + r.err = errors.New("state size is not initialized yet") + r.result <- nil + + case <-ticker.C: + // Only check timer if build hasn't started yet + if done != nil { + continue + } + root := rawdb.ReadSnapshotRoot(t.db) + if root == (common.Hash{}) { + continue + } + entry, exists := updates[root] + if !exists { + continue + } + done = make(chan buildResult) + go t.build(entry.root, entry.blockNumber, done) + log.Info("Measuring persistent state size", "root", root.Hex(), "number", entry.blockNumber) + + case result := <-done: + if result.err != nil { + return nil, result.err + } + var ( + stats = make(map[common.Hash]SizeStats) + apply func(root common.Hash, stat SizeStats) error + ) + apply = func(root common.Hash, base SizeStats) error { + for _, child := range children[root] { + entry, ok := updates[child] + if !ok { + return fmt.Errorf("the state update is not found, %x", child) + } + diff, err := calSizeStats(entry) + if err != nil { + return err + } + stats[child] = base.add(diff) + if err := apply(child, stats[child]); err != nil { + return err + } + } + return nil + } + if err := apply(result.root, result.stat); err != nil { + return nil, err + } + + // Set initial latest stats + stats[result.root] = result.stat + log.Info("Measured persistent state size", "root", result.root, "number", result.blockNumber, "stat", result.stat, "elapsed", common.PrettyDuration(result.elapsed)) + return stats, nil + + case <-t.abort: + return nil, errors.New("size tracker closed") + } + } +} + +func (t *SizeTracker) build(root common.Hash, blockNumber uint64, done chan buildResult) { + // Metrics will be directly updated by each goroutine + var ( + accounts, accountBytes int64 + storages, storageBytes int64 + codes, codeBytes int64 + + accountTrienodes, accountTrienodeBytes int64 + storageTrienodes, storageTrienodeBytes int64 + + group errgroup.Group + start = time.Now() + ) + + // Start all table iterations concurrently with direct metric updates + group.Go(func() error { + count, bytes, err := t.iterateTableParallel(t.abort, rawdb.SnapshotAccountPrefix, "account") + if err != nil { + return err + } + accounts, accountBytes = count, bytes + return nil + }) + + group.Go(func() error { + count, bytes, err := t.iterateTableParallel(t.abort, rawdb.SnapshotStoragePrefix, "storage") + if err != nil { + return err + } + storages, storageBytes = count, bytes + return nil + }) + + group.Go(func() error { + count, bytes, err := t.iterateTableParallel(t.abort, rawdb.TrieNodeAccountPrefix, "accountnode") + if err != nil { + return err + } + accountTrienodes, accountTrienodeBytes = count, bytes + return nil + }) + + group.Go(func() error { + count, bytes, err := t.iterateTableParallel(t.abort, rawdb.TrieNodeStoragePrefix, "storagenode") + if err != nil { + return err + } + storageTrienodes, storageTrienodeBytes = count, bytes + return nil + }) + + group.Go(func() error { + count, bytes, err := t.iterateTable(t.abort, rawdb.CodePrefix, "contractcode") + if err != nil { + return err + } + codes, codeBytes = count, bytes + return nil + }) + + // Wait for all goroutines to complete + if err := group.Wait(); err != nil { + done <- buildResult{err: err} + } else { + stat := SizeStats{ + StateRoot: root, + BlockNumber: blockNumber, + Accounts: accounts, + AccountBytes: accountBytes, + Storages: storages, + StorageBytes: storageBytes, + AccountTrienodes: accountTrienodes, + AccountTrienodeBytes: accountTrienodeBytes, + StorageTrienodes: storageTrienodes, + StorageTrienodeBytes: storageTrienodeBytes, + ContractCodes: codes, + ContractCodeBytes: codeBytes, + } + done <- buildResult{ + root: root, + blockNumber: blockNumber, + stat: stat, + elapsed: time.Since(start), + } + } +} + +// iterateTable performs iteration over a specific table and returns the results. +func (t *SizeTracker) iterateTable(closed chan struct{}, prefix []byte, name string) (int64, int64, error) { + var ( + start = time.Now() + logged = time.Now() + count, bytes int64 + ) + + iter := t.db.NewIterator(prefix, nil) + defer iter.Release() + + log.Debug("Iterating state", "category", name) + for iter.Next() { + count++ + bytes += int64(len(iter.Key()) + len(iter.Value())) + + if time.Since(logged) > time.Second*8 { + logged = time.Now() + + select { + case <-closed: + log.Debug("State iteration cancelled", "category", name) + return 0, 0, errors.New("size tracker closed") + default: + log.Debug("Iterating state", "category", name, "count", count, "size", common.StorageSize(bytes)) + } + } + } + // Check for iterator errors + if err := iter.Error(); err != nil { + log.Error("Iterator error", "category", name, "err", err) + return 0, 0, err + } + log.Debug("Finished state iteration", "category", name, "count", count, "size", common.StorageSize(bytes), "elapsed", common.PrettyDuration(time.Since(start))) + return count, bytes, nil +} + +// iterateTableParallel performs parallel iteration over a table by splitting into +// hex ranges. For storage tables, it splits on the first byte of the account hash +// (after the prefix). +func (t *SizeTracker) iterateTableParallel(closed chan struct{}, prefix []byte, name string) (int64, int64, error) { + var ( + totalCount int64 + totalBytes int64 + + start = time.Now() + workers = runtime.NumCPU() + group errgroup.Group + mu sync.Mutex + ) + group.SetLimit(workers) + log.Debug("Starting parallel state iteration", "category", name, "workers", workers) + + if len(prefix) > 0 { + if blob, err := t.db.Get(prefix); err == nil && len(blob) > 0 { + // If there's a direct hit on the prefix, include it in the stats + totalCount = 1 + totalBytes = int64(len(prefix) + len(blob)) + } + } + for i := 0; i < 256; i++ { + h := byte(i) + group.Go(func() error { + count, bytes, err := t.iterateTable(closed, slices.Concat(prefix, []byte{h}), fmt.Sprintf("%s-%02x", name, h)) + if err != nil { + return err + } + mu.Lock() + totalCount += count + totalBytes += bytes + mu.Unlock() + return nil + }) + } + if err := group.Wait(); err != nil { + return 0, 0, err + } + log.Debug("Finished parallel state iteration", "category", name, "count", totalCount, "size", common.StorageSize(totalBytes), "elapsed", common.PrettyDuration(time.Since(start))) + return totalCount, totalBytes, nil +} + +// Notify is an async method used to send the state update to the size tracker. +// It ignores empty updates (where no state changes occurred). +// If the channel is full, it drops the update to avoid blocking. +func (t *SizeTracker) Notify(update *stateUpdate) { + if update == nil || update.empty() { + return + } + select { + case t.updateCh <- update: + case <-t.abort: + return + } +} + +// Query returns the state size specified by the root, or nil if not available. +// If the root is nil, query the size of latest chain head; +// If the root is non-nil, query the size of the specified state; +func (t *SizeTracker) Query(root *common.Hash) (*SizeStats, error) { + r := &stateSizeQuery{ + root: root, + result: make(chan *SizeStats, 1), + } + select { + case <-t.aborted: + return nil, errors.New("state sizer has been closed") + case t.queryCh <- r: + return <-r.result, r.err + } +} diff --git a/core/state/state_sizer_test.go b/core/state/state_sizer_test.go new file mode 100644 index 0000000000..cab0c38163 --- /dev/null +++ b/core/state/state_sizer_test.go @@ -0,0 +1,231 @@ +// Copyright 2025 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 . + +package state + +import ( + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/holiman/uint256" +) + +func TestSizeTracker(t *testing.T) { + db := rawdb.NewMemoryDatabase() + defer db.Close() + + tdb := triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults}) + sdb := NewDatabase(tdb, nil) + + // Generate 50 blocks to establish a baseline + baselineBlockNum := uint64(50) + currentRoot := types.EmptyRootHash + + addr1 := common.BytesToAddress([]byte{1, 0, 0, 1}) + addr2 := common.BytesToAddress([]byte{1, 0, 0, 2}) + addr3 := common.BytesToAddress([]byte{1, 0, 0, 3}) + + // Create initial state with fixed accounts + state, _ := New(currentRoot, sdb) + state.AddBalance(addr1, uint256.NewInt(1000), tracing.BalanceChangeUnspecified) + state.SetNonce(addr1, 1, tracing.NonceChangeUnspecified) + state.SetState(addr1, common.HexToHash("0x1111"), common.HexToHash("0xaaaa")) + state.SetState(addr1, common.HexToHash("0x2222"), common.HexToHash("0xbbbb")) + + state.AddBalance(addr2, uint256.NewInt(2000), tracing.BalanceChangeUnspecified) + state.SetNonce(addr2, 2, tracing.NonceChangeUnspecified) + state.SetCode(addr2, []byte{0x60, 0x80, 0x60, 0x40, 0x52}, tracing.CodeChangeUnspecified) + + state.AddBalance(addr3, uint256.NewInt(3000), tracing.BalanceChangeUnspecified) + state.SetNonce(addr3, 3, tracing.NonceChangeUnspecified) + + currentRoot, _, err := state.CommitWithUpdate(1, true, false) + if err != nil { + t.Fatalf("Failed to commit initial state: %v", err) + } + if err := tdb.Commit(currentRoot, false); err != nil { + t.Fatalf("Failed to commit initial trie: %v", err) + } + + for i := 1; i < 50; i++ { // blocks 2-50 + blockNum := uint64(i + 1) + + newState, err := New(currentRoot, sdb) + if err != nil { + t.Fatalf("Failed to create new state at block %d: %v", blockNum, err) + } + testAddr := common.BigToAddress(uint256.NewInt(uint64(i + 100)).ToBig()) + newState.AddBalance(testAddr, uint256.NewInt(uint64((i+1)*1000)), tracing.BalanceChangeUnspecified) + newState.SetNonce(testAddr, uint64(i+10), tracing.NonceChangeUnspecified) + + if i%2 == 0 { + newState.SetState(addr1, common.BigToHash(uint256.NewInt(uint64(i+0x1000)).ToBig()), common.BigToHash(uint256.NewInt(uint64(i+0x2000)).ToBig())) + } + if i%3 == 0 { + newState.SetCode(testAddr, []byte{byte(i), 0x60, 0x80, byte(i + 1), 0x52}, tracing.CodeChangeUnspecified) + } + root, _, err := newState.CommitWithUpdate(blockNum, true, false) + if err != nil { + t.Fatalf("Failed to commit state at block %d: %v", blockNum, err) + } + if err := tdb.Commit(root, false); err != nil { + t.Fatalf("Failed to commit trie at block %d: %v", blockNum, err) + } + currentRoot = root + } + baselineRoot := currentRoot + + // Wait for snapshot completion + for !tdb.SnapshotCompleted() { + time.Sleep(100 * time.Millisecond) + } + + // Calculate baseline from the intermediate persisted state + baselineTracker := &SizeTracker{ + db: db, + triedb: tdb, + abort: make(chan struct{}), + } + done := make(chan buildResult) + + go baselineTracker.build(baselineRoot, baselineBlockNum, done) + var baselineResult buildResult + select { + case baselineResult = <-done: + if baselineResult.err != nil { + t.Fatalf("Failed to get baseline stats: %v", baselineResult.err) + } + case <-time.After(30 * time.Second): + t.Fatal("Timeout waiting for baseline stats") + } + baseline := baselineResult.stat + + // Now start the tracker and notify it of updates that happen AFTER the baseline + tracker, err := NewSizeTracker(db, tdb) + if err != nil { + t.Fatalf("Failed to create size tracker: %v", err) + } + defer tracker.Stop() + + var trackedUpdates []SizeStats + currentRoot = baselineRoot + + // Generate additional blocks beyond the baseline and track them + for i := 49; i < 130; i++ { // blocks 51-132 + blockNum := uint64(i + 2) + newState, err := New(currentRoot, sdb) + if err != nil { + t.Fatalf("Failed to create new state at block %d: %v", blockNum, err) + } + testAddr := common.BigToAddress(uint256.NewInt(uint64(i + 100)).ToBig()) + newState.AddBalance(testAddr, uint256.NewInt(uint64((i+1)*1000)), tracing.BalanceChangeUnspecified) + newState.SetNonce(testAddr, uint64(i+10), tracing.NonceChangeUnspecified) + + if i%2 == 0 { + newState.SetState(addr1, common.BigToHash(uint256.NewInt(uint64(i+0x1000)).ToBig()), common.BigToHash(uint256.NewInt(uint64(i+0x2000)).ToBig())) + } + if i%3 == 0 { + newState.SetCode(testAddr, []byte{byte(i), 0x60, 0x80, byte(i + 1), 0x52}, tracing.CodeChangeUnspecified) + } + root, update, err := newState.CommitWithUpdate(blockNum, true, false) + if err != nil { + t.Fatalf("Failed to commit state at block %d: %v", blockNum, err) + } + if err := tdb.Commit(root, false); err != nil { + t.Fatalf("Failed to commit trie at block %d: %v", blockNum, err) + } + + diff, err := calSizeStats(update) + if err != nil { + t.Fatalf("Failed to calculate size stats for block %d: %v", blockNum, err) + } + trackedUpdates = append(trackedUpdates, diff) + tracker.Notify(update) + currentRoot = root + } + finalRoot := rawdb.ReadSnapshotRoot(db) + + // Ensure all commits are flushed to disk + if err := tdb.Close(); err != nil { + t.Fatalf("Failed to close triedb: %v", err) + } + // Reopen the database to simulate a restart + tdb = triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults}) + defer tdb.Close() + + finalTracker := &SizeTracker{ + db: db, + triedb: tdb, + abort: make(chan struct{}), + } + finalDone := make(chan buildResult) + + go finalTracker.build(finalRoot, uint64(132), finalDone) + var result buildResult + select { + case result = <-finalDone: + if result.err != nil { + t.Fatalf("Failed to build final stats: %v", result.err) + } + case <-time.After(30 * time.Second): + t.Fatal("Timeout waiting for final stats") + } + actualStats := result.stat + + expectedStats := baseline + for _, diff := range trackedUpdates { + expectedStats = expectedStats.add(diff) + } + + // The final measured stats should match our calculated expected stats exactly + if actualStats.Accounts != expectedStats.Accounts { + t.Errorf("Account count mismatch: baseline(%d) + tracked_changes = %d, but final_measurement = %d", baseline.Accounts, expectedStats.Accounts, actualStats.Accounts) + } + if actualStats.AccountBytes != expectedStats.AccountBytes { + t.Errorf("Account bytes mismatch: expected %d, got %d", expectedStats.AccountBytes, actualStats.AccountBytes) + } + if actualStats.Storages != expectedStats.Storages { + t.Errorf("Storage count mismatch: baseline(%d) + tracked_changes = %d, but final_measurement = %d", baseline.Storages, expectedStats.Storages, actualStats.Storages) + } + if actualStats.StorageBytes != expectedStats.StorageBytes { + t.Errorf("Storage bytes mismatch: expected %d, got %d", expectedStats.StorageBytes, actualStats.StorageBytes) + } + if actualStats.ContractCodes != expectedStats.ContractCodes { + t.Errorf("Contract code count mismatch: baseline(%d) + tracked_changes = %d, but final_measurement = %d", baseline.ContractCodes, expectedStats.ContractCodes, actualStats.ContractCodes) + } + if actualStats.ContractCodeBytes != expectedStats.ContractCodeBytes { + t.Errorf("Contract code bytes mismatch: expected %d, got %d", expectedStats.ContractCodeBytes, actualStats.ContractCodeBytes) + } + // TODO: failed on github actions, need to investigate + // if actualStats.AccountTrienodes != expectedStats.AccountTrienodes { + // t.Errorf("Account trie nodes mismatch: expected %d, got %d", expectedStats.AccountTrienodes, actualStats.AccountTrienodes) + // } + // if actualStats.AccountTrienodeBytes != expectedStats.AccountTrienodeBytes { + // t.Errorf("Account trie node bytes mismatch: expected %d, got %d", expectedStats.AccountTrienodeBytes, actualStats.AccountTrienodeBytes) + // } + if actualStats.StorageTrienodes != expectedStats.StorageTrienodes { + t.Errorf("Storage trie nodes mismatch: expected %d, got %d", expectedStats.StorageTrienodes, actualStats.StorageTrienodes) + } + if actualStats.StorageTrienodeBytes != expectedStats.StorageTrienodeBytes { + t.Errorf("Storage trie node bytes mismatch: expected %d, got %d", expectedStats.StorageTrienodeBytes, actualStats.StorageTrienodeBytes) + } +} diff --git a/core/state/statedb.go b/core/state/statedb.go index cdfd638221..b770698255 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1155,7 +1155,7 @@ func (s *StateDB) GetTrie() Trie { // commit gathers the state mutations accumulated along with the associated // trie changes, resetting all internal flags with the new state as the base. -func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool) (*stateUpdate, error) { +func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNumber uint64) (*stateUpdate, error) { // Short circuit in case any database failure occurred earlier. if s.dbErr != nil { return nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) @@ -1307,13 +1307,13 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool) (*stateU origin := s.originalRoot s.originalRoot = root - return newStateUpdate(noStorageWiping, origin, root, deletes, updates, nodes), nil + return newStateUpdate(noStorageWiping, origin, root, blockNumber, deletes, updates, nodes), nil } // commitAndFlush is a wrapper of commit which also commits the state mutations // to the configured data stores. func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorageWiping bool) (*stateUpdate, error) { - ret, err := s.commit(deleteEmptyObjects, noStorageWiping) + ret, err := s.commit(deleteEmptyObjects, noStorageWiping, block) if err != nil { return nil, err } @@ -1378,6 +1378,16 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, noStorageWiping return ret.root, nil } +// CommitWithUpdate writes the state mutations and returns both the root hash and the state update. +// This is useful for tracking state changes at the blockchain level. +func (s *StateDB) CommitWithUpdate(block uint64, deleteEmptyObjects bool, noStorageWiping bool) (common.Hash, *stateUpdate, error) { + ret, err := s.commitAndFlush(block, deleteEmptyObjects, noStorageWiping) + if err != nil { + return common.Hash{}, nil, err + } + return ret.root, ret, nil +} + // Prepare handles the preparatory steps for executing a state transition with. // This method must be invoked before state transition. // diff --git a/core/state/stateupdate.go b/core/state/stateupdate.go index 75c4ca028c..a62e2b2d2d 100644 --- a/core/state/stateupdate.go +++ b/core/state/stateupdate.go @@ -64,8 +64,10 @@ type accountUpdate struct { // execution. It contains information about mutated contract codes, accounts, // and storage slots, along with their original values. type stateUpdate struct { - originRoot common.Hash // hash of the state before applying mutation - root common.Hash // hash of the state after applying mutation + originRoot common.Hash // hash of the state before applying mutation + root common.Hash // hash of the state after applying mutation + blockNumber uint64 // Associated block number + accounts map[common.Hash][]byte // accounts stores mutated accounts in 'slim RLP' encoding accountsOrigin map[common.Address][]byte // accountsOrigin stores the original values of mutated accounts in 'slim RLP' encoding @@ -95,7 +97,7 @@ func (sc *stateUpdate) empty() bool { // // rawStorageKey is a flag indicating whether to use the raw storage slot key or // the hash of the slot key for constructing state update object. -func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet) *stateUpdate { +func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash, blockNumber uint64, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet) *stateUpdate { var ( accounts = make(map[common.Hash][]byte) accountsOrigin = make(map[common.Address][]byte) @@ -164,6 +166,7 @@ func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash return &stateUpdate{ originRoot: originRoot, root: root, + blockNumber: blockNumber, accounts: accounts, accountsOrigin: accountsOrigin, storages: storages, diff --git a/eth/api_debug.go b/eth/api_debug.go index 188dee11aa..9cedbcbb2a 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -443,3 +443,51 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) { } return api.eth.blockchain.GetTrieFlushInterval().String(), nil } + +// StateSize returns the current state size statistics from the state size tracker. +// Returns an error if the state size tracker is not initialized or if stats are not ready. +func (api *DebugAPI) StateSize(blockHashOrNumber *rpc.BlockNumberOrHash) (interface{}, error) { + sizer := api.eth.blockchain.StateSizer() + if sizer == nil { + return nil, errors.New("state size tracker is not enabled") + } + var ( + err error + stats *state.SizeStats + ) + if blockHashOrNumber == nil { + stats, err = sizer.Query(nil) + } else { + header, herr := api.eth.APIBackend.HeaderByNumberOrHash(context.Background(), *blockHashOrNumber) + if herr != nil || header == nil { + return nil, fmt.Errorf("block %s is unknown", blockHashOrNumber) + } + stats, err = sizer.Query(&header.Root) + } + if err != nil { + return nil, err + } + if stats == nil { + var s string + if blockHashOrNumber == nil { + s = "chain head" + } else { + s = blockHashOrNumber.String() + } + return nil, fmt.Errorf("state size of %s is not available", s) + } + return map[string]interface{}{ + "stateRoot": stats.StateRoot, + "blockNumber": hexutil.Uint64(stats.BlockNumber), + "accounts": hexutil.Uint64(stats.Accounts), + "accountBytes": hexutil.Uint64(stats.AccountBytes), + "storages": hexutil.Uint64(stats.Storages), + "storageBytes": hexutil.Uint64(stats.StorageBytes), + "accountTrienodes": hexutil.Uint64(stats.AccountTrienodes), + "accountTrienodeBytes": hexutil.Uint64(stats.AccountTrienodeBytes), + "storageTrienodes": hexutil.Uint64(stats.StorageTrienodes), + "storageTrienodeBytes": hexutil.Uint64(stats.StorageTrienodeBytes), + "contractCodes": hexutil.Uint64(stats.ContractCodes), + "contractCodeBytes": hexutil.Uint64(stats.ContractCodeBytes), + }, nil +} diff --git a/eth/backend.go b/eth/backend.go index 7616ec9d31..4356733189 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -241,6 +241,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // - DATADIR/triedb/merkle.journal // - DATADIR/triedb/verkle.journal TrieJournalDirectory: stack.ResolvePath("triedb"), + StateSizeTracking: config.EnableStateSizeTracking, } ) if config.VMTrace != "" { diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 82c3c500a7..dc77141081 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -144,6 +144,9 @@ type Config struct { // Enables tracking of SHA3 preimages in the VM EnablePreimageRecording bool + // Enables tracking of state size + EnableStateSizeTracking bool + // Enables VM tracing VMTrace string VMTraceJsonConfig string diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 0a188ba23c..2fdd219dee 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -49,6 +49,7 @@ func (c Config) MarshalTOML() (interface{}, error) { BlobPool blobpool.Config GPO gasprice.Config EnablePreimageRecording bool + EnableStateSizeTracking bool VMTrace string VMTraceJsonConfig string RPCGasCap uint64 @@ -90,6 +91,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.BlobPool = c.BlobPool enc.GPO = c.GPO enc.EnablePreimageRecording = c.EnablePreimageRecording + enc.EnableStateSizeTracking = c.EnableStateSizeTracking enc.VMTrace = c.VMTrace enc.VMTraceJsonConfig = c.VMTraceJsonConfig enc.RPCGasCap = c.RPCGasCap @@ -135,6 +137,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { BlobPool *blobpool.Config GPO *gasprice.Config EnablePreimageRecording *bool + EnableStateSizeTracking *bool VMTrace *string VMTraceJsonConfig *string RPCGasCap *uint64 @@ -243,6 +246,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.EnablePreimageRecording != nil { c.EnablePreimageRecording = *dec.EnablePreimageRecording } + if dec.EnableStateSizeTracking != nil { + c.EnableStateSizeTracking = *dec.EnableStateSizeTracking + } if dec.VMTrace != nil { c.VMTrace = *dec.VMTrace } diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index e81e23ef16..0aedffe230 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -468,6 +468,12 @@ web3._extend({ call: 'debug_sync', params: 1 }), + new web3._extend.Method({ + name: 'stateSize', + call: 'debug_stateSize', + params: 1, + inputFormatter: [null], + }), ], properties: [] }); diff --git a/triedb/database.go b/triedb/database.go index e2f4334d6e..d2637bd909 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -375,3 +375,12 @@ func (db *Database) IsVerkle() bool { func (db *Database) Disk() ethdb.Database { return db.disk } + +// SnapshotCompleted returns the indicator if the snapshot is completed. +func (db *Database) SnapshotCompleted() bool { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + return false + } + return pdb.SnapshotCompleted() +} diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 423b921d47..ae9574963e 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -681,3 +681,14 @@ func (db *Database) StorageIterator(root common.Hash, account common.Hash, seek } return newFastStorageIterator(db, root, account, seek) } + +// SnapshotCompleted returns the flag indicating if the snapshot generation is completed. +func (db *Database) SnapshotCompleted() bool { + db.lock.RLock() + wait := db.waitSync + db.lock.RUnlock() + if wait { + return false + } + return db.tree.bottom().genComplete() +} From bc4ee71a5d25bbd2a9777c17eab6c9ab2c50f0ef Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 8 Sep 2025 16:07:00 +0800 Subject: [PATCH 014/119] triedb/pathdb: add recovery mechanism in state indexer (#32447) Alternative of #32335, enhancing the history indexer recovery after unclean shutdown. --- triedb/pathdb/database_test.go | 152 +++++++++++++++++++++++++-- triedb/pathdb/history_indexer.go | 56 +++++++++- triedb/pathdb/history_reader_test.go | 14 ++- triedb/pathdb/journal.go | 2 +- 4 files changed, 210 insertions(+), 14 deletions(-) diff --git a/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go index 99de4380bf..8cca7b1b3c 100644 --- a/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -121,6 +121,8 @@ func (ctx *genctx) storageOriginSet(rawStorageKey bool, t *tester) map[common.Ad type tester struct { db *Database roots []common.Hash + nodes []*trienode.MergedNodeSet + states []*StateSetWithOrigin preimages map[common.Hash][]byte // current state set @@ -135,12 +137,38 @@ type tester struct { snapNodes map[common.Hash]*trienode.MergedNodeSet } +// testerConfig holds configuration parameters for running a test scenario. type testerConfig struct { - stateHistory uint64 - isVerkle bool - layers int - enableIndex bool - journalDir string + stateHistory uint64 // Number of historical states to retain + layers int // Number of state transitions to generate for + enableIndex bool // Enable state history indexing or not + journalDir string // Directory path for persisting journal files + isVerkle bool // Enables Verkle trie mode if true + + writeBuffer *int // Optional, the size of memory allocated for write buffer + trieCache *int // Optional, the size of memory allocated for trie cache + stateCache *int // Optional, the size of memory allocated for state cache +} + +func (c *testerConfig) trieCacheSize() int { + if c.trieCache != nil { + return *c.trieCache + } + return 256 * 1024 +} + +func (c *testerConfig) stateCacheSize() int { + if c.stateCache != nil { + return *c.stateCache + } + return 256 * 1024 +} + +func (c *testerConfig) writeBufferSize() int { + if c.writeBuffer != nil { + return *c.writeBuffer + } + return 256 * 1024 } func newTester(t *testing.T, config *testerConfig) *tester { @@ -149,9 +177,9 @@ func newTester(t *testing.T, config *testerConfig) *tester { db = New(disk, &Config{ StateHistory: config.stateHistory, EnableStateIndexing: config.enableIndex, - TrieCleanSize: 256 * 1024, - StateCleanSize: 256 * 1024, - WriteBufferSize: 256 * 1024, + TrieCleanSize: config.trieCacheSize(), + StateCleanSize: config.stateCacheSize(), + WriteBufferSize: config.writeBufferSize(), NoAsyncFlush: true, JournalDirectory: config.journalDir, }, config.isVerkle) @@ -177,6 +205,8 @@ func newTester(t *testing.T, config *testerConfig) *tester { panic(fmt.Errorf("failed to update state changes, err: %w", err)) } obj.roots = append(obj.roots, root) + obj.nodes = append(obj.nodes, nodes) + obj.states = append(obj.states, states) } return obj } @@ -200,6 +230,8 @@ func (t *tester) extend(layers int) { panic(fmt.Errorf("failed to update state changes, err: %w", err)) } t.roots = append(t.roots, root) + t.nodes = append(t.nodes, nodes) + t.states = append(t.states, states) } } @@ -885,3 +917,107 @@ func copyStorages(set map[common.Hash]map[common.Hash][]byte) map[common.Hash]ma } return copied } + +func TestDatabaseIndexRecovery(t *testing.T) { + maxDiffLayers = 4 + defer func() { + maxDiffLayers = 128 + }() + + //log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) + writeBuffer := 512 * 1024 + config := &testerConfig{ + layers: 64, + enableIndex: true, + writeBuffer: &writeBuffer, + } + env := newTester(t, config) + defer env.release() + + // Ensure the buffer in disk layer is not empty + var ( + bRoot = env.db.tree.bottom().rootHash() + dRoot = crypto.Keccak256Hash(rawdb.ReadAccountTrieNode(env.db.diskdb, nil)) + ) + for dRoot == bRoot { + env.extend(1) + + bRoot = env.db.tree.bottom().rootHash() + dRoot = crypto.Keccak256Hash(rawdb.ReadAccountTrieNode(env.db.diskdb, nil)) + } + waitIndexing(env.db) + + var ( + dIndex int + roots = env.roots + hr = newHistoryReader(env.db.diskdb, env.db.stateFreezer) + ) + for i, root := range roots { + if root == dRoot { + dIndex = i + } + if root == bRoot { + break + } + if err := checkHistoricalState(env, root, uint64(i+1), hr); err != nil { + t.Fatal(err) + } + } + + // Terminate the database and mutate the journal, it's for simulating + // the unclean shutdown + env.db.Journal(env.lastHash()) + env.db.Close() + + // Mutate the journal in disk, it should be regarded as invalid + blob := rawdb.ReadTrieJournal(env.db.diskdb) + blob[0] = 0xa + rawdb.WriteTrieJournal(env.db.diskdb, blob) + + // Reload the database, the extra state histories should be removed + env.db = New(env.db.diskdb, env.db.config, false) + + for i := range roots { + _, err := readStateHistory(env.db.stateFreezer, uint64(i+1)) + if i <= dIndex && err != nil { + t.Fatalf("State history is not found, %d", i) + } + if i > dIndex && err == nil { + t.Fatalf("Unexpected state history found, %d", i) + } + } + remain, err := env.db.IndexProgress() + if err != nil { + t.Fatalf("Failed to obtain the progress, %v", err) + } + if remain == 0 { + t.Fatalf("Unexpected progress remain, %d", remain) + } + + // Apply new states on top, ensuring state indexing can respond correctly + for i := dIndex + 1; i < len(roots); i++ { + if err := env.db.Update(roots[i], roots[i-1], uint64(i), env.nodes[i], env.states[i]); err != nil { + panic(fmt.Errorf("failed to update state changes, err: %w", err)) + } + } + remain, err = env.db.IndexProgress() + if err != nil { + t.Fatalf("Failed to obtain the progress, %v", err) + } + if remain != 0 { + t.Fatalf("Unexpected progress remain, %d", remain) + } + waitIndexing(env.db) + + // Ensure the truncated state histories become accessible + bRoot = env.db.tree.bottom().rootHash() + hr = newHistoryReader(env.db.diskdb, env.db.stateFreezer) + for i, root := range roots { + if root == bRoot { + break + } + if err := checkHistoricalState(env, root, uint64(i+1), hr); err != nil { + t.Fatal(err) + } + } +} diff --git a/triedb/pathdb/history_indexer.go b/triedb/pathdb/history_indexer.go index 14b9af5367..b4e89c3f17 100644 --- a/triedb/pathdb/history_indexer.go +++ b/triedb/pathdb/history_indexer.go @@ -322,15 +322,22 @@ func newIndexIniter(disk ethdb.KeyValueStore, freezer ethdb.AncientStore, lastID closed: make(chan struct{}), } // Load indexing progress + var recover bool initer.last.Store(lastID) metadata := loadIndexMetadata(disk) if metadata != nil { initer.indexed.Store(metadata.Last) + recover = metadata.Last > lastID } // Launch background indexer initer.wg.Add(1) - go initer.run(lastID) + if recover { + log.Info("History indexer is recovering", "history", lastID, "indexed", metadata.Last) + go initer.recover(lastID) + } else { + go initer.run(lastID) + } return initer } @@ -364,8 +371,8 @@ func (i *indexIniter) remain() uint64 { default: last, indexed := i.last.Load(), i.indexed.Load() if last < indexed { - log.Error("Invalid state indexing range", "last", last, "indexed", indexed) - return 0 + log.Warn("State indexer is in recovery", "indexed", indexed, "last", last) + return indexed - last } return last - indexed } @@ -569,6 +576,49 @@ func (i *indexIniter) index(done chan struct{}, interrupt *atomic.Int32, lastID log.Info("Indexed state history", "from", beginID, "to", lastID, "elapsed", common.PrettyDuration(time.Since(start))) } +// recover handles unclean shutdown recovery. After an unclean shutdown, any +// extra histories are typically truncated, while the corresponding history index +// entries may still have been written. Ideally, we would unindex these histories +// in reverse order, but there is no guarantee that the required histories will +// still be available. +// +// As a workaround, indexIniter waits until the missing histories are regenerated +// by chain recovery, under the assumption that the recovered histories will be +// identical to the lost ones. Fork-awareness should be added in the future to +// correctly handle histories affected by reorgs. +func (i *indexIniter) recover(lastID uint64) { + defer i.wg.Done() + + for { + select { + case signal := <-i.interrupt: + newLastID := signal.newLastID + if newLastID != lastID+1 && newLastID != lastID-1 { + signal.result <- fmt.Errorf("invalid history id, last: %d, got: %d", lastID, newLastID) + continue + } + + // Update the last indexed flag + lastID = newLastID + signal.result <- nil + i.last.Store(newLastID) + log.Debug("Updated history index flag", "last", lastID) + + // Terminate the recovery routine once the histories are fully aligned + // with the index data, indicating that index initialization is complete. + metadata := loadIndexMetadata(i.disk) + if metadata != nil && metadata.Last == lastID { + close(i.done) + log.Info("History indexer is recovered", "last", lastID) + return + } + + case <-i.closed: + return + } + } +} + // historyIndexer manages the indexing and unindexing of state histories, // providing access to historical states. // diff --git a/triedb/pathdb/history_reader_test.go b/triedb/pathdb/history_reader_test.go index 2ae1cfdd29..75c5f701f9 100644 --- a/triedb/pathdb/history_reader_test.go +++ b/triedb/pathdb/history_reader_test.go @@ -144,7 +144,13 @@ func testHistoryReader(t *testing.T, historyLimit uint64) { maxDiffLayers = 128 }() - env := newTester(t, &testerConfig{stateHistory: historyLimit, layers: 64, enableIndex: true}) + //log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) + config := &testerConfig{ + stateHistory: historyLimit, + layers: 64, + enableIndex: true, + } + env := newTester(t, config) defer env.release() waitIndexing(env.db) @@ -183,7 +189,11 @@ func TestHistoricalStateReader(t *testing.T) { }() //log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) - config := &testerConfig{stateHistory: 0, layers: 64, enableIndex: true} + config := &testerConfig{ + stateHistory: 0, + layers: 64, + enableIndex: true, + } env := newTester(t, config) defer env.release() waitIndexing(env.db) diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index bd9081a28f..7a634dc974 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -267,7 +267,7 @@ func (dl *diskLayer) journal(w io.Writer) error { if err := dl.buffer.states.encode(w); err != nil { return err } - log.Debug("Journaled pathdb disk layer", "root", dl.root) + log.Debug("Journaled pathdb disk layer", "root", dl.root, "id", dl.id) return nil } From d95ca2e056ab2b6e702fb1ab2b992050386ac608 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 8 Sep 2025 12:33:17 +0200 Subject: [PATCH 015/119] cmd/devp2p/internal/ethtest: fix possible infinite wait (#32551) TestBlobTxWithoutSidecar test could run infinitely in case of a client not requesting the good transaction. This adds a timeout to make the test fail in this case. Fixes https://github.com/ethereum/go-ethereum/issues/32422 Signed-off-by: Csaba Kiraly --- cmd/devp2p/internal/ethtest/suite.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 47d00761f3..47327b6844 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -1133,7 +1133,10 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types // transmit the same tx but with correct sidecar from the good peer. var req *eth.GetPooledTransactionsPacket - req, err = readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn) + ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) + defer cancel() + + req, err = readUntil[eth.GetPooledTransactionsPacket](ctx, conn) if err != nil { errc <- fmt.Errorf("reading pooled tx request failed: %v", err) return From b381804eb145684a39653aca42936bb17388a7fb Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 8 Sep 2025 13:33:10 +0200 Subject: [PATCH 016/119] core/vm: switch modexp gas computation to uint64 (#32527) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit supersedes https://github.com/ethereum/go-ethereum/pull/32508/files and builts on top of https://github.com/ethereum/go-ethereum/pull/32184 looks like a ~60% decrease in allocations / op and ~20% speed increase (very variable, between 2-200%) ``` goos: linux goarch: amd64 pkg: github.com/ethereum/go-ethereum/core/vm cpu: Intel(R) Core(TM) Ultra 7 155U │ /tmp/old.txt │ /tmp/new.txt │ │ sec/op │ sec/op vs base │ PrecompiledModExp/eip_example1-Gas=13056-14 23.70µ ± 3% 22.31µ ± 6% -5.89% (p=0.004 n=10) PrecompiledModExp/eip_example2-Gas=13056-14 566.2n ± 12% 267.0n ± 3% -52.85% (p=0.000 n=10) PrecompiledModExp/nagydani-1-square-Gas=204-14 1285.5n ± 3% 995.8n ± 3% -22.54% (p=0.000 n=10) PrecompiledModExp/nagydani-1-qube-Gas=204-14 1.757µ ± 30% 1.410µ ± 16% -19.75% (p=0.000 n=10) PrecompiledModExp/nagydani-1-pow0x10001-Gas=3276-14 8.897µ ± 14% 6.664µ ± 2% -25.10% (p=0.000 n=10) PrecompiledModExp/nagydani-2-square-Gas=665-14 2.107µ ± 8% 1.470µ ± 11% -30.24% (p=0.000 n=10) PrecompiledModExp/nagydani-2-qube-Gas=665-14 3.142µ ± 3% 2.289µ ± 7% -27.16% (p=0.000 n=10) PrecompiledModExp/nagydani-2-pow0x10001-Gas=10649-14 14.76µ ± 3% 13.59µ ± 4% -7.94% (p=0.000 n=10) PrecompiledModExp/nagydani-3-square-Gas=1894-14 3.984µ ± 3% 3.211µ ± 3% -19.42% (p=0.000 n=10) PrecompiledModExp/nagydani-3-qube-Gas=1894-14 7.572µ ± 12% 6.153µ ± 3% -18.74% (p=0.000 n=10) PrecompiledModExp/nagydani-3-pow0x10001-Gas=30310-14 39.07µ ± 7% 37.76µ ± 5% ~ (p=0.123 n=10) PrecompiledModExp/nagydani-4-square-Gas=5580-14 9.613µ ± 2% 9.221µ ± 2% -4.08% (p=0.003 n=10) PrecompiledModExp/nagydani-4-qube-Gas=5580-14 21.66µ ± 5% 21.48µ ± 17% ~ (p=0.971 n=10) PrecompiledModExp/nagydani-4-pow0x10001-Gas=89292-14 120.7µ ± 8% 116.0µ ± 3% -3.93% (p=0.043 n=10) PrecompiledModExp/nagydani-5-square-Gas=17868-14 23.93µ ± 3% 24.82µ ± 3% +3.73% (p=0.000 n=10) PrecompiledModExp/nagydani-5-qube-Gas=17868-14 54.98µ ± 1% 58.08µ ± 4% +5.64% (p=0.000 n=10) PrecompiledModExp/nagydani-5-pow0x10001-Gas=285900-14 340.3µ ± 2% 341.2µ ± 3% ~ (p=0.529 n=10) PrecompiledModExpEip2565/eip_example1-Gas=1360-14 21.99µ ± 3% 21.24µ ± 2% -3.43% (p=0.000 n=10) PrecompiledModExpEip2565/eip_example2-Gas=1360-14 568.1n ± 12% 221.1n ± 2% -61.07% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-square-Gas=200-14 1272.0n ± 2% 822.3n ± 13% -35.35% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-qube-Gas=200-14 1.633µ ± 3% 1.397µ ± 21% -14.45% (p=0.023 n=10) PrecompiledModExpEip2565/nagydani-1-pow0x10001-Gas=341-14 7.276µ ± 3% 6.703µ ± 5% -7.88% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-square-Gas=200-14 1.959µ ± 3% 1.618µ ± 2% -17.43% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-qube-Gas=200-14 2.959µ ± 6% 2.611µ ± 5% -11.78% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-pow0x10001-Gas=1365-14 14.99µ ± 5% 13.76µ ± 4% -8.17% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-square-Gas=341-14 3.870µ ± 4% 3.521µ ± 2% -9.04% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-qube-Gas=341-14 6.871µ ± 3% 6.454µ ± 3% -6.07% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-pow0x10001-Gas=5461-14 40.18µ ± 7% 35.55µ ± 3% -11.52% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-square-Gas=1365-14 9.694µ ± 3% 8.427µ ± 3% -13.08% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-qube-Gas=1365-14 21.45µ ± 2% 19.48µ ± 7% -9.18% (p=0.001 n=10) PrecompiledModExpEip2565/nagydani-4-pow0x10001-Gas=21845-14 118.1µ ± 2% 113.7µ ± 2% -3.76% (p=0.003 n=10) PrecompiledModExpEip2565/nagydani-5-square-Gas=5461-14 25.80µ ± 5% 24.05µ ± 3% -6.76% (p=0.001 n=10) PrecompiledModExpEip2565/nagydani-5-qube-Gas=5461-14 57.28µ ± 8% 55.76µ ± 6% ~ (p=0.796 n=10) PrecompiledModExpEip2565/nagydani-5-pow0x10001-Gas=87381-14 335.2µ ± 2% 346.2µ ± 3% +3.28% (p=0.000 n=10) PrecompiledModExpEip2565/marius-1-even-Gas=2057-14 56.24µ ± 3% 55.53µ ± 1% ~ (p=0.280 n=10) PrecompiledModExpEip2565/guido-1-even-Gas=2298-14 38.74µ ± 3% 38.63µ ± 6% ~ (p=0.631 n=10) PrecompiledModExpEip2565/guido-2-even-Gas=2300-14 61.94µ ± 2% 61.38µ ± 11% ~ (p=0.529 n=10) PrecompiledModExpEip2565/guido-3-even-Gas=5400-14 20.27µ ± 2% 20.19µ ± 5% ~ (p=0.853 n=10) PrecompiledModExpEip2565/guido-4-even-Gas=1026-14 805.8n ± 3% 359.1n ± 2% -55.44% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-base-heavy-Gas=200-14 4.717µ ± 5% 3.925µ ± 5% -16.79% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-exp-heavy-Gas=215-14 15.51µ ± 3% 14.77µ ± 2% -4.76% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-balanced-Gas=200-14 10.42µ ± 2% 10.01µ ± 5% -3.94% (p=0.011 n=10) PrecompiledModExpEip2565/marcin-2-base-heavy-Gas=867-14 15.99µ ± 4% 15.65µ ± 4% ~ (p=0.190 n=10) PrecompiledModExpEip2565/marcin-2-exp-heavy-Gas=852-14 24.45µ ± 2% 23.36µ ± 3% -4.47% (p=0.002 n=10) PrecompiledModExpEip2565/marcin-2-balanced-Gas=996-14 34.59µ ± 29% 32.90µ ± 1% -4.90% (p=0.001 n=10) PrecompiledModExpEip2565/marcin-3-base-heavy-Gas=677-14 12.45µ ± 2% 12.00µ ± 16% ~ (p=0.105 n=10) PrecompiledModExpEip2565/marcin-3-exp-heavy-Gas=765-14 16.21µ ± 3% 15.58µ ± 6% ~ (p=0.063 n=10) PrecompiledModExpEip2565/marcin-3-balanced-Gas=1360-14 23.03µ ± 3% 21.34µ ± 2% -7.35% (p=0.000 n=10) PrecompiledModExpEip2565/mod-8-exp-648-Gas=215-14 17.44µ ± 3% 16.03µ ± 1% -8.07% (p=0.000 n=10) PrecompiledModExpEip2565/mod-8-exp-896-Gas=298-14 21.00µ ± 5% 19.52µ ± 2% -7.04% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-32-Gas=200-14 9.549µ ± 2% 9.322µ ± 2% -2.38% (p=0.005 n=10) PrecompiledModExpEip2565/mod-32-exp-36-Gas=200-14 10.74µ ± 1% 10.31µ ± 2% -4.06% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-40-Gas=208-14 11.95µ ± 2% 11.43µ ± 2% -4.33% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-64-Gas=336-14 18.77µ ± 3% 18.10µ ± 2% -3.54% (p=0.002 n=10) PrecompiledModExpEip2565/mod-32-exp-65-Gas=341-14 13.58µ ± 1% 13.47µ ± 1% ~ (p=0.280 n=10) PrecompiledModExpEip2565/mod-32-exp-128-Gas=677-14 13.61µ ± 3% 13.36µ ± 1% -1.83% (p=0.000 n=10) PrecompiledModExpEip2565/mod-256-exp-2-Gas=341-14 6.984µ ± 2% 6.053µ ± 3% -13.33% (p=0.000 n=10) PrecompiledModExpEip2565/mod-264-exp-2-Gas=363-14 7.126µ ± 5% 6.344µ ± 7% -10.97% (p=0.000 n=10) PrecompiledModExpEip2565/mod-1024-exp-2-Gas=5461-14 52.41µ ± 22% 49.85µ ± 4% ~ (p=0.089 n=10) PrecompiledModExpEip2565/pawel-1-exp-heavy-Gas=298-14 20.93µ ± 32% 20.15µ ± 2% -3.69% (p=0.001 n=10) PrecompiledModExpEip2565/pawel-2-exp-heavy-Gas=425-14 19.32µ ± 17% 14.46µ ± 4% -25.16% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-3-exp-heavy-Gas=501-14 20.22µ ± 15% 14.30µ ± 3% -29.27% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-4-exp-heavy-Gas=506-14 14.33µ ± 3% 13.93µ ± 2% -2.80% (p=0.001 n=10) PrecompiledModExpEip2565/mod_vul_pawel_3_exp_8-Gas=200-14 24.30µ ± 16% 16.94µ ± 18% -30.30% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-square-Gas=500-14 1477.5n ± 5% 872.8n ± 9% -40.93% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-qube-Gas=500-14 2.099µ ± 8% 1.131µ ± 2% -46.13% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-pow0x10001-Gas=2048-14 8.920µ ± 4% 6.356µ ± 2% -28.74% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-square-Gas=512-14 2.227µ ± 4% 1.396µ ± 2% -37.34% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-qube-Gas=512-14 3.507µ ± 8% 2.421µ ± 13% -30.97% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-pow0x10001-Gas=8192-14 17.53µ ± 4% 14.17µ ± 5% -19.20% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-square-Gas=2048-14 4.801µ ± 12% 3.498µ ± 3% -27.15% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-qube-Gas=2048-14 8.284µ ± 4% 6.536µ ± 2% -21.10% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-pow0x10001-Gas=32768-14 44.82µ ± 6% 37.01µ ± 24% -17.43% (p=0.007 n=10) PrecompiledModExpEip7883/nagydani-4-square-Gas=8192-14 11.742µ ± 4% 9.000µ ± 4% -23.35% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-qube-Gas=8192-14 27.49µ ± 10% 21.34µ ± 5% -22.35% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-pow0x10001-Gas=131072-14 141.4µ ± 11% 113.4µ ± 3% -19.80% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-square-Gas=32768-14 31.46µ ± 5% 23.53µ ± 1% -25.21% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-qube-Gas=32768-14 78.12µ ± 20% 53.74µ ± 4% -31.20% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-pow0x10001-Gas=524288-14 446.3µ ± 6% 338.5µ ± 2% -24.15% (p=0.000 n=10) PrecompiledModExpEip7883/marius-1-even-Gas=45296-14 71.25µ ± 4% 55.33µ ± 1% -22.34% (p=0.000 n=10) PrecompiledModExpEip7883/guido-1-even-Gas=51136-14 51.06µ ± 14% 38.19µ ± 3% -25.20% (p=0.000 n=10) PrecompiledModExpEip7883/guido-2-even-Gas=51152-14 86.12µ ± 20% 60.65µ ± 2% -29.57% (p=0.000 n=10) PrecompiledModExpEip7883/guido-3-even-Gas=32400-14 27.53µ ± 32% 20.37µ ± 2% -26.02% (p=0.000 n=10) PrecompiledModExpEip7883/guido-4-even-Gas=94448-14 950.4n ± 5% 354.8n ± 28% -62.67% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-base-heavy-Gas=1152-14 5.619µ ± 7% 4.440µ ± 5% -20.98% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-exp-heavy-Gas=16624-14 19.03µ ± 19% 15.67µ ± 22% -17.63% (p=0.003 n=10) PrecompiledModExpEip7883/marcin-1-balanced-Gas=1200-14 13.83µ ± 19% 10.91µ ± 10% -21.16% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-base-heavy-Gas=5202-14 23.69µ ± 9% 17.79µ ± 13% -24.91% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-exp-heavy-Gas=16368-14 38.86µ ± 12% 25.69µ ± 14% -33.89% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-balanced-Gas=5978-14 51.47µ ± 28% 33.67µ ± 15% -34.58% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-base-heavy-Gas=2032-14 16.69µ ± 17% 12.08µ ± 2% -27.60% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-exp-heavy-Gas=4080-14 19.75µ ± 5% 16.13µ ± 11% -18.36% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-balanced-Gas=4080-14 28.66µ ± 9% 22.47µ ± 4% -21.59% (p=0.000 n=10) PrecompiledModExpEip7883/mod-8-exp-648-Gas=16624-14 20.78µ ± 14% 16.38µ ± 2% -21.17% (p=0.000 n=10) PrecompiledModExpEip7883/mod-8-exp-896-Gas=24560-14 24.35µ ± 10% 19.62µ ± 1% -19.43% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-32-Gas=500-14 10.312µ ± 4% 9.293µ ± 3% -9.88% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-36-Gas=560-14 11.69µ ± 18% 10.37µ ± 3% -11.27% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-40-Gas=624-14 14.17µ ± 24% 11.60µ ± 6% -18.13% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-64-Gas=1008-14 22.03µ ± 8% 18.22µ ± 2% -17.28% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-65-Gas=1024-14 16.48µ ± 11% 13.34µ ± 1% -19.04% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-128-Gas=2032-14 17.03µ ± 9% 13.44µ ± 1% -21.03% (p=0.000 n=10) PrecompiledModExpEip7883/mod-256-exp-2-Gas=2048-14 8.123µ ± 14% 5.962µ ± 3% -26.61% (p=0.000 n=10) PrecompiledModExpEip7883/mod-264-exp-2-Gas=2178-14 8.155µ ± 28% 6.799µ ± 4% -16.64% (p=0.000 n=10) PrecompiledModExpEip7883/mod-1024-exp-2-Gas=32768-14 62.32µ ± 4% 49.82µ ± 1% -20.06% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-1-exp-heavy-Gas=24560-14 26.90µ ± 7% 20.30µ ± 2% -24.55% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-2-exp-heavy-Gas=6128-14 17.67µ ± 2% 15.61µ ± 11% -11.66% (p=0.001 n=10) PrecompiledModExpEip7883/pawel-3-exp-heavy-Gas=2672-14 17.50µ ± 3% 14.88µ ± 8% -14.98% (p=0.002 n=10) PrecompiledModExpEip7883/pawel-4-exp-heavy-Gas=1520-14 17.32µ ± 2% 14.14µ ± 5% -18.39% (p=0.000 n=10) PrecompiledModExpEip7883/mod_vul_pawel_3_exp_8-Gas=1008-14 21.68µ ± 24% 17.07µ ± 3% -21.25% (p=0.000 n=10) geomean 14.64µ 12.02µ -17.89% │ /tmp/old.txt │ /tmp/new.txt │ │ gas/op │ gas/op vs base │ PrecompiledModExp/eip_example1-Gas=13056-14 13.06k ± 0% 13.06k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/eip_example2-Gas=13056-14 13.06k ± 0% 13.06k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-1-square-Gas=204-14 204.0 ± 0% 204.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-1-qube-Gas=204-14 204.0 ± 0% 204.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-1-pow0x10001-Gas=3276-14 3.276k ± 0% 3.276k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-2-square-Gas=665-14 665.0 ± 0% 665.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-2-qube-Gas=665-14 665.0 ± 0% 665.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-2-pow0x10001-Gas=10649-14 10.65k ± 0% 10.65k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-3-square-Gas=1894-14 1.894k ± 0% 1.894k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-3-qube-Gas=1894-14 1.894k ± 0% 1.894k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-3-pow0x10001-Gas=30310-14 30.31k ± 0% 30.31k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-4-square-Gas=5580-14 5.580k ± 0% 5.580k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-4-qube-Gas=5580-14 5.580k ± 0% 5.580k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-4-pow0x10001-Gas=89292-14 89.29k ± 0% 89.29k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-5-square-Gas=17868-14 17.87k ± 0% 17.87k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-5-qube-Gas=17868-14 17.87k ± 0% 17.87k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExp/nagydani-5-pow0x10001-Gas=285900-14 285.9k ± 0% 285.9k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/eip_example1-Gas=1360-14 1.360k ± 0% 1.360k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/eip_example2-Gas=1360-14 1.360k ± 0% 1.360k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-1-square-Gas=200-14 200.0 ± 0% 200.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-1-qube-Gas=200-14 200.0 ± 0% 200.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-1-pow0x10001-Gas=341-14 341.0 ± 0% 341.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-2-square-Gas=200-14 200.0 ± 0% 200.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-2-qube-Gas=200-14 200.0 ± 0% 200.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-2-pow0x10001-Gas=1365-14 1.365k ± 0% 1.365k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-3-square-Gas=341-14 341.0 ± 0% 341.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-3-qube-Gas=341-14 341.0 ± 0% 341.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-3-pow0x10001-Gas=5461-14 5.461k ± 0% 5.461k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-4-square-Gas=1365-14 1.365k ± 0% 1.365k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-4-qube-Gas=1365-14 1.365k ± 0% 1.365k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-4-pow0x10001-Gas=21845-14 21.84k ± 0% 21.84k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-5-square-Gas=5461-14 5.461k ± 0% 5.461k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-5-qube-Gas=5461-14 5.461k ± 0% 5.461k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/nagydani-5-pow0x10001-Gas=87381-14 87.38k ± 0% 87.38k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marius-1-even-Gas=2057-14 2.057k ± 0% 2.057k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/guido-1-even-Gas=2298-14 2.298k ± 0% 2.298k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/guido-2-even-Gas=2300-14 2.300k ± 0% 2.300k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/guido-3-even-Gas=5400-14 5.400k ± 0% 5.400k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/guido-4-even-Gas=1026-14 1.026k ± 0% 1.026k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marcin-1-base-heavy-Gas=200-14 200.0 ± 0% 200.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marcin-1-exp-heavy-Gas=215-14 215.0 ± 0% 215.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marcin-1-balanced-Gas=200-14 200.0 ± 0% 200.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marcin-2-base-heavy-Gas=867-14 867.0 ± 0% 867.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marcin-2-exp-heavy-Gas=852-14 852.0 ± 0% 852.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marcin-2-balanced-Gas=996-14 996.0 ± 0% 996.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marcin-3-base-heavy-Gas=677-14 677.0 ± 0% 677.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marcin-3-exp-heavy-Gas=765-14 765.0 ± 0% 765.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/marcin-3-balanced-Gas=1360-14 1.360k ± 0% 1.360k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-8-exp-648-Gas=215-14 215.0 ± 0% 215.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-8-exp-896-Gas=298-14 298.0 ± 0% 298.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-32-exp-32-Gas=200-14 200.0 ± 0% 200.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-32-exp-36-Gas=200-14 200.0 ± 0% 200.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-32-exp-40-Gas=208-14 208.0 ± 0% 208.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-32-exp-64-Gas=336-14 336.0 ± 0% 336.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-32-exp-65-Gas=341-14 341.0 ± 0% 341.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-32-exp-128-Gas=677-14 677.0 ± 0% 677.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-256-exp-2-Gas=341-14 341.0 ± 0% 341.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-264-exp-2-Gas=363-14 363.0 ± 0% 363.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod-1024-exp-2-Gas=5461-14 5.461k ± 0% 5.461k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/pawel-1-exp-heavy-Gas=298-14 298.0 ± 0% 298.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/pawel-2-exp-heavy-Gas=425-14 425.0 ± 0% 425.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/pawel-3-exp-heavy-Gas=501-14 501.0 ± 0% 501.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/pawel-4-exp-heavy-Gas=506-14 506.0 ± 0% 506.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip2565/mod_vul_pawel_3_exp_8-Gas=200-14 200.0 ± 0% 200.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-1-square-Gas=500-14 500.0 ± 0% 500.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-1-qube-Gas=500-14 500.0 ± 0% 500.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-1-pow0x10001-Gas=2048-14 2.048k ± 0% 2.048k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-2-square-Gas=512-14 512.0 ± 0% 512.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-2-qube-Gas=512-14 512.0 ± 0% 512.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-2-pow0x10001-Gas=8192-14 8.192k ± 0% 8.192k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-3-square-Gas=2048-14 2.048k ± 0% 2.048k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-3-qube-Gas=2048-14 2.048k ± 0% 2.048k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-3-pow0x10001-Gas=32768-14 32.77k ± 0% 32.77k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-4-square-Gas=8192-14 8.192k ± 0% 8.192k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-4-qube-Gas=8192-14 8.192k ± 0% 8.192k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-4-pow0x10001-Gas=131072-14 131.1k ± 0% 131.1k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-5-square-Gas=32768-14 32.77k ± 0% 32.77k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-5-qube-Gas=32768-14 32.77k ± 0% 32.77k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/nagydani-5-pow0x10001-Gas=524288-14 524.3k ± 0% 524.3k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marius-1-even-Gas=45296-14 45.30k ± 0% 45.30k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/guido-1-even-Gas=51136-14 51.14k ± 0% 51.14k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/guido-2-even-Gas=51152-14 51.15k ± 0% 51.15k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/guido-3-even-Gas=32400-14 32.40k ± 0% 32.40k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/guido-4-even-Gas=94448-14 94.45k ± 0% 94.45k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marcin-1-base-heavy-Gas=1152-14 1.152k ± 0% 1.152k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marcin-1-exp-heavy-Gas=16624-14 16.62k ± 0% 16.62k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marcin-1-balanced-Gas=1200-14 1.200k ± 0% 1.200k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marcin-2-base-heavy-Gas=5202-14 5.202k ± 0% 5.202k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marcin-2-exp-heavy-Gas=16368-14 16.37k ± 0% 16.37k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marcin-2-balanced-Gas=5978-14 5.978k ± 0% 5.978k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marcin-3-base-heavy-Gas=2032-14 2.032k ± 0% 2.032k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marcin-3-exp-heavy-Gas=4080-14 4.080k ± 0% 4.080k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/marcin-3-balanced-Gas=4080-14 4.080k ± 0% 4.080k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-8-exp-648-Gas=16624-14 16.62k ± 0% 16.62k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-8-exp-896-Gas=24560-14 24.56k ± 0% 24.56k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-32-exp-32-Gas=500-14 500.0 ± 0% 500.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-32-exp-36-Gas=560-14 560.0 ± 0% 560.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-32-exp-40-Gas=624-14 624.0 ± 0% 624.0 ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-32-exp-64-Gas=1008-14 1.008k ± 0% 1.008k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-32-exp-65-Gas=1024-14 1.024k ± 0% 1.024k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-32-exp-128-Gas=2032-14 2.032k ± 0% 2.032k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-256-exp-2-Gas=2048-14 2.048k ± 0% 2.048k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-264-exp-2-Gas=2178-14 2.178k ± 0% 2.178k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod-1024-exp-2-Gas=32768-14 32.77k ± 0% 32.77k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/pawel-1-exp-heavy-Gas=24560-14 24.56k ± 0% 24.56k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/pawel-2-exp-heavy-Gas=6128-14 6.128k ± 0% 6.128k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/pawel-3-exp-heavy-Gas=2672-14 2.672k ± 0% 2.672k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/pawel-4-exp-heavy-Gas=1520-14 1.520k ± 0% 1.520k ± 0% ~ (p=1.000 n=10) Âą PrecompiledModExpEip7883/mod_vul_pawel_3_exp_8-Gas=1008-14 1.008k ± 0% 1.008k ± 0% ~ (p=1.000 n=10) Âą geomean 2.342k 2.342k +0.00% Âą all samples are equal │ /tmp/old.txt │ /tmp/new.txt │ │ mgas/s │ mgas/s vs base │ PrecompiledModExp/eip_example1-Gas=13056-14 550.8 ± 4% 585.3 ± 5% +6.26% (p=0.004 n=10) PrecompiledModExp/eip_example2-Gas=13056-14 23.06k ± 11% 48.91k ± 3% +112.09% (p=0.000 n=10) PrecompiledModExp/nagydani-1-square-Gas=204-14 158.7 ± 3% 204.8 ± 3% +29.08% (p=0.000 n=10) PrecompiledModExp/nagydani-1-qube-Gas=204-14 116.3 ± 23% 144.6 ± 14% +24.33% (p=0.000 n=10) PrecompiledModExp/nagydani-1-pow0x10001-Gas=3276-14 368.3 ± 16% 491.5 ± 2% +33.45% (p=0.000 n=10) PrecompiledModExp/nagydani-2-square-Gas=665-14 315.7 ± 7% 452.9 ± 10% +43.47% (p=0.000 n=10) PrecompiledModExp/nagydani-2-qube-Gas=665-14 211.6 ± 3% 290.6 ± 7% +37.28% (p=0.000 n=10) PrecompiledModExp/nagydani-2-pow0x10001-Gas=10649-14 721.6 ± 3% 783.8 ± 4% +8.62% (p=0.000 n=10) PrecompiledModExp/nagydani-3-square-Gas=1894-14 475.4 ± 3% 590.0 ± 3% +24.10% (p=0.000 n=10) PrecompiledModExp/nagydani-3-qube-Gas=1894-14 250.1 ± 11% 307.8 ± 3% +23.05% (p=0.000 n=10) PrecompiledModExp/nagydani-3-pow0x10001-Gas=30310-14 776.1 ± 6% 802.7 ± 5% ~ (p=0.123 n=10) PrecompiledModExp/nagydani-4-square-Gas=5580-14 580.5 ± 2% 605.1 ± 2% +4.25% (p=0.003 n=10) PrecompiledModExp/nagydani-4-qube-Gas=5580-14 257.6 ± 5% 259.8 ± 15% ~ (p=0.971 n=10) PrecompiledModExp/nagydani-4-pow0x10001-Gas=89292-14 739.7 ± 9% 770.0 ± 3% +4.09% (p=0.043 n=10) PrecompiledModExp/nagydani-5-square-Gas=17868-14 746.6 ± 3% 719.8 ± 3% -3.60% (p=0.000 n=10) PrecompiledModExp/nagydani-5-qube-Gas=17868-14 325.0 ± 1% 307.6 ± 4% -5.35% (p=0.000 n=10) PrecompiledModExp/nagydani-5-pow0x10001-Gas=285900-14 840.1 ± 2% 838.0 ± 3% ~ (p=0.529 n=10) PrecompiledModExpEip2565/eip_example1-Gas=1360-14 61.84 ± 3% 64.03 ± 2% +3.56% (p=0.000 n=10) PrecompiledModExpEip2565/eip_example2-Gas=1360-14 2.394k ± 11% 6.150k ± 2% +156.89% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-square-Gas=200-14 157.2 ± 2% 243.2 ± 11% +54.66% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-qube-Gas=200-14 122.4 ± 3% 143.2 ± 17% +16.90% (p=0.023 n=10) PrecompiledModExpEip2565/nagydani-1-pow0x10001-Gas=341-14 46.86 ± 3% 50.87 ± 4% +8.55% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-square-Gas=200-14 102.1 ± 3% 123.7 ± 2% +21.17% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-qube-Gas=200-14 67.59 ± 5% 76.60 ± 5% +13.34% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-pow0x10001-Gas=1365-14 91.06 ± 5% 99.16 ± 4% +8.90% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-square-Gas=341-14 88.09 ± 4% 96.86 ± 2% +9.96% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-qube-Gas=341-14 49.62 ± 3% 52.84 ± 3% +6.48% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-pow0x10001-Gas=5461-14 136.0 ± 6% 153.6 ± 3% +12.94% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-square-Gas=1365-14 140.8 ± 3% 161.9 ± 3% +15.02% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-qube-Gas=1365-14 63.62 ± 2% 70.05 ± 6% +10.12% (p=0.001 n=10) PrecompiledModExpEip2565/nagydani-4-pow0x10001-Gas=21845-14 184.9 ± 2% 192.2 ± 2% +3.92% (p=0.003 n=10) PrecompiledModExpEip2565/nagydani-5-square-Gas=5461-14 211.7 ± 5% 227.0 ± 3% +7.25% (p=0.001 n=10) PrecompiledModExpEip2565/nagydani-5-qube-Gas=5461-14 95.34 ± 9% 97.93 ± 6% ~ (p=0.796 n=10) PrecompiledModExpEip2565/nagydani-5-pow0x10001-Gas=87381-14 260.7 ± 2% 252.4 ± 3% -3.18% (p=0.000 n=10) PrecompiledModExpEip2565/marius-1-even-Gas=2057-14 36.57 ± 3% 37.03 ± 1% ~ (p=0.280 n=10) PrecompiledModExpEip2565/guido-1-even-Gas=2298-14 59.31 ± 3% 59.48 ± 5% ~ (p=0.631 n=10) PrecompiledModExpEip2565/guido-2-even-Gas=2300-14 37.13 ± 2% 37.47 ± 10% ~ (p=0.529 n=10) PrecompiledModExpEip2565/guido-3-even-Gas=5400-14 266.4 ± 2% 267.5 ± 5% ~ (p=0.838 n=10) PrecompiledModExpEip2565/guido-4-even-Gas=1026-14 1.274k ± 3% 2.857k ± 3% +124.34% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-base-heavy-Gas=200-14 42.39 ± 5% 50.95 ± 5% +20.18% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-exp-heavy-Gas=215-14 13.86 ± 3% 14.55 ± 2% +5.02% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-balanced-Gas=200-14 19.19 ± 2% 19.98 ± 5% +4.09% (p=0.011 n=10) PrecompiledModExpEip2565/marcin-2-base-heavy-Gas=867-14 54.23 ± 4% 55.39 ± 4% ~ (p=0.197 n=10) PrecompiledModExpEip2565/marcin-2-exp-heavy-Gas=852-14 34.84 ± 2% 36.47 ± 2% +4.68% (p=0.002 n=10) PrecompiledModExpEip2565/marcin-2-balanced-Gas=996-14 28.79 ± 23% 30.27 ± 1% +5.16% (p=0.001 n=10) PrecompiledModExpEip2565/marcin-3-base-heavy-Gas=677-14 54.37 ± 2% 56.42 ± 14% ~ (p=0.093 n=10) PrecompiledModExpEip2565/marcin-3-exp-heavy-Gas=765-14 47.19 ± 3% 49.11 ± 6% ~ (p=0.063 n=10) PrecompiledModExpEip2565/marcin-3-balanced-Gas=1360-14 59.05 ± 3% 63.73 ± 2% +7.94% (p=0.000 n=10) PrecompiledModExpEip2565/mod-8-exp-648-Gas=215-14 12.32 ± 3% 13.41 ± 1% +8.81% (p=0.000 n=10) PrecompiledModExpEip2565/mod-8-exp-896-Gas=298-14 14.19 ± 5% 15.26 ± 2% +7.58% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-32-Gas=200-14 20.94 ± 2% 21.45 ± 2% +2.41% (p=0.005 n=10) PrecompiledModExpEip2565/mod-32-exp-36-Gas=200-14 18.61 ± 1% 19.40 ± 2% +4.25% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-40-Gas=208-14 17.40 ± 2% 18.19 ± 2% +4.54% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-64-Gas=336-14 17.90 ± 3% 18.55 ± 2% +3.66% (p=0.002 n=10) PrecompiledModExpEip2565/mod-32-exp-65-Gas=341-14 25.10 ± 1% 25.30 ± 1% ~ (p=0.306 n=10) PrecompiledModExpEip2565/mod-32-exp-128-Gas=677-14 49.74 ± 3% 50.66 ± 1% +1.86% (p=0.000 n=10) PrecompiledModExpEip2565/mod-256-exp-2-Gas=341-14 48.82 ± 2% 56.33 ± 3% +15.38% (p=0.000 n=10) PrecompiledModExpEip2565/mod-264-exp-2-Gas=363-14 50.94 ± 5% 57.22 ± 6% +12.33% (p=0.000 n=10) PrecompiledModExpEip2565/mod-1024-exp-2-Gas=5461-14 104.2 ± 18% 109.5 ± 4% ~ (p=0.085 n=10) PrecompiledModExpEip2565/pawel-1-exp-heavy-Gas=298-14 14.23 ± 24% 14.78 ± 2% +3.83% (p=0.001 n=10) PrecompiledModExpEip2565/pawel-2-exp-heavy-Gas=425-14 22.01 ± 20% 29.39 ± 4% +33.55% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-3-exp-heavy-Gas=501-14 24.78 ± 17% 35.03 ± 3% +41.38% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-4-exp-heavy-Gas=506-14 35.30 ± 3% 36.31 ± 2% +2.88% (p=0.001 n=10) PrecompiledModExpEip2565/mod_vul_pawel_3_exp_8-Gas=200-14 8.250 ± 14% 11.805 ± 15% +43.09% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-square-Gas=500-14 338.4 ± 5% 574.2 ± 9% +69.72% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-qube-Gas=500-14 238.3 ± 7% 442.1 ± 2% +85.54% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-pow0x10001-Gas=2048-14 229.6 ± 4% 322.2 ± 2% +40.32% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-square-Gas=512-14 229.9 ± 4% 366.8 ± 2% +59.55% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-qube-Gas=512-14 146.1 ± 7% 211.8 ± 11% +44.98% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-pow0x10001-Gas=8192-14 467.2 ± 4% 578.3 ± 5% +23.77% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-square-Gas=2048-14 426.6 ± 11% 585.5 ± 3% +37.26% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-qube-Gas=2048-14 247.2 ± 4% 313.4 ± 2% +26.76% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-pow0x10001-Gas=32768-14 731.1 ± 5% 885.5 ± 19% +21.11% (p=0.007 n=10) PrecompiledModExpEip7883/nagydani-4-square-Gas=8192-14 697.7 ± 4% 910.3 ± 4% +30.47% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-qube-Gas=8192-14 298.0 ± 9% 383.8 ± 5% +28.75% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-pow0x10001-Gas=131072-14 926.6 ± 10% 1155.5 ± 3% +24.70% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-square-Gas=32768-14 1.042k ± 5% 1.393k ± 1% +33.64% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-qube-Gas=32768-14 419.5 ± 16% 609.7 ± 4% +45.33% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-pow0x10001-Gas=524288-14 1.175k ± 5% 1.549k ± 2% +31.89% (p=0.000 n=10) PrecompiledModExpEip7883/marius-1-even-Gas=45296-14 635.8 ± 4% 818.6 ± 1% +28.76% (p=0.000 n=10) PrecompiledModExpEip7883/guido-1-even-Gas=51136-14 1.002k ± 13% 1.339k ± 3% +33.66% (p=0.000 n=10) PrecompiledModExpEip7883/guido-2-even-Gas=51152-14 594.0 ± 17% 843.3 ± 2% +41.97% (p=0.000 n=10) PrecompiledModExpEip7883/guido-3-even-Gas=32400-14 1.177k ± 24% 1.591k ± 2% +35.17% (p=0.000 n=10) PrecompiledModExpEip7883/guido-4-even-Gas=94448-14 99.37k ± 5% 266.13k ± 22% +167.81% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-base-heavy-Gas=1152-14 205.0 ± 7% 259.5 ± 5% +26.56% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-exp-heavy-Gas=16624-14 873.8 ± 16% 1061.0 ± 18% +21.43% (p=0.003 n=10) PrecompiledModExpEip7883/marcin-1-balanced-Gas=1200-14 86.77 ± 16% 110.00 ± 9% +26.77% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-base-heavy-Gas=5202-14 219.6 ± 8% 293.5 ± 12% +33.67% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-exp-heavy-Gas=16368-14 421.2 ± 14% 637.2 ± 13% +51.28% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-balanced-Gas=5978-14 116.2 ± 22% 177.6 ± 13% +52.86% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-base-heavy-Gas=2032-14 121.8 ± 14% 168.1 ± 2% +38.11% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-exp-heavy-Gas=4080-14 206.6 ± 5% 253.0 ± 10% +22.49% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-balanced-Gas=4080-14 142.4 ± 8% 181.6 ± 4% +27.57% (p=0.000 n=10) PrecompiledModExpEip7883/mod-8-exp-648-Gas=16624-14 799.9 ± 12% 1014.5 ± 2% +26.84% (p=0.000 n=10) PrecompiledModExpEip7883/mod-8-exp-896-Gas=24560-14 1.009k ± 9% 1.252k ± 1% +24.05% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-32-Gas=500-14 48.48 ± 4% 53.80 ± 3% +10.96% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-36-Gas=560-14 47.91 ± 15% 53.99 ± 3% +12.70% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-40-Gas=624-14 44.05 ± 19% 53.80 ± 6% +22.12% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-64-Gas=1008-14 45.75 ± 8% 55.31 ± 2% +20.90% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-65-Gas=1024-14 62.24 ± 12% 76.77 ± 1% +23.34% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-128-Gas=2032-14 119.3 ± 8% 151.1 ± 1% +26.66% (p=0.000 n=10) PrecompiledModExpEip7883/mod-256-exp-2-Gas=2048-14 252.2 ± 12% 343.6 ± 3% +36.25% (p=0.000 n=10) PrecompiledModExpEip7883/mod-264-exp-2-Gas=2178-14 267.1 ± 22% 320.4 ± 5% +19.91% (p=0.000 n=10) PrecompiledModExpEip7883/mod-1024-exp-2-Gas=32768-14 525.9 ± 3% 657.7 ± 1% +25.07% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-1-exp-heavy-Gas=24560-14 913.0 ± 7% 1210.0 ± 1% +32.52% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-2-exp-heavy-Gas=6128-14 346.8 ± 2% 392.7 ± 10% +13.24% (p=0.001 n=10) PrecompiledModExpEip7883/pawel-3-exp-heavy-Gas=2672-14 152.7 ± 3% 179.7 ± 8% +17.65% (p=0.002 n=10) PrecompiledModExpEip7883/pawel-4-exp-heavy-Gas=1520-14 87.73 ± 2% 107.55 ± 4% +22.59% (p=0.000 n=10) PrecompiledModExpEip7883/mod_vul_pawel_3_exp_8-Gas=1008-14 46.50 ± 20% 59.03 ± 3% +26.97% (p=0.000 n=10) geomean 159.9 194.8 +21.79% │ /tmp/old.txt │ /tmp/new.txt │ │ B/op │ B/op vs base │ PrecompiledModExp/eip_example1-Gas=13056-14 2.595Ki ± 0% 2.282Ki ± 0% -12.04% (p=0.000 n=10) PrecompiledModExp/eip_example2-Gas=13056-14 672.0 ± 0% 352.0 ± 0% -47.62% (p=0.000 n=10) PrecompiledModExp/nagydani-1-square-Gas=204-14 1160.0 ± 0% 888.0 ± 0% -23.45% (p=0.000 n=10) PrecompiledModExp/nagydani-1-qube-Gas=204-14 1.414Ki ± 0% 1.148Ki ± 0% -18.78% (p=0.000 n=10) PrecompiledModExp/nagydani-1-pow0x10001-Gas=3276-14 1.898Ki ± 0% 1.633Ki ± 0% -13.99% (p=0.000 n=10) PrecompiledModExp/nagydani-2-square-Gas=665-14 1.789Ki ± 0% 1.383Ki ± 0% -22.71% (p=0.000 n=10) PrecompiledModExp/nagydani-2-qube-Gas=665-14 2.321Ki ± 0% 1.915Ki ± 0% -17.50% (p=0.000 n=10) PrecompiledModExp/nagydani-2-pow0x10001-Gas=10649-14 3.196Ki ± 0% 2.790Ki ± 0% -12.71% (p=0.000 n=10) PrecompiledModExp/nagydani-3-square-Gas=1894-14 2.821Ki ± 0% 2.415Ki ± 0% -14.40% (p=0.000 n=10) PrecompiledModExp/nagydani-3-qube-Gas=1894-14 4.010Ki ± 0% 3.604Ki ± 0% -10.13% (p=0.000 n=10) PrecompiledModExp/nagydani-3-pow0x10001-Gas=30310-14 5.698Ki ± 0% 5.292Ki ± 0% -7.13% (p=0.000 n=10) PrecompiledModExp/nagydani-4-square-Gas=5580-14 5.042Ki ± 0% 4.636Ki ± 0% -8.06% (p=0.000 n=10) PrecompiledModExp/nagydani-4-qube-Gas=5580-14 11.93Ki ± 0% 11.52Ki ± 0% -3.42% (p=0.000 n=10) PrecompiledModExp/nagydani-4-pow0x10001-Gas=89292-14 15.30Ki ± 0% 14.89Ki ± 0% -2.66% (p=0.000 n=10) PrecompiledModExp/nagydani-5-square-Gas=17868-14 9.616Ki ± 0% 9.208Ki ± 0% -4.24% (p=0.000 n=10) PrecompiledModExp/nagydani-5-qube-Gas=17868-14 23.26Ki ± 0% 22.85Ki ± 0% -1.77% (p=0.000 n=10) PrecompiledModExp/nagydani-5-pow0x10001-Gas=285900-14 31.90Ki ± 0% 31.49Ki ± 0% -1.29% (p=0.000 n=10) PrecompiledModExpEip2565/eip_example1-Gas=1360-14 2.595Ki ± 0% 2.282Ki ± 0% -12.04% (p=0.000 n=10) PrecompiledModExpEip2565/eip_example2-Gas=1360-14 672.0 ± 0% 352.0 ± 0% -47.62% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-square-Gas=200-14 1160.0 ± 0% 888.0 ± 0% -23.45% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-qube-Gas=200-14 1.414Ki ± 0% 1.148Ki ± 0% -18.78% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-pow0x10001-Gas=341-14 1.898Ki ± 0% 1.633Ki ± 0% -13.99% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-square-Gas=200-14 1.648Ki ± 0% 1.383Ki ± 0% -16.11% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-qube-Gas=200-14 2.181Ki ± 0% 1.914Ki ± 0% -12.23% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-pow0x10001-Gas=1365-14 3.056Ki ± 0% 2.790Ki ± 0% -8.69% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-square-Gas=341-14 2.681Ki ± 0% 2.415Ki ± 0% -9.91% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-qube-Gas=341-14 3.869Ki ± 0% 3.604Ki ± 0% -6.87% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-pow0x10001-Gas=5461-14 5.558Ki ± 0% 5.292Ki ± 0% -4.78% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-square-Gas=1365-14 4.901Ki ± 0% 4.636Ki ± 0% -5.42% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-qube-Gas=1365-14 11.78Ki ± 0% 11.52Ki ± 0% -2.25% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-pow0x10001-Gas=21845-14 15.16Ki ± 0% 14.89Ki ± 0% -1.76% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-5-square-Gas=5461-14 9.475Ki ± 0% 9.209Ki ± 0% -2.80% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-5-qube-Gas=5461-14 23.12Ki ± 0% 22.85Ki ± 0% -1.15% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-5-pow0x10001-Gas=87381-14 31.75Ki ± 0% 31.49Ki ± 0% -0.83% (p=0.000 n=10) PrecompiledModExpEip2565/marius-1-even-Gas=2057-14 2.501Ki ± 0% 2.134Ki ± 0% -14.68% (p=0.000 n=10) PrecompiledModExpEip2565/guido-1-even-Gas=2298-14 2.470Ki ± 0% 2.103Ki ± 0% -14.87% (p=0.000 n=10) PrecompiledModExpEip2565/guido-2-even-Gas=2300-14 2.587Ki ± 0% 2.228Ki ± 0% -13.89% (p=0.000 n=10) PrecompiledModExpEip2565/guido-3-even-Gas=5400-14 8.271Ki ± 0% 8.014Ki ± 0% -3.12% (p=0.000 n=10) PrecompiledModExpEip2565/guido-4-even-Gas=1026-14 1056.0 ± 0% 680.0 ± 0% -35.61% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-base-heavy-Gas=200-14 2.954Ki ± 0% 2.696Ki ± 0% -8.73% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-exp-heavy-Gas=215-14 1.641Ki ± 0% 1.273Ki ± 0% -22.38% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-balanced-Gas=200-14 1.266Ki ± 0% 1.008Ki ± 0% -20.37% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-2-base-heavy-Gas=867-14 9.555Ki ± 0% 9.296Ki ± 0% -2.71% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-2-exp-heavy-Gas=852-14 2.047Ki ± 0% 1.688Ki ± 0% -17.56% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-2-balanced-Gas=996-14 1.993Ki ± 0% 1.727Ki ± 0% -13.38% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-3-base-heavy-Gas=677-14 2.587Ki ± 0% 2.282Ki ± 0% -11.78% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-3-exp-heavy-Gas=765-14 2.313Ki ± 0% 2.000Ki ± 0% -13.55% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-3-balanced-Gas=1360-14 2.610Ki ± 0% 2.298Ki ± 0% -11.97% (p=0.000 n=10) PrecompiledModExpEip2565/mod-8-exp-648-Gas=215-14 1.860Ki ± 0% 1.492Ki ± 0% -19.79% (p=0.000 n=10) PrecompiledModExpEip2565/mod-8-exp-896-Gas=298-14 1.876Ki ± 0% 1.508Ki ± 0% -19.60% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-32-Gas=200-14 1192.0 ± 0% 920.0 ± 0% -22.82% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-36-Gas=200-14 1.305Ki ± 0% 1.039Ki ± 0% -20.36% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-40-Gas=208-14 1184.0 ± 0% 920.0 ± 0% -22.30% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-64-Gas=336-14 1184.0 ± 0% 920.0 ± 0% -22.30% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-65-Gas=341-14 3.071Ki ± 0% 2.767Ki ± 0% -9.92% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-128-Gas=677-14 3.071Ki ± 0% 2.767Ki ± 0% -9.92% (p=0.000 n=10) PrecompiledModExpEip2565/mod-256-exp-2-Gas=341-14 3.869Ki ± 0% 3.604Ki ± 0% -6.87% (p=0.000 n=10) PrecompiledModExpEip2565/mod-264-exp-2-Gas=363-14 3.986Ki ± 0% 3.729Ki ± 0% -6.47% (p=0.000 n=10) PrecompiledModExpEip2565/mod-1024-exp-2-Gas=5461-14 24.12Ki ± 0% 23.85Ki ± 0% -1.10% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-1-exp-heavy-Gas=298-14 1.931Ki ± 0% 1.563Ki ± 0% -19.02% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-2-exp-heavy-Gas=425-14 2.438Ki ± 0% 2.071Ki ± 0% -15.06% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-3-exp-heavy-Gas=501-14 2.798Ki ± 0% 2.485Ki ± 0% -11.17% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-4-exp-heavy-Gas=506-14 3.080Ki ± 0% 2.774Ki ± 0% -9.92% (p=0.000 n=10) PrecompiledModExpEip2565/mod_vul_pawel_3_exp_8-Gas=200-14 1064.0 ± 0% 800.0 ± 0% -24.81% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-square-Gas=500-14 1152.0 ± 0% 888.0 ± 0% -22.92% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-qube-Gas=500-14 1.406Ki ± 0% 1.148Ki ± 0% -18.33% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-pow0x10001-Gas=2048-14 1.891Ki ± 0% 1.633Ki ± 0% -13.64% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-square-Gas=512-14 1.641Ki ± 0% 1.383Ki ± 0% -15.71% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-qube-Gas=512-14 2.173Ki ± 0% 1.915Ki ± 0% -11.87% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-pow0x10001-Gas=8192-14 3.048Ki ± 0% 2.790Ki ± 0% -8.46% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-square-Gas=2048-14 2.673Ki ± 0% 2.415Ki ± 0% -9.65% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-qube-Gas=2048-14 3.861Ki ± 0% 3.604Ki ± 0% -6.68% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-pow0x10001-Gas=32768-14 5.550Ki ± 0% 5.292Ki ± 0% -4.65% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-square-Gas=8192-14 4.894Ki ± 0% 4.636Ki ± 0% -5.27% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-qube-Gas=8192-14 11.78Ki ± 0% 11.52Ki ± 0% -2.20% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-pow0x10001-Gas=131072-14 15.15Ki ± 0% 14.89Ki ± 0% -1.71% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-square-Gas=32768-14 9.468Ki ± 0% 9.208Ki ± 0% -2.74% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-qube-Gas=32768-14 23.11Ki ± 0% 22.85Ki ± 0% -1.13% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-pow0x10001-Gas=524288-14 31.75Ki ± 0% 31.49Ki ± 0% -0.81% (p=0.000 n=10) PrecompiledModExpEip7883/marius-1-even-Gas=45296-14 2.548Ki ± 0% 2.134Ki ± 0% -16.25% (p=0.000 n=10) PrecompiledModExpEip7883/guido-1-even-Gas=51136-14 2.517Ki ± 0% 2.103Ki ± 0% -16.45% (p=0.000 n=10) PrecompiledModExpEip7883/guido-2-even-Gas=51152-14 2.642Ki ± 0% 2.228Ki ± 0% -15.67% (p=0.000 n=10) PrecompiledModExpEip7883/guido-3-even-Gas=32400-14 8.271Ki ± 0% 8.014Ki ± 0% -3.12% (p=0.000 n=10) PrecompiledModExpEip7883/guido-4-even-Gas=94448-14 1104.0 ± 0% 680.0 ± 0% -38.41% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-base-heavy-Gas=1152-14 2.954Ki ± 0% 2.696Ki ± 0% -8.73% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-exp-heavy-Gas=16624-14 1.688Ki ± 0% 1.273Ki ± 0% -24.54% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-balanced-Gas=1200-14 1.266Ki ± 0% 1.008Ki ± 0% -20.37% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-base-heavy-Gas=5202-14 9.554Ki ± 0% 9.296Ki ± 0% -2.70% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-exp-heavy-Gas=16368-14 2.102Ki ± 0% 1.688Ki ± 0% -19.72% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-balanced-Gas=5978-14 1.985Ki ± 0% 1.727Ki ± 0% -13.03% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-base-heavy-Gas=2032-14 2.634Ki ± 0% 2.282Ki ± 0% -13.35% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-exp-heavy-Gas=4080-14 2.368Ki ± 0% 2.000Ki ± 0% -15.55% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-balanced-Gas=4080-14 2.665Ki ± 0% 2.298Ki ± 0% -13.78% (p=0.000 n=10) PrecompiledModExpEip7883/mod-8-exp-648-Gas=16624-14 1.907Ki ± 0% 1.492Ki ± 0% -21.76% (p=0.000 n=10) PrecompiledModExpEip7883/mod-8-exp-896-Gas=24560-14 1.923Ki ± 0% 1.508Ki ± 0% -21.58% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-32-Gas=500-14 1240.0 ± 0% 920.0 ± 0% -25.81% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-36-Gas=560-14 1.352Ki ± 0% 1.039Ki ± 0% -23.12% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-40-Gas=624-14 1240.0 ± 0% 920.0 ± 0% -25.81% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-64-Gas=1008-14 1240.0 ± 0% 920.0 ± 0% -25.81% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-65-Gas=1024-14 3.118Ki ± 0% 2.767Ki ± 0% -11.27% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-128-Gas=2032-14 3.118Ki ± 0% 2.767Ki ± 0% -11.27% (p=0.000 n=10) PrecompiledModExpEip7883/mod-256-exp-2-Gas=2048-14 3.861Ki ± 0% 3.604Ki ± 0% -6.68% (p=0.000 n=10) PrecompiledModExpEip7883/mod-264-exp-2-Gas=2178-14 3.986Ki ± 0% 3.729Ki ± 0% -6.47% (p=0.000 n=10) PrecompiledModExpEip7883/mod-1024-exp-2-Gas=32768-14 24.12Ki ± 0% 23.85Ki ± 0% -1.09% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-1-exp-heavy-Gas=24560-14 1.978Ki ± 0% 1.562Ki ± 0% -20.99% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-2-exp-heavy-Gas=6128-14 2.485Ki ± 0% 2.071Ki ± 0% -16.66% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-3-exp-heavy-Gas=2672-14 2.853Ki ± 0% 2.485Ki ± 0% -12.87% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-4-exp-heavy-Gas=1520-14 3.127Ki ± 0% 2.774Ki ± 0% -11.27% (p=0.000 n=10) PrecompiledModExpEip7883/mod_vul_pawel_3_exp_8-Gas=1008-14 1120.0 ± 0% 800.0 ± 0% -28.57% (p=0.000 n=10) geomean 3.164Ki 2.718Ki -14.08% │ /tmp/old.txt │ /tmp/new.txt │ │ allocs/op │ allocs/op vs base │ PrecompiledModExp/eip_example1-Gas=13056-14 37.00 ± 0% 30.00 ± 0% -18.92% (p=0.000 n=10) PrecompiledModExp/eip_example2-Gas=13056-14 13.000 ± 0% 6.000 ± 0% -53.85% (p=0.000 n=10) PrecompiledModExp/nagydani-1-square-Gas=204-14 18.00 ± 0% 10.00 ± 0% -44.44% (p=0.000 n=10) PrecompiledModExp/nagydani-1-qube-Gas=204-14 19.00 ± 0% 11.00 ± 0% -42.11% (p=0.000 n=10) PrecompiledModExp/nagydani-1-pow0x10001-Gas=3276-14 22.00 ± 0% 14.00 ± 0% -36.36% (p=0.000 n=10) PrecompiledModExp/nagydani-2-square-Gas=665-14 23.00 ± 0% 10.00 ± 0% -56.52% (p=0.000 n=10) PrecompiledModExp/nagydani-2-qube-Gas=665-14 24.00 ± 0% 11.00 ± 0% -54.17% (p=0.000 n=10) PrecompiledModExp/nagydani-2-pow0x10001-Gas=10649-14 27.00 ± 0% 14.00 ± 0% -48.15% (p=0.000 n=10) PrecompiledModExp/nagydani-3-square-Gas=1894-14 23.00 ± 0% 10.00 ± 0% -56.52% (p=0.000 n=10) PrecompiledModExp/nagydani-3-qube-Gas=1894-14 24.00 ± 0% 11.00 ± 0% -54.17% (p=0.000 n=10) PrecompiledModExp/nagydani-3-pow0x10001-Gas=30310-14 27.00 ± 0% 14.00 ± 0% -48.15% (p=0.000 n=10) PrecompiledModExp/nagydani-4-square-Gas=5580-14 23.00 ± 0% 10.00 ± 0% -56.52% (p=0.000 n=10) PrecompiledModExp/nagydani-4-qube-Gas=5580-14 25.00 ± 0% 12.00 ± 0% -52.00% (p=0.000 n=10) PrecompiledModExp/nagydani-4-pow0x10001-Gas=89292-14 28.00 ± 0% 15.00 ± 0% -46.43% (p=0.000 n=10) PrecompiledModExp/nagydani-5-square-Gas=17868-14 24.00 ± 0% 11.00 ± 0% -54.17% (p=0.000 n=10) PrecompiledModExp/nagydani-5-qube-Gas=17868-14 26.00 ± 0% 13.00 ± 0% -50.00% (p=0.000 n=10) PrecompiledModExp/nagydani-5-pow0x10001-Gas=285900-14 44.00 ± 0% 31.00 ± 0% -29.55% (p=0.000 n=10) PrecompiledModExpEip2565/eip_example1-Gas=1360-14 37.00 ± 0% 30.00 ± 0% -18.92% (p=0.000 n=10) PrecompiledModExpEip2565/eip_example2-Gas=1360-14 13.000 ± 0% 6.000 ± 0% -53.85% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-square-Gas=200-14 18.00 ± 0% 10.00 ± 0% -44.44% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-qube-Gas=200-14 19.00 ± 0% 11.00 ± 0% -42.11% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-1-pow0x10001-Gas=341-14 22.00 ± 0% 14.00 ± 0% -36.36% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-square-Gas=200-14 18.00 ± 0% 10.00 ± 0% -44.44% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-qube-Gas=200-14 19.00 ± 0% 11.00 ± 0% -42.11% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-2-pow0x10001-Gas=1365-14 22.00 ± 0% 14.00 ± 0% -36.36% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-square-Gas=341-14 18.00 ± 0% 10.00 ± 0% -44.44% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-qube-Gas=341-14 19.00 ± 0% 11.00 ± 0% -42.11% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-3-pow0x10001-Gas=5461-14 22.00 ± 0% 14.00 ± 0% -36.36% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-square-Gas=1365-14 18.00 ± 0% 10.00 ± 0% -44.44% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-qube-Gas=1365-14 20.00 ± 0% 12.00 ± 0% -40.00% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-4-pow0x10001-Gas=21845-14 23.00 ± 0% 15.00 ± 0% -34.78% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-5-square-Gas=5461-14 19.00 ± 0% 11.00 ± 0% -42.11% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-5-qube-Gas=5461-14 21.00 ± 0% 13.00 ± 0% -38.10% (p=0.000 n=10) PrecompiledModExpEip2565/nagydani-5-pow0x10001-Gas=87381-14 39.00 ± 0% 31.00 ± 0% -20.51% (p=0.000 n=10) PrecompiledModExpEip2565/marius-1-even-Gas=2057-14 52.00 ± 0% 43.00 ± 0% -17.31% (p=0.000 n=10) PrecompiledModExpEip2565/guido-1-even-Gas=2298-14 55.00 ± 0% 46.00 ± 0% -16.36% (p=0.000 n=10) PrecompiledModExpEip2565/guido-2-even-Gas=2300-14 52.00 ± 0% 44.00 ± 0% -15.38% (p=0.000 n=10) PrecompiledModExpEip2565/guido-3-even-Gas=5400-14 19.00 ± 0% 12.00 ± 0% -36.84% (p=0.000 n=10) PrecompiledModExpEip2565/guido-4-even-Gas=1026-14 18.000 ± 0% 9.000 ± 0% -50.00% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-base-heavy-Gas=200-14 18.00 ± 0% 11.00 ± 0% -38.89% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-exp-heavy-Gas=215-14 37.00 ± 0% 28.00 ± 0% -24.32% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-1-balanced-Gas=200-14 19.00 ± 0% 12.00 ± 0% -36.84% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-2-base-heavy-Gas=867-14 19.00 ± 0% 12.00 ± 0% -36.84% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-2-exp-heavy-Gas=852-14 36.00 ± 0% 28.00 ± 0% -22.22% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-2-balanced-Gas=996-14 23.00 ± 0% 15.00 ± 0% -34.78% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-3-base-heavy-Gas=677-14 36.00 ± 0% 28.00 ± 0% -22.22% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-3-exp-heavy-Gas=765-14 35.00 ± 0% 28.00 ± 0% -20.00% (p=0.000 n=10) PrecompiledModExpEip2565/marcin-3-balanced-Gas=1360-14 35.00 ± 0% 28.00 ± 0% -20.00% (p=0.000 n=10) PrecompiledModExpEip2565/mod-8-exp-648-Gas=215-14 50.00 ± 0% 41.00 ± 0% -18.00% (p=0.000 n=10) PrecompiledModExpEip2565/mod-8-exp-896-Gas=298-14 50.00 ± 0% 41.00 ± 0% -18.00% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-32-Gas=200-14 21.00 ± 0% 13.00 ± 0% -38.10% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-36-Gas=200-14 22.00 ± 0% 14.00 ± 0% -36.36% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-40-Gas=208-14 20.00 ± 0% 13.00 ± 0% -35.00% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-64-Gas=336-14 20.00 ± 0% 13.00 ± 0% -35.00% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-65-Gas=341-14 52.00 ± 0% 44.00 ± 0% -15.38% (p=0.000 n=10) PrecompiledModExpEip2565/mod-32-exp-128-Gas=677-14 52.00 ± 0% 44.00 ± 0% -15.38% (p=0.000 n=10) PrecompiledModExpEip2565/mod-256-exp-2-Gas=341-14 19.00 ± 0% 11.00 ± 0% -42.11% (p=0.000 n=10) PrecompiledModExpEip2565/mod-264-exp-2-Gas=363-14 18.00 ± 0% 11.00 ± 0% -38.89% (p=0.000 n=10) PrecompiledModExpEip2565/mod-1024-exp-2-Gas=5461-14 22.00 ± 0% 14.00 ± 0% -36.36% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-1-exp-heavy-Gas=298-14 52.00 ± 0% 43.00 ± 0% -17.31% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-2-exp-heavy-Gas=425-14 54.00 ± 0% 45.00 ± 0% -16.67% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-3-exp-heavy-Gas=501-14 52.00 ± 0% 45.00 ± 0% -13.46% (p=0.000 n=10) PrecompiledModExpEip2565/pawel-4-exp-heavy-Gas=506-14 53.00 ± 0% 45.00 ± 0% -15.09% (p=0.000 n=10) PrecompiledModExpEip2565/mod_vul_pawel_3_exp_8-Gas=200-14 19.00 ± 0% 12.00 ± 0% -36.84% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-square-Gas=500-14 17.00 ± 0% 10.00 ± 0% -41.18% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-qube-Gas=500-14 18.00 ± 0% 11.00 ± 0% -38.89% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-1-pow0x10001-Gas=2048-14 21.00 ± 0% 14.00 ± 0% -33.33% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-square-Gas=512-14 17.00 ± 0% 10.00 ± 0% -41.18% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-qube-Gas=512-14 18.00 ± 0% 11.00 ± 0% -38.89% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-2-pow0x10001-Gas=8192-14 21.00 ± 0% 14.00 ± 0% -33.33% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-square-Gas=2048-14 17.00 ± 0% 10.00 ± 0% -41.18% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-qube-Gas=2048-14 18.00 ± 0% 11.00 ± 0% -38.89% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-3-pow0x10001-Gas=32768-14 21.00 ± 0% 14.00 ± 0% -33.33% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-square-Gas=8192-14 17.00 ± 0% 10.00 ± 0% -41.18% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-qube-Gas=8192-14 19.00 ± 0% 12.00 ± 0% -36.84% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-4-pow0x10001-Gas=131072-14 22.00 ± 0% 15.00 ± 0% -31.82% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-square-Gas=32768-14 18.00 ± 0% 11.00 ± 0% -38.89% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-qube-Gas=32768-14 20.00 ± 0% 13.00 ± 0% -35.00% (p=0.000 n=10) PrecompiledModExpEip7883/nagydani-5-pow0x10001-Gas=524288-14 38.00 ± 0% 31.00 ± 0% -18.42% (p=0.000 n=10) PrecompiledModExpEip7883/marius-1-even-Gas=45296-14 53.00 ± 0% 43.00 ± 0% -18.87% (p=0.000 n=10) PrecompiledModExpEip7883/guido-1-even-Gas=51136-14 56.00 ± 0% 46.00 ± 0% -17.86% (p=0.000 n=10) PrecompiledModExpEip7883/guido-2-even-Gas=51152-14 54.00 ± 0% 44.00 ± 0% -18.52% (p=0.000 n=10) PrecompiledModExpEip7883/guido-3-even-Gas=32400-14 19.00 ± 0% 12.00 ± 0% -36.84% (p=0.000 n=10) PrecompiledModExpEip7883/guido-4-even-Gas=94448-14 19.000 ± 0% 9.000 ± 0% -52.63% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-base-heavy-Gas=1152-14 18.00 ± 0% 11.00 ± 0% -38.89% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-exp-heavy-Gas=16624-14 38.00 ± 0% 28.00 ± 0% -26.32% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-1-balanced-Gas=1200-14 19.00 ± 0% 12.00 ± 0% -36.84% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-base-heavy-Gas=5202-14 19.00 ± 0% 12.00 ± 0% -36.84% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-exp-heavy-Gas=16368-14 38.00 ± 0% 28.00 ± 0% -26.32% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-2-balanced-Gas=5978-14 22.00 ± 0% 15.00 ± 0% -31.82% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-base-heavy-Gas=2032-14 37.00 ± 0% 28.00 ± 0% -24.32% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-exp-heavy-Gas=4080-14 37.00 ± 0% 28.00 ± 0% -24.32% (p=0.000 n=10) PrecompiledModExpEip7883/marcin-3-balanced-Gas=4080-14 37.00 ± 0% 28.00 ± 0% -24.32% (p=0.000 n=10) PrecompiledModExpEip7883/mod-8-exp-648-Gas=16624-14 51.00 ± 0% 41.00 ± 0% -19.61% (p=0.000 n=10) PrecompiledModExpEip7883/mod-8-exp-896-Gas=24560-14 51.00 ± 0% 41.00 ± 0% -19.61% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-32-Gas=500-14 22.00 ± 0% 13.00 ± 0% -40.91% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-36-Gas=560-14 23.00 ± 0% 14.00 ± 0% -39.13% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-40-Gas=624-14 22.00 ± 0% 13.00 ± 0% -40.91% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-64-Gas=1008-14 22.00 ± 0% 13.00 ± 0% -40.91% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-65-Gas=1024-14 53.00 ± 0% 44.00 ± 0% -16.98% (p=0.000 n=10) PrecompiledModExpEip7883/mod-32-exp-128-Gas=2032-14 53.00 ± 0% 44.00 ± 0% -16.98% (p=0.000 n=10) PrecompiledModExpEip7883/mod-256-exp-2-Gas=2048-14 18.00 ± 0% 11.00 ± 0% -38.89% (p=0.000 n=10) PrecompiledModExpEip7883/mod-264-exp-2-Gas=2178-14 18.00 ± 0% 11.00 ± 0% -38.89% (p=0.000 n=10) PrecompiledModExpEip7883/mod-1024-exp-2-Gas=32768-14 21.00 ± 0% 14.00 ± 0% -33.33% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-1-exp-heavy-Gas=24560-14 53.00 ± 0% 43.00 ± 0% -18.87% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-2-exp-heavy-Gas=6128-14 55.00 ± 0% 45.00 ± 0% -18.18% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-3-exp-heavy-Gas=2672-14 54.00 ± 0% 45.00 ± 0% -16.67% (p=0.000 n=10) PrecompiledModExpEip7883/pawel-4-exp-heavy-Gas=1520-14 54.00 ± 0% 45.00 ± 0% -16.67% (p=0.000 n=10) PrecompiledModExpEip7883/mod_vul_pawel_3_exp_8-Gas=1008-14 21.00 ± 0% 12.00 ± 0% -42.86% (p=0.000 n=10) geomean 26.59 17.27 -35.03% ``` Co-authored-by: @kevaundray Co-authored-by: Felix Lange --- core/vm/contracts.go | 303 +++++++++++------- .../testdata/precompiles/modexp_eip2565.json | 210 ++++++++++++ .../testdata/precompiles/modexp_eip7883.json | 210 ++++++++++++ 3 files changed, 613 insertions(+), 110 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index da099edb57..a06b0d09ca 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -24,6 +24,7 @@ import ( "maps" "math" "math/big" + "math/bits" "github.com/consensys/gnark-crypto/ecc" bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" @@ -38,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/secp256r1" "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" "golang.org/x/crypto/ripemd160" ) @@ -378,21 +380,7 @@ type bigModExp struct { eip7883 bool } -var ( - big1 = big.NewInt(1) - big3 = big.NewInt(3) - big7 = big.NewInt(7) - big20 = big.NewInt(20) - big32 = big.NewInt(32) - big64 = big.NewInt(64) - big96 = big.NewInt(96) - big480 = big.NewInt(480) - big1024 = big.NewInt(1024) - big3072 = big.NewInt(3072) - big199680 = big.NewInt(199680) -) - -// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198 +// byzantiumMultComplexity implements the bigModexp multComplexity formula, as defined in EIP-198. // // def mult_complexity(x): // if x <= 64: return x ** 2 @@ -400,120 +388,215 @@ var ( // else: return x ** 2 // 16 + 480 * x - 199680 // // where is x is max(length_of_MODULUS, length_of_BASE) -func modexpMultComplexity(x *big.Int) *big.Int { +// returns MaxUint64 if an overflow occurred. +func byzantiumMultComplexity(x uint64) uint64 { switch { - case x.Cmp(big64) <= 0: - x.Mul(x, x) // x ** 2 - case x.Cmp(big1024) <= 0: - // (x ** 2 // 4 ) + ( 96 * x - 3072) - x = new(big.Int).Add( - new(big.Int).Rsh(new(big.Int).Mul(x, x), 2), - new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072), - ) + case x <= 64: + return x * x + case x <= 1024: + // x^2 / 4 + 96*x - 3072 + return x*x/4 + 96*x - 3072 + default: - // (x ** 2 // 16) + (480 * x - 199680) - x = new(big.Int).Add( - new(big.Int).Rsh(new(big.Int).Mul(x, x), 4), - new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680), - ) + // For large x, use uint256 arithmetic to avoid overflow + // x^2 / 16 + 480*x - 199680 + + // xSqr = x^2 / 16 + carry, xSqr := bits.Mul64(x, x) + if carry != 0 { + return math.MaxUint64 + } + xSqr = xSqr >> 4 + + // Calculate 480 * x (can't overflow if x^2 didn't overflow) + x480 := x * 480 + // Calculate 480 * x - 199680 (will not underflow, since x > 1024) + x480 = x480 - 199680 + + // xSqr + x480 + sum, carry := bits.Add64(xSqr, x480, 0) + if carry != 0 { + return math.MaxUint64 + } + return sum + } +} + +// berlinMultComplexity implements the multiplication complexity formula for Berlin. +// +// def mult_complexity(x): +// +// ceiling(x/8)^2 +// +// where is x is max(length_of_MODULUS, length_of_BASE) +func berlinMultComplexity(x uint64) uint64 { + // x = (x + 7) / 8 + x, carry := bits.Add64(x, 7, 0) + if carry != 0 { + return math.MaxUint64 + } + x /= 8 + + // x^2 + carry, x = bits.Mul64(x, x) + if carry != 0 { + return math.MaxUint64 } return x } +// osakaMultComplexity implements the multiplication complexity formula for Osaka. +// +// For x <= 32: returns 16 +// For x > 32: returns 2 * ceiling(x/8)^2 +func osakaMultComplexity(x uint64) uint64 { + if x <= 32 { + return 16 + } + // For x > 32, return 2 * berlinMultComplexity(x) + result := berlinMultComplexity(x) + carry, result := bits.Mul64(result, 2) + if carry != 0 { + return math.MaxUint64 + } + return result +} + +// modexpIterationCount calculates the number of iterations for the modexp precompile. +// This is the adjusted exponent length used in gas calculation. +func modexpIterationCount(expLen uint64, expHead uint256.Int, multiplier uint64) uint64 { + var iterationCount uint64 + + // For large exponents (expLen > 32), add (expLen - 32) * multiplier + if expLen > 32 { + iterationCount = (expLen - 32) * multiplier + } + + // Add the MSB position - 1 if expHead is non-zero + if bitLen := expHead.BitLen(); bitLen > 0 { + iterationCount += uint64(bitLen - 1) + } + + return max(iterationCount, 1) +} + +// byzantiumModexpGas calculates the gas cost for the modexp precompile using Byzantium rules. +func byzantiumModexpGas(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 { + const ( + multiplier = 8 + divisor = 20 + ) + + maxLen := max(baseLen, modLen) + multComplexity := byzantiumMultComplexity(maxLen) + if multComplexity == math.MaxUint64 { + return math.MaxUint64 + } + iterationCount := modexpIterationCount(expLen, expHead, multiplier) + + // Calculate gas: (multComplexity * iterationCount) / divisor + carry, gas := bits.Mul64(iterationCount, multComplexity) + gas /= divisor + if carry != 0 { + return math.MaxUint64 + } + return gas +} + +// berlinModexpGas calculates the gas cost for the modexp precompile using Berlin rules. +func berlinModexpGas(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 { + const ( + multiplier = 8 + divisor = 3 + minGas = 200 + ) + + maxLen := max(baseLen, modLen) + multComplexity := berlinMultComplexity(maxLen) + if multComplexity == math.MaxUint64 { + return math.MaxUint64 + } + iterationCount := modexpIterationCount(expLen, expHead, multiplier) + + // Calculate gas: (multComplexity * iterationCount) / divisor + carry, gas := bits.Mul64(iterationCount, multComplexity) + gas /= divisor + if carry != 0 { + return math.MaxUint64 + } + return max(gas, minGas) +} + +// osakaModexpGas calculates the gas cost for the modexp precompile using Osaka rules. +func osakaModexpGas(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 { + const ( + multiplier = 16 + divisor = 3 + minGas = 500 + ) + + maxLen := max(baseLen, modLen) + multComplexity := osakaMultComplexity(maxLen) + if multComplexity == math.MaxUint64 { + return math.MaxUint64 + } + iterationCount := modexpIterationCount(expLen, expHead, multiplier) + + // Calculate gas: (multComplexity * iterationCount) / osakaDivisor + carry, gas := bits.Mul64(iterationCount, multComplexity) + if carry != 0 { + return math.MaxUint64 + } + return max(gas, minGas) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bigModExp) RequiredGas(input []byte) uint64 { - var ( - baseLen = new(big.Int).SetBytes(getData(input, 0, 32)) - expLen = new(big.Int).SetBytes(getData(input, 32, 32)) - modLen = new(big.Int).SetBytes(getData(input, 64, 32)) - ) + // Parse input lengths + baseLenBig := new(uint256.Int).SetBytes(getData(input, 0, 32)) + expLenBig := new(uint256.Int).SetBytes(getData(input, 32, 32)) + modLenBig := new(uint256.Int).SetBytes(getData(input, 64, 32)) + + // Convert to uint64, capping at max value + baseLen := baseLenBig.Uint64() + if !baseLenBig.IsUint64() { + baseLen = math.MaxUint64 + } + expLen := expLenBig.Uint64() + if !expLenBig.IsUint64() { + expLen = math.MaxUint64 + } + modLen := modLenBig.Uint64() + if !modLenBig.IsUint64() { + modLen = math.MaxUint64 + } + + // Skip the header if len(input) > 96 { input = input[96:] } else { input = input[:0] } + // Retrieve the head 32 bytes of exp for the adjusted exponent length - var expHead *big.Int - if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 { - expHead = new(big.Int) - } else { - if expLen.Cmp(big32) > 0 { - expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), 32)) + var expHead uint256.Int + if uint64(len(input)) > baseLen { + if expLen > 32 { + expHead.SetBytes(getData(input, baseLen, 32)) } else { - expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), expLen.Uint64())) + // TODO: Check that if expLen < baseLen, then getData will return an empty slice + expHead.SetBytes(getData(input, baseLen, expLen)) } } - // Calculate the adjusted exponent length - var msb int - if bitlen := expHead.BitLen(); bitlen > 0 { - msb = bitlen - 1 - } - adjExpLen := new(big.Int) - if expLen.Cmp(big32) > 0 { - adjExpLen.Sub(expLen, big32) - if c.eip7883 { - adjExpLen.Lsh(adjExpLen, 4) - } else { - adjExpLen.Lsh(adjExpLen, 3) - } - } - adjExpLen.Add(adjExpLen, big.NewInt(int64(msb))) - // Calculate the gas cost of the operation - gas := new(big.Int) - if modLen.Cmp(baseLen) < 0 { - gas.Set(baseLen) + + // Choose the appropriate gas calculation based on the EIP flags + if c.eip7883 { + return osakaModexpGas(baseLen, expLen, modLen, expHead) + } else if c.eip2565 { + return berlinModexpGas(baseLen, expLen, modLen, expHead) } else { - gas.Set(modLen) + return byzantiumModexpGas(baseLen, expLen, modLen, expHead) } - - maxLenOver32 := gas.Cmp(big32) > 0 - if c.eip2565 { - // EIP-2565 (Berlin fork) has three changes: - // - // 1. Different multComplexity (inlined here) - // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565): - // - // def mult_complexity(x): - // ceiling(x/8)^2 - // - // where is x is max(length_of_MODULUS, length_of_BASE) - gas.Add(gas, big7) - gas.Rsh(gas, 3) - gas.Mul(gas, gas) - - var minPrice uint64 = 200 - if c.eip7883 { - minPrice = 500 - if maxLenOver32 { - gas.Add(gas, gas) - } else { - gas = big.NewInt(16) - } - } - - if adjExpLen.Cmp(big1) > 0 { - gas.Mul(gas, adjExpLen) - } - // 2. Different divisor (`GQUADDIVISOR`) (3) - if !c.eip7883 { - gas.Div(gas, big3) - } - if gas.BitLen() > 64 { - return math.MaxUint64 - } - return max(minPrice, gas.Uint64()) - } - - // Pre-Berlin logic. - gas = modexpMultComplexity(gas) - if adjExpLen.Cmp(big1) > 0 { - gas.Mul(gas, adjExpLen) - } - gas.Div(gas, big20) - if gas.BitLen() > 64 { - return math.MaxUint64 - } - return gas.Uint64() } func (c *bigModExp) Run(input []byte) ([]byte, error) { diff --git a/core/vm/testdata/precompiles/modexp_eip2565.json b/core/vm/testdata/precompiles/modexp_eip2565.json index c55441439e..24455eeca7 100644 --- a/core/vm/testdata/precompiles/modexp_eip2565.json +++ b/core/vm/testdata/precompiles/modexp_eip2565.json @@ -117,5 +117,215 @@ "Name": "nagydani-5-pow0x10001", "Gas": 87381, "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c1000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000007d7d7d83828282348286877d7d827d407d797d7d7d7d7d7d7d7d7d7d7d5b00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000021000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4000007d7d7d83828282348286877d7d82", + "Expected": "36a385a417859b5e178d3ab9", + "Name": "marius-1-even", + "Gas": 2057, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000d80000000000000000000000000000000000000000000000000000000000000010ffffffffffffffff76ffffffffffffff1cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c76ec7c7c7c7ffffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7ffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c76ec7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7ffffffffff3f000000000000000000000000", + "Expected": "c3745de81615f80088ffffffffffffff", + "Name": "guido-1-even", + "Gas": 2298, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000d80000000000000000000000000000000000000000000000000000000000000010e0060000a921212121212121ff0000212b212121ffff1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f00feffff212121212121ffffffff1fe1e0e0e01e1f1f169f1f1f1f490afcefffffffffffffffff82828282828282828282828282828282828282828200ffff28ff2b212121ffff1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1fffffffffff0afceffffff7ffffffffff7c8282828282a1828282828282828282828282828200ffff28ff2b212121ffff1f1f1f1f1f1fd11f1f1f1f1f1f1f1f1f1f1fffffffffffffffff21212121212121fb2121212121ffff1f1f1f1f1f1f1f1fffaf82828282828200ffff28ff2b21828200", + "Expected": "458ef0af2549d46d24c89079499479e1", + "Name": "guido-2-even", + "Gas": 2300, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000001e7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000002cb0193585a48e18aad777e9c1b54221a0f58140392e4f091cd5f42b2e8644a9384fbd58ae1edec2477ebf7edbf7c0a3f8bd21d1890ee87646feab3c47be716f842cc3da9b940af312dc54450a960e3fc0b86e56abddd154068e10571a96fff6259431632bc15695c6c8679057e66c2c25c127e97e64ee5de6ea1fc0a4a0e431343fed1daafa072c238a45841da86a9806680bc9f298411173210790359209cd454b5af7b4d5688b4403924e5f863d97e2c5349e1a04b54fcf385b1e9d7714bab8fbf5835f6ff9ed575e77dff7af5cbb641db5d537933bae1fa6555d6c12d6fb31ca27b57771f4aebfbe0bf95e8990c0108ffe7cbdaf370be52cf3ade594543af75ad9329d2d11a402270b5b9a6bf4b83307506e118fca4862749d04e916fc7a039f0d13f2a02e0eedb800199ec95df15b4ccd8669b52586879624d51219e72102fad810b5909b1e372ddf33888fb9beb09b416e4164966edbabd89e4a286be36277fc576ed519a15643dac602e92b63d0b9121f0491da5b16ef793a967f096d80b6c81ecaaffad7e3f06a4a5ac2796f1ed9f68e6a0fd5cf191f0c5c2eec338952ff8d31abc68bf760febeb57e088995ba1d7726a2fdd6d8ca28a181378b8b4ab699bfd4b696739bbf17a9eb2df6251143046137fdbbfacac312ebf67a67da9741b596000000000000419a2917c61722b0713d3b00a2f0e1dd5aebbbe09615de424700eea3c3020fe6e9ea5de9fa1ace781df28b21f746d2ab61d0da496e08473c90ff7dfe25b43bcde76f4bafb82e0975bea75f5a0591dba80ba2fff80a07d8853bea5be13ab326ba70c57b153acc646151948d1cf061ca31b02d4719fac710e7c723ca44f5b1737824b7ccc74ba5bff980aabdbf267621cafc3d6dcc29d0ca9c16839a92ed34de136da7900aa3ee43d21aa57498981124357cf0ca9b86f9a8d3f9c604ca00c726e48f7a9945021ea6dfff92d6b2d6514693169ca133e993541bfa4c4c191de806aa80c48109bcfc9901eccfdeb2395ab75fe63c67de900829d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "1194e971c875bb45f030316793bc6916343335b18670dfad7a4646fba99749b30283b78b818836de7400ff1a68ddad1a2dd850ec0f227441e2d4d13f5ee4b5d7a856db0a9ad1f86987e1f117d70f9e6a1a2b8d083fa82653aa16f1773b6deb2ed8a1f9e7f3a5db121c4a0c91cb954e2ec53e63422efe86c7984d79cd0e7b5e3eb8ca4980551d63f302c7d72500a84baf12c82fc7bd9b5c2ab8b9c33baf1df28b2031c58a8b2928a42c9f456e98874e22fe13cf17aa5915b11bb108b6ae40842d434604ccddcb4f64324c67b2dde32e6cd759d964f17d9cdf0046cd0ed3588e1fc4b88f67a5d4f3a870aad1cba89ead265d6ad327c8ea7ff54fe4b5e7dbe87c5c59c468543eab3675751111bfa1d6c51daf789d41dc21fd8ba9e05490f881a973a3c1567ff3129a49aa6658cf06f0a79530a7256ce5a07c2a77b4306383d538866bba376d90621c4f82d1f5f32304ee2b7170805d42418fc6967642e5648d8c64fe9c0fdff2d7c114a47add7767c8fccb8808de8c3c6e1a8880c05e16fafc1513fded8eba222dcaaa809bdb36999cc27ab8d0055009e9690e8a35b859df865dc510d25c7812d8eebbb35607ad595573f0fabd1b57970a2bc113ac6f0ca01e985032b9c2c139316ac099ed1632d2bc0fcc341343d303db2a9c3cf2ad572c6c43084b08d458bf822e92da16079f39cdb0dd10ef47f87ecae404117fc72660372cee9ea42266e7f8d973e7f6b09930ca5f96e04976bf23b9d356bbd2429597b04b7663e0e1a1228f4dfda3b854233e4888dc60c6886c1e0e8aec1705f681027b1e0b3017337557f107ef5cd272df5fd31dfec2bdffe163a8369895ffe124c0aa0ee00ca0fe1db4d5cf37b4af0e49bd73a89d88ced3d88f8e6f00d8e61ad09946d0e72cd3e25bc688a021a83758b5023daae7c269a6cbbd447aba5da7629b75801e1654ce85b8e21ccb9865654f8662e538625d75fea31000000000000000000000000000000000000000000000", + "Name": "guido-3-even", + "Gas": 5400, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000181000000000000000000000000000000000000000000000000000000000000000801ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2cffffffffffffffffffffffffffffffffffffffffffffffffffffffff3b10000000006c01ffffffffffffffffffffffffffffffffffffffffffffffdffffb97ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3bffffffffffffffffffffffffffffffffffffffffffffffffffffffffebafd93b37ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc5bb6affffffff3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2a", + "Expected": "0000000000000001", + "Name": "guido-4-even", + "Gas": 1026, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e66303f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43", + "Expected": "120cf297dbe810911c7d060e109e03699ccefa00a257d296c5a14b4180776c5f7c0d7f1cd789c694807689729af267b53f00373f395dee264a3daba11fcac1fa8875aee0950acd8fa656f1fc58077a7549d794dd160506ecea1acc9c0cda13795749c94f9973b683ce2162866e8d6b5b1165a4c7fa4234964d394d6ec4e0113698b89d173e24a962dd7a41a1819b0fef188ef64e7ee264595dce0d76fbc3ba42d5de833b143c8744366effede8bc8197e8f747ff8cdbc0bf1a93560bec960ca9", + "Name": "marcin-1-base-heavy", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000080001020304050607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0001020304050607", + "Expected": "0000000000000000", + "Name": "marcin-1-exp-heavy", + "Gas": 215, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000028e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060701fffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c950001020304050607", + "Expected": "1abce71dc2205cce4eb6934397a88136f94641342e283cbcd30e929e85605c6718ed67f475192ffd", + "Name": "marcin-1-balanced", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000019800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000198e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e663c29021b0e084f7dc16f6ec88cc597f1aea9f8e0b9501e0f7a546805d2a20eeda0bf080aeb3ed7ea6f9174d804bd242f0b31ff1ea24800344abb580cd87f61ca75a013a87733553966400242399dee3760877fead2cd87287747155e47a854acb50fe07922f57ae3b4553201bfd7c11aca85e1541f91db8e62dca9c418dc5feae086c9487350539c884510044efce5e3f2aaffca4215c12b9044506375097fecd9b22e2ef46f01f1af8aff742aebf96bdcaf55a341600971dc62555376b9e98a8000102030405060708090a0b0c0d0e0f101112131415161703f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43ddce2bd0cdc6c22c4dcd345735040d5bfe3f09b7c61362089f728e2222db96cab2f2c2ccf43574f9e119f4860fd0f1b6036a43ad9db8a428ea09a4ee385112f3fe9c6656ea2cec604cbb5a9227526653bfa7035e4ae80010b1ba16a76608d5dde0a62bc019e9047b5ec05b1005fd017366130a4ba555e7be654561ee3f539c93cb2c9988fca71bf0ad9c4a426b924641a28e1e4adb93609bfa5b2bc81714cbba1110208b86d7b87be28bdf63a62e33ae81dbcc43de9192bd192c40e85faab539000102030405060708090a0b0c0d0e0f1011121314151617", + "Expected": "817d05c4d540ace3f250fd082e2deb8c2097410fcfe4ce40862cfa015b7f62a7bb72ec1af2915cad66294447b45d177fe759eb80370b0dbd3c0e1c448d54db81eadc11e40c19e394d066a5c019c443798a98d4afd116fc220593d42fbf191b6af0ae75410badb641187ba24a0b968f742a75e2822853f137151d9ea972fd1f36b7c7cc4e71355ecf50648aec094b864cfae9316c0a7be3ddb8ab2d0050b2a029ee956c2366d49430c8f889f29ab514aea8e5b8dec40ba1b49432e30aee32ec45e96dd548205a79d8f8f918eed46acb2115c59086b1011b1d2b093cea723535c3d95efc8e51a7da43b80586d69eb7f213dfb06f7a8e789a9472392bd224411b50f8ca6f2862bcd63431912d1ff99c8d76408245da9e4bea649f0eb930b32922f2d0a345a206160be44d418f1a6c74bd49c4618392ef9350b264a461dfc684c7343211e27675b027054f1cb3d4a5b1a066d3a3ea2eed9caf13251d8be936818f15274e8e3d7539b7c5f216cef327a270fd2a886fbf679c163fb5806249f2c74da5ee0e3ffe9ad1fde2634b29b35da6da6d184ab6ae70199229", + "Name": "marcin-2-base-heavy", + "Gas": 867, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000102030405060708090a0b0c0d0e0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f", + "Expected": "00000000000000000000000000000000", + "Name": "marcin-2-exp-heavy", + "Gas": 852, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000038e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060708090a0b0c0d0e0f10111213141516172bfffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95000102030405060708090a0b0c0d0e0f1011121314151617", + "Expected": "86bef3367fc7117c8a6b825cadebe80f3e94c321dda73e9e240b98188a1d5c071c60a195097c8d1fb85ce03a2e1b6964846edee5aa2c3f46", + "Name": "marcin-2-balanced", + "Gas": 996, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95", + "Expected": "4004762c491606a5132134da6086284f74cc8e14b08f18b90fc09f31bca3d78f", + "Name": "marcin-3-base-heavy", + "Gas": 677, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000018000102030405060708090a0b0c0d0e0f1011121314151617ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f1011121314151617", + "Expected": "000000000000000000000000000000000000000000000000", + "Name": "marcin-3-exp-heavy", + "Gas": 765, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95", + "Expected": "2d3feee20d394af68dd6744b86a8aca6a4a0b7f01bbcd3c3eec768245ca6acee", + "Name": "marcin-3-balanced", + "Gas": 1360, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000000800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffff", + "Name": "mod-8-exp-648", + "Gas": 215, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffff", + "Name": "mod-8-exp-896", + "Gas": 298, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-32", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-36", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-40", + "Gas": 208, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-64", + "Gas": 336, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-65", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-128", + "Gas": 677, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "02fd01000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03feffffffffff", + "Name": "mod-256-exp-2", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000001080000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff00", + "Expected": "0100fefffffffffffff710f80000000006f108000000000000feffffffffffff0100fefffffffffffef90ff80000000105f008ffffffffff02fdffffffffffff0100feffffffffff00f80ff80000010101f407fffffffd06fc0000000000fd020000feffffffff00fff80ff8000101fa08f207fffffb0afa0000000001fb03000000feffffff00fffff80ff80102f80405f207fff90ef80000000002f90400000000feffff00fffffff80ff903f6050005f207f712f60000000003f7050000000000feff00fffffffff810fcf406000005f1fd16f40000000004f506000000000000fe00fffffffffff915ea0700000005e522f20000000005f306ffffffffffffffffffffffffff", + "Name": "mod-264-exp-2", + "Gas": 363, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000040000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02feffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-1024-exp-2", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000008ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "2a02d5f86c2375ff", + "Name": "pawel-1-exp-heavy", + "Gas": 298, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000010ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "823ef7dc60d6d9616756c48f69b7c4ff", + "Name": "pawel-2-exp-heavy", + "Gas": 425, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000018ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "c817dd5aa60a41948eed409706c2aa97be3000d4da0261ff", + "Name": "pawel-3-exp-heavy", + "Gas": 501, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000020ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "2defaca0137d6edacbbd5d36d6ed70cbf8a998ffb19fc270d45a18d37e0f35ff", + "Name": "pawel-4-exp-heavy", + "Gas": 506, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000017bffffffffffffffffffffffffffffffffffffffffffffbffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffe", + "Expected": "200f14de1d474710c1c979920452e0ffc2ac6f618afba5", + "Name": "mod_vul_pawel_3_exp_8", + "Gas": 200, + "NoBenchmark": false } ] \ No newline at end of file diff --git a/core/vm/testdata/precompiles/modexp_eip7883.json b/core/vm/testdata/precompiles/modexp_eip7883.json index 85e9ad1849..fb3169d148 100644 --- a/core/vm/testdata/precompiles/modexp_eip7883.json +++ b/core/vm/testdata/precompiles/modexp_eip7883.json @@ -103,5 +103,215 @@ "Name": "nagydani-5-pow0x10001", "Gas": 524288, "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c1000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000007d7d7d83828282348286877d7d827d407d797d7d7d7d7d7d7d7d7d7d7d5b00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000021000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4000007d7d7d83828282348286877d7d82", + "Expected": "36a385a417859b5e178d3ab9", + "Name": "marius-1-even", + "Gas": 45296, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000d80000000000000000000000000000000000000000000000000000000000000010ffffffffffffffff76ffffffffffffff1cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c76ec7c7c7c7ffffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7ffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c76ec7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7ffffffffff3f000000000000000000000000", + "Expected": "c3745de81615f80088ffffffffffffff", + "Name": "guido-1-even", + "Gas": 51136, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000d80000000000000000000000000000000000000000000000000000000000000010e0060000a921212121212121ff0000212b212121ffff1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f00feffff212121212121ffffffff1fe1e0e0e01e1f1f169f1f1f1f490afcefffffffffffffffff82828282828282828282828282828282828282828200ffff28ff2b212121ffff1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1fffffffffff0afceffffff7ffffffffff7c8282828282a1828282828282828282828282828200ffff28ff2b212121ffff1f1f1f1f1f1fd11f1f1f1f1f1f1f1f1f1f1fffffffffffffffff21212121212121fb2121212121ffff1f1f1f1f1f1f1f1fffaf82828282828200ffff28ff2b21828200", + "Expected": "458ef0af2549d46d24c89079499479e1", + "Name": "guido-2-even", + "Gas": 51152, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000001e7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000002cb0193585a48e18aad777e9c1b54221a0f58140392e4f091cd5f42b2e8644a9384fbd58ae1edec2477ebf7edbf7c0a3f8bd21d1890ee87646feab3c47be716f842cc3da9b940af312dc54450a960e3fc0b86e56abddd154068e10571a96fff6259431632bc15695c6c8679057e66c2c25c127e97e64ee5de6ea1fc0a4a0e431343fed1daafa072c238a45841da86a9806680bc9f298411173210790359209cd454b5af7b4d5688b4403924e5f863d97e2c5349e1a04b54fcf385b1e9d7714bab8fbf5835f6ff9ed575e77dff7af5cbb641db5d537933bae1fa6555d6c12d6fb31ca27b57771f4aebfbe0bf95e8990c0108ffe7cbdaf370be52cf3ade594543af75ad9329d2d11a402270b5b9a6bf4b83307506e118fca4862749d04e916fc7a039f0d13f2a02e0eedb800199ec95df15b4ccd8669b52586879624d51219e72102fad810b5909b1e372ddf33888fb9beb09b416e4164966edbabd89e4a286be36277fc576ed519a15643dac602e92b63d0b9121f0491da5b16ef793a967f096d80b6c81ecaaffad7e3f06a4a5ac2796f1ed9f68e6a0fd5cf191f0c5c2eec338952ff8d31abc68bf760febeb57e088995ba1d7726a2fdd6d8ca28a181378b8b4ab699bfd4b696739bbf17a9eb2df6251143046137fdbbfacac312ebf67a67da9741b596000000000000419a2917c61722b0713d3b00a2f0e1dd5aebbbe09615de424700eea3c3020fe6e9ea5de9fa1ace781df28b21f746d2ab61d0da496e08473c90ff7dfe25b43bcde76f4bafb82e0975bea75f5a0591dba80ba2fff80a07d8853bea5be13ab326ba70c57b153acc646151948d1cf061ca31b02d4719fac710e7c723ca44f5b1737824b7ccc74ba5bff980aabdbf267621cafc3d6dcc29d0ca9c16839a92ed34de136da7900aa3ee43d21aa57498981124357cf0ca9b86f9a8d3f9c604ca00c726e48f7a9945021ea6dfff92d6b2d6514693169ca133e993541bfa4c4c191de806aa80c48109bcfc9901eccfdeb2395ab75fe63c67de900829d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "1194e971c875bb45f030316793bc6916343335b18670dfad7a4646fba99749b30283b78b818836de7400ff1a68ddad1a2dd850ec0f227441e2d4d13f5ee4b5d7a856db0a9ad1f86987e1f117d70f9e6a1a2b8d083fa82653aa16f1773b6deb2ed8a1f9e7f3a5db121c4a0c91cb954e2ec53e63422efe86c7984d79cd0e7b5e3eb8ca4980551d63f302c7d72500a84baf12c82fc7bd9b5c2ab8b9c33baf1df28b2031c58a8b2928a42c9f456e98874e22fe13cf17aa5915b11bb108b6ae40842d434604ccddcb4f64324c67b2dde32e6cd759d964f17d9cdf0046cd0ed3588e1fc4b88f67a5d4f3a870aad1cba89ead265d6ad327c8ea7ff54fe4b5e7dbe87c5c59c468543eab3675751111bfa1d6c51daf789d41dc21fd8ba9e05490f881a973a3c1567ff3129a49aa6658cf06f0a79530a7256ce5a07c2a77b4306383d538866bba376d90621c4f82d1f5f32304ee2b7170805d42418fc6967642e5648d8c64fe9c0fdff2d7c114a47add7767c8fccb8808de8c3c6e1a8880c05e16fafc1513fded8eba222dcaaa809bdb36999cc27ab8d0055009e9690e8a35b859df865dc510d25c7812d8eebbb35607ad595573f0fabd1b57970a2bc113ac6f0ca01e985032b9c2c139316ac099ed1632d2bc0fcc341343d303db2a9c3cf2ad572c6c43084b08d458bf822e92da16079f39cdb0dd10ef47f87ecae404117fc72660372cee9ea42266e7f8d973e7f6b09930ca5f96e04976bf23b9d356bbd2429597b04b7663e0e1a1228f4dfda3b854233e4888dc60c6886c1e0e8aec1705f681027b1e0b3017337557f107ef5cd272df5fd31dfec2bdffe163a8369895ffe124c0aa0ee00ca0fe1db4d5cf37b4af0e49bd73a89d88ced3d88f8e6f00d8e61ad09946d0e72cd3e25bc688a021a83758b5023daae7c269a6cbbd447aba5da7629b75801e1654ce85b8e21ccb9865654f8662e538625d75fea31000000000000000000000000000000000000000000000", + "Name": "guido-3-even", + "Gas": 32400, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000181000000000000000000000000000000000000000000000000000000000000000801ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2cffffffffffffffffffffffffffffffffffffffffffffffffffffffff3b10000000006c01ffffffffffffffffffffffffffffffffffffffffffffffdffffb97ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3bffffffffffffffffffffffffffffffffffffffffffffffffffffffffebafd93b37ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc5bb6affffffff3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2a", + "Expected": "0000000000000001", + "Name": "guido-4-even", + "Gas": 94448, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e66303f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43", + "Expected": "120cf297dbe810911c7d060e109e03699ccefa00a257d296c5a14b4180776c5f7c0d7f1cd789c694807689729af267b53f00373f395dee264a3daba11fcac1fa8875aee0950acd8fa656f1fc58077a7549d794dd160506ecea1acc9c0cda13795749c94f9973b683ce2162866e8d6b5b1165a4c7fa4234964d394d6ec4e0113698b89d173e24a962dd7a41a1819b0fef188ef64e7ee264595dce0d76fbc3ba42d5de833b143c8744366effede8bc8197e8f747ff8cdbc0bf1a93560bec960ca9", + "Name": "marcin-1-base-heavy", + "Gas": 1152, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000080001020304050607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0001020304050607", + "Expected": "0000000000000000", + "Name": "marcin-1-exp-heavy", + "Gas": 16624, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000028e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060701fffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c950001020304050607", + "Expected": "1abce71dc2205cce4eb6934397a88136f94641342e283cbcd30e929e85605c6718ed67f475192ffd", + "Name": "marcin-1-balanced", + "Gas": 1200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000019800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000198e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e663c29021b0e084f7dc16f6ec88cc597f1aea9f8e0b9501e0f7a546805d2a20eeda0bf080aeb3ed7ea6f9174d804bd242f0b31ff1ea24800344abb580cd87f61ca75a013a87733553966400242399dee3760877fead2cd87287747155e47a854acb50fe07922f57ae3b4553201bfd7c11aca85e1541f91db8e62dca9c418dc5feae086c9487350539c884510044efce5e3f2aaffca4215c12b9044506375097fecd9b22e2ef46f01f1af8aff742aebf96bdcaf55a341600971dc62555376b9e98a8000102030405060708090a0b0c0d0e0f101112131415161703f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43ddce2bd0cdc6c22c4dcd345735040d5bfe3f09b7c61362089f728e2222db96cab2f2c2ccf43574f9e119f4860fd0f1b6036a43ad9db8a428ea09a4ee385112f3fe9c6656ea2cec604cbb5a9227526653bfa7035e4ae80010b1ba16a76608d5dde0a62bc019e9047b5ec05b1005fd017366130a4ba555e7be654561ee3f539c93cb2c9988fca71bf0ad9c4a426b924641a28e1e4adb93609bfa5b2bc81714cbba1110208b86d7b87be28bdf63a62e33ae81dbcc43de9192bd192c40e85faab539000102030405060708090a0b0c0d0e0f1011121314151617", + "Expected": "817d05c4d540ace3f250fd082e2deb8c2097410fcfe4ce40862cfa015b7f62a7bb72ec1af2915cad66294447b45d177fe759eb80370b0dbd3c0e1c448d54db81eadc11e40c19e394d066a5c019c443798a98d4afd116fc220593d42fbf191b6af0ae75410badb641187ba24a0b968f742a75e2822853f137151d9ea972fd1f36b7c7cc4e71355ecf50648aec094b864cfae9316c0a7be3ddb8ab2d0050b2a029ee956c2366d49430c8f889f29ab514aea8e5b8dec40ba1b49432e30aee32ec45e96dd548205a79d8f8f918eed46acb2115c59086b1011b1d2b093cea723535c3d95efc8e51a7da43b80586d69eb7f213dfb06f7a8e789a9472392bd224411b50f8ca6f2862bcd63431912d1ff99c8d76408245da9e4bea649f0eb930b32922f2d0a345a206160be44d418f1a6c74bd49c4618392ef9350b264a461dfc684c7343211e27675b027054f1cb3d4a5b1a066d3a3ea2eed9caf13251d8be936818f15274e8e3d7539b7c5f216cef327a270fd2a886fbf679c163fb5806249f2c74da5ee0e3ffe9ad1fde2634b29b35da6da6d184ab6ae70199229", + "Name": "marcin-2-base-heavy", + "Gas": 5202, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000102030405060708090a0b0c0d0e0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f", + "Expected": "00000000000000000000000000000000", + "Name": "marcin-2-exp-heavy", + "Gas": 16368, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000038e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060708090a0b0c0d0e0f10111213141516172bfffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95000102030405060708090a0b0c0d0e0f1011121314151617", + "Expected": "86bef3367fc7117c8a6b825cadebe80f3e94c321dda73e9e240b98188a1d5c071c60a195097c8d1fb85ce03a2e1b6964846edee5aa2c3f46", + "Name": "marcin-2-balanced", + "Gas": 5978, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95", + "Expected": "4004762c491606a5132134da6086284f74cc8e14b08f18b90fc09f31bca3d78f", + "Name": "marcin-3-base-heavy", + "Gas": 2032, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000018000102030405060708090a0b0c0d0e0f1011121314151617ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f1011121314151617", + "Expected": "000000000000000000000000000000000000000000000000", + "Name": "marcin-3-exp-heavy", + "Gas": 4080, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95", + "Expected": "2d3feee20d394af68dd6744b86a8aca6a4a0b7f01bbcd3c3eec768245ca6acee", + "Name": "marcin-3-balanced", + "Gas": 4080, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000000800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffff", + "Name": "mod-8-exp-648", + "Gas": 16624, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffff", + "Name": "mod-8-exp-896", + "Gas": 24560, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-32", + "Gas": 500, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-36", + "Gas": 560, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-40", + "Gas": 624, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-64", + "Gas": 1008, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-65", + "Gas": 1024, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-32-exp-128", + "Gas": 2032, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "02fd01000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03feffffffffff", + "Name": "mod-256-exp-2", + "Gas": 2048, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000001080000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff00", + "Expected": "0100fefffffffffffff710f80000000006f108000000000000feffffffffffff0100fefffffffffffef90ff80000000105f008ffffffffff02fdffffffffffff0100feffffffffff00f80ff80000010101f407fffffffd06fc0000000000fd020000feffffffff00fff80ff8000101fa08f207fffffb0afa0000000001fb03000000feffffff00fffff80ff80102f80405f207fff90ef80000000002f90400000000feffff00fffffff80ff903f6050005f207f712f60000000003f7050000000000feff00fffffffff810fcf406000005f1fd16f40000000004f506000000000000fe00fffffffffff915ea0700000005e522f20000000005f306ffffffffffffffffffffffffff", + "Name": "mod-264-exp-2", + "Gas": 2178, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000040000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "Expected": "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02feffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Name": "mod-1024-exp-2", + "Gas": 32768, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000008ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "2a02d5f86c2375ff", + "Name": "pawel-1-exp-heavy", + "Gas": 24560, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000010ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "823ef7dc60d6d9616756c48f69b7c4ff", + "Name": "pawel-2-exp-heavy", + "Gas": 6128, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000018ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "c817dd5aa60a41948eed409706c2aa97be3000d4da0261ff", + "Name": "pawel-3-exp-heavy", + "Gas": 2672, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000020ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "2defaca0137d6edacbbd5d36d6ed70cbf8a998ffb19fc270d45a18d37e0f35ff", + "Name": "pawel-4-exp-heavy", + "Gas": 1520, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000017bffffffffffffffffffffffffffffffffffffffffffffbffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffe", + "Expected": "200f14de1d474710c1c979920452e0ffc2ac6f618afba5", + "Name": "mod_vul_pawel_3_exp_8", + "Gas": 1008, + "NoBenchmark": false } ] From 2a3f4cea1349142bf9a19361977960cab3b2f296 Mon Sep 17 00:00:00 2001 From: Forostovec Date: Tue, 9 Sep 2025 04:56:53 +0300 Subject: [PATCH 017/119] rlp: fix typo in decode test message (#32554) --- rlp/decode_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rlp/decode_test.go b/rlp/decode_test.go index 3e492188e8..4b3a322873 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -350,7 +350,7 @@ func TestDecodeErrors(t *testing.T) { } if err := Decode(r, new(uint)); err != io.EOF { - t.Errorf("Decode(r, new(int)) error mismatch, got %q, want %q", err, io.EOF) + t.Errorf("Decode(r, new(uint)) error mismatch, got %q, want %q", err, io.EOF) } } From 503506442117d4bb0043f161463eb7645725cfa3 Mon Sep 17 00:00:00 2001 From: VolodymyrBg Date: Tue, 9 Sep 2025 10:19:50 +0300 Subject: [PATCH 018/119] core/rawdb: improve the test suite for ancient store (#32555) --- core/rawdb/ancienttest/testsuite.go | 80 ++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/core/rawdb/ancienttest/testsuite.go b/core/rawdb/ancienttest/testsuite.go index e33e768947..7512c1f44b 100644 --- a/core/rawdb/ancienttest/testsuite.go +++ b/core/rawdb/ancienttest/testsuite.go @@ -48,12 +48,16 @@ func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { ) defer db.Close() - db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { for i := 0; i < len(data); i++ { - op.AppendRaw("a", uint64(i), data[i]) + if err := op.AppendRaw("a", uint64(i), data[i]); err != nil { + return err + } } return nil - }) + }); err != nil { + t.Fatalf("Failed to write ancient data %v", err) + } db.TruncateTail(10) db.TruncateHead(90) @@ -109,12 +113,16 @@ func batchRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { ) defer db.Close() - db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { for i := 0; i < 100; i++ { - op.AppendRaw("a", uint64(i), data[i]) + if err := op.AppendRaw("a", uint64(i), data[i]); err != nil { + return err + } } return nil - }) + }); err != nil { + t.Fatalf("Failed to write ancient data %v", err) + } db.TruncateTail(10) db.TruncateHead(90) @@ -189,7 +197,9 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { // The ancient write to tables should be aligned _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { for i := 0; i < 100; i++ { - op.AppendRaw("a", uint64(i), dataA[i]) + if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil { + return err + } } return nil }) @@ -200,8 +210,12 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { // Test normal ancient write size, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { for i := 0; i < 100; i++ { - op.AppendRaw("a", uint64(i), dataA[i]) - op.AppendRaw("b", uint64(i), dataB[i]) + if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil { + return err + } + if err := op.AppendRaw("b", uint64(i), dataB[i]); err != nil { + return err + } } return nil }) @@ -217,8 +231,12 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { db.TruncateHead(90) _, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error { for i := 90; i < 100; i++ { - op.AppendRaw("a", uint64(i), dataA[i]) - op.AppendRaw("b", uint64(i), dataB[i]) + if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil { + return err + } + if err := op.AppendRaw("b", uint64(i), dataB[i]); err != nil { + return err + } } return nil }) @@ -227,11 +245,15 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { } // Write should work after truncating everything - db.TruncateTail(0) + db.TruncateHead(0) _, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error { for i := 0; i < 100; i++ { - op.AppendRaw("a", uint64(i), dataA[i]) - op.AppendRaw("b", uint64(i), dataB[i]) + if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil { + return err + } + if err := op.AppendRaw("b", uint64(i), dataB[i]); err != nil { + return err + } } return nil }) @@ -245,14 +267,18 @@ func nonMutable(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { defer db.Close() // We write 100 zero-bytes to the freezer and immediately mutate the slice - db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { data := make([]byte, 100) - op.AppendRaw("a", uint64(0), data) + if err := op.AppendRaw("a", uint64(0), data); err != nil { + return err + } for i := range data { data[i] = 0xff } return nil - }) + }); err != nil { + t.Fatalf("Failed to write ancient data %v", err) + } // Now read it. data, err := db.Ancient("a", uint64(0)) if err != nil { @@ -275,23 +301,31 @@ func TestResettableAncientSuite(t *testing.T, newFn func(kinds []string) ethdb.R ) defer db.Close() - db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { for i := 0; i < 100; i++ { - op.AppendRaw("a", uint64(i), data[i]) + if err := op.AppendRaw("a", uint64(i), data[i]); err != nil { + return err + } } return nil - }) + }); err != nil { + t.Fatalf("Failed to write ancient data %v", err) + } db.TruncateTail(10) db.TruncateHead(90) // Ancient write should work after resetting db.Reset() - db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { for i := 0; i < 100; i++ { - op.AppendRaw("a", uint64(i), data[i]) + if err := op.AppendRaw("a", uint64(i), data[i]); err != nil { + return err + } } return nil - }) + }); err != nil { + t.Fatalf("Failed to write ancient data %v", err) + } }) } From ca6e2d141b14e67d2ba826c6213e9e413148b6ec Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 9 Sep 2025 20:39:54 +0800 Subject: [PATCH 019/119] triedb/pathdb: sync ancient store before journal (#32557) This pull request addresses the corrupted path database with log indicating: `history head truncation out of range, tail: 122557, head: 212208, target: 212557` This is a rare edge case where the in-memory layers, including the write buffer in the disk layer, are fully persisted (e.g., written to file), but the state history freezer is not properly closed (e.g., Geth is terminated after journaling but before freezer.Close). In this situation, the recent state history writes will be truncated on the next startup, while the in-memory layers resolve correctly. As a result, the state history falls behind the disk layer (including the write buffer). In this pull request, the state history freezer is always synced before journal, ensuring the state history writes are always persisted before the others. Edit: It's confirmed that devops team has 10s container termination setting. It explains why Geth didn't finish the entire termination without state history being closed. https://github.com/ethpandaops/fusaka-devnets/pull/63/files --- triedb/pathdb/journal.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 7a634dc974..02bdef5d34 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -333,7 +333,16 @@ func (db *Database) Journal(root common.Hash) error { if db.readOnly { return errDatabaseReadOnly } - + // Forcibly sync the ancient store before persisting the in-memory layers. + // This prevents an edge case where the in-memory layers are persisted + // but the ancient store is not properly closed, resulting in recent writes + // being lost. After a restart, the ancient store would then be misaligned + // with the disk layer, causing data corruption. + if db.stateFreezer != nil { + if err := db.stateFreezer.SyncAncient(); err != nil { + return err + } + } // Store the journal into the database and return var ( file *os.File From 5cc443609f80f228512091bc8352952d3076d6be Mon Sep 17 00:00:00 2001 From: maskpp Date: Wed, 10 Sep 2025 11:15:47 +0800 Subject: [PATCH 020/119] core/txpool/blobpool: disallow legacy sidecar after osaka (#32534) This PR removes the conversion of legacy sidecars after Osaka and instead rejects them to the pool. --------- Co-authored-by: lightclient --- core/txpool/blobpool/blobpool.go | 31 +-------------------------- core/txpool/blobpool/blobpool_test.go | 7 +++--- 2 files changed, 4 insertions(+), 34 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index edc8eb3e55..5cf1218b75 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1408,31 +1408,6 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int { return available } -// convertSidecar converts the legacy sidecar in the submitted transactions -// if Osaka fork has been activated. -func (p *BlobPool) convertSidecar(txs []*types.Transaction) ([]*types.Transaction, []error) { - head := p.chain.CurrentBlock() - if !p.chain.Config().IsOsaka(head.Number, head.Time) { - return txs, make([]error, len(txs)) - } - var errs []error - for _, tx := range txs { - sidecar := tx.BlobTxSidecar() - if sidecar == nil { - errs = append(errs, errors.New("missing sidecar in blob transaction")) - continue - } - if sidecar.Version == types.BlobSidecarVersion0 { - if err := sidecar.ToV1(); err != nil { - errs = append(errs, err) - continue - } - } - errs = append(errs, nil) - } - return txs, errs -} - // Add inserts a set of blob transactions into the pool if they pass validation (both // consensus validity and pool restrictions). // @@ -1440,14 +1415,10 @@ func (p *BlobPool) convertSidecar(txs []*types.Transaction) ([]*types.Transactio // related to the add is finished. Only use this during tests for determinism. func (p *BlobPool) Add(txs []*types.Transaction, sync bool) []error { var ( - errs []error + errs = make([]error, len(txs)) adds = make([]*types.Transaction, 0, len(txs)) ) - txs, errs = p.convertSidecar(txs) for i, tx := range txs { - if errs[i] != nil { - continue - } errs[i] = p.add(tx) if errs[i] == nil { adds = append(adds, tx.WithoutBlobTxSidecar()) diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 8171ae294a..c9609e1259 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -1691,8 +1691,7 @@ func TestAdd(t *testing.T) { } } -// Tests that adding the transactions with legacy sidecar and expect them to -// be converted to new format correctly. +// Tests adding transactions with legacy sidecars are correctly rejected. func TestAddLegacyBlobTx(t *testing.T) { var ( key1, _ = crypto.GenerateKey() @@ -1726,8 +1725,8 @@ func TestAddLegacyBlobTx(t *testing.T) { ) errs := pool.Add([]*types.Transaction{tx1, tx2, tx3}, true) for _, err := range errs { - if err != nil { - t.Fatalf("failed to add tx: %v", err) + if err == nil { + t.Fatalf("expected tx add to fail") } } verifyPoolInternals(t, pool) From 586ac9b334028209770de5cbbe22828335b570c1 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Wed, 10 Sep 2025 01:25:25 -0600 Subject: [PATCH 021/119] params: use spaces instead of tabs in config description (#32564) The tabs aren't consistently spaced with the other items in the description. This fixes it. --- params/config.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/params/config.go b/params/config.go index 7576170d26..a69610c9ba 100644 --- a/params/config.go +++ b/params/config.go @@ -531,25 +531,25 @@ func (c *ChainConfig) Description() string { banner += fmt.Sprintf(" - Prague: @%-10v\n", *c.PragueTime) } if c.OsakaTime != nil { - banner += fmt.Sprintf(" - Osaka: @%-10v\n", *c.OsakaTime) + banner += fmt.Sprintf(" - Osaka: @%-10v\n", *c.OsakaTime) } if c.VerkleTime != nil { banner += fmt.Sprintf(" - Verkle: @%-10v\n", *c.VerkleTime) } if c.BPO1Time != nil { - banner += fmt.Sprintf(" - BPO1: @%-10v\n", *c.BPO1Time) + banner += fmt.Sprintf(" - BPO1: @%-10v\n", *c.BPO1Time) } if c.BPO2Time != nil { - banner += fmt.Sprintf(" - BPO2: @%-10v\n", *c.BPO2Time) + banner += fmt.Sprintf(" - BPO2: @%-10v\n", *c.BPO2Time) } if c.BPO3Time != nil { - banner += fmt.Sprintf(" - BPO3: @%-10v\n", *c.BPO3Time) + banner += fmt.Sprintf(" - BPO3: @%-10v\n", *c.BPO3Time) } if c.BPO4Time != nil { - banner += fmt.Sprintf(" - BPO4: @%-10v\n", *c.BPO4Time) + banner += fmt.Sprintf(" - BPO4: @%-10v\n", *c.BPO4Time) } if c.BPO5Time != nil { - banner += fmt.Sprintf(" - BPO5: @%-10v\n", *c.BPO5Time) + banner += fmt.Sprintf(" - BPO5: @%-10v\n", *c.BPO5Time) } return banner } From d68528cadb62141a423fc8bf66f65e4269ba5d90 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 10 Sep 2025 15:33:15 +0200 Subject: [PATCH 022/119] core/txpool: add sanity overflow check (#32544) Adds a sanity check in the transaction pool Co-authored-by @rjl493456442 --- core/txpool/legacypool/list.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/txpool/legacypool/list.go b/core/txpool/legacypool/list.go index 507c0b429a..0c9f13c62f 100644 --- a/core/txpool/legacypool/list.go +++ b/core/txpool/legacypool/list.go @@ -323,15 +323,22 @@ func (l *list) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transa if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 { return false, nil } - // Old is being replaced, subtract old cost - l.subTotalCost([]*types.Transaction{old}) } // Add new tx cost to totalcost cost, overflow := uint256.FromBig(tx.Cost()) if overflow { return false, nil } - l.totalcost.Add(l.totalcost, cost) + total, overflow := new(uint256.Int).AddOverflow(l.totalcost, cost) + if overflow { + return false, nil + } + l.totalcost = total + + // Old is being replaced, subtract old cost + if old != nil { + l.subTotalCost([]*types.Transaction{old}) + } // Otherwise overwrite the old transaction with the current one l.txs.Put(tx) From dd7fe1be4b4103ad84a4a14674cdfc38b579615d Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 10 Sep 2025 15:37:49 +0200 Subject: [PATCH 023/119] core/vm: fix modexp gas calculation (#32568) fixes a bug in the gas calculation found by oss-fuzz --- core/vm/contracts.go | 13 ++++++++++--- core/vm/contracts_test.go | 26 +++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index a06b0d09ca..77fac2f907 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -469,12 +469,19 @@ func modexpIterationCount(expLen uint64, expHead uint256.Int, multiplier uint64) // For large exponents (expLen > 32), add (expLen - 32) * multiplier if expLen > 32 { - iterationCount = (expLen - 32) * multiplier + carry, count := bits.Mul64(expLen-32, multiplier) + if carry > 0 { + return math.MaxUint64 + } + iterationCount = count } - // Add the MSB position - 1 if expHead is non-zero if bitLen := expHead.BitLen(); bitLen > 0 { - iterationCount += uint64(bitLen - 1) + count, carry := bits.Add64(iterationCount, uint64(bitLen-1), 0) + if carry > 0 { + return math.MaxUint64 + } + iterationCount = count } return max(iterationCount, 1) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index da44e250e1..3200ace5cc 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -118,7 +118,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) - gas := p.RequiredGas(in) - 1 + gas := test.Gas - 1 t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { _, _, err := RunPrecompiledContract(p, in, gas, nil) @@ -257,6 +257,30 @@ func TestPrecompiledModExpOOG(t *testing.T) { for _, test := range modexpTests { testPrecompiledOOG("05", test, t) } + modexpTestsEIP2565, err := loadJson("modexp_eip2565") + if err != nil { + t.Fatal(err) + } + for _, test := range modexpTestsEIP2565 { + testPrecompiledOOG("f5", test, t) + } + modexpTestsEIP7883, err := loadJson("modexp_eip7883") + if err != nil { + t.Fatal(err) + } + for _, test := range modexpTestsEIP7883 { + testPrecompiledOOG("f6", test, t) + } + gasCostTest := precompiledTest{ + Input: "000000000000000000000000000000000000000000000000000000000000082800000000000000000000000000000000000000000000000040000000000000090000000000000000000000000000000000000000000000000000000000000600000000adadadad00000000ff31ff00000006ffffffffffffffffffffffffffffffffffffffff0000000000000004ffffffffffffff0000000000000000000000000000000000000000d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0000001000200fefffeff01000100000000000000ffff01000100ffffffff01000100ffffffff0000050001000100fefffdff02000300ff000000000000012b000000000000090000000000000000000000000000000000000000000000000000ffffff000000000200fffffeff00000001000000000001000200fefffeff010001000000000000000000423034000000000011006161ffbf640053004f00ff00fffffffffffffff3ff00000000000f00002dffffffffff0000000000000000000061999999999999999999999999899961ffffffff0100010000000000000000000000000600000000adadadad00000000ffff00000006fffffdffffffffffffffffffffffffffffffffff0000000000000004ffffffffffffff000000000000000000000000000000000000000098000000966375726c2f66000030000000000011006161ffbf640053004f002d00000000a200000000000000ff1818183fffffffff3a6e756c6c2c22223a6e7500006c2000000000002d2d0000000000000000000144ccef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000fdff000000ff00290001000009000000000000000000000000000000000000000000000000a50004ff2800000000000000000000000000000000000000000000000001000000000000090000000000000000000000030000000000000000002b00000000000000000600000000adadadad00000000ffff00000006ffffffffffffffffffffffffffffffffffffffff0000000000000004ffffffffffffff0000000000000000000000000000000000000000d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d000000000717a1a001a1a1a1a1a1a000000121212121212121212121212121212121212121212d0d0d0d01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212373800002d35373837346137346161610000000000000000d0d0d0d0d0d0d0d0002d3533321a1a000000d0d0d0d0d0d0d0d0d0d0d0d0d0d000000000717a1a001a1a1a1a1a1a000000121212121212121212121212121212121212121212d0d0d0d012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121a1212121212121212000000000000000000000000d0d0d0d0d0d0d0d0002d3533321a1a0000000000000000000000003300000001000f5b00001100712c6eff9e61000000000061000000fbffff1a1a3a6e353900756c6c7d3b00000000009100002d35ff00600000000000000000002d3533321a1a1a1a3a6e353900756c6c7d3b000000000091373800002d3537383734613734616161d0d0d0d0d000000000717a1a001a1a1a1a1a1a000000121212121212121212121212121212121212121212d0d0d0d012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121a1212121212121212000000000000000000000000d0d0d0d0d0d0d0d0002d3533321a1a0000000000000000000000003300000001000f5b00001100712c6eff9e61000000000061000000fbffff1a1a3a6e353900756c6c7d3b00000000009100002d35ff00600000000000000000002d3533321a1a1a1a3a6e353900756c6c7d3b000000000091373800002d353738373461373461616100000000000000000000000000000000000000000000000001000000000000090000000000000000000000030000000000000000002b00000000000000000600000000adadadad00000000ffff00000006ffffffffffffffffffffffffffffffffffffffff0000000000000004ffffffffffffff0000000000000000000000000000000000000000d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d000000000717a1a001a1a1a1a1a1a000000121212121212121212121212121212121212121212d0d0d0d01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212373800002d35373837346137346161610000000000000000d0d0d0d0d0d0d0d0002d3533321a1a000000d0d0d0d0d0d0d0d0d0d0d0d0d0d000000000717a1a001a1a1a1a1a1a000000121212121212121212121212121212121212121212d0d0d0d012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121a1212121212121212000000000000000000000000d0d0d0d0d0d0d0d0002d3533321a1a0000000000000000000000003300000001000f5b00001100712c6eff9e61000000000061000000fbffff1a1a3a6e353900756c6c7d3b00000000009100002d35ff00600000000000000000002d3533321a1a1a1a3a6e353900756c6c7d3b000000000091373800002d3537383734613734616161d0d0d0d0d000000000717a1a001a1a1a1a1a1a0000001212121212121212121212121212121212121212000000000000003300000001000f5b00001100712c6eff9e61000000000061000000fbffff1a1a3a6e353900756c6c7d3b00000000009100002d35ff00600000000000000000002d3533321a1a1a1a3a6e353900756c6c7d3b000000000091373800002d3537383734613734616161", + Expected: "000000000000000000000000000000000000000000000000", + Name: "oss_fuzz_gas_calc", + Gas: 18446744073709551615, + NoBenchmark: false, + } + testPrecompiledOOG("05", gasCostTest, t) + testPrecompiledOOG("f5", gasCostTest, t) + testPrecompiledOOG("f6", gasCostTest, t) } // Tests the sample inputs from the elliptic curve scalar multiplication EIP 213. From b708feb9c34b0f62fc76cee549dbde6fbe939f22 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 10 Sep 2025 21:44:18 +0800 Subject: [PATCH 024/119] core/vm: fix comment and remove unused divisor in osakaModexpGas (#32553) The `divisor` const is not needed in the gas cost calculation in `osakaModexpGas`. --- core/vm/contracts.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 77fac2f907..34cb766708 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -538,7 +538,6 @@ func berlinModexpGas(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 func osakaModexpGas(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 { const ( multiplier = 16 - divisor = 3 minGas = 500 ) @@ -549,7 +548,7 @@ func osakaModexpGas(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 } iterationCount := modexpIterationCount(expLen, expHead, multiplier) - // Calculate gas: (multComplexity * iterationCount) / osakaDivisor + // Calculate gas: multComplexity * iterationCount carry, gas := bits.Mul64(iterationCount, multComplexity) if carry != 0 { return math.MaxUint64 From 1f7f95d7182104e705a0735df456877764d1f562 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 10 Sep 2025 19:51:04 +0200 Subject: [PATCH 025/119] p2p/discover: remove delay from discv5 RandomNodes (#32517) Refresh is doing some lookups and thus it could block for some time. We do not want the initializer of an iterator to block. If there is something blocking, it should happen when calling Next. Here, next will start a lookup, which will wait if needed (no nodes), making sure the iterator's Next is not creating a busy loop. Signed-off-by: Csaba Kiraly --- p2p/discover/v5_udp.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index 9679f5c61a..c13032e1af 100644 --- a/p2p/discover/v5_udp.go +++ b/p2p/discover/v5_udp.go @@ -328,12 +328,6 @@ func (t *UDPv5) TalkRequestToID(id enode.ID, addr netip.AddrPort, protocol strin // RandomNodes returns an iterator that finds random nodes in the DHT. func (t *UDPv5) RandomNodes() enode.Iterator { - if t.tab.len() == 0 { - // All nodes were dropped, refresh. The very first query will hit this - // case and run the bootstrapping logic. - <-t.tab.refresh() - } - return newLookupIterator(t.closeCtx, t.newRandomLookup) } From 46e4f0b5c1d269e29d26a273016b18afbd13bbc4 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 29 Aug 2025 14:23:45 +0200 Subject: [PATCH 026/119] p2p/discover: add waitForNodes --- p2p/discover/lookup.go | 17 +++-------------- p2p/discover/table.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index 09808b71e0..db1bbe32f9 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -19,7 +19,6 @@ package discover import ( "context" "errors" - "time" "github.com/ethereum/go-ethereum/p2p/enode" ) @@ -106,11 +105,10 @@ func (it *lookup) startQueries() bool { // The first query returns nodes from the local table. if it.queries == -1 { closest := it.tab.findnodeByID(it.result.target, bucketSize, false) - // Avoid finishing the lookup too quickly if table is empty. It'd be better to wait - // for the table to fill in this case, but there is no good mechanism for that - // yet. + // Avoid finishing the lookup too quickly if table is empty. + // Wait for the table to fill. if len(closest.entries) == 0 { - it.slowdown() + it.tab.waitForNodes(1) } it.queries = 1 it.replyCh <- closest.entries @@ -130,15 +128,6 @@ func (it *lookup) startQueries() bool { return it.queries > 0 } -func (it *lookup) slowdown() { - sleep := time.NewTimer(1 * time.Second) - defer sleep.Stop() - select { - case <-sleep.C: - case <-it.tab.closeReq: - } -} - func (it *lookup) query(n *enode.Node, reply chan<- []*enode.Node) { r, err := it.queryfunc(n) if !errors.Is(err, errClosed) { // avoid recording failures on shutdown. diff --git a/p2p/discover/table.go b/p2p/discover/table.go index b6c35aaaa9..63aa655256 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p/enode" @@ -84,6 +85,7 @@ type Table struct { closeReq chan struct{} closed chan struct{} + nodeFeed event.FeedOf[*enode.Node] nodeAddedHook func(*bucket, *tableNode) nodeRemovedHook func(*bucket, *tableNode) } @@ -567,6 +569,8 @@ func (tab *Table) nodeAdded(b *bucket, n *tableNode) { } n.addedToBucket = time.Now() tab.revalidation.nodeAdded(tab, n) + + tab.nodeFeed.Send(n.Node) if tab.nodeAddedHook != nil { tab.nodeAddedHook(b, n) } @@ -702,3 +706,36 @@ func (tab *Table) deleteNode(n *enode.Node) { b := tab.bucket(n.ID()) tab.deleteInBucket(b, n.ID()) } + +// waitForNodes blocks until the table contains at least n nodes. +func (tab *Table) waitForNodes(n int) bool { + getlength := func() (count int) { + for _, b := range &tab.buckets { + count += len(b.entries) + } + return count + } + + var ch chan *enode.Node + for { + tab.mutex.Lock() + if getlength() >= n { + tab.mutex.Unlock() + return true + } + if ch == nil { + // Init subscription. + ch = make(chan *enode.Node) + sub := tab.nodeFeed.Subscribe(ch) + defer sub.Unsubscribe() + } + tab.mutex.Unlock() + + // Wait for a node add event. + select { + case <-ch: + case <-tab.closeReq: + return false + } + } +} From f8e0e8dc550621711d6702966a06c35fe9c99126 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 29 Aug 2025 15:47:09 +0200 Subject: [PATCH 027/119] p2p/discover: add context in waitForNodes --- p2p/discover/table.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/p2p/discover/table.go b/p2p/discover/table.go index 63aa655256..6a1c7494ee 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -708,7 +708,7 @@ func (tab *Table) deleteNode(n *enode.Node) { } // waitForNodes blocks until the table contains at least n nodes. -func (tab *Table) waitForNodes(n int) bool { +func (tab *Table) waitForNodes(ctx context.Context, n int) error { getlength := func() (count int) { for _, b := range &tab.buckets { count += len(b.entries) @@ -721,7 +721,7 @@ func (tab *Table) waitForNodes(n int) bool { tab.mutex.Lock() if getlength() >= n { tab.mutex.Unlock() - return true + return nil } if ch == nil { // Init subscription. @@ -734,8 +734,10 @@ func (tab *Table) waitForNodes(n int) bool { // Wait for a node add event. select { case <-ch: + case <-ctx.Done(): + return ctx.Err() case <-tab.closeReq: - return false + return errClosed } } } From f4046b0cfbfb60d1117a74d05a07dcd50d8dc753 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 29 Aug 2025 15:47:28 +0200 Subject: [PATCH 028/119] p2p/discover: move wait condition to lookupIterator --- p2p/discover/lookup.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index db1bbe32f9..de4761d737 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -19,6 +19,7 @@ package discover import ( "context" "errors" + "time" "github.com/ethereum/go-ethereum/p2p/enode" ) @@ -105,10 +106,8 @@ func (it *lookup) startQueries() bool { // The first query returns nodes from the local table. if it.queries == -1 { closest := it.tab.findnodeByID(it.result.target, bucketSize, false) - // Avoid finishing the lookup too quickly if table is empty. - // Wait for the table to fill. if len(closest.entries) == 0 { - it.tab.waitForNodes(1) + return false } it.queries = 1 it.replyCh <- closest.entries @@ -171,6 +170,7 @@ func (it *lookupIterator) Next() bool { if len(it.buffer) > 0 { it.buffer = it.buffer[1:] } + // Advance the lookup to refill the buffer. for len(it.buffer) == 0 { if it.ctx.Err() != nil { @@ -183,6 +183,7 @@ func (it *lookupIterator) Next() bool { continue } if !it.lookup.advance() { + it.lookupFailed(it.lookup.tab) it.lookup = nil continue } @@ -191,6 +192,16 @@ func (it *lookupIterator) Next() bool { return true } +// lookupFailed handles failed lookup attempts. This can be called when the table has +// exited, or when it runs out of nodes. +func (it *lookupIterator) lookupFailed(tab *Table) { + timeout, cancel := context.WithTimeout(it.ctx, 1*time.Minute) + defer cancel() + tab.waitForNodes(timeout, 1) + + // TODO: here we need to trigger a table refresh somehow +} + // Close ends the iterator. func (it *lookupIterator) Close() { it.cancel() From 4ed8f5ee2b99a42fb3dca9e7083de1c7e05c39c4 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 5 Sep 2025 11:15:09 +0200 Subject: [PATCH 029/119] p2p/discover: improve iterator --- p2p/discover/lookup.go | 48 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index de4761d737..91d4d7a919 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -49,11 +49,14 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l result: nodesByDistance{target: target}, replyCh: make(chan []*enode.Node, alpha), cancelCh: ctx.Done(), - queries: -1, } // Don't query further if we hit ourself. // Unlikely to happen often in practice. it.asked[tab.self().ID()] = true + + // Initialize the lookup with nodes from table. + closest := it.tab.findnodeByID(it.result.target, bucketSize, false) + it.addNodes(closest.entries) return it } @@ -64,22 +67,18 @@ func (it *lookup) run() []*enode.Node { return it.result.entries } +func (it *lookup) empty() bool { + return len(it.replyBuffer) == 0 +} + // advance advances the lookup until any new nodes have been found. // It returns false when the lookup has ended. func (it *lookup) advance() bool { for it.startQueries() { select { case nodes := <-it.replyCh: - it.replyBuffer = it.replyBuffer[:0] - for _, n := range nodes { - if n != nil && !it.seen[n.ID()] { - it.seen[n.ID()] = true - it.result.push(n, bucketSize) - it.replyBuffer = append(it.replyBuffer, n) - } - } it.queries-- - if len(it.replyBuffer) > 0 { + if it.addNodes(nodes) { return true } case <-it.cancelCh: @@ -89,6 +88,18 @@ func (it *lookup) advance() bool { return false } +func (it *lookup) addNodes(nodes []*enode.Node) (done bool) { + it.replyBuffer = it.replyBuffer[:0] + for _, n := range nodes { + if n != nil && !it.seen[n.ID()] { + it.seen[n.ID()] = true + it.result.push(n, bucketSize) + it.replyBuffer = append(it.replyBuffer, n) + } + } + return len(it.replyBuffer) == 0 +} + func (it *lookup) shutdown() { for it.queries > 0 { <-it.replyCh @@ -103,17 +114,6 @@ func (it *lookup) startQueries() bool { return false } - // The first query returns nodes from the local table. - if it.queries == -1 { - closest := it.tab.findnodeByID(it.result.target, bucketSize, false) - if len(closest.entries) == 0 { - return false - } - it.queries = 1 - it.replyCh <- closest.entries - return true - } - // Ask the closest nodes that we haven't asked yet. for i := 0; i < len(it.result.entries) && it.queries < alpha; i++ { n := it.result.entries[i] @@ -180,10 +180,14 @@ func (it *lookupIterator) Next() bool { } if it.lookup == nil { it.lookup = it.nextLookup(it.ctx) + if it.lookup.empty() { + // If the lookup is empty right after creation, it means the local table + // is in a degraded state, and we need to wait for it to fill again. + it.lookupFailed(it.lookup.tab) + } continue } if !it.lookup.advance() { - it.lookupFailed(it.lookup.tab) it.lookup = nil continue } From e58e7f79272bdec8f0fd367ea943130461d5e94b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 5 Sep 2025 14:15:09 +0200 Subject: [PATCH 030/119] p2p/discover: fix bug in lookup --- p2p/discover/lookup.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index 91d4d7a919..c2b39a8c2b 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -78,7 +78,8 @@ func (it *lookup) advance() bool { select { case nodes := <-it.replyCh: it.queries-- - if it.addNodes(nodes) { + it.addNodes(nodes) + if !it.empty() { return true } case <-it.cancelCh: @@ -88,7 +89,7 @@ func (it *lookup) advance() bool { return false } -func (it *lookup) addNodes(nodes []*enode.Node) (done bool) { +func (it *lookup) addNodes(nodes []*enode.Node) { it.replyBuffer = it.replyBuffer[:0] for _, n := range nodes { if n != nil && !it.seen[n.ID()] { @@ -97,7 +98,6 @@ func (it *lookup) addNodes(nodes []*enode.Node) (done bool) { it.replyBuffer = append(it.replyBuffer, n) } } - return len(it.replyBuffer) == 0 } func (it *lookup) shutdown() { From 721c8de7389cb713c5013817356a99a67a0a2dce Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 5 Sep 2025 14:16:17 +0200 Subject: [PATCH 031/119] p2p/discover: trigger refresh in lookupIterator --- p2p/discover/lookup.go | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index c2b39a8c2b..d15cdccc48 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -142,11 +142,12 @@ func (it *lookup) query(n *enode.Node, reply chan<- []*enode.Node) { // lookupIterator performs lookup operations and iterates over all seen nodes. // When a lookup finishes, a new one is created through nextLookup. type lookupIterator struct { - buffer []*enode.Node - nextLookup lookupFunc - ctx context.Context - cancel func() - lookup *lookup + buffer []*enode.Node + nextLookup lookupFunc + ctx context.Context + cancel func() + lookup *lookup + tabRefreshing <-chan struct{} } type lookupFunc func(ctx context.Context) *lookup @@ -187,11 +188,11 @@ func (it *lookupIterator) Next() bool { } continue } - if !it.lookup.advance() { - it.lookup = nil - continue - } + newNodes := it.lookup.advance() it.buffer = it.lookup.replyBuffer + if !newNodes { + it.lookup = nil + } } return true } @@ -201,9 +202,27 @@ func (it *lookupIterator) Next() bool { func (it *lookupIterator) lookupFailed(tab *Table) { timeout, cancel := context.WithTimeout(it.ctx, 1*time.Minute) defer cancel() - tab.waitForNodes(timeout, 1) - // TODO: here we need to trigger a table refresh somehow + // Wait for Table initialization to complete, in case it is still in progress. + select { + case <-tab.initDone: + case <-timeout.Done(): + return + } + + // Wait for ongoing refresh operation, or trigger one. + if it.tabRefreshing == nil { + it.tabRefreshing = tab.refresh() + } + select { + case <-it.tabRefreshing: + it.tabRefreshing = nil + case <-timeout.Done(): + return + } + + // Wait for the table to fill. + tab.waitForNodes(timeout, 1) } // Close ends the iterator. From cf0503da7ceffb5c24937b1f31e49acfacf9437d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 5 Sep 2025 14:16:51 +0200 Subject: [PATCH 032/119] p2p/discover: track missing nodes in test --- p2p/discover/v4_udp_test.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go index 1af31f4f1b..b1363c73b0 100644 --- a/p2p/discover/v4_udp_test.go +++ b/p2p/discover/v4_udp_test.go @@ -509,18 +509,27 @@ func TestUDPv4_smallNetConvergence(t *testing.T) { // they have all found each other. status := make(chan error, len(nodes)) for i := range nodes { - node := nodes[i] + self := nodes[i] go func() { - found := make(map[enode.ID]bool, len(nodes)) - it := node.RandomNodes() + missing := make(map[enode.ID]bool, len(nodes)) + for _, n := range nodes { + if n.Self().ID() == self.Self().ID() { + continue // skip self + } + missing[n.Self().ID()] = true + } + + it := self.RandomNodes() for it.Next() { - found[it.Node().ID()] = true - if len(found) == len(nodes) { + fmt.Println(self.Self().ID(), "found:", it.Node().ID()) + delete(missing, it.Node().ID()) + if len(missing) == 0 { status <- nil return } } - status <- fmt.Errorf("node %s didn't find all nodes", node.Self().ID().TerminalString()) + missingIDs := slices.Collect(maps.Keys(missing)) + status <- fmt.Errorf("node %s didn't find all nodes, missing %v", self.Self().ID().TerminalString(), missingIDs) }() } @@ -537,7 +546,6 @@ func TestUDPv4_smallNetConvergence(t *testing.T) { received++ if err != nil { t.Error("ERROR:", err) - return } } } From 394670893546240b9c01987e075523ca3c33a300 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 9 Sep 2025 12:05:04 +0200 Subject: [PATCH 033/119] p2p/discover: fix two bugs in lookup iterator The lookup would add self into the replyBuffer if returned by another node. Avoid doing that by marking self as seen. With the changed initialization behavior of lookup, the lookupIterator needs to yield the buffer right after creation. This fixes the smallNetConvergence test, where all results are straight out of the local table. --- p2p/discover/lookup.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index d15cdccc48..88c93a57cf 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -53,6 +53,7 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l // Don't query further if we hit ourself. // Unlikely to happen often in practice. it.asked[tab.self().ID()] = true + it.seen[tab.self().ID()] = true // Initialize the lookup with nodes from table. closest := it.tab.findnodeByID(it.result.target, bucketSize, false) @@ -186,8 +187,12 @@ func (it *lookupIterator) Next() bool { // is in a degraded state, and we need to wait for it to fill again. it.lookupFailed(it.lookup.tab) } + // Otherwise, yield the initial nodes from the iterator before advancing + // the lookup. + it.buffer = it.lookup.replyBuffer continue } + newNodes := it.lookup.advance() it.buffer = it.lookup.replyBuffer if !newNodes { From 3133fd369a7bde62a6964ef1907a07fe2eb3c91a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 9 Sep 2025 12:07:54 +0200 Subject: [PATCH 034/119] p2p/discover: remove print in test --- p2p/discover/v4_udp_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go index b1363c73b0..21c4daada3 100644 --- a/p2p/discover/v4_udp_test.go +++ b/p2p/discover/v4_udp_test.go @@ -521,7 +521,6 @@ func TestUDPv4_smallNetConvergence(t *testing.T) { it := self.RandomNodes() for it.Next() { - fmt.Println(self.Self().ID(), "found:", it.Node().ID()) delete(missing, it.Node().ID()) if len(missing) == 0 { status <- nil From a9f9e0d5894eea88b4ae5acdb637e0448de19c85 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 9 Sep 2025 12:08:44 +0200 Subject: [PATCH 035/119] p2p/discover: add imports in test --- p2p/discover/v4_udp_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go index 21c4daada3..44863183fa 100644 --- a/p2p/discover/v4_udp_test.go +++ b/p2p/discover/v4_udp_test.go @@ -24,10 +24,12 @@ import ( "errors" "fmt" "io" + "maps" "math/rand" "net" "net/netip" "reflect" + "slices" "sync" "testing" "time" From 046e2cd7a4315a9ebdcc2945059c0884f857b5d6 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Thu, 11 Sep 2025 00:18:11 -0600 Subject: [PATCH 036/119] params: add bpo forks to eth_config (#32579) BPO forks should also be included in eth_config response. --- params/config.go | 36 +++++++++++++++++++++++++++++++++--- params/forks/forks.go | 5 +++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/params/config.go b/params/config.go index a69610c9ba..b1297144c3 100644 --- a/params/config.go +++ b/params/config.go @@ -972,6 +972,16 @@ func (c *ChainConfig) LatestFork(time uint64) forks.Fork { london := c.LondonBlock switch { + case c.IsBPO5(london, time): + return forks.BPO5 + case c.IsBPO4(london, time): + return forks.BPO4 + case c.IsBPO3(london, time): + return forks.BPO3 + case c.IsBPO2(london, time): + return forks.BPO2 + case c.IsBPO1(london, time): + return forks.BPO1 case c.IsOsaka(london, time): return forks.Osaka case c.IsPrague(london, time): @@ -988,12 +998,22 @@ func (c *ChainConfig) LatestFork(time uint64) forks.Fork { // BlobConfig returns the blob config associated with the provided fork. func (c *ChainConfig) BlobConfig(fork forks.Fork) *BlobConfig { switch fork { + case forks.BPO5: + return c.BlobScheduleConfig.BPO5 + case forks.BPO4: + return c.BlobScheduleConfig.BPO4 + case forks.BPO3: + return c.BlobScheduleConfig.BPO3 + case forks.BPO2: + return c.BlobScheduleConfig.BPO2 + case forks.BPO1: + return c.BlobScheduleConfig.BPO1 case forks.Osaka: - return DefaultOsakaBlobConfig + return c.BlobScheduleConfig.Osaka case forks.Prague: - return DefaultPragueBlobConfig + return c.BlobScheduleConfig.Prague case forks.Cancun: - return DefaultCancunBlobConfig + return c.BlobScheduleConfig.Cancun default: return nil } @@ -1023,6 +1043,16 @@ func (c *ChainConfig) ActiveSystemContracts(time uint64) map[string]common.Addre // the fork isn't defined or isn't a time-based fork. func (c *ChainConfig) Timestamp(fork forks.Fork) *uint64 { switch { + case fork == forks.BPO5: + return c.BPO5Time + case fork == forks.BPO4: + return c.BPO4Time + case fork == forks.BPO3: + return c.BPO3Time + case fork == forks.BPO2: + return c.BPO2Time + case fork == forks.BPO1: + return c.BPO1Time case fork == forks.Osaka: return c.OsakaTime case fork == forks.Prague: diff --git a/params/forks/forks.go b/params/forks/forks.go index 5c9612a625..aab0a54ab7 100644 --- a/params/forks/forks.go +++ b/params/forks/forks.go @@ -40,6 +40,11 @@ const ( Cancun Prague Osaka + BPO1 + BPO2 + BPO3 + BPO4 + BPO5 ) // String implements fmt.Stringer. From 92fbbe63c8453d2497f6fc1f939439ef241c8467 Mon Sep 17 00:00:00 2001 From: GarmashAlex Date: Thu, 11 Sep 2025 11:05:17 +0300 Subject: [PATCH 037/119] ethdb/pebble: set metric namespace correctly (#32563) Ensure Database.namespace is initialized in pebble.New(...). Without this, the write-stall metrics registered in onWriteStallBegin/End are emitted without the intended namespace prefix, while other Pebble metrics use the provided constructor parameter. This aligns stall metrics with the rest of the Pebble metric set and fixes inconsistent metric naming. --------- Co-authored-by: rjl493456442 --- ethdb/pebble/pebble.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index 2370d4654f..8abe7d4bc7 100644 --- a/ethdb/pebble/pebble.go +++ b/ethdb/pebble/pebble.go @@ -218,9 +218,10 @@ func New(file string, cache int, handles int, namespace string, readonly bool) ( memTableSize = maxMemTableSize - 1 } db := &Database{ - fn: file, - log: logger, - quitChan: make(chan chan error), + fn: file, + log: logger, + quitChan: make(chan chan error), + namespace: namespace, // Use asynchronous write mode by default. Otherwise, the overhead of frequent fsync // operations can be significant, especially on platforms with slow fsync performance From cbf0b5bc92c4ba857d79778f646f0b6b9507d07f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 11 Sep 2025 13:07:04 +0200 Subject: [PATCH 038/119] go.mod, build: require go 1.24 and upgrade linter (#32584) --- .github/workflows/validate_pr.yml | 2 +- build/checksums.txt | 83 +++++++++++------------------ crypto/blake2b/blake2b.go | 4 +- crypto/blake2b/blake2b_generic.go | 2 +- crypto/bn256/cloudflare/gfp_decl.go | 2 +- go.mod | 2 +- 6 files changed, 36 insertions(+), 59 deletions(-) diff --git a/.github/workflows/validate_pr.yml b/.github/workflows/validate_pr.yml index 8c9ae1688f..02ebf431b0 100644 --- a/.github/workflows/validate_pr.yml +++ b/.github/workflows/validate_pr.yml @@ -13,7 +13,7 @@ jobs: with: script: | const prTitle = context.payload.pull_request.title; - const titleRegex = /^(\.?[\w\s,{}/]+): .+/; + const titleRegex = /^(\.?[\w\s,{}/.]+): .+/; if (!titleRegex.test(prTitle)) { core.setFailed(`PR title "${prTitle}" does not match required format: directory, ...: description`); diff --git a/build/checksums.txt b/build/checksums.txt index ab0f7547f6..19a3a5fadd 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -54,60 +54,37 @@ afd9e0a8d2665ff122c8302bb4a3ce4a5331e4e630ddc388be1f9238adfa8fe3 go1.25.0.windo 27bab004c72b3d7bd05a69b6ec0fc54a309b4b78cc569dd963d8b3ec28bfdb8c go1.25.0.windows-arm64.zip 357d030b217ff68e700b6cfc56097bc21ad493bb45b79733a052d112f5031ed9 go1.25.0.windows-arm64.msi -# version:golangci 2.0.2 +# version:golangci 2.4.0 # https://github.com/golangci/golangci-lint/releases/ -# https://github.com/golangci/golangci-lint/releases/download/v2.0.2/ -a88cbdc86b483fe44e90bf2dcc3fec2af8c754116e6edf0aa6592cac5baa7a0e golangci-lint-2.0.2-darwin-amd64.tar.gz -664550e7954f5f4451aae99b4f7382c1a47039c66f39ca605f5d9af1a0d32b49 golangci-lint-2.0.2-darwin-arm64.tar.gz -bda0f0f27d300502faceda8428834a76ca25986f6d9fc2bd41d201c3ed73f08e golangci-lint-2.0.2-freebsd-386.tar.gz -1cbd0c7ade3fb027d61d38a646ec1b51be5846952b4b04a5330e7f4687f2270c golangci-lint-2.0.2-freebsd-amd64.tar.gz -1e828a597726198b2e35acdbcc5f3aad85244d79846d2d2bdb05241c5a535f9e golangci-lint-2.0.2-freebsd-armv6.tar.gz -848b49315dc5cddd0c9ce35e96ab33d584db0ea8fb57bcbf9784f1622bec0269 golangci-lint-2.0.2-freebsd-armv7.tar.gz -cabf9a6beab574c7f98581eb237919e580024759e3cdc05c4d516b044dce6770 golangci-lint-2.0.2-illumos-amd64.tar.gz -2fde80d15ed6527791f106d606120620e913c3a663c90a8596861d0a4461169e golangci-lint-2.0.2-linux-386.deb -804bc6e350a8c613aaa0a33d8d45414a80157b0ba1b2c2335ac859f85ad98ebd golangci-lint-2.0.2-linux-386.rpm -e64beb72fecf581e57d88ae3adb1c9d4bf022694b6bd92e3c8e460910bbdc37d golangci-lint-2.0.2-linux-386.tar.gz -9c55aed174d7a52bb1d4006b36e7edee9023631f6b814a80cb39c9860d6f75c3 golangci-lint-2.0.2-linux-amd64.deb -c55a2ef741a687b4c679696931f7fd4a467babd64c9457cf17bb9632fd1cecd1 golangci-lint-2.0.2-linux-amd64.rpm -89cc8a7810dc63b9a37900da03e37c3601caf46d42265d774e0f1a5d883d53e2 golangci-lint-2.0.2-linux-amd64.tar.gz -a3e78583c4e7ea1b63e82559f126bb3a5b12788676f158526752d53e67824b99 golangci-lint-2.0.2-linux-arm64.deb -bd5dd52b5c9f18aa7a2904eda9a9f91c628e98623fe70b7afcbb847e2de84422 golangci-lint-2.0.2-linux-arm64.rpm -789d5b91219ac68c2336f77d41cd7e33a910420594780f455893f8453d09595b golangci-lint-2.0.2-linux-arm64.tar.gz -534cd4c464a66178714ed68152c1ed7aa73e5700bf409e4ed1a8363adf96afca golangci-lint-2.0.2-linux-armv6.deb -cf7d02905a5fc80b96c9a64621693b4cc7337b1ce29986c19fd72608dafe66c5 golangci-lint-2.0.2-linux-armv6.rpm -a0d81cb527d8fe878377f2356b5773e219b0b91832a6b59e7b9bcf9a90fe0b0e golangci-lint-2.0.2-linux-armv6.tar.gz -dedd5be7fff8cba8fe15b658a59347ea90d7d02a9fff87f09c7687e6da05a8b6 golangci-lint-2.0.2-linux-armv7.deb -85521b6f3ad2f5a2bc9bfe14b9b08623f764964048f75ed6dfcfaf8eb7d57cc1 golangci-lint-2.0.2-linux-armv7.rpm -96471046c7780dda4ea680f65e92c2ef56ff58d40bcffaf6cfe9d6d48e3c27aa golangci-lint-2.0.2-linux-armv7.tar.gz -815d914a7738e4362466b2d11004e8618b696b49e8ace13df2c2b25f28fb1e17 golangci-lint-2.0.2-linux-loong64.deb -f16381e3d8a0f011b95e086d83d620248432b915d01f4beab4d29cfe4dc388b0 golangci-lint-2.0.2-linux-loong64.rpm -1bd8d7714f9c92db6a0f23bae89f39c85ba047bec8eeb42b8748d30ae3228d18 golangci-lint-2.0.2-linux-loong64.tar.gz -ea6e9d4aabb526aa298e47e8b026d8893d918c5eb919ba0ab403e315def74cc5 golangci-lint-2.0.2-linux-mips64.deb -519d8d53af83fdc9c25cc3fba8b663331ac22ef68131d4b0084cb6f425b6f79a golangci-lint-2.0.2-linux-mips64.rpm -80d655a0a1ac1b19dcef4b58fa2a7dadb646cc50ad08d460b5c53cdb421165e4 golangci-lint-2.0.2-linux-mips64.tar.gz -aa0e75384bb482c865d4dfc95d23ceb25666bf20461b67a832f0eea6670312ec golangci-lint-2.0.2-linux-mips64le.deb -f2a8b500fb69bdea1b01df6267aaa5218fa4a58aeb781c1a20d0d802fe465a52 golangci-lint-2.0.2-linux-mips64le.rpm -e66a0c0c9a275f02d27a7caa9576112622306f001d73dfc082cf1ae446fc1242 golangci-lint-2.0.2-linux-mips64le.tar.gz -e85ad51aac6428be2d8a37000d053697371a538a5bcbc1644caa7c5e77f6d0af golangci-lint-2.0.2-linux-ppc64le.deb -906798365eac1944af2a9b9a303e6fd49ec9043307bc681b7a96277f7f8beea5 golangci-lint-2.0.2-linux-ppc64le.rpm -f7f1a271b0af274d6c9ce000f5dc6e1fb194350c67bcc62494f96f791882ba92 golangci-lint-2.0.2-linux-ppc64le.tar.gz -eea8bf643a42bf05de9780530db22923e5ab0d588f0e173594dc6518f2a25d2a golangci-lint-2.0.2-linux-riscv64.deb -4ff40f9fe2954400836e2a011ba4744d00ffab5068a51368552dfce6aba3b81b golangci-lint-2.0.2-linux-riscv64.rpm -531d8f225866674977d630afbf0533eb02f9bec607fb13895f7a2cd7b2e0a648 golangci-lint-2.0.2-linux-riscv64.tar.gz -6f827647046c603f40d97ea5aadc6f48cd0bb5d19f7a3d56500c3b833d2a0342 golangci-lint-2.0.2-linux-s390x.deb -387a090e9576d19ca86aac738172e58e07c19f2784a13bb387f4f0d75fb9c8d3 golangci-lint-2.0.2-linux-s390x.rpm -57de1fb7722a9feb2d11ed0a007a93959d05b9db5929a392abc222e30012467e golangci-lint-2.0.2-linux-s390x.tar.gz -ed95e0492ea86bf79eb661f0334474b2a4255093685ff587eccd797c5a54db7e golangci-lint-2.0.2-netbsd-386.tar.gz -eab81d729778166415d349a80e568b2f2b3a781745a9be3212a92abb1e732daf golangci-lint-2.0.2-netbsd-amd64.tar.gz -d20add73f7c2de2c3b01ed4fd7b63ffcf0a6597d5ea228d1699e92339a3cd047 golangci-lint-2.0.2-netbsd-arm64.tar.gz -4e4f44e6057879cd62424ff1800a767d25a595c0e91d6d48809eea9186b4c739 golangci-lint-2.0.2-netbsd-armv6.tar.gz -51ec17b16d8743ae4098a0171f04f0ed4d64561e3051b982778b0e6c306a1b03 golangci-lint-2.0.2-netbsd-armv7.tar.gz -5482cf27b93fae1765c70ee2a95d4074d038e9dee61bdd61d017ce8893d3a4a8 golangci-lint-2.0.2-source.tar.gz -a35d8fdf3e14079a10880dbbb7586b46faec89be96f086b244b3e565aac80313 golangci-lint-2.0.2-windows-386.zip -fe4b946cc01366b989001215687003a9c4a7098589921f75e6228d6d8cffc15c golangci-lint-2.0.2-windows-amd64.zip -646bd9250ef8c771d85cd22fe8e6f2397ae39599179755e3bbfa9ef97ad44090 golangci-lint-2.0.2-windows-arm64.zip -ce1dc0bad6f8a61d64e6b3779eeb773479c175125d6f686b0e67ef9c8432d16e golangci-lint-2.0.2-windows-armv6.zip -92684a48faabe792b11ac27ca8b25551eff940b0a1e84ad7244e98b4994962db golangci-lint-2.0.2-windows-armv7.zip +# https://github.com/golangci/golangci-lint/releases/download/v2.4.0/ +7904ce63f79db44934939cf7a063086ea0ea98e9b19eba0a9d52ccdd0d21951c golangci-lint-2.4.0-darwin-amd64.tar.gz +cd4dd53fa09b6646baff5fd22b8c64d91db02c21c7496df27992d75d34feec59 golangci-lint-2.4.0-darwin-arm64.tar.gz +d58f426ebe14cc257e81562b4bf37a488ffb4ffbbb3ec73041eb3b38bb25c0e1 golangci-lint-2.4.0-freebsd-386.tar.gz +6ec4a6177fc6c0dd541fbcb3a7612845266d020d35cc6fa92959220cdf64ca39 golangci-lint-2.4.0-freebsd-amd64.tar.gz +4d473e3e71c01feaa915a0604fb35758b41284fb976cdeac3f842118d9ee7e17 golangci-lint-2.4.0-freebsd-armv6.tar.gz +58727746c6530801a3f9a702a5945556a5eb7e88809222536dd9f9d54cafaeff golangci-lint-2.4.0-freebsd-armv7.tar.gz +fbf28c662760e24c32f82f8d16dffdb4a82de7726a52ba1fad94f890c22997ea golangci-lint-2.4.0-illumos-amd64.tar.gz +a15a000a8981ef665e971e0f67e2acda9066a9e37a59344393b7351d8fb49c81 golangci-lint-2.4.0-linux-386.tar.gz +fae792524c04424c0ac369f5b8076f04b45cf29fc945a370e55d369a8dc11840 golangci-lint-2.4.0-linux-amd64.tar.gz +70ac11f55b80ec78fd3a879249cc9255121b8dfd7f7ed4fc46ed137f4abf17e7 golangci-lint-2.4.0-linux-arm64.tar.gz +4acdc40e5cebe99e4e7ced358a05b2e71789f409b41cb4f39bbb86ccfa14b1dc golangci-lint-2.4.0-linux-armv6.tar.gz +2a68749568fa22b4a97cb88dbea655595563c795076536aa6c087f7968784bf3 golangci-lint-2.4.0-linux-armv7.tar.gz +9e3369afb023711036dcb0b4f45c9fe2792af962fa1df050c9f6ac101a6c5d73 golangci-lint-2.4.0-linux-loong64.tar.gz +bb9143d6329be2c4dbfffef9564078e7da7d88e7dde6c829b6263d98e072229e golangci-lint-2.4.0-linux-mips64.tar.gz +5ad1765b40d56cd04d4afd805b3ba6f4bfd9b36181da93c31e9b17e483d8608d golangci-lint-2.4.0-linux-mips64le.tar.gz +918936fb9c0d5ba96bef03cf4348b03938634cfcced49be1e9bb29cb5094fa73 golangci-lint-2.4.0-linux-ppc64le.tar.gz +f7474c638e1fb67ebbdc654b55ca0125377ea0bc88e8fee8d964a4f24eacf828 golangci-lint-2.4.0-linux-riscv64.tar.gz +b617a9543997c8bfceaffa88a75d4e595030c6add69fba800c1e4d8f5fe253dd golangci-lint-2.4.0-linux-s390x.tar.gz +7db027b03a9ba328f795215b04f594036837bc7dd0dd7cd16776b02a6167981c golangci-lint-2.4.0-netbsd-386.tar.gz +52d8f9393f4313df0a62b752c37775e3af0b818e43e8dd28954351542d7c60bc golangci-lint-2.4.0-netbsd-amd64.tar.gz +5c0086027fb5a4af3829e530c8115db4b35d11afe1914322eef528eb8cd38c69 golangci-lint-2.4.0-netbsd-arm64.tar.gz +6b779d6ed1aed87cefe195cc11759902b97a76551b593312c6833f2635a3488f golangci-lint-2.4.0-netbsd-armv6.tar.gz +f00d1f4b7ec3468a0f9fffd0d9ea036248b029b7621cbc9a59c449ef94356d09 golangci-lint-2.4.0-netbsd-armv7.tar.gz +3ce671b0b42b58e35066493aab75a7e2826c9e079988f1ba5d814a4029faaf87 golangci-lint-2.4.0-windows-386.zip +003112f7a56746feaabf20b744054bf9acdf900c9e77176383623c4b1d76aaa9 golangci-lint-2.4.0-windows-amd64.zip +dc0c2092af5d47fc2cd31a1dfe7b4c7e765fab22de98bd21ef2ffcc53ad9f54f golangci-lint-2.4.0-windows-arm64.zip +0263d23e20a260cb1592d35e12a388f99efe2c51b3611fdc66fbd9db1fce664d golangci-lint-2.4.0-windows-armv6.zip +9403c03bf648e6313036e0273149d44bad1b9ad53889b6d00e4ccb842ba3c058 golangci-lint-2.4.0-windows-armv7.zip # This is the builder on PPA that will build Go itself (inception-y), don't modify! # diff --git a/crypto/blake2b/blake2b.go b/crypto/blake2b/blake2b.go index c24a88b99d..e00ee2e6d2 100644 --- a/crypto/blake2b/blake2b.go +++ b/crypto/blake2b/blake2b.go @@ -302,7 +302,7 @@ func appendUint64(b []byte, x uint64) []byte { return append(b, a[:]...) } -//nolint:unused,deadcode +//nolint:unused func appendUint32(b []byte, x uint32) []byte { var a [4]byte binary.BigEndian.PutUint32(a[:], x) @@ -314,7 +314,7 @@ func consumeUint64(b []byte) ([]byte, uint64) { return b[8:], x } -//nolint:unused,deadcode +//nolint:unused func consumeUint32(b []byte) ([]byte, uint32) { x := binary.BigEndian.Uint32(b) return b[4:], x diff --git a/crypto/blake2b/blake2b_generic.go b/crypto/blake2b/blake2b_generic.go index 61e678fdf5..4d3f69d292 100644 --- a/crypto/blake2b/blake2b_generic.go +++ b/crypto/blake2b/blake2b_generic.go @@ -25,7 +25,7 @@ var precomputed = [10][16]byte{ {10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0}, } -// nolint:unused,deadcode +// nolint:unused func hashBlocksGeneric(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) { var m [16]uint64 c0, c1 := c[0], c[1] diff --git a/crypto/bn256/cloudflare/gfp_decl.go b/crypto/bn256/cloudflare/gfp_decl.go index 1954d14a4a..b7dd1a8aac 100644 --- a/crypto/bn256/cloudflare/gfp_decl.go +++ b/crypto/bn256/cloudflare/gfp_decl.go @@ -10,7 +10,7 @@ import ( "golang.org/x/sys/cpu" ) -//nolint:varcheck,unused,deadcode +//nolint:unused var hasBMI2 = cpu.X86.HasBMI2 //go:noescape diff --git a/go.mod b/go.mod index d701c08ad5..28c9af9259 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ethereum/go-ethereum -go 1.23.0 +go 1.24.0 require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 From 1c3703c88874b85bdc7b9eba4d966de4c2a66159 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 11 Sep 2025 20:15:51 +0200 Subject: [PATCH 039/119] .github: run tests with ci.go (#32590) `ci.go` is the place to add custom build flags, build tags, etc. for the test run. So we should use it for CI. --- .github/workflows/go.yml | 2 +- cmd/geth/logging_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 2ff47ce042..cc8ea36d74 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -55,4 +55,4 @@ jobs: cache: false - name: Run tests - run: go test ./... + run: go run build/ci.go test diff --git a/cmd/geth/logging_test.go b/cmd/geth/logging_test.go index 37fffecc30..d420c2d078 100644 --- a/cmd/geth/logging_test.go +++ b/cmd/geth/logging_test.go @@ -91,7 +91,7 @@ func testConsoleLogging(t *testing.T, format string, tStart, tEnd int) { have = censor(have, tStart, tEnd) want = censor(want, tStart, tEnd) if have != want { - t.Logf(nicediff([]byte(have), []byte(want))) + t.Log(nicediff([]byte(have), []byte(want))) t.Fatalf("format %v, line %d\nhave %v\nwant %v", format, i, have, want) } } @@ -142,7 +142,7 @@ func TestJsonLogging(t *testing.T) { } if !bytes.Equal(have, want) { // show an intelligent diff - t.Logf(nicediff(have, want)) + t.Log(nicediff(have, want)) t.Errorf("file content wrong") } } @@ -211,7 +211,7 @@ func TestFileOut(t *testing.T) { } if !bytes.Equal(have, want) { // show an intelligent diff - t.Logf(nicediff(have, want)) + t.Log(nicediff(have, want)) t.Errorf("file content wrong") } } @@ -231,7 +231,7 @@ func TestRotatingFileOut(t *testing.T) { } if !bytes.Equal(have, want) { // show an intelligent diff - t.Logf(nicediff(have, want)) + t.Log(nicediff(have, want)) t.Errorf("file content wrong") } } From 8deb682a35fd4c01b4e63308361a9cd48b26a981 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 11 Sep 2025 20:57:18 +0200 Subject: [PATCH 040/119] build: upgrade to go 1.25.1 (#32593) --- build/checksums.txt | 89 +++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index 19a3a5fadd..6e65fa47fb 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -5,54 +5,49 @@ # https://github.com/ethereum/execution-spec-tests/releases/download/fusaka-devnet-3%40v1.0.0 576261e1280e5300c458aa9b05eccb2fec5ff80a0005940dc52fa03fdd907249 fixtures_fusaka-devnet-3.tar.gz -# version:golang 1.25.0 +# version:golang 1.25.1 # https://go.dev/dl/ -4bd01e91297207bfa450ea40d4d5a93b1b531a5e438473b2a06e18e077227225 go1.25.0.src.tar.gz -e5234a7dac67bc86c528fe9752fc9d63557918627707a733ab4cac1a6faed2d4 go1.25.0.aix-ppc64.tar.gz -5bd60e823037062c2307c71e8111809865116714d6f6b410597cf5075dfd80ef go1.25.0.darwin-amd64.tar.gz -95e836238bcf8f9a71bffea43344cbd35ee1f16db3aaced2f98dbac045d102db go1.25.0.darwin-amd64.pkg -544932844156d8172f7a28f77f2ac9c15a23046698b6243f633b0a0b00c0749c go1.25.0.darwin-arm64.tar.gz -202a0d8338c152cb4c9f04782429e9ba8bef31d9889272380837e4043c9d800a go1.25.0.darwin-arm64.pkg -5ed3cf9a810a1483822538674f1336c06b51aa1b94d6d545a1a0319a48177120 go1.25.0.dragonfly-amd64.tar.gz -abea5d5c6697e6b5c224731f2158fe87c602996a2a233ac0c4730cd57bf8374e go1.25.0.freebsd-386.tar.gz -86e6fe0a29698d7601c4442052dac48bd58d532c51cccb8f1917df648138730b go1.25.0.freebsd-amd64.tar.gz -d90b78e41921f72f30e8bbc81d9dec2cff7ff384a33d8d8debb24053e4336bfe go1.25.0.freebsd-arm.tar.gz -451d0da1affd886bfb291b7c63a6018527b269505db21ce6e14724f22ab0662e go1.25.0.freebsd-arm64.tar.gz -7b565f76bd8bda46549eeaaefe0e53b251e644c230577290c0f66b1ecdb3cdbe go1.25.0.freebsd-riscv64.tar.gz -b1e1fdaab1ad25aa1c08d7a36c97d45d74b98b89c3f78c6d2145f77face54a2c go1.25.0.illumos-amd64.tar.gz -8c602dd9d99bc9453b3995d20ce4baf382cc50855900a0ece5de9929df4a993a go1.25.0.linux-386.tar.gz -2852af0cb20a13139b3448992e69b868e50ed0f8a1e5940ee1de9e19a123b613 go1.25.0.linux-amd64.tar.gz -05de75d6994a2783699815ee553bd5a9327d8b79991de36e38b66862782f54ae go1.25.0.linux-arm64.tar.gz -a5a8f8198fcf00e1e485b8ecef9ee020778bf32a408a4e8873371bfce458cd09 go1.25.0.linux-armv6l.tar.gz -cab86b1cf761b1cb3bac86a8877cfc92e7b036fc0d3084123d77013d61432afc go1.25.0.linux-loong64.tar.gz -d66b6fb74c3d91b9829dc95ec10ca1f047ef5e89332152f92e136cf0e2da5be1 go1.25.0.linux-mips.tar.gz -4082e4381a8661bc2a839ff94ba3daf4f6cde20f8fb771b5b3d4762dc84198a2 go1.25.0.linux-mips64.tar.gz -70002c299ec7f7175ac2ef673b1b347eecfa54ae11f34416a6053c17f855afcc go1.25.0.linux-mips64le.tar.gz -b00a3a39eff099f6df9f1c7355bf28e4589d0586f42d7d4a394efb763d145a73 go1.25.0.linux-mipsle.tar.gz -df166f33bd98160662560a72ff0b4ba731f969a80f088922bddcf566a88c1ec1 go1.25.0.linux-ppc64.tar.gz -0f18a89e7576cf2c5fa0b487a1635d9bcbf843df5f110e9982c64df52a983ad0 go1.25.0.linux-ppc64le.tar.gz -c018ff74a2c48d55c8ca9b07c8e24163558ffec8bea08b326d6336905d956b67 go1.25.0.linux-riscv64.tar.gz -34e5a2e19f2292fbaf8783e3a241e6e49689276aef6510a8060ea5ef54eee408 go1.25.0.linux-s390x.tar.gz -f8586cdb7aa855657609a5c5f6dbf523efa00c2bbd7c76d3936bec80aa6c0aba go1.25.0.netbsd-386.tar.gz -ae8dc1469385b86a157a423bb56304ba45730de8a897615874f57dd096db2c2a go1.25.0.netbsd-amd64.tar.gz -1ff7e4cc764425fc9dd6825eaee79d02b3c7cafffbb3691687c8d672ade76cb7 go1.25.0.netbsd-arm.tar.gz -e1b310739f26724216aa6d7d7208c4031f9ff54c9b5b9a796ddc8bebcb4a5f16 go1.25.0.netbsd-arm64.tar.gz -4802a9b20e533da91adb84aab42e94aa56cfe3e5475d0550bed3385b182e69d8 go1.25.0.openbsd-386.tar.gz -c016cd984bebe317b19a4f297c4f50def120dc9788490540c89f28e42f1dabe1 go1.25.0.openbsd-amd64.tar.gz -a1e31d0bf22172ddde42edf5ec811ef81be43433df0948ece52fecb247ccfd8d go1.25.0.openbsd-arm.tar.gz -343ea8edd8c218196e15a859c6072d0dd3246fbbb168481ab665eb4c4140458d go1.25.0.openbsd-arm64.tar.gz -694c14da1bcaeb5e3332d49bdc2b6d155067648f8fe1540c5de8f3cf8e157154 go1.25.0.openbsd-ppc64.tar.gz -aa510ad25cf54c06cd9c70b6d80ded69cb20188ac6e1735655eef29ff7e7885f go1.25.0.openbsd-riscv64.tar.gz -46f8cef02086cf04bf186c5912776b56535178d4cb319cd19c9fdbdd29231986 go1.25.0.plan9-386.tar.gz -29b34391d84095e44608a228f63f2f88113a37b74a79781353ec043dfbcb427b go1.25.0.plan9-amd64.tar.gz -0a047107d13ebe7943aaa6d54b1d7bbd2e45e68ce449b52915a818da715799c2 go1.25.0.plan9-arm.tar.gz -9977f9e4351984364a3b2b78f8b88bfd1d339812356d5237678514594b7d3611 go1.25.0.solaris-amd64.tar.gz -df9f39db82a803af0db639e3613a36681ab7a42866b1384b3f3a1045663961a7 go1.25.0.windows-386.zip -afd9e0a8d2665ff122c8302bb4a3ce4a5331e4e630ddc388be1f9238adfa8fe3 go1.25.0.windows-386.msi -89efb4f9b30812eee083cc1770fdd2913c14d301064f6454851428f9707d190b go1.25.0.windows-amd64.zip -936bd87109da515f79d80211de5bc6cbda071f2cc577f7e6af1a9e754ea34819 go1.25.0.windows-amd64.msi -27bab004c72b3d7bd05a69b6ec0fc54a309b4b78cc569dd963d8b3ec28bfdb8c go1.25.0.windows-arm64.zip -357d030b217ff68e700b6cfc56097bc21ad493bb45b79733a052d112f5031ed9 go1.25.0.windows-arm64.msi +d010c109cee94d80efe681eab46bdea491ac906bf46583c32e9f0dbb0bd1a594 go1.25.1.src.tar.gz +1d622468f767a1b9fe1e1e67bd6ce6744d04e0c68712adc689748bbeccb126bb go1.25.1.darwin-amd64.tar.gz +68deebb214f39d542e518ebb0598a406ab1b5a22bba8ec9ade9f55fb4dd94a6c go1.25.1.darwin-arm64.tar.gz +d03cdcbc9bd8baf5cf028de390478e9e2b3e4d0afe5a6582dedc19bfe6a263b2 go1.25.1.linux-386.tar.gz +7716a0d940a0f6ae8e1f3b3f4f36299dc53e31b16840dbd171254312c41ca12e go1.25.1.linux-amd64.tar.gz +65a3e34fb2126f55b34e1edfc709121660e1be2dee6bdf405fc399a63a95a87d go1.25.1.linux-arm64.tar.gz +eb949be683e82a99e9861dafd7057e31ea40b161eae6c4cd18fdc0e8c4ae6225 go1.25.1.linux-armv6l.tar.gz +be13d5479b8c75438f2efcaa8c191fba3af684b3228abc9c99c7aa8502f34424 go1.25.1.windows-386.zip +4a974de310e7ee1d523d2fcedb114ba5fa75408c98eb3652023e55ccf3fa7cab go1.25.1.windows-amd64.zip +45ab4290adbd6ee9e7f18f0d57eaa9008fdbef590882778ed93eac3c8cca06c5 go1.25.1.aix-ppc64.tar.gz +2e3c1549bed3124763774d648f291ac42611232f48320ebbd23517c909c09b81 go1.25.1.dragonfly-amd64.tar.gz +dc0198dd4ec520e13f26798def8750544edf6448d8e9c43fd2a814e4885932af go1.25.1.freebsd-386.tar.gz +c4f1a7e7b258406e6f3b677ecdbd97bbb23ff9c0d44be4eb238a07d360f69ac8 go1.25.1.freebsd-amd64.tar.gz +7772fc5ff71ed39297ec0c1599fc54e399642c9b848eac989601040923b0de9c go1.25.1.freebsd-arm.tar.gz +5bb011d5d5b6218b12189f07aa0be618ab2002662fff1ca40afba7389735c207 go1.25.1.freebsd-arm64.tar.gz +ccac716240cb049bebfafcb7eebc3758512178a4c51fc26da9cc032035d850c8 go1.25.1.freebsd-riscv64.tar.gz +cc53910ffb9fcfdd988a9fa25b5423bae1cfa01b19616be646700e1f5453b466 go1.25.1.illumos-amd64.tar.gz +efe809f923bcedab44bf7be2b3af8d182b512b1bf9c07d302e0c45d26c8f56f3 go1.25.1.linux-loong64.tar.gz +c0de33679f6ed68991dc42dc4a602e74a666e3e166c1748ee1b5d1a7ea2ffbb2 go1.25.1.linux-mips.tar.gz +c270f7b0c0bdfbcd54fef4481227c40d41bb518f9ae38ee930870f04a0a6a589 go1.25.1.linux-mips64.tar.gz +80be871ba9c944f34d1868cdf5047e1cf2e1289fe08cdb90e2453d2f0d6965ae go1.25.1.linux-mips64le.tar.gz +9f09defa9bb22ebf2cde76162f40958564e57ce5c2b3649bc063bebcbc9294c1 go1.25.1.linux-mipsle.tar.gz +2c76b7d278c1d43ad19d478ad3f0f05e7b782b64b90870701b314fa48b5f43c6 go1.25.1.linux-ppc64.tar.gz +8b0c8d3ee5b1b5c28b6bd63dc4438792012e01d03b4bf7a61d985c87edab7d1f go1.25.1.linux-ppc64le.tar.gz +22fe934a9d0c9c57275716c55b92d46ebd887cec3177c9140705efa9f84ba1e2 go1.25.1.linux-riscv64.tar.gz +9cfe517ba423f59f3738ca5c3d907c103253cffbbcc2987142f79c5de8c1bf93 go1.25.1.linux-s390x.tar.gz +6af8a08353e76205d5b743dd7a3f0126684f96f62be0a31b75daf9837e512c46 go1.25.1.netbsd-386.tar.gz +e5d534ff362edb1bd8c8e10892b6a027c4c1482454245d1529167676498684c7 go1.25.1.netbsd-amd64.tar.gz +88bcf39254fdcea6a199c1c27d787831b652427ce60851ae9e41a3d7eb477f45 go1.25.1.netbsd-arm.tar.gz +d7c2eabe1d04ee47bcaea2816fdd90dbd25d90d4dfa756faa9786c788e4f3a4e go1.25.1.netbsd-arm64.tar.gz +14a2845977eb4dde11d929858c437a043467c427db87899935e90cee04a38d72 go1.25.1.openbsd-386.tar.gz +d27ac54b38a13a09c81e67c82ac70d387037341c85c3399291c73e13e83fdd8c go1.25.1.openbsd-amd64.tar.gz +0f4ab5f02500afa4befd51fed1e8b45e4d07ca050f641cc3acc76eaa4027b2c3 go1.25.1.openbsd-arm.tar.gz +d46c3bd156843656f7f3cb0dec27ea51cd926ec3f7b80744bf8156e67c1c812f go1.25.1.openbsd-arm64.tar.gz +c550514c67f22e409be10e40eace761e2e43069f4ef086ae6e60aac736c2b679 go1.25.1.openbsd-ppc64.tar.gz +8a09a8714a2556eb13fc1f10b7ce2553fcea4971e3330fc3be0efd24aab45734 go1.25.1.openbsd-riscv64.tar.gz +b0e1fefaf0c7abd71f139a54eee9767944aff5f0bc9d69c968234804884e552f go1.25.1.plan9-386.tar.gz +e94732c94f149690aa0ab11c26090577211b4a988137cb2c03ec0b54e750402e go1.25.1.plan9-amd64.tar.gz +7eb80e9de1e817d9089a54e8c7c5c8d8ed9e5fb4d4a012fc0f18fc422a484f0c go1.25.1.plan9-arm.tar.gz +1261dfad7c4953c0ab90381bc1242dc54e394db7485c59349428d532b2273343 go1.25.1.solaris-amd64.tar.gz +04bc3c078e9e904c4d58d6ac2532a5bdd402bd36a9ff0b5949b3c5e6006a05ee go1.25.1.windows-arm64.zip # version:golangci 2.4.0 # https://github.com/golangci/golangci-lint/releases/ From 8a19582c8d939e62cdb8a5172779076b9d71902c Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:09:56 -0600 Subject: [PATCH 041/119] eth/catalyst: enable newpayloadV4 on BPOs (#32589) Fixes an issue I accidentally introduced in #32579. Essentially, because we gate the engine methods based on particular forks and I did not add the BPOs as allowed forks to the method. --- eth/catalyst/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index b40698b999..71912d5822 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -636,8 +636,8 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas return invalidStatus, paramsErr("nil beaconRoot post-cancun") case executionRequests == nil: return invalidStatus, paramsErr("nil executionRequests post-prague") - case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka): - return invalidStatus, unsupportedForkErr("newPayloadV4 must only be called for prague payloads") + case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka, forks.BPO1, forks.BPO2, forks.BPO3, forks.BPO4, forks.BPO5): + return invalidStatus, unsupportedForkErr("newPayloadV4 must only be called for Prague payloads") } requests := convertRequests(executionRequests) if err := validateRequests(requests); err != nil { From 72d3e881b32c214b51863661f0d625bb3e5bf319 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Fri, 12 Sep 2025 10:52:53 +0200 Subject: [PATCH 042/119] p2p/discover: clarify lookup behavior on empty table We have changed this behavior, better clarify in comment. Signed-off-by: Csaba Kiraly --- p2p/discover/lookup.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index 88c93a57cf..7382bbe41a 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -27,6 +27,7 @@ import ( // lookup performs a network search for nodes close to the given target. It approaches the // target by querying nodes that are closer to it on each iteration. The given target does // not need to be an actual node identifier. +// lookup on an empty table will return immediately with no nodes. type lookup struct { tab *Table queryfunc queryFunc @@ -142,6 +143,9 @@ func (it *lookup) query(n *enode.Node, reply chan<- []*enode.Node) { // lookupIterator performs lookup operations and iterates over all seen nodes. // When a lookup finishes, a new one is created through nextLookup. +// LookupIterator waits for table initialization and triggers a table refresh +// when necessary. + type lookupIterator struct { buffer []*enode.Node nextLookup lookupFunc From 3eab4616a69bc313a94f073e24cd5093b0dc922b Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Fri, 12 Sep 2025 10:59:29 +0200 Subject: [PATCH 043/119] p2p/discover: add test for lookup returning immediately Signed-off-by: Csaba Kiraly --- p2p/discover/v4_lookup_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/p2p/discover/v4_lookup_test.go b/p2p/discover/v4_lookup_test.go index 29a9dd6645..88ca82039e 100644 --- a/p2p/discover/v4_lookup_test.go +++ b/p2p/discover/v4_lookup_test.go @@ -23,6 +23,7 @@ import ( "slices" "sync" "testing" + "time" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/discover/v4wire" @@ -34,11 +35,15 @@ func TestUDPv4_Lookup(t *testing.T) { t.Parallel() test := newUDPTest(t) - // Lookup on empty table returns no nodes. + // Lookup on empty table returns immediately with no nodes. targetKey, _ := v4wire.DecodePubkey(crypto.S256(), lookupTestnet.target) + start := time.Now() if results := test.udp.LookupPubkey(targetKey); len(results) > 0 { t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) } + if time.Since(start) > 100*time.Millisecond { + t.Fatalf("lookup on empty table took too long: %s", time.Since(start)) + } // Seed table with initial node. fillTable(test.table, []*enode.Node{lookupTestnet.node(256, 0)}, true) From 25d6596cd5ec963e4326e7af400cff76f3e3dd24 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 12 Sep 2025 11:08:47 +0200 Subject: [PATCH 044/119] .github: remove redundant regexp check for heading dot (#32597) --- .github/workflows/validate_pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate_pr.yml b/.github/workflows/validate_pr.yml index 02ebf431b0..0719ca2e3d 100644 --- a/.github/workflows/validate_pr.yml +++ b/.github/workflows/validate_pr.yml @@ -13,7 +13,7 @@ jobs: with: script: | const prTitle = context.payload.pull_request.title; - const titleRegex = /^(\.?[\w\s,{}/.]+): .+/; + const titleRegex = /^([\w\s,{}/.]+): .+/; if (!titleRegex.test(prTitle)) { core.setFailed(`PR title "${prTitle}" does not match required format: directory, ...: description`); From 97afa2815beefec5bb90a06b0fcf06ec25d99eb8 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Fri, 12 Sep 2025 11:29:43 +0200 Subject: [PATCH 045/119] Revert "p2p/discover: add test for lookup returning immediately" This reverts commit 3eab4616a69bc313a94f073e24cd5093b0dc922b. --- p2p/discover/v4_lookup_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/p2p/discover/v4_lookup_test.go b/p2p/discover/v4_lookup_test.go index 88ca82039e..29a9dd6645 100644 --- a/p2p/discover/v4_lookup_test.go +++ b/p2p/discover/v4_lookup_test.go @@ -23,7 +23,6 @@ import ( "slices" "sync" "testing" - "time" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/discover/v4wire" @@ -35,15 +34,11 @@ func TestUDPv4_Lookup(t *testing.T) { t.Parallel() test := newUDPTest(t) - // Lookup on empty table returns immediately with no nodes. + // Lookup on empty table returns no nodes. targetKey, _ := v4wire.DecodePubkey(crypto.S256(), lookupTestnet.target) - start := time.Now() if results := test.udp.LookupPubkey(targetKey); len(results) > 0 { t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) } - if time.Since(start) > 100*time.Millisecond { - t.Fatalf("lookup on empty table took too long: %s", time.Since(start)) - } // Seed table with initial node. fillTable(test.table, []*enode.Node{lookupTestnet.node(256, 0)}, true) From 68c18ede06df9e28bbd735ccc5f76dc81863054e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 12 Sep 2025 11:34:44 +0200 Subject: [PATCH 046/119] Update lookup.go --- p2p/discover/lookup.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index 7382bbe41a..efa8191ae0 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -191,8 +191,7 @@ func (it *lookupIterator) Next() bool { // is in a degraded state, and we need to wait for it to fill again. it.lookupFailed(it.lookup.tab) } - // Otherwise, yield the initial nodes from the iterator before advancing - // the lookup. + // Yield the initial nodes from the iterator before advancing the lookup. it.buffer = it.lookup.replyBuffer continue } From 5d09aa316f6b2c78463a2784b3ff1b0905b2d15b Mon Sep 17 00:00:00 2001 From: Mobin Mohanan <47410557+tr1sm0s1n@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:44:47 +0530 Subject: [PATCH 047/119] accounts/abi/bind/v2: add Address method to BoundContract (#32559) --- accounts/abi/bind/v2/base.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/accounts/abi/bind/v2/base.go b/accounts/abi/bind/v2/base.go index 535c0ed4fd..f714848efb 100644 --- a/accounts/abi/bind/v2/base.go +++ b/accounts/abi/bind/v2/base.go @@ -150,6 +150,11 @@ func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller } } +// Address returns the deployment address of the contract. +func (c *BoundContract) Address() common.Address { + return c.address +} + // Call invokes the (constant) contract method with params as input values and // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named From 06434279655f034529a16c6fe76d7c5ae785ec12 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 12 Sep 2025 12:50:07 +0200 Subject: [PATCH 048/119] p2p/discover: continue --- p2p/discover/lookup.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index efa8191ae0..ff2dc907cd 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -190,6 +190,8 @@ func (it *lookupIterator) Next() bool { // If the lookup is empty right after creation, it means the local table // is in a degraded state, and we need to wait for it to fill again. it.lookupFailed(it.lookup.tab) + it.lookup = nil + continue } // Yield the initial nodes from the iterator before advancing the lookup. it.buffer = it.lookup.replyBuffer From 41f580f20944b42f4a9a7e2f8d1b6ea8d53d9b65 Mon Sep 17 00:00:00 2001 From: Antonio Viggiano Date: Fri, 12 Sep 2025 14:06:57 -0300 Subject: [PATCH 049/119] core/vm: fix typo in CLZ doc (#32604) Fixes typo in CLZ doc. --- core/vm/eips.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 10ca1fe9ab..d7ed18648e 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -294,7 +294,7 @@ func opBlobBaseFee(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { return nil, nil } -// opCLZ implements the CLZ opcode (count leading zero bytes) +// opCLZ implements the CLZ opcode (count leading zero bits) func opCLZ(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { x := scope.Stack.peek() x.SetUint64(256 - uint64(x.BitLen())) From a3062390f760eb5c8dd7a8f39e92a75ec43d6933 Mon Sep 17 00:00:00 2001 From: hero5512 Date: Sun, 14 Sep 2025 22:42:14 -0400 Subject: [PATCH 050/119] core/filtermaps: use slices.Sort to remove duplicated elements (#32602) --- core/filtermaps/math.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/core/filtermaps/math.go b/core/filtermaps/math.go index 33ac07f721..68fd6debd6 100644 --- a/core/filtermaps/math.go +++ b/core/filtermaps/math.go @@ -22,7 +22,7 @@ import ( "fmt" "hash/fnv" "math" - "sort" + "slices" "github.com/ethereum/go-ethereum/common" ) @@ -245,7 +245,7 @@ func (p *Params) potentialMatches(rows []FilterRow, mapIndex uint32, logValue co panic("potentialMatches: insufficient list of row alternatives") } } - sort.Sort(results) + slices.Sort(results) // remove duplicates j := 0 for i, match := range results { @@ -260,12 +260,7 @@ func (p *Params) potentialMatches(rows []FilterRow, mapIndex uint32, logValue co // potentialMatches is a strictly monotonically increasing list of log value // indices in the range of a filter map that are potential matches for certain // filter criteria. -// potentialMatches implements sort.Interface. // Note that nil is used as a wildcard and therefore means that all log value // indices in the filter map range are potential matches. If there are no // potential matches in the given map's range then an empty slice should be used. type potentialMatches []uint64 - -func (p potentialMatches) Len() int { return len(p) } -func (p potentialMatches) Less(i, j int) bool { return p[i] < p[j] } -func (p potentialMatches) Swap(i, j int) { p[i], p[j] = p[j], p[i] } From c2fcc27132388e678b5579e14b1c53b67d07fbce Mon Sep 17 00:00:00 2001 From: radik878 Date: Mon, 15 Sep 2025 05:45:50 +0300 Subject: [PATCH 051/119] core/rawdb: fix misleading comment in HasTrieNode (#32599) --- core/rawdb/accessors_trie.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go index e154ab527b..7d8b266c15 100644 --- a/core/rawdb/accessors_trie.go +++ b/core/rawdb/accessors_trie.go @@ -150,7 +150,7 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c if len(blob) == 0 { return false } - return crypto.Keccak256Hash(blob) == hash // exists but not match + return crypto.Keccak256Hash(blob) == hash // exist and match default: panic(fmt.Sprintf("Unknown scheme %v", scheme)) } @@ -173,7 +173,7 @@ func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash return nil } if crypto.Keccak256Hash(blob) != hash { - return nil // exists but not match + return nil // exist but not match } return blob default: From ec99444804f82049c6eb07ce7a98e5cde2aed901 Mon Sep 17 00:00:00 2001 From: Galoretka Date: Mon, 15 Sep 2025 10:26:16 +0300 Subject: [PATCH 052/119] core/overlay: copy BaseRoot in TransitionState.Copy (#32613) This change ensures TransitionState.Copy preserves BaseRoot. During a Verkle transition, ts.BaseRoot is required to construct the overlay MPT when ts.InTransition() is true. Previously, copies dropped BaseRoot, risking an invalid zero-hash base trie and inconsistent behavior. --- core/overlay/state_transition.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/overlay/state_transition.go b/core/overlay/state_transition.go index 90b5c9431a..67ca0f9671 100644 --- a/core/overlay/state_transition.go +++ b/core/overlay/state_transition.go @@ -60,6 +60,7 @@ func (ts *TransitionState) Copy() *TransitionState { CurrentSlotHash: ts.CurrentSlotHash, CurrentPreimageOffset: ts.CurrentPreimageOffset, StorageProcessed: ts.StorageProcessed, + BaseRoot: ts.BaseRoot, } if ts.CurrentAccountAddress != nil { addr := *ts.CurrentAccountAddress From 4824942b97d5fe35e9fd5677c680c9577a8e4d6e Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 15 Sep 2025 14:48:59 +0200 Subject: [PATCH 053/119] core/txpool/blobpool: filter blob txs with sidecar version (#32577) As a consequence of moving blob sidecar version migration code around, we ended up building blocks with a mix of v0 and v1 blob transactions (different proof encoding in the sidecar). This PR makes sure we are not building illegal blocks after Osaka. Blob migration is left for another PR. Related issues and PRs: - https://github.com/ethereum/go-ethereum/pull/31791 - https://github.com/ethereum/go-ethereum/pull/32347 - https://github.com/ethereum/go-ethereum/pull/31966 - https://github.com/ethereum/go-ethereum/issues/32235 --------- Signed-off-by: Csaba Kiraly --- core/txpool/blobpool/blobpool.go | 14 +++++++++++++- core/txpool/legacypool/legacypool.go | 2 +- core/txpool/subpool.go | 6 ++++-- eth/sync.go | 2 +- miner/worker.go | 9 +++++++-- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 5cf1218b75..722c176bb1 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -92,6 +92,7 @@ const ( type blobTxMeta struct { hash common.Hash // Transaction hash to maintain the lookup table vhashes []common.Hash // Blob versioned hashes to maintain the lookup table + version byte // Blob transaction version to determine proof type id uint64 // Storage ID in the pool's persistent store storageSize uint32 // Byte size in the pool's persistent store @@ -115,10 +116,16 @@ type blobTxMeta struct { // newBlobTxMeta retrieves the indexed metadata fields from a blob transaction // and assembles a helper struct to track in memory. +// Requires the transaction to have a sidecar (or that we introduce a special version tag for no-sidecar). func newBlobTxMeta(id uint64, size uint64, storageSize uint32, tx *types.Transaction) *blobTxMeta { + if tx.BlobTxSidecar() == nil { + // This should never happen, as the pool only admits blob transactions with a sidecar + panic("missing blob tx sidecar") + } meta := &blobTxMeta{ hash: tx.Hash(), vhashes: tx.BlobHashes(), + version: tx.BlobTxSidecar().Version, id: id, storageSize: storageSize, size: size, @@ -1660,7 +1667,7 @@ func (p *BlobPool) drop() { func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction { // If only plain transactions are requested, this pool is unsuitable as it // contains none, don't even bother. - if filter.OnlyPlainTxs { + if !filter.BlobTxs { return nil } // Track the amount of time waiting to retrieve the list of pending blob txs @@ -1681,6 +1688,11 @@ func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*tx for addr, txs := range p.index { lazies := make([]*txpool.LazyTransaction, 0, len(txs)) for _, tx := range txs { + // Skip v0 or v1 blob transactions depending on the filter + if tx.version != filter.BlobVersion { + break // skip the rest because of nonce ordering + } + // If transaction filtering was requested, discard badly priced ones if filter.MinTip != nil && filter.BaseFee != nil { if tx.execFeeCap.Lt(filter.BaseFee) { diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 425def170b..80a9faf23f 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -508,7 +508,7 @@ func (pool *LegacyPool) ContentFrom(addr common.Address) ([]*types.Transaction, func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction { // If only blob transactions are requested, this pool is unsuitable as it // contains none, don't even bother. - if filter.OnlyBlobTxs { + if filter.BlobTxs { return nil } pool.mu.Lock() diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index f1f6056686..519ae7b989 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -78,8 +78,10 @@ type PendingFilter struct { BlobFee *uint256.Int // Minimum 4844 blobfee needed to include a blob transaction GasLimitCap uint64 // Maximum gas can be used for a single transaction execution (0 means no limit) - OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling) - OnlyBlobTxs bool // Return only blob transactions (block blob-space filling) + // When BlobTxs true, return only blob transactions (block blob-space filling) + // when false, return only non-blob txs (peer-join announces, block space filling) + BlobTxs bool + BlobVersion byte // Blob tx version to include. 0 means pre-Osaka, 1 means Osaka and later } // TxMetadata denotes the metadata of a transaction. diff --git a/eth/sync.go b/eth/sync.go index 61f2b2b376..ddae8443a3 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -25,7 +25,7 @@ import ( // syncTransactions starts sending all currently pending transactions to the given peer. func (h *handler) syncTransactions(p *eth.Peer) { var hashes []common.Hash - for _, batch := range h.txpool.Pending(txpool.PendingFilter{OnlyPlainTxs: true}) { + for _, batch := range h.txpool.Pending(txpool.PendingFilter{BlobTxs: false}) { for _, tx := range batch { hashes = append(hashes, tx.Hash) } diff --git a/miner/worker.go b/miner/worker.go index 6baec5e365..c0574eac23 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -481,10 +481,15 @@ func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) { filter.GasLimitCap = params.MaxTxGas } - filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false + filter.BlobTxs = false pendingPlainTxs := miner.txpool.Pending(filter) - filter.OnlyPlainTxs, filter.OnlyBlobTxs = false, true + filter.BlobTxs = true + if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) { + filter.BlobVersion = types.BlobSidecarVersion1 + } else { + filter.BlobVersion = types.BlobSidecarVersion0 + } pendingBlobTxs := miner.txpool.Pending(filter) // Split the pending transactions into locals and remotes. From df0bd8960cc14527fe0c08bc1b008ad221482d4b Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 15 Sep 2025 15:34:57 +0200 Subject: [PATCH 054/119] core/txpool/blobpool: migrate billy to new slot size (#31966) Implements a migration path for the blobpool slotter --------- Co-authored-by: lightclient Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com> Co-authored-by: Gary Rong --- core/txpool/blobpool/blobpool.go | 21 ++++- core/txpool/blobpool/blobpool_test.go | 109 ++++++++++++++++++++++++++ core/txpool/blobpool/limbo.go | 16 +++- core/txpool/blobpool/slotter.go | 84 +++++++++++++++++++- core/txpool/blobpool/slotter_test.go | 45 ++++++++++- crypto/kzg4844/kzg4844.go | 4 +- go.mod | 2 +- go.sum | 4 +- 8 files changed, 274 insertions(+), 11 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 722c176bb1..55d24c7a93 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -55,6 +55,12 @@ const ( // tiny overflows causing all txs to move a shelf higher, wasting disk space. txAvgSize = 4 * 1024 + // txBlobOverhead is an approximation of the overhead that an additional blob + // has on transaction size. This is added to the slotter to avoid tiny + // overflows causing all txs to move a shelf higher, wasting disk space. A + // small buffer is added to the proof overhead. + txBlobOverhead = uint32(kzg4844.CellProofsPerBlob*len(kzg4844.Proof{}) + 64) + // txMaxSize is the maximum size a single transaction can have, outside // the included blobs. Since blob transactions are pulled instead of pushed, // and only a small metadata is kept in ram, the rest is on disk, there is @@ -83,6 +89,10 @@ const ( // limboedTransactionStore is the subfolder containing the currently included // but not yet finalized transaction blobs. limboedTransactionStore = "limbo" + + // storeVersion is the current slotter layout used for the billy.Database + // store. + storeVersion = 1 ) // blobTxMeta is the minimal subset of types.BlobTx necessary to validate and @@ -392,6 +402,14 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser } p.head, p.state = head, state + // Create new slotter for pre-Osaka blob configuration. + slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) + + // See if we need to migrate the queue blob store after fusaka + slotter, err = tryMigrate(p.chain.Config(), slotter, queuedir) + if err != nil { + return err + } // Index all transactions on disk and delete anything unprocessable var fails []uint64 index := func(id uint64, size uint32, blob []byte) { @@ -399,7 +417,6 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser fails = append(fails, id) } } - slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) store, err := billy.Open(billy.Options{Path: queuedir, Repair: true}, slotter, index) if err != nil { return err @@ -433,7 +450,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser // Pool initialized, attach the blob limbo to it to track blobs included // recently but not yet finalized - p.limbo, err = newLimbo(limbodir, eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) + p.limbo, err = newLimbo(p.chain.Config(), limbodir) if err != nil { p.Close() return err diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index c9609e1259..e46529a241 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -1165,6 +1165,115 @@ func TestChangingSlotterSize(t *testing.T) { } } +// TestBillyMigration tests the billy migration from the default slotter to +// the PeerDAS slotter. This tests both the migration of the slotter +// as well as increasing the slotter size of the new slotter. +func TestBillyMigration(t *testing.T) { + //log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true))) + + // Create a temporary folder for the persistent backend + storage := t.TempDir() + + os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) + os.MkdirAll(filepath.Join(storage, limboedTransactionStore), 0700) + // Create the billy with the old slotter + oldSlotter := newSlotterEIP7594(6) + store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, oldSlotter, nil) + + // Create transactions from a few accounts. + var ( + key1, _ = crypto.GenerateKey() + key2, _ = crypto.GenerateKey() + key3, _ = crypto.GenerateKey() + + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + addr3 = crypto.PubkeyToAddress(key3.PublicKey) + + tx1 = makeMultiBlobTx(0, 1, 1000, 100, 6, 0, key1, types.BlobSidecarVersion0) + tx2 = makeMultiBlobTx(0, 1, 800, 70, 6, 0, key2, types.BlobSidecarVersion0) + tx3 = makeMultiBlobTx(0, 1, 800, 110, 24, 0, key3, types.BlobSidecarVersion0) + + blob1, _ = rlp.EncodeToBytes(tx1) + blob2, _ = rlp.EncodeToBytes(tx2) + ) + + // Write the two safely sized txs to store. note: although the store is + // configured for a blob count of 6, it can also support around ~1mb of call + // data - all this to say that we aren't using the the absolute largest shelf + // available. + store.Put(blob1) + store.Put(blob2) + store.Close() + + // Mimic a blobpool with max blob count of 6 upgrading to a max blob count of 24. + for _, maxBlobs := range []int{6, 24} { + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.Commit(0, true, false) + + // Make custom chain config where the max blob count changes based on the loop variable. + zero := uint64(0) + config := ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + LondonBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + CancunTime: &zero, + OsakaTime: &zero, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: ¶ms.BlobConfig{ + Target: maxBlobs / 2, + Max: maxBlobs, + UpdateFraction: params.DefaultCancunBlobConfig.UpdateFraction, + }, + Osaka: ¶ms.BlobConfig{ + Target: maxBlobs / 2, + Max: maxBlobs, + UpdateFraction: params.DefaultCancunBlobConfig.UpdateFraction, + }, + }, + } + chain := &testBlockChain{ + config: config, + basefee: uint256.NewInt(1050), + blobfee: uint256.NewInt(105), + statedb: statedb, + } + pool := New(Config{Datadir: storage}, chain, nil) + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { + t.Fatalf("failed to create blob pool: %v", err) + } + + // Try to add the big blob tx. In the initial iteration it should overflow + // the pool. On the subsequent iteration it should be accepted. + errs := pool.Add([]*types.Transaction{tx3}, true) + if _, ok := pool.index[addr3]; ok && maxBlobs == 6 { + t.Errorf("expected insert of oversized blob tx to fail: blobs=24, maxBlobs=%d, err=%v", maxBlobs, errs[0]) + } else if !ok && maxBlobs == 10 { + t.Errorf("expected insert of oversized blob tx to succeed: blobs=24, maxBlobs=%d, err=%v", maxBlobs, errs[0]) + } + + // Verify the regular two txs are always available. + if got := pool.Get(tx1.Hash()); got == nil { + t.Errorf("expected tx %s from %s in pool", tx1.Hash(), addr1) + } + if got := pool.Get(tx2.Hash()); got == nil { + t.Errorf("expected tx %s from %s in pool", tx2.Hash(), addr2) + } + + // Verify all the calculated pool internals. Interestingly, this is **not** + // a duplication of the above checks, this actually validates the verifier + // using the above already hard coded checks. + // + // Do not remove this, nor alter the above to be generic. + verifyPoolInternals(t, pool) + + pool.Close() + } +} + // TestBlobCountLimit tests the blobpool enforced limits on the max blob count. func TestBlobCountLimit(t *testing.T) { var ( diff --git a/core/txpool/blobpool/limbo.go b/core/txpool/blobpool/limbo.go index 99d1b4ad6b..50c40c9d83 100644 --- a/core/txpool/blobpool/limbo.go +++ b/core/txpool/blobpool/limbo.go @@ -20,8 +20,10 @@ import ( "errors" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/billy" ) @@ -48,11 +50,21 @@ type limbo struct { } // newLimbo opens and indexes a set of limboed blob transactions. -func newLimbo(datadir string, maxBlobsPerTransaction int) (*limbo, error) { +func newLimbo(config *params.ChainConfig, datadir string) (*limbo, error) { l := &limbo{ index: make(map[common.Hash]uint64), groups: make(map[uint64]map[uint64]common.Hash), } + + // Create new slotter for pre-Osaka blob configuration. + slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(config)) + + // See if we need to migrate the limbo after fusaka. + slotter, err := tryMigrate(config, slotter, datadir) + if err != nil { + return nil, err + } + // Index all limboed blobs on disk and delete anything unprocessable var fails []uint64 index := func(id uint64, size uint32, data []byte) { @@ -60,7 +72,7 @@ func newLimbo(datadir string, maxBlobsPerTransaction int) (*limbo, error) { fails = append(fails, id) } } - store, err := billy.Open(billy.Options{Path: datadir, Repair: true}, newSlotter(maxBlobsPerTransaction), index) + store, err := billy.Open(billy.Options{Path: datadir, Repair: true}, slotter, index) if err != nil { return nil, err } diff --git a/core/txpool/blobpool/slotter.go b/core/txpool/blobpool/slotter.go index 84ccc0f27b..9b793e366c 100644 --- a/core/txpool/blobpool/slotter.go +++ b/core/txpool/blobpool/slotter.go @@ -16,6 +16,49 @@ package blobpool +import ( + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/billy" +) + +// tryMigrate checks if the billy needs to be migrated and migrates if needed. +// Returns a slotter that can be used for the database. +func tryMigrate(config *params.ChainConfig, slotter billy.SlotSizeFn, datadir string) (billy.SlotSizeFn, error) { + // Check if we need to migrate our blob db to the new slotter. + if config.OsakaTime != nil { + // Open the store using the version slotter to see if any version has been + // written. + var version int + index := func(_ uint64, _ uint32, blob []byte) { + version = max(version, parseSlotterVersion(blob)) + } + store, err := billy.Open(billy.Options{Path: datadir}, newVersionSlotter(), index) + if err != nil { + return nil, err + } + store.Close() + + // If the version found is less than the currently configured store version, + // perform a migration then write the updated version of the store. + if version < storeVersion { + newSlotter := newSlotterEIP7594(eip4844.LatestMaxBlobsPerBlock(config)) + if err := billy.Migrate(billy.Options{Path: datadir, Repair: true}, slotter, newSlotter); err != nil { + return nil, err + } + store, err = billy.Open(billy.Options{Path: datadir}, newVersionSlotter(), nil) + if err != nil { + return nil, err + } + writeSlotterVersion(store, storeVersion) + store.Close() + } + // Set the slotter to the format now that the Osaka is active. + slotter = newSlotterEIP7594(eip4844.LatestMaxBlobsPerBlock(config)) + } + return slotter, nil +} + // newSlotter creates a helper method for the Billy datastore that returns the // individual shelf sizes used to store transactions in. // @@ -25,7 +68,7 @@ package blobpool // The slotter also creates a shelf for 0-blob transactions. Whilst those are not // allowed in the current protocol, having an empty shelf is not a relevant use // of resources, but it makes stress testing with junk transactions simpler. -func newSlotter(maxBlobsPerTransaction int) func() (uint32, bool) { +func newSlotter(maxBlobsPerTransaction int) billy.SlotSizeFn { slotsize := uint32(txAvgSize) slotsize -= uint32(blobSize) // underflows, it's ok, will overflow back in the first return @@ -36,3 +79,42 @@ func newSlotter(maxBlobsPerTransaction int) func() (uint32, bool) { return slotsize, finished } } + +// newSlotterEIP7594 creates a different slotter for EIP-7594 transactions. +// EIP-7594 (PeerDAS) changes the average transaction size which means the current +// static 4KB average size is not enough anymore. +// This slotter adds a dynamic overhead component to the slotter, which also +// captures the notion that blob transactions with more blobs are also more likely to +// to have more calldata. +func newSlotterEIP7594(maxBlobsPerTransaction int) billy.SlotSizeFn { + slotsize := uint32(txAvgSize) + slotsize -= uint32(blobSize) + txBlobOverhead // underflows, it's ok, will overflow back in the first return + + return func() (size uint32, done bool) { + slotsize += blobSize + txBlobOverhead + finished := slotsize > uint32(maxBlobsPerTransaction)*(blobSize+txBlobOverhead)+txMaxSize + + return slotsize, finished + } +} + +// newVersionSlotter creates a slotter with a single 8 byte shelf to store +// version metadata in. +func newVersionSlotter() billy.SlotSizeFn { + return func() (size uint32, done bool) { + return 8, true + } +} + +// parseSlotterVersion will parse the slotter's version from a given data blob. +func parseSlotterVersion(blob []byte) int { + if len(blob) > 0 { + return int(blob[0]) + } + return 0 +} + +// writeSlotterVersion writes the current slotter version into the store. +func writeSlotterVersion(store billy.Database, version int) { + store.Put([]byte{byte(version)}) +} diff --git a/core/txpool/blobpool/slotter_test.go b/core/txpool/blobpool/slotter_test.go index 8d46f47d2c..e4cf232f4e 100644 --- a/core/txpool/blobpool/slotter_test.go +++ b/core/txpool/blobpool/slotter_test.go @@ -16,7 +16,9 @@ package blobpool -import "testing" +import ( + "testing" +) // Tests that the slotter creates the expected database shelves. func TestNewSlotter(t *testing.T) { @@ -58,3 +60,44 @@ func TestNewSlotter(t *testing.T) { } } } + +// Tests that the slotter creates the expected database shelves. +func TestNewSlotterEIP7594(t *testing.T) { + // Generate the database shelve sizes + slotter := newSlotterEIP7594(6) + + var shelves []uint32 + for { + shelf, done := slotter() + shelves = append(shelves, shelf) + if done { + break + } + } + // Compare the database shelves to the expected ones + want := []uint32{ + 0*blobSize + 0*txBlobOverhead + txAvgSize, // 0 blob + some expected tx infos + 1*blobSize + 1*txBlobOverhead + txAvgSize, // 1 blob + some expected tx infos + 2*blobSize + 2*txBlobOverhead + txAvgSize, // 2 blob + some expected tx infos (could be fewer blobs and more tx data) + 3*blobSize + 3*txBlobOverhead + txAvgSize, // 3 blob + some expected tx infos (could be fewer blobs and more tx data) + 4*blobSize + 4*txBlobOverhead + txAvgSize, // 4 blob + some expected tx infos (could be fewer blobs and more tx data) + 5*blobSize + 5*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 6*blobSize + 6*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 7*blobSize + 7*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 8*blobSize + 8*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 9*blobSize + 9*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 10*blobSize + 10*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 11*blobSize + 11*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 12*blobSize + 12*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 13*blobSize + 13*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 14*blobSize + 14*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos >= 4 blobs + max tx metadata size + } + if len(shelves) != len(want) { + t.Errorf("shelves count mismatch: have %d, want %d", len(shelves), len(want)) + } + for i := 0; i < len(shelves) && i < len(want); i++ { + if shelves[i] != want[i] { + t.Errorf("shelf %d mismatch: have %d, want %d", i, shelves[i], want[i]) + } + } +} diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 9da2386368..3ccc204838 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -34,10 +34,10 @@ var ( blobT = reflect.TypeFor[Blob]() commitmentT = reflect.TypeFor[Commitment]() proofT = reflect.TypeFor[Proof]() - - CellProofsPerBlob = 128 ) +const CellProofsPerBlob = 128 + // Blob represents a 4844 data blob. type Blob [131072]byte diff --git a/go.mod b/go.mod index 28c9af9259..058fe3bd8e 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/graph-gophers/graphql-go v1.3.0 github.com/hashicorp/go-bexpr v0.1.10 - github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 + github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/uint256 v1.3.2 github.com/huin/goupnp v1.3.0 diff --git a/go.sum b/go.sum index 53913262ae..16518fdf43 100644 --- a/go.sum +++ b/go.sum @@ -188,8 +188,8 @@ github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY4 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/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= -github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +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= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= From 791e9fb23a154491d21378a091c11a8d1c4fca62 Mon Sep 17 00:00:00 2001 From: radik878 Date: Mon, 15 Sep 2025 17:16:06 +0300 Subject: [PATCH 055/119] rlp: remove duplicate optionalAndTailField test case (#32614) --- rlp/encode_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/rlp/encode_test.go b/rlp/encode_test.go index 314958eb56..e63ea319b4 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -317,7 +317,6 @@ var encTests = []encTest{ {val: &optionalAndTailField{A: 1}, output: "C101"}, {val: &optionalAndTailField{A: 1, B: 2}, output: "C20102"}, {val: &optionalAndTailField{A: 1, Tail: []uint{5, 6}}, output: "C401800506"}, - {val: &optionalAndTailField{A: 1, Tail: []uint{5, 6}}, output: "C401800506"}, {val: &optionalBigIntField{A: 1}, output: "C101"}, {val: &optionalPtrField{A: 1}, output: "C101"}, {val: &optionalPtrFieldNil{A: 1}, output: "C101"}, From 6924eeaee07aa35901e5b8c5e62473aa480a81ad Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 15 Sep 2025 16:26:09 +0200 Subject: [PATCH 056/119] eth/catalyst: allow fcuV3 for BPO forks (#32615) This fixes an issue with the engine API after BPO forks have passed. --- eth/catalyst/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 71912d5822..b222470228 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -213,8 +213,8 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa return engine.STATUS_INVALID, attributesErr("missing withdrawals") case params.BeaconRoot == nil: return engine.STATUS_INVALID, attributesErr("missing beacon root") - case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague, forks.Osaka): - return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads") + case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague, forks.Osaka, forks.BPO1, forks.BPO2, forks.BPO3, forks.BPO4, forks.BPO5): + return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun/prague/osaka payloads") } } // TODO(matt): the spec requires that fcu is applied when called on a valid From 89f364f7eda6073edad6d13d85ff059d377dbda9 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 16 Sep 2025 00:46:18 +0800 Subject: [PATCH 057/119] go.mod: add tool section in module file (#32598) This removes the tools.go workaround in favor of the official tool management infrastructure, which was added in Go 1.24. --- build/tools/tools.go | 27 --------------------------- go.mod | 8 +++++++- 2 files changed, 7 insertions(+), 28 deletions(-) delete mode 100644 build/tools/tools.go diff --git a/build/tools/tools.go b/build/tools/tools.go deleted file mode 100644 index e9e2241d2f..0000000000 --- a/build/tools/tools.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 . - -//go:build tools -// +build tools - -package tools - -import ( - // Tool imports for go:generate. - _ "github.com/fjl/gencodec" - _ "golang.org/x/tools/cmd/stringer" - _ "google.golang.org/protobuf/cmd/protoc-gen-go" -) diff --git a/go.mod b/go.mod index 058fe3bd8e..71bc6b5a1c 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,6 @@ require ( github.com/ethereum/go-verkle v0.2.2 github.com/fatih/color v1.16.0 github.com/ferranbt/fastssz v0.1.4 - github.com/fjl/gencodec v0.1.0 github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff github.com/gofrs/flock v0.12.1 @@ -103,6 +102,7 @@ require ( github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/emicklei/dot v1.6.2 // indirect + github.com/fjl/gencodec v0.1.0 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -146,3 +146,9 @@ require ( golang.org/x/net v0.38.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) + +tool ( + github.com/fjl/gencodec + golang.org/x/tools/cmd/stringer + google.golang.org/protobuf/cmd/protoc-gen-go +) From b05fe4aa6453520cfc5a664318b6b3e7d1855f46 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 15 Sep 2025 19:47:41 +0200 Subject: [PATCH 058/119] cmd/keeper: add the keeper zkvm guest program (#32543) Keeper is a zmvm guest program that runs the block transition. It relies on the zkvm maker implementing `getInput`. For now, we only provide a single implementation for the 'ziren' VM. Why keeper? In the _Mass Effect_ lore, the keepers are animals (?) who maintain the citadel. Nothing is known from them, and attempts at tampering with them have failed, as they self-destruct upon inquiry. They have a secret, nefarious purpose that is only revealed later in the game series, don't want any spoilers so I didn't dig deeper. All in all, a good metaphor for zkvms. --------- Co-authored-by: weilzkm <140377101+weilzkm@users.noreply.github.com> Co-authored-by: Felix Lange --- build/ci.go | 49 ++++++++-- cmd/keeper/1192c3_block.rlp | Bin 0 -> 1368 bytes cmd/keeper/1192c3_witness.rlp | Bin 0 -> 40843 bytes cmd/keeper/README.md | 69 ++++++++++++++ cmd/keeper/chainconfig.go | 38 ++++++++ cmd/keeper/getpayload_example.go | 102 +++++++++++++++++++++ cmd/keeper/getpayload_ziren.go | 31 +++++++ cmd/keeper/go.mod | 48 ++++++++++ cmd/keeper/go.sum | 148 +++++++++++++++++++++++++++++++ cmd/keeper/main.go | 63 +++++++++++++ cmd/keeper/stubs.go | 26 ++++++ go.work | 6 ++ 12 files changed, 573 insertions(+), 7 deletions(-) create mode 100644 cmd/keeper/1192c3_block.rlp create mode 100644 cmd/keeper/1192c3_witness.rlp create mode 100644 cmd/keeper/README.md create mode 100644 cmd/keeper/chainconfig.go create mode 100644 cmd/keeper/getpayload_example.go create mode 100644 cmd/keeper/getpayload_ziren.go create mode 100644 cmd/keeper/go.mod create mode 100644 cmd/keeper/go.sum create mode 100644 cmd/keeper/main.go create mode 100644 cmd/keeper/stubs.go create mode 100644 go.work diff --git a/build/ci.go b/build/ci.go index 3856f32925..6a9848876d 100644 --- a/build/ci.go +++ b/build/ci.go @@ -322,9 +322,9 @@ func doTest(cmdline []string) { gotest.Args = append(gotest.Args, "-short") } - packages := []string{"./..."} - if len(flag.CommandLine.Args()) > 0 { - packages = flag.CommandLine.Args() + packages := flag.CommandLine.Args() + if len(packages) == 0 { + packages = workspacePackagePatterns() } gotest.Args = append(gotest.Args, packages...) build.MustRun(gotest) @@ -364,7 +364,7 @@ func doCheckGenerate() { protocPath = downloadProtoc(*cachedir) protocGenGoPath = downloadProtocGenGo(*cachedir) ) - c := tc.Go("generate", "./...") + c := tc.Go("generate", workspacePackagePatterns()...) pathList := []string{filepath.Join(protocPath, "bin"), protocGenGoPath, os.Getenv("PATH")} c.Env = append(c.Env, "PATH="+strings.Join(pathList, string(os.PathListSeparator))) build.MustRun(c) @@ -424,9 +424,16 @@ func doLint(cmdline []string) { cachedir = flag.String("cachedir", "./build/cache", "directory for caching golangci-lint binary.") ) flag.CommandLine.Parse(cmdline) - packages := []string{"./..."} - if len(flag.CommandLine.Args()) > 0 { - packages = flag.CommandLine.Args() + + packages := flag.CommandLine.Args() + if len(packages) == 0 { + // Get module directories in workspace. + packages = []string{"./..."} + modules := workspaceModules() + for _, m := range modules[1:] { + dir := strings.TrimPrefix(m, modules[0]) + packages = append(packages, "."+dir+"/...") + } } linter := downloadLinter(*cachedir) @@ -1169,3 +1176,31 @@ func doSanityCheck() { csdb := download.MustLoadChecksums("build/checksums.txt") csdb.DownloadAndVerifyAll() } + +// workspaceModules lists the module paths in the current work. +func workspaceModules() []string { + listing, err := new(build.GoToolchain).Go("list", "-m").Output() + if err != nil { + log.Fatalf("go list failed:", err) + } + var modules []string + for _, m := range bytes.Split(listing, []byte("\n")) { + m = bytes.TrimSpace(m) + if len(m) > 0 { + modules = append(modules, string(m)) + } + } + if len(modules) == 0 { + panic("no modules found") + } + return modules +} + +func workspacePackagePatterns() []string { + modules := workspaceModules() + patterns := make([]string, len(modules)) + for i, m := range modules { + patterns[i] = m + "/..." + } + return patterns +} diff --git a/cmd/keeper/1192c3_block.rlp b/cmd/keeper/1192c3_block.rlp new file mode 100644 index 0000000000000000000000000000000000000000..e788c2b8fdc08b6e4fab78b51e1e541c62c822e2 GIT binary patch literal 1368 zcmey#8v2u|VS%@n*`7#Slhn}Ssa}=+s*MgCE~`xvwPal?U2+&dmy zwYqg{`nEGyq%I4&N>Ax35&p2y>950;$?j7McQ)==xAx`w5443Zd^exu5Y{MP zQu@dIz<=e(xBs8KeB64$s})B$W-gO@I!*Na%+{}uB>np@ZLL+eerA0y-T1|9q18JX z8Adv2XcnAwxP^IgJ3~_tM@zM!AL`7x;P9Fo*Hu5weNcF0 z@3lkPK~vLyHS0`0&{pg$cA0;Y?j7@HCI*Iv1Wh`z)C{Q@SE(N9d0^J7zKe znBL6My`h<9r%Q|VtWyjvJHMn)0{UnQ!;PIU!}p!xtXSEh&vW&8+1Z+Cg@%R$U_}oX zFs|61q0zKreMX_mv131Vn0E`L6&9{K##5eJb8Bb7&4&#O=Dz**)V&~K8}s$|dY%o2 zrR?@ng0364ZH=3KZxvsJTkwLwLwo)>bgI8AzT@}SOIy}1m$g6JOwi!$eX~761zi!- z4*X;?c+|oup8B9!Xwu~=WlEKNLG~Zt$M52}|6+BjyS_oOZ?mEDX0VcnKqXgjC~-># zD|rM|autUXjx?~6$3P|5uq!e6WCT|71gPXXb|r=e@?a%Tfl6**S7Pu>5Uk`GP{~c~ zN(>u5fR#K4D!GMSiNX3%u#y)*CAYCFF_f?eD|rc2atDW!FiWtKS3o6qr(Ac-iNDrf za-Az)bi((o^6m2^brqTo??6m>4ODU;yAmVqMzASwfJz=s@l^K^|7OLNSzsk^fl6LYaVfrYcYk^MtQ70pslI#LuGRM%eQGwc>HsTw2UPMJhZ6mr QU?uN?O5R{sqW0Ym02GW4_y7O^ literal 0 HcmV?d00001 diff --git a/cmd/keeper/1192c3_witness.rlp b/cmd/keeper/1192c3_witness.rlp new file mode 100644 index 0000000000000000000000000000000000000000..4990f2299f9e71ecc36283953af6d68555627b66 GIT binary patch literal 40843 zcmb@t1yCJLxGjpiySqCCcL)&NT{poKEI0&rcMIBBg1fr~2@)KF6D$y10wfR~`Q@DR z>Yh_|-@CV~W@b&#*WcIOYt8JQp5Ax)k#{imcQAHf#_=Tg)=zZK3n)0WRQLNVz;fgZjE=n6t9u6Jm8ROv_ZMQX*PanP|gX?zR(v1*cx} zC2BHM%LoLfJCP01h|j*O)7y2sKKgA%0rLU}xN1a%UVhZIX@qrLJw^O<9E>)-Qh4OR zEaIhs*)sIzp$!`9PeOXyu>JsOsz9hfm_I1+X`_1Do>Cwb5)2f|-}v9XBoqwXzZfF< z4|;MV2_^NntH38`eK19lmSEaSGd3J{@%mE#Xf;Q zA&{V8{yGLq|EUQT6yz@h$$u&r`sWPhY2$){k$ggMRDVwXF8LH535O~WI#Lo2O7fq| z{PBT+L?ub}rzU^g2L^^j#2;Zj1*Gu8+g zQ$B_`g|Nh_OSxI*Xq#R|{t5CYg$mZPN&{u;Eha5t{l0AdZqLQaY&r=}*_Q8nKOi3_ zXqC-M=Bq-Ux+W073JRH23Y9xPXtMTSmbRBFU9R>Tm4;BHa;XX6W#R;b?b<=<%zHAt ztY23n@0{A`7^|grEVEOTi__kbdTF-m+`(zLnm==W{9n^=Jcx=Ogo+;zLZ@eD_F!bj z7l;p0(0d6*`51(d7^IB{5&bLwPX@HMnI?qF?9%9u;hzj?1nD3hGrq*XF6uzFYW+_k znDFX*Vy{-=4IeNAh6nj5Lg%p9Q3mwou@1_zP}C-D!xRW zXAlVKQz=0+2%Z2$*$n(C0jmRL^i&1{qa7Fw8wkO|hHU7mL$Hj@jDmum>KLLI9Q0)L z$6pXc874^k$wiPR1n=n#8e01aQ9&m{V4(k-2LG+7|J)MT4La|CqJH*&pZcu-kJLl{p+4&m^;!Rw`ahojEA?>y9rb@pK=MB& z0QpM-ZvU+WV6Y+kPZEInR|3p`N#HNPK@hk9Rsw(M`Bx0E(AtTbe+WAG4}wJgAxQ53 z7eTK75(ND>L6LtG6#s`H=>H_h?H_{fGP_%CZ>@8MlZQH$aZ8S5H3QIy4x@PVBbYq$ zhO0mCeYxb2Zk4;0fer+z<*Qx_>`?BW=(U(r+w_$N-^|-Rucxf}+{2xtRYJ(Xwa{TsW>ziArT+!_pdrWtH*;}%bxrq2UQ$nGWg-Z~n|Z<4;Vuj++< z$=S4R(MSzG&d5QB-_VLC`}@_e4#W2DLN#lB3T6~}`*R7-B&^uE4chGv`VFAf3Smkm zeOcKPQ_4kb^_Xfat+gS1f}9T@`)zM6@6b9B$P@S+!%gBhbKQHcyt4Y@t=v4jW*TKY zH!LQA%r;?i92f}pYSOy2o%Ehv|6l_zbM6$JkFBwAcyO|=YiKm0Khtgkn-2`)_INJX zby-s8LL~8P+vnV1xM9Re$a%BRy7<+HvUTY96S;%ha094VR3n7548K zavO*upTwqV=gwi!&}{$UiFpMwNs~72QefwGDLfh_Ive=3%*PquPlPPth9rghc}yMy z-a!oum=NJt3+m;H>{=qNU39P3O8NrgvNy+gzs#YVJT>(6X8Vjig=;QAGTNK%j8yL4 ziCqBs+fo(8& zcQDvMy?H=#kG_Q!JvJdLZwYP)@!MM=UA~_0M~|WuJsYDuKt{<{?KYTQpJa4mKhWl* z9@m%Ez>jaZ&mUS%8+)YEBLMfg`+J_Rj5eBIe!hux>fcsb(Rve*6G!Vcmp_Cwd_Ds1 z*Gaa?^am)ZdOgxdrMJGWnFdre;1nuMNw>dF|D^nceaNc(q3uLn^B6mT5)=jWI2fp`iWbIr!0@^R3;>OIr{MPTP4&V*1bxxi< zYUJF_d<+vuI9&Qve}I#1+w4>|pO8u|yAI$oT$5mPsFQ!>3;eMO`7H-siAYzY3H9{B~52A?|2&O@< zwv?zc1}t&hN2u!l+R#vzM#~@BfbCKGXK9rS^}uXo!-_a;3zHu43(PL{bTOf(=IbRk z6;O?}sO1!d*CpfjhGcO6=YT`>zQrVyudEsUZt!Dx1~(8WtTr{QT>>CXC)L`8r`nAc zaRAbB>q*nCCGI2?jXj2+s>5o;N-#KeCfZ-EO0n z_WM#~;MJD%F0c)n_YRsn@JV8`j>>2SZs~0PcpKR9seBpZe%b@&1vtHo+6au?jjdwbI-Tu$Za`;VF#(A-Wm@2Ctm(gW8Rdona|$I1?dAHvE-0BeEfUHy%c>!#G;bI%1ADa0o@>YJU{^JdnR0( z0W2T1`HDTvA5ltt#G!{j20l8y1Y3QAgm>>)Cd{&IM&s=)+SQlDKamv^9bAqZ6zSEa zrv8c2+uw#^3FkX7FtO~nz=fH@Ver|Tf3LwP#`K&<367=V-$zJw&-ae8@3{q@a1oY| z$*SLe*y(MkRw(W`eY7+gzdw8cY@zh=uk1!|FWR?_8y${`oZXSnX1jiqGca2yEf5|5 z0^IQ&YoG9?lxgF+!$Ut~&=G3WSg)NDQmIxB+tp^469>+}s^%-u^Jmm*Q4QxW&#DsLzta`;4J@=}u~) zO!UbECEj$f(zMu~ORb(q!HZ#S8E?C}vyG&F1Fy$S`&p55X0*hus!q#P<@`#bSRzL@ zzoxl_ytzzZ3mQZ_NhDFUKmMBSv^Pzk`As4~kGmWil5gZbhNDV$DF-?)?&G(L`FZlq z&UEtylKtmmsJa-!0!#}Q{AG%v#@>MWqgnQg0ztLHWD5PsBy2JzW|%9MVb&rU5n71j z1+E0JEl%?fN%z}Yo_o_fkIEBqhQ@ZnxdHPuILv>cb^oIKP zfuJ>bzSqqTALkapU=oa1Aw(WAo3HgXZkab>e2&(|V_?HYV(1oSuq_nBzp^+Wto7(i zOG-fR=JieCF~p-r4XC6~#NTxC53HZbB((x$OgGslE(}GQ zy25qTry;JnucGDM;kSV}F`pi7D*++#c~mHjud2B=G8=yGuZV__G;%gxD}n0Q6Jd z;%337f+marx59lW=XXI}X!Y_g+3q|V!kx3Zt6+s$MJH?0(OKkXx*;Q{uJL=s2@&78 z>uS_1#zXj%C34{BsV-26p30e{FYzT@Q?=%>2SX?;al-UDOh6gi9+M!5VqT(NHnH&i zko7AIt)IwK{>*^L622sNyna?Chc^NZd}hojSU7S|YFVUU)+F1Ae4ZXY*pZ}<@&iGjG;~Fd4EXVnr?=!s<;gJ~18*@1AlHgqJWdOHxiktc^AsU16L~?%O z$(7}NNfzRfD3(FaZ!s>0bb5f~PpEZ#1vN#(WWzXzEk>3^%;PobZAUL5+2BmK$e_`u zUzWedc|3PF`JAb#@B3Mt_D%Qk>Lm%{ViHy+ekQPe*r~GeW&~mi+>84p*Pi^yb$U=q zmn=l<`8lTxextt$t86a>NMM1>XBpT*6E=!WrW6FZX>aM)`oNdNbPeSGF=?>hlTW8X z#=J$`pXum{F#?%?)&^5U?j{#f|UnwG2jG5QYM7eYH|2PdJd5EH=v}}vu%UpmOvrwu{STcnkzbfqPUk!zc*}QK4zf`q>X5OYvlHWmq~NBydF!3 zBf(&(Si=)^Y5>EJl&=uxv&`#8;cEBCi3r7o#2gJSzsj$ga@?l{+;e zh43t0KHK=5WcglMe}hBZdhic8*|=Q^1YEE^Z*#1fTB^67Wj9%_O1>x~QmwSSNSHs) zw37G|w*?M}vuv0WW}I{ReLACFsJdW2Xj;K!*%Lf}JuCALi_8OjP}8gnW)PKxAoEaR zS=zM=b*`Cox@KVWL$J1y9t;Wtfrh#rNYoW6Oi_51d1;|mU{5XROoU1_XTnX>^Gz_i z=BZl?1Qyzr+5;bI&vc~Jr_#%ca! zDY(J^7h<~JzIp1B;>#B>`zdc1FT&{98SL~Gin%fH{P(P!-MwDo2^+g}{^>SLU&=j? zozK|1ljLe655H=*dxP^Z_I1R&T@~Dh&OcWFu_5Q$b>3V+P`K|A)MX&+p&&)v>$WF$ z9Ap6w^c>|Jkxw>H>7Q{XsAi7C>ck4iaAVw4yVru8Mw^>*#IV!Ko?p^_TtP_73c0MTw6{F@dljBc2FW5>87P_a$xh~RM*}J z?{Sl?QYkhcAI}c|^R^eKqf^V?>dogQ+SV@34{H>3lj&Ts2gSVqrs5#aAw&q=)f8m* zeJYI3BY)`n9wt^!(uj0L_KRqm#WHfUuR$^yc$kCK9N+1R-@TO8$)O(nyf^Pd z9x;U_8I8lz!172pBL&BmY!dQhJP{(z+}>L}$L-`!!bLt2hlww#sG!sG1DygVX4)8Z z-K05WyB^MQTL<+N?i8T|e?6hqw+`?I}5FeV@(JacH{iHPzfO7i)_S>b+NhzfS6TIdjY8&~Wj}Ucy+` z$oI_dwVGKq#h@X6@WA$FNSy*XdxZm?HIx`3YH;(OCgf^v9 zB($Nd#b)r5FiUVkgcmWBeJXD4=5$PzAU!*qiCS#^M{QzjOK+a3btijiXYrOtSC4{1E{;y{{56|?=E^7B7t+10_c1~n z0`6h8UrQf-9FWgwiiKYI0$)GA&EJtirh|wwJdZJm2y1*|QllNhb_wO}6zn|EB zH75s?RNEfWKF;{bdIRI|+mRs9Q+nrcdpX?ZIqF^cs58m3O-|=kw7`61oioNz1wN%+6yrS+`>C(#CdPmJbU(|h zmhD&4O7U;_QjgbpGX_X+lhris2wk>(V(lX|9Aa{tt~4ngV%J|WcJPmr<{SYtcG(*- zz4XFw`kk&}E&# z?fU_SXKv$qjAv>YTEh!u@m0r@CqoDw@i3CgN9x#c05;T7kj;6|NY#fk7HPmVv&3U@ z?IYrAsvYOn`r(#CF~EJX(Enh5%lXETm7#NELY3w5&74RV-fi?r8Qj|EOGGfJ3Zy1t zn+~DG;#G`9Yue{B-|-dP8AiWKtx-Hg2;~GMa)d&5HeyNQ8cY$Uc!G4v*JYFJNZeC% zW-9{$mh%b#t$nP0c{p9IBLc6V0V=h2#IQ6dhn%Z8s zoPxUukxDw+0Qi_V=&Hgow?Z*uWR+#))lOt*IaWj~x6*%Q(c1k1hcpxQ7zRs5XT!^Y zhBesCkNLwszr6YlvsW*5I>@*7Za$P`JbjqlsAveoj~X3I?K0~NHK&v(KJ@w3)d-~e zyhvb)N4H5%I&e_(cxGapCLOH`E_G;5xg>`Ti+KT~D8aDiym(*4XxCRmiM7>0?GfD6 zO-=oX@};lw1o2nki&TQuMGdKhpN`%MO0F`V_4RmHkMsOVc5tQ;oJG?aIR71ZHUR&v zcDaxe{vKE{!h0^X$>r#9k0`pZ&ZbNqf+Sh!=R_5EhtcIin{&$zx8yVElMHxW-JQj_ zP2>qY-+?iF8O}!ClFu#7DSzCLsrvfgzmpQ4C+iKUkS|giOQry|*5Mea(?CmWJ$6I$ zn*n!K$IjuNhX9NM#pv%OE!&=8-kz?XO|bDD$c%LRc!~f1q`qU{)Q93oylA1DX1RkA zMB+B`%f@59%zh!xVdsO$*qVYX@n(&ULRItPzKqWV3Isr*CXMQ2cS6tgkVi6HTjRqs zuDRm^|B>XdR1KF&MQ~J!=|Ovo-fw!V5!*X9x95bQRC24iI+*_M{ec3zp8(J&D!HCL zlM`oqr4(Nbr<&mnFe}adEI7B=~*-%MJdI8xV;0)}Nvg`eu|K`cXkbFx7$X$5DxQ zEs^r5%gAr5;cx$HS>qDRwgeifk4#h6alY0XG%)iat&PCe3bD}W8@o2jw#VAEo z87%E_PCt+rM~tC&zLAA(M@TTJ^F=h#&ZjdLc&?C(Z4YL7KhXUuhyS#=Le2amhiX*^ z9cHMd&L-@QbB>rph7Ag+J;*?}7F$wt{)kg;F8`Zu2c6CErFy8)3p?hoh?YG);8Evo zTKIJY76pkX!{{DuN6mB|^U(Ahg2C9hu~%bhFPO^?n=Q>0`ABkFYT)-B3YH?}%FGDv z`7S+{<+M%Zb1a~Wjy*!p`U0)aik*{E6yLH&8NNpZy8F0sYEWs%vd9f=rRxMd?_E2e z5LM!cY9kZ2;g!QE30h76tQB5AI;yn;^v#S?4)^YE6HiDN(V)DdP~0V<5bM99_O|L- z$QHdP0wOn7dNQ@#HIa5+^)Jgci++b|Ofy8Rjq8bBzkTlsF9a+}54)Fr1b3Ns;;FYW zPOA^9=BKvc2cABOmD}r9II#w{YTar8wg6Lwu#2*DC*tl{1v&GLj}Cf%;)^D>^I>uI zjD(Ade+l)E%JvWarnPZ9EH|FlMk{eL?y>1%a1}A<6cx(0K4Vo%H~Po$pTW15dbPdC z^eTn*QFP~novDOu1PbBnG~BFc$uk0GO_U3;k=-t7rVr&>VMt0hzs*mqohr`~;ZXC4 z`5EnJlDexEfS={+(NGu={Hish@~f6awJzddI|G$9vlr#D6Y1;icObA*U1zE!iM(_Y zC4Sv{P&=Hed6sPoH}JEtO)7<(R1G8{!i-4 z;u{Apgy^}KDPdwh&L}x&7F~bjM&s$B4wc5c2KB-k*D7y7pr0SFt{lv_R`#@gr0q%N zZ8Ti>XG1=yf*VB-N9ZxI7qtx>;rD63Il1@)1sl_H_o5JP<@}{L#>Q* zebWeZAh1S3!?e&%g`)e-$ZVt!#N8e+e!7ocG<+rI@{~e22=p%+oy6(pPYfw=GUL-9 z{C7eu4$|LVmihu!A6cc>=-xb@@Rom{qpN#nceXLGPQe5l^shIvwA*`W_uJ6Hk;nIM zmn|pR|9sC;nLg*&&6iG$3Ox6grW#A2V2NXm3d!OZaiu+I%Q3zfd?xM~B@dvxxCT-W zH%<$?INaJfDpSlJ#n|Q{-v{{>qzla9aDvsB6xITPc#o3oNOp(*AgX#*5H!iRPrWS( zXiz79C=qRZ(fH{_PoI_vmm`W1!|AQX2%3N9lq8#CX?`vLbjLk=@Wp4&nTQm0E zSXv1d2ANan(4=n0&#%G>jy)!XnXQvVt(%NN0Cu@xB&Nb!7D zKg*>)TvG%qJ>5`dSw$w#db_toL@}3TIZ=MOc$IB+Z2LSnW;sUVg=D=2hxnhkRp0v- za$sUrEg(}&Sd1Ti&*_71Z-1XboCBX|2FM+7C4_Q69Z-MKtI>-OjhawZP*Ucf3+>}- z_lljxLVTKe`1`}Bsc=@*9+cr)+Rm7g@yI&|`PxVH8ABCkMTz+XU-=X!uxjM;MtP5P zOZQXb$xdAFg9`PVuQnUx+}wQdA+Ur$cYwXk>s#o|!lLz4^&CYkxwk44?&;46CoHRr zVAPinl#4-^W2ZI^$>5vKq%=0>1$O<@c`g#M4;y9VmYY;{cjWgVu261sElp=>fN=Nt zm!BO$;HZ^4{fX-j@1Hp1ocS~Gz@D^bW_Nt=dm#CpewDTZp5{H2UpJJGC13n$`){05 z2tXdjYkWxtEiZJ$G~o;Tyg;fT0Ak%c@ADas!{^jva0h55s~6gw1zfPW1niu@M>Lz>yt7uLm z-2K$PQL8#7qxAhr?Y1C9&@&wId&g`*6ff2&2(v-=yC!BxCvV4zNbPj^WQ%d?z?&#p zyD8EvqwH6j4rq)sY3tk7TqN`lTrpj!bmHmGVBYE}DhkcBN_5JaVS{!u}H<(cZ&c5tMf?TaE|@ z;toUtqt33(wl{`%(+<41hF8r-Sl|6WkeMXSEO0bs>b-Nc1}8LBpDgd%>E`*hvDsF; z`tz~d?#AOUnOaM>_~=$eFL&y>I_4bVcB#M^hF^8-&xI0|(8iQebiWF; zfY0rg!-ptn32$a7bcu!&3P$eJtGkG_1BkGz_!ZHWMu1vL9QU=AL8tPRHkc^w)^m|0 zE!);TEy^6vdquWP@e0U|eLPXa>_AW}(U@Nhq1~*g^TGOMtuo8xY5g!h_Mir;Z^jls zv|VsUveJ%DdJ2Y{K$>0+`3g@3>}RyXqp*hq@spLTo59GV0)fL6s@~E?_HIeeJf+d3 z-!iDJ`yA~=IqSJN^1Tc3k1%ZUDUF~(PtLO?u8tLG~`p`?Jk z?iO)b7GH05-mgfkE!>Nm#~HV)qXur@GHARm-L_P*axy!aM2U6cA=GG?QcErVSi(PZ zxO4|tFi?Wt7Es9Kju3sST>Wq8ZKjBkDvNl-TKed%foT-Wc|g9wkUMN=#NoB2Zo z7aDEXs&=my2u+~B%i5+8ffH)68n&(JBH!^dd*v={OLK!nOHpG#vvO0{85cSIFc5A{ z5r_Tyv&Phi>Yt&dV}{qT)CF6v$Mx$+w82G*l4@W>YtuIXW`XGj4-pd=Cp~yq>uKJp z#`CqKOe<5jD_tm{m40D*WYoE|?qsN??6^DpYx0M(aFYym^B0>|qd6I00Go_(6H#hK zc^lX=uWH_iDE#T<)r8-({1>Qxl;69~qyLqKItmUs58UE~f>ce?t1nEg5DA~mL7%{-44q4Rd)mmo7SE-{C5WrN&kIsP!%Om_ zmmp=1eJ}1BH*qksoQIdD8JRb1{y48+hM_oL;S}VqlQ=&bSp2O++NeIJk?jl^byt6Jt*h@*6uVGWJl^wxa- z<(t4n_+w6KWtd!W=N6T@DtNzQpQ56ZXgX1N(}H~Jv~5D$4m(HBS_aVp6!bw_*fB_I zk{GpJdHj@_##5+diH1`OYT>1va-=tMK%>d8>~MQv9|amjtX^|vzw$?(9~|`#tQ#jx zs$CG+0MvKoi7P&DzDVl(5mCeKEMb^FA5;3Q8$I&CiREH0yaU#fwBxSy&Zlrq>!1q! zU&+l!CHX|^e7Aa&)=hgkqtFQ;WLx$YcN~wO>o83d2@TN(9yF#T9+z@26e`UdJfX%)YI`a~`>zQs(4^lCc zt;cA98C8x$G$Jm>Ez1Q!U}Yo8NpS7|-cbAlSBOO|I)@o6@);(zsyH0H1G{}TxUOEM zBHzpHJVSV9j12`kTZ>}X(iah5(1cvOo~%2_fMu!<>l4FsznKEB8^2ED(8KTA@C~&@ z9ps@D0$mfGA^@EytG*?{Vq#3bZYyxU{-SQ@CCK<{Xn-xiz+f-^wgl`*UI@#xc4V7uV15)J@#olZH5K4nhZp_=H^Q@IctLeqyVs{Bd%jaKkTaHDuF&|9lJ#N|$-M|`x>5k|sgA}E2(XXVK4`nli1>u%c~aEp&U5h&3jD_mi7+E)iPc%VV2Vgg z;fdi5RPT!n!?lk+6s^3>`^EZ}q#!fO_&0h4xUl8Ymx+2+^v%e7s?M(s&rS;Z zpq_!k5)F*iFmvWc$Mg-9>$-^PoRXCYvGF{G&cUc3GM91SN{@J*ztQD*#3zPR>Fkq= zjgP{ z30su0b*RpfHCjw!TK&Y&@9W2MIUE?)K(JVXd;YSHIw8wc37j}?svVMm1R+ska5(?HBrDy<~CR*>1y-r0$;^@u+y+_DC1|+UeO3gsRL@-5p*Tdn^sLI37kE84a&j?DFuQ9*md)XOlSst>Y_)>?EHL zju#0K|Ae0`a2+JgB>Z#s*VLMUKv*rzN;V}qGRH*$Pns{F!cJ3;aYm(811bx(IG=sW z2{1M#W?-?A^~XYmVJvJ^fBF64;p;V}kety}PxVqwuPi``jH06yEP))?yscQVC;Ilm z06SGSWAh3%4t{L&{X7du`7l_s@99*DNFCwvGi`CoDqA?&5=WaA$#1tB1$piVsPNdb zujEDQGw8TWkIve9wYG8+Z>ks>8^^WYks3&i2L`YZ6gjkz;OCO+p1r%?LfN| z6k&3dde5a8$O(|HGEH*Lo(oJ{-znr@x5N#}lJa8GGxS|`qZy*&C69yJ%(zK(0p-_q z2d4U%+IMc+=afi~Y~MbQ>@zfydTIDfTtlvHzllZyQM zC}y}H|H>lHJ>%O(<>wn@JvR}Z-5^N}e~;0}*CFq5E#4LGSwX!4uiR2lGuVhOcNS-w za=*ey1(bfCN&(P@_;2HN&49&ka1QbQXnK+(lo(-4sBIqaxi|D1id{NM@QnRKM+7QEqZUGWiX4a(uBvn{gT%IcPw`wFr z2rEegw*|9c|B`^;?-_Vb?fqd-qdt~u@<;5=BTu`kVH5e?O$NJGX}2H+^AAao-&6hV z_He!vE%cL#_{(Fl9~+^UOr%J~iJp_M*e_6ou4YBj#q4=~uJf z=YmbHX-hN$o8b4UiF~+LFrL-8A@(J?#1KaZPXL?!%8`?Ku7b-DMmpH_j#GnH=9L(Y zBPI88k1Gw4LV|>95nVK~A^6?d~jU01$dLwg}46Ix#MplARf+l@RvuPunB-Ly~}xJ_f+4Rn05 zMhhng7=C_i7x)AMTLo_bRkl!)zbfSH_il3pDOXXvx)!;|bZv+ic9(@lgS_@pTB?le z>ROMdO_rs|ij_)N_(DJ&&&8kFtEabUXIMies`#eH58k(@vm6{RsbbA>9}>D%#5miQ zMrzz`|6~5jP_0Wg75a+oCy`9{YD#*~wEjbWsDRvugx5YAA5b9ye;Ky^wLIVt1pUk$ z{M=3~OE;zq7K7|RmglAAzVokrK~{^8UbHtr$O!})dh#R4#i73ry{!#@a)Rc!q&XLWlT=4~|866rEpR@-H?1dv1ea(%Pg}?+69fxvPkB5fgi;mxqrn9kfxD#)4jE#@YK9 zbeVL!ql0H)ArQ_6o{BS;epeeq3vN6(hem8v=pvT6?p@=%w+ra}|8 zC5_u_>2~;5icRItpV)p@tz(DNA3y@n`t-TK0_o4WFX~xXbonp^h$55G0D`G^W4n^fUQ++2J43eWN z8je)adL5DOTHYvTn@b&$?WwasqF9~w$9NN6;sI+>&p_yT1dIAPlBg_CB=Qfgg(m$2 zlB|c1A@puqE-qlu6&5X8o3DTt5{(kx&YGz&KWa0vEpbl@r6OojV66bS{)*VE1qK7 zHiZ|_fvqyP()nVX9Ip9r@&?dz6tA+6B2{dgS=ow_)nMsNWz>kbmI^?Fob`D*-jDcM zdHc|~T9!%P=gNiwj)pE|+MEIi8}b`%f3HMq_)P>o=+ygiH<*xNkJmOn%0Xn4aUnsu zzHU|z{nUaAc#xq^)tN8Mv6X`pS1kO}TNrC9Z)xgw(O=cxAv+uEz;ppyl+gl4<;6L= z^EMhnVU*JgOwBl4ruvMr_hMM}cHrg1N3;5>*Abcm|zxJMgYwFFe z-|INZc=BAe=~;b{d?CdlfTZ!8;wUqyO(+bS7~O6*oLPP0ybMI3isC$H)^(y12WW;x zqKe3A0U9%_hA8^)NX*eZ?#KOltRfFX)|s#xo|Z#_84bGP*RL$4mmwGaK1nb6+Kabc zpQCk-5J;<9V^75<0Pk0ANzg~d4)dJe$O4^AN1{TK+qnmpeve@VTJ$YxF96(Z>}sl} zYMt%8fE_A>*YC#qeM7{OQ>p;9J=%x7_P9Sc^*{G?Maj=!>-F>SnlxzG^eOrUPOHyo zaM;+NuNFw~ey&X+b4bF86fftW4D6~`rf#B#u?f4-W^<*EX?!} zoGe^qq3edL#MR~AKqXRSXqx8BE#l2dq5TGCKlwT2?-=70cQ2kjL!Yg?g}jNW5G4}r zqjAn5j9YyK8}G0!au{`jhnz$q%60e2!gEHVMm;2ZkuW?dM}&7qfR`3-j{TboO_4eV z2f|Ut!ql^DnfE9xJakd8C%(1JO+YS=nQ2x*#68{uCnUfD=k5KmasF-tw}z&_U$tjG z^f-uyBKEDa^L+DFm6c^^A;I{3xN8quc-KU#A6eQh2A+-+Vqsde`|M z^QuELj$6^zKmfU4jZt`B&+|4XO}UH~ye25wHAcc`?I=9Z$u>@d-g#ABi=V`*o2T`b zQ8dt?1z5%fS$J;JF_iYeW@4rWcxT{yEd4yIeqC=W{$sy{9`#>Yv=TkStxtz%W8D)g zUq91?CBELO$4PDFnyiw4SpnNh19q^{+Z>b{esu2G6MW_x$d_TI`VFJ*UP{u06}MWV z@e@#f)qS9O$Yp}PpSXRrw{a-=-Q)X1sb|Hy9QVj=RTTUqEWx- z5LVymZxG7QBh}a}?(HK5td@u*Ms~z`^=tRPZg_N$UfoAd=Oi3h2r8L9p8F#}1GMWl z^t7Y$&q6cg?;cwG%2wlRsrb9jb2abPB+e~{?0{}k@$jemnE_3$UVzjCsy^&@wQ;Qx z45UgJSYp|Q`T%y;1a@*H;l_kkhbX(4%>LEXDnSeO+cn{Dq>-G>bKii^VzzlgSZeLt zStIDdIufeBS7+W1uy(k}niGCj&#ayXrZEbQF{w=HK@EfNsPE(Pk1NvU``;JLp@3Lq9sZY zbiQVW!98@uiTUfP;o-)*FJD%78=4&#k!$?jvv0csZ)E%`fozJ9= zW2snD@(Ba&`#ebY!KSi;UiPX3^6-UvQ%R|ZgvD6REwXh`%Cbe*IuJN0@aq7PqXI>ukVqhY!Jk*wr zno|!cT1>EPfUD#)G$xbcAUmIn2`;uVO){IRhve&DMQekUm*2X!2tkD>1(EU$L{G^D zscb&}k%T@UMJB+yVM+hFcFEdm!8-uGXUIj0cBFgJ zw%-C&eo%#K(04bPtkms_9~a+OF%FX>Lpw8>r#Idyjy4VfxUzUv_9HuzrIf_%qXPSH zd(9@dS@v^f$ZLO*KCAb;0!r_WnW`XBBpJ-VnVcDCh72XXwq~CtV<`x2&v_P*r2?aY zeIaF?&dG(0FOhoP8Ww1;x#5I%IOlgW=nsDIo#X*D?U1ZT{|rr7Q5d z(SvK?V#35~-iVkS-u6A=(I_-3dE)#TAsEf!zO`Gzu}03eW!)n(GS)&SAIsNC4;oOJ zIZ0#2F$Dwzy!C$F-tpI}8kKauUs2ajS3FTkY!C?d9jIbjXFh@!%Tv@PN)iV|==s-a za++UkU3ML%7?aeaH*m|&+Y;Kr=>%7UBk2nbaa8V#-~|kty(SwHGKt-zi(E`roIX8~ ze`WD$2IgyL-49OkoU_&O(nro%%!;TO8AdUj6!fnm{wGp^2&K*=dvK4s>!P*r(Ik5V z*?X40#e$ky>>$53q{nXtn7{aDB5RvfEXC_p##M9WR~IK8TI+S|@z(7tJW}2<2GCuU zaI$e1(pqqVD~xpz(d&O%K)ClR1Dq$ICy9CM*@I0O__i{rtpi_@=aQdBq{oz-_T(yE z?6~v3TE0Dl-M$37*OpG=->dH^7;u6nr{^u8rs2^vjQXlE3Vg-|0(jw ze@~XLhcW?<_`_|aXf#h%qKk6!z4ufc7I*3P8G@@`%u&_FW$3( zo>Nn6c(ZD%Sp180C|_7dk43xt=c#ow)~^Jz9nFWNe$|5*A?J{v z=(hLErKXMQ+87<;FL9~zJUyZp=z>UzcLy>-qmB~8W96Qv5Qk=-LXytiQ3~tvrRUPA zDbjU^2y;8Uo?K)9K~*&%J}Y}{eB{?ltvr-k^KER4809RA{OeL`iuVBm0X6w@-fDl@D<(6wol zTPgC&Q=M>+hy-KjXnx_rEf_4e9Y$L=xM?5v;BMZ_)_@S>wkU+%fno##qW3TTC)oh5 z6n_>cIq_^2Uj6pujBR!&6~O=q%ja@-n^H^LiJt|E6OVelwIwFL2~oKvs@HHmXUp5_ z#v03BP_>pG5ksi}(Ve7hobk8bdR(*9>w%jV(J9nvLb@L%qM7;O5eh&KymKsywYPhL zk=UdNF4j{@F#^Jr+ZralW_B)j`>s~WK_=%Z52<30Z0Qc!FVl_;?z}}6zcQPHuvb&O zH1VN{3xNBYlj8MP6I5j{Ww)6Bf#4T8iAn-jL zPO16yTN#0iW))}4;ogDvChS&#cA!^!iOFH1i7DGe&-BogjkkZR59#C;jb2SLYU?)v z@b;*MW1K#<;<_f`O&<2j<)nih0zYDCcQ`#OEHehFfy^XUzztHc?wi*3X$NdRJN!)e zotb;z$&I;|^mxHt&3|Q4WD)ZvhLA~Jo;o*O#4Lweyn=fNiaH z`5>L!$vQXo|PyFo%pJ$yLU23oG~9MSK@a zzPPaWa*QWor3Fs(^sCXHgQpv78sk#9p^Ov+vK4Y$VQ+r6^~t9bY$5@wFT^sL!`}7p zCj1YS@TFUHLsmz9cox5Qsw$jugp3q`R^73fee>Jzt8W)75e^i>Vcr-l5-v)h&-n4} zu-y^;_;(BeQ?&X zKDG_Bqr2pXi0{@Ado6e)eYLf3P8lc5d_f-kHc+-ov2*MC1FWt)!QY zo)a8ql_N%78Nz$1xt!z3en8Ibaosl8fS=<0sW1&3#A++}U4LdDBgdJ*@|&f@jh^1# zy)406SkUFl&=N3No&9bIu|UZ39`@U^%}pL$o}KU6{SO&-yCjf$7!p~)f$^E{gMooe zCDO)YJ=2^D=IO2sx!@-cd0zt%gFvckm;V{Qz7=H-8;i*&UJ2`4w0t!IBktyAzLK&# zptnHKRb*+$-6=dq>w6z~=lbW?NPB1|adZX0{XGsL|G%&x@pRT1ACFaA!U^&XKod-m zk@OTZ6e%25>F_i+lgXF|%x$i{65oCVL-+P7%~&GE@n9K{RpB(jn$dimA_^C(1`5!a zs%e7KT@MIfl7x8MnqamAau1oGhYoZyc*lb{umKGj5?zb;piL*v7+ccUc{IeM*QC+6 zE3AAYZH4^iXCA;6n#|SFKj<0m^B1UeR<+U@o+yKohA0jALnMU^Et@9bDVqET+8~0t z>?%EGdPB5s4m>VP=d|wE(O?qO)aG4*4RUI)TZtBpeDP8(ndEV0)UcfApy6MwWn=?* zFC`IVU~%6;?HBnuWj}kqL5L?shY+{r)3L392*SzX@%dcPJh+}1!+ca#s9|iOJFt5l zu2{CrCvFk=iTBpeZK$e2Jro#rigdSP2InEUo%74v_TBC0VcvG{s)!9&1X8Jnn@ob? ze3sm<_6%-Z1%cbMm%uk6LS(P@-34rLMqiKdEgmKTEB7zBo2>dY<+UG2hvO6n>#owo z{83p$gk2#oQbmsnAYTck4RP%a<8*Ts+$O>)xiL3;3S>+Hw^j7dWZmtR9uP;!dBau} z@bJiawT<&Np1xFyeRn~xe7rJ!f7QxQ|4ElR)6b0Rz!(R3(>7KTIL<}E**B;0w&^yUYtAvXbn|uxmr|J? z{YGm9k#wEzd!2ksXI^h{sQr}63CeqZEfiAUU{5LMRwWjp0u!4%1U|0Vx^L6e%CVQY zaa%qU@Y9sqX*_$yYUDn34*>h?Znc`fKB^nJpPRT7X^AbEz(GdW9J>A7fn!u zQiGKvjl!?BDErv}8->ojVZPUgM)5*4>Z)Duhhi}BObghAC5pGDUS!kzSecYnu};d6 zG3%pv!cO>P=2%_}6yY~)e$Tb4CBiNRAovJHW#2RhbSp;wisEhUUHafT0Nmss+53qg zk6L~FelA$eG0 zzMH*R2QnZiOf6TVm|WIWOCd-~?Vhl6KBU8h)nm1-swzZ@nW^>}*0snp5!I|XF9h>(w6Yw+1o z0M~ky^lO%vdG+25veB|glx)?+xI)y*4@S(v=pQBNW#AARgW#H@m7G^<2uBP1gBLZT z2A-Jn>@?yj$t;(mEgm>zB8R>tyN0VlyxH_9fr#`rFW^xc@HKd8H(yU)bg=OMM-wBL zoK6#(=UXLXZKy2%rX^#HDr$m?^Yh@uYjNM}FY8u?J30UG$Pp@-hyw_S|6i0Wf3Wd; zMG9th^V9X=aXzlNcg<2XN3}C_NkwQeV7& ze>A+hhRH_Y0_}Iq2f(mFxtuIn4y9e05xsgmvee_^*}oqVxxZB|!sLy>^9V2w0@EP} zgQ9LL*|@5D$;qc&uWqMEW3Y2Ctl}&!-rj>L|FI;rs(Sul932VLsNl`MfLU&!6#{>K zP3waFy-5Sm&@nW#xNFLaRA|u91Mq9?^+xm$e)Z;iiN~p zKa&$*O~05llrcX+4kG~RNloih^3%BN4Z`D3{bOilHF+An2h|#oG3L@YviRwMtw6R+ z$@JkS!}(o^K@+Nh9x4lJ!QwH)JuHrpl&DcX;3#Tl7P)0Tdz?O{k5R^w@*GXe*GAnR zVtq@B0d@2~4y4uk=Bu<6%X_UPqeaA+%MJ}@4I(dH1l!MH(hrF0v;dq9Hc}nsVqg2l zUWW(#gjTAg-vJLewvko%PC1eb(E{MZLy6GL5@Hlnyjg4$>2>Gn+?e#IbIf_>%;q*tjdbSQwFg3iC_b>kTJ8a8Yq z#Z#WjSHi{>Q*&`<#WcUw)nfnxexxsiYs;`{?!f-~ZWMJT{XEY!6r|JD`;(0z3Z;4$ zRCXKQC)}?9zVZ?B2LAqD2{%yA(p04WwAbw|x9Um@+^n2z<3_Ue-;viZ2{ZBz-!Y{6 zd8mZCx1uW)bqXtT0DdRf$5ZJ6sdyc%=TfKj8sAD7ClZK?FVs&$`=nboZD8a>4`KOO z9V$ob9foA_r;(KnJmKET!GLT%N2gB<=r~}IGm7WDvuWK3BbpqiTo5via-8Q+O@mSE z)tH84VZ{I<77HtrhVt!e91?X-5P`S`5U_C-WHGuSC0rt(0{RL7@u5UiZ?^n zo+pTV5}-y^BWY?0peh3rz1Mi1{`DUMfxd4p3$4Au#3(16=FeE~olPY93*%l;$9H0p zR{V2Al-?4fnWoiiaIbw(E*y2jI+wMSzBU30#A_lTQ}|EyXoVa6{4LwYFZ@G1L0HN^ zbls8x3ohJs`#+u1{=kq?MTlxp>sy+M?dO&gfrfZVwD!-aKkr*IF%hq|3CaHPJ%3$P z#&X*_nT-C=ydD4dpYpy4A0xrD^=s80Ovk6gDvmPn|If1jEfI^f6T!G0ggX{_5-;qV zxk6&7!k84dG_u|!C)WjQV3UH>6usfOjf@+YXIAs2C`Gm-8v%^+;M;dl%&5g2zbY+2 z#i#DYCc%{E`h=i-A)~usoKv{gK_j>h5=pbbw}dMpfRBGb4AKD~-BAWUl6EZL!k9_@ z`a0{0jr4~#mZU6}ACOkd8OBu7j(zFNnmo;k(maYd)r~|bz$}dH@D%T7B?eM3-(h!0 zu6Hh(k)rI}%C59L3!j``jP4W%XM1Q(p#ng>?Bn`q9*d2{k_45BbFDVg&Nb^QA3R~J z-laOi4_Y`-C|zD-YxEU@0ychjr2?t7$NKkH#0JFMpxWt;#E)On9z4_X2q_#I4UCQBPtH2-l)1T=YktUX z5un@R=O7Nc0{3^Ag153s+43BS#n`kWj>EKu7eqSC{I(#*B@s9MQDW@Rh`4}+$Uu>1 zwvMjIw#t_ssLgpi6s`m?AMD|pC8C2qRpcx=H&k>Ov|tlelb$twnWc>BX8xC^*ostD z@#biGk*^mSCg(Tzkwa&E`f&^V5L?Rt{Paj{%SHy-U`nzX!m2mo5wj{tHnu1*_pBP(E!+(SpBak4Oqa-l}KGF z<$T}3TGwMYLpvTEp^RlTD@|9aSkKS9js)a~p52rnlHMU_{jhu*gBNVwS>*f8?$RBr z**R$BYn=ink+vyg6P8jj33o_$S99iS$9;J*rI<*mh~5QtjG7FA&&|Mt;9flQt1iqk zznx-A3|7O+hDOB2MlsL15^F-EKj^|AV)R{M=Hs$_*EfwpD)EHtMXydE)TGSRy7KPB z-lIQ|@$bh!#QfCC5j}u_*72ULD!dD~`V2KnF|jEwMoA?Q0HuM{&qhkPTD>211Zq|; zcz#KH64XkuI-^ifd$~=>Jd*f36$rK&E;%%<$e1HLb^&itT67gJ{LA~E%$ufsq!@0DU}?_D6)W|OP;XADK{qH% z2>%W3(d`bi#sxdcMC&hs@MQ~W1*1E0{PNypTEYS}SC3GcOQ(H)<4pb`@pn&X>dZuy zn^RX8lw8&kp^d>R5boYZnJX{Z_T&N&UU3;%%v-Bu|N$-OH1QE_HOFM@+kXe*b ztzzNamM``bKB!0S1VV<{cF^w+qrp$SEZPUzNWe#Ep{SFt0#B_P-{!x{rSn=@crOn%@cA|_L)d~#9yD_!L;t6NB# z+mC2-m}ev)d}mL|_3#%|-83}<+K*lvgUFkXGYnb5!=7lp=_`y(aDMgsUh&eipwPP= zf8*yyY22vpn!+1#mBp#t#I*G)W`H`%GTO%GOSQ-~fg9?qfMc*agx)?WtV?geU(e)E zHw^lfHkf>H*iYX0P+yZ|Z(>U4d{a?I^Jb9y{!LrMXrcu4j5~~G1Hs!0GO78&z#A~)fgCxhGjRL0O2FCV%(w;hU0PWv?a3i5h9?MtiLsko|WJ?!U5Kf)m z`+>euoNR04yVh^RxLlJ_WU-2l!~nb)uEBg(V)ZE zDfDSyvhd~cl@-)d>C3`LrJ#Fq*GdwpYU@Rj372#X7vF4Z{(S#~z2p^aKdet)^0t0O z2$CV%VQB2KdlT6i{zC@w-Ju}M>nzzCV0FjUAv+iQbWanF?2jDKuctuz^+x9Xl}-G+ zalrdz0GvFTbfDx1p;=fm&>9eG04DT##m_&;oq7$HbiD@k*dX~c#XRMFfp4==wqyr} zx=|Hmcdi!17diaG$(QH&x9OnUf4pbaXIAbpc%4(lejthSwa+B)VX$~S4r5q4ipcO^ zSl}&xlh}0$79iMDaJ04i{xWy)y5U3JB1Ld67I~{= zvR_77d2$nkH#T(#IyKbDWN6(lhE}ekg1iQ0uEy7GN;|?Y3v}jQRNP9O1(Dtpuz* zbkLW}^RF4_w*dWAr1VyPEV0hv`7Z;U;sfV(Qmah>^`%ewv3uWQPz=aK`5>E|&AD%0 zgVOefw$zJY{wV0J`wweR^WQJG zn}u?6@QM@Df`UG?9;5rv(Hoj;&vD z?Xd4lt~5X9kVE<|8}Y7Yh)N_r>3G+&Jp!JEWy!wZ-0@=v;rVpD7=pr_zxTS6XsWjL zMPB4_&PM%P#qUG`IX|Q8?4OqIAgRC|jD}xjU%y)1aYEEkuW^Vb%yGqh$p4!tpU=0Z zwLb0}Q1wQ8I>G;6an}F2`)@3YCP+F(MC(k`nx-}9jYwYFP{Rtom<+kN)>NJ5D}p}; zG^Txn@@+NSzdc404VUpM*LHkbgeN59%OiUCxsRvqfgA!R!H|b@fd)cr0V(HJN9igj z+>;ChVx4rXp*DNJEzl-d-B?5qM8jy&(b&{N=CHfyo6Cv%^wEj7* zM2R3q(7L zy_#xfV9Ex($7ouu@@aGjEXTeWbh-}Y)awkY$FE<-v3yTjnc{b(2XcygS_7$UL9GA` z6K3jH)*~#DSW>cIZe$*>%4^DIrs%JI@Px&rbwE&>9O@Nb@^nUA9Cpjr)+V9XDF>xE zPl&F87$;iJ@jH0(r|{C!-GaU6RrhE(R@c;$Uoz>ZE)F!(3tu`C7f=Q$AtIuY+RCAF ziT8md5Z3rRbo#h;q*|7v4yNq#%4iP%I-TWaW#_kyd^35q8w=XadUra`I+4GBzQNW! zVP;8FepVzM;VlQDS|;RDU)qUl5d`l1)T z+$UpwzLW7K6lX%X@y}&sJ{Rjcd@`ij2*GQ3pv&8}lx@O#D9Yg>JkaH-zJn*DB}0Az z%Y(UxDiD5Q4P=@q1XF#)J~O9M=hrdqOI)U~jeEtt45`bf5t*J{u?9U-K}qoiFh(Ug z6-pB^t?O(o_*Xxe6N9xc`H5z^dtty5sya$@K`h+w4A19@Rw@@nRNIq`d$%c3n5$9l z9T-S3rm?#4!Muk(qER)%)>suHPxS@f&``HD-u{eXf}l|n#2`MoyhY`B5BPq>)RCPcWu685QYjni@)Ty{&MOu^Y+ z(R$_0{+Yq-R-lUzL5msY7ZS*CWyAhkNO*T&Yv}%k1zr8ZpI*D`{*Bdh2%igQxsM_Z zIMB=M)GkkI{!ORLX+SI>jeBQ~NuUtrsp^AuWaAmw7jya92SO!HI@xrx;6R(2TwyFu zakHC)@}yC7)rH4*UDJJdajFa!frqnEQb{m$m}Oq)UqURzXHKafFod*QP|0$J)BoV_ z5WRIkRLcs+?HJNZv$Ot83n^GeWFLZLpS8HxUOSyGz0YwW=`BIP9TAPY)f8G)eTqF2 zninCM6t^U4IJLG*qMnF9Oh6z2m}*^p#$W9SFHcp6H)yeWbEOyMg4yt5Tn0t2M0rhE z50HBKsueBMW2&Z+XV3(%xBB>6g&eGcsmR|_lPet$JAoEN2g@8-Rt!U{>uGtLKB`8| zy~f@@?tA4YT@2qJ8tB2>SGouDMJ7%xBPyJaof5bDTy=Z%Yl44J*S@8%p;fMeP{mkn z(NG2j?0J~$E9BH(0Pk3(x$oAftvo~ubzRs8Gz}lyzWB}SM6<`=3$MP{c>HpZc1n!q z6xRDEe`AAO0!R!Uc{=k`hkRpmNm_S9>wRyi)#SO@yYRSC7tuWHssY^Zn!F7Pp|tq$ z;SQ`YeDdq^Q$jXc+%6k}MQ#JBW;53gaQZgEAwH%{&zozf7h zUjyPmkeC-Qqg-o`lM&pS<$}X7`}e;M_tVJyEC!FT%r{X;ZVzX!)DvGv!@iw&0er=BeJZ{X5@c*4LlKR#bpFCTY{Ic(6) zzPRdEBkS$}9~IxFEfDB=6~+zRwUc_|#S&9K2TtMq)rsWkqJL>{Q+=MB@ zE@hub{f$l^cF_rr+P}rAnzL2a7#+k6$hx_MYbCGs-=Y37f~&-w2J-IrHDU=aUZl+n ztS=|TY1?)NKrQgx;tkp`VhZY1Rvnp*XOFJx)!Cw5Iy;`$SX+%|C1_omPef%j<5}{) zI=MF%ser9hVMY0lxV{HxpUCdbD|bds7R%4f`@Ycbo2 z0T@tU@zyBvxY2b$q&B76Bp*< zXW*lwPUvHb^&lnXqtsUd>$Fo=5q&^V5u?Em)s}jYt)j9tlDd015i}#gOFE8}25go1RO_M9fqpCxxjkD1^@eylbrG$3_;NekZ#$0}d4y!0KMvs-T8=7v!n*Z*C z??|4&dP{_Y7hFr;A|>&DQkElmqHk-qqX{OaKTjyx=Mnqic2K(E+5ayr^qs=<7C)?( zpo8CL-&lg|^h25oA`5Tz>gfxIgMCZY0YswJhklw%_u8o>Pgpmt$^#E>MBM`Vu$~d# zRzFQ5eXzuvzkn1@d}88I$mMYtz?_WIH;g#cTdbddaR#*?b_vwOYQ$2+@B$KcTl*MJ z>Ypow4{A3*cX*2Jqkh&yzP|@=8&jlyO30W(Q0Bg_EQh1MH^>;#Qj~tgXM&#;T)1ll zK8z1!I;*Ce1xlY4rbuqu)2jB1e80qd8BAz&w*B0$1bhY>}`Z|#ro~I>2NV5fOo&F&V;YW(I zq3P6j#b;N@V1q;tYF$ZbMIQLPq;x|pl-GVXZ425ed~N-LCJ%LS4W1-XAwT8TsS<1s zS+tVrC|+=|8@#FD)=-GlvPv4V|10YE$)IQWD7!TR0oLvPEDKybG&7r8KwPj4L$z|Y z@@F^jQ3I;y>gUr%pF^H(;(Ps{Dfaf42#R-XY}Ny}WtupBz?d%S1zX~-i=F6c^dgC2 z^7@R>hRf!nr7h#9mhxG&FL3#V_CiuF>!&Q}{jH7j`K5yzp8TGrvZ$WIh4D|Nh0z+Q=^mV3$oBZOfwJtGUi-yXmyZOO7eL*g-{*)aEf-$2m&<)|d8=Ep zX!6wo?M#WwBrw3Iu>(Adn-yKJ?{gYW)a&J2;Ff;yc>{MVY$7F{-V$B5nic%Fax0ay zpI+!H6bYrV~H30zd_vNLe_$$*<%^cu_8a9*4dJB{xX_m){?& zv2tqEJU@ffW(4sTuRkM~F85`VTh!%9iH{LjeO1#G5_o#eIBxa-TC3Mu0~wWyZi{#c zbWSF|8cALy$HGM(`DZynrOM}1v~@rvSp2QgF;9EW>57|bS9q;ozutxK&f#7j|J9;I zf!G-==^6eaVs;6w&O=gV&2JIbXg+f-BXEZnX9=#tzVw#{Jj{`wSJ}!oA2DD4YUcb| zm#GuwN@RgBx)`k-%lJrM3zmv`?3cIewDOJ)Vk)9_#=3|Z{yafdV&G%Utou;xV}Kh3 z)jd(821SF@Op}-hWyer~_)@Ab3;?aVT7;_$l@cInb+C)k^x#{cuEZ#n>j&$k$&_&&z%FC;JD^3FSw>xw?wwt)8zJTC&6w? z<_RCj00YZ`3m9zK!zK$A*PM8k0tl`bbDTfAP|pQ?n)Vm zm(o&423;*Og1RetVJls;w_n@2lzlD(t3w=GXo*%R_opsJ&HGoi7mV6+}SNzLR_ms8%!UwTaty*ca)Fh@Pjav5xpA z&^iE|cgF}kLypigz0=G87LM} zDJ3vxQjd)_$01^4)(rUQVBGeNF?W1JgW>P%hf-ys6~;0vM&p#gU>SH%<-z21Vi1h_ zqRxUF)EM$r_l~!efTW0A{5kRye}44#jrb*pRuXt^9ykC_Lnb@>%XLuTkkie2KEen+urh(fyJ@ADy`_@=VHUZiD$lPo^YOt&Bi z4gyc)?rD>@q9LL63h9F9A9ML9k^wcefI}IombBSU?D^x|CdZmaU)?Gve0}HJ%4w`v z+naci7(hT8bu>Dma!J(ltFk|8uPqzAKK-IPc2wiuJoDYe?gXhNwRUI+M84t+XZZGQ zqxip?D2_=Y5lyL`BWB9b*1iLDbZ5aX-<=;Z1`-OnT!H2+q^0raT?kVlZF@y?k0r4F zrF(hgv`3|Qw`owLtZ`bB5_g`2_dVoCI9Y}So41Ah9bNI~9`XXL&eai2 zoQ=bnoY$kD8AV=x*Fb%UsB3&%r(8k+Yp^)n5Ttz{WYfvU8QNvLht7$d zh#?T^mlSudZIBWSI$AvXAE4H81iz!ciE3lmWU$24T5NLoUxpJqZtx|Ba@x)D;JCxGIN)+#NUw>^Y zCU9s!A{u+$px@PHHLIw3y7DuMQra^qc?HjcapRNk&?&ZG#UnotYTH3y z*24ikX}Q5;a;vB1=WUj5aMsl6JAc#0uPU@P)+L2*b$7N7Kf{k-%Q>V>m~f3h0qz`y zCsEYuA#<_d(mtA=7;<&nqASk0d6Xt=+S``d%>iL$V}e9Ac8kqdoELuLo?53rL>Zhu zXWRp7$^5j?5@P_B-~tk2G2S=p%JAb@k1)zF1s8wMFc^aYDY^taM~zTmR?8GC!+901 z%u*sh2v=v>%<6c;#Y_zKXl%b5ksIv-`ZROZ`O5eETpTBV)F_-t+y8-CXO$cn)CRS`N)G&e z$4@Dg+2;=6@X?>oXcP|tP(HEZ)V zW|-J5N&j#Jor3MdUibt~--7k{D!KT}ng2i9&AH@S#FVfe1#Rhs^f~kIz3Ga`A!Emp zH4*x-(S_zcBPLrIbj4YkyoBK5-v`MX`av@c)>sS-OH-c6hRefYZ}sv=-D#&S*>*R zmZ+pHvpb&sRd@EvUtZ}NqL1s(>^WF-#EK1%Nz5zxD*U5M>EjH7tuAmuC`0htbqT6u zs)Ap49v~PK)BFZE@^bT2IWFIWFoVG#EUE?-^WUBDEq)gC*W{i7-AbHWNYnEM+L#fcm$owm&u)C_n1k&=%WuG#+us;=x5IqF7-K2G(UBs1{5jhc=#T3d&Re5Jl5nM zfBm`}6{xJq_R(rWquk4vTMteJ}&L?0^3ER$6{OCB#yR#H_>GwaB9 z$@Uqcp;$IQ%w(BZg2%a+Pm~c@<-$`z`NV`oJnt~Mb8NV2I;gIrO)*CV|A3c#WynAQ zs6}HZou9DUB|Q@KvzI)gcHTse{Sk=^#O1(}r99C}5j&See=1IIx7w)`TP(KS_wful z2tNhK=eiZBP`2Ua9Y@hWz#<%?x)X)T;o5oQdj6J26{@K-z(3styv9PlHTn?nM)8Vn z^Y=KIwEIC0VjgeYiyOwTW6r2%{{Gyt{~eA4JfYN2i8hQdgrPXwSvd;HJm-ShL;bL* z=6LM}>q{3Y0d8ma(xya)n~+yu?B2TpnJMz;DXGkoNjcI!y}bP?@_+1=CQnimC?-q2 zN|_@NApbtgzTAKnw5Z7Bg?=YVWFaBu53pZ%?-@VRSnNnU7eOCF6sb^X4oc+j)o$et zk0Tqa=K^^+)Ht->$r#)crrVwxU-Y0)O}(gM@2=2b&}@?6q7gwCXO!BZQf@>&32vo&_k|uxe?J?}JtU?-SY5cRi<Y1}8b zDH=wH_;)Yj6Y|{Hsj_z_WuJlqb;fOmBzoh%%864xlByQ-GUGi11w?F48I%>~zbx)~ zyp7T=ztq{;#|L!?;w!VHSCVV!zYp|xn@Ocri-(VgY+4UAHUVFY^#W{!Yi{5%rPsyP z?;%f{3PJy2$azntEBP}G4g$}0#$&MF*M=+ib01 zm8)d5JHXP;bSn&hNdG8QWcNa2QfPX7y)(~ZurWC8Unheex;rVS_yQkWNzIHUdQn#| zwzNJ@;2PnJ)RJp$kRT0QCzS?9cuTt8ejKT}a}v*=+n8{Te}n&qEo$Eq(lojNG?Aoh zuizl3b2YHzsFNxZ$+zk&qR`NBH;9N~6j!3fs@`VTxTTP0S)JY6pbG#wY26Mr4MEXF{*Nm9xmo!2DwTL^ z+VD3b98i=f>JSs)RC>krG?wD7|Db}GCnAL5xLEy8EkAT~p}gUFf-CY21a^f5B~;*T z*>;r~ylo-lQ(sT7Rjx%pg3Q+Xsd(%(0X$`n?8p!{$bZFz?!@tqI=OvvmQ{SWbiEBj zkh+auoC8c;)&q#{xoh%8dz`J(ho-;IeBO3WG2V6v9yCreYhnIR*Uo=X{ND@U-$p;x zJ~&oJYyZkW0NGDQ44GN=pnuw6{&7R(D6&Tp#Nw`2doPw!AHhK&nn*fK_{ua`mJ?5DzK}dmOpK4a(YA;~Y^bOUdanVQ`Lx z6*CN!*P_!f24R!q+eP>}zfMHm-3b(gW_1*-lV;Z5p1YXuT@HO3Ao2%5qJ&+prk$za z9WKQf{|CC8URkj;%%8UVj;o1yLuYCJ;1AVvzI@WWpiI&nLNd!n2@t+ z;j>7^x$*PIB5nBF`%NDFzj=+p&)=U08 z!yjn$)Yx_X=}p|}l;OM{nB9uzuM7>9lYIDG<~EL*+VEH2wbl8~F_&B*c5$LlSRIDx z>BMU{9yE{8BV|gtHLp6%am9wUD&HvvLh3}2GWvGEecC9>`Ty2pbtL^=FY^7tYqY&m z4Z*>yKidHEd?~ein`?;2pBA*PGvK|YC&ks?oID=SV^B_#*i0;@%6N{%7iR2zWXB&V ze+>HLR|BDI-|JEZG?LE{?>0R+H)AGxP6x2QpD(nY>>UI2AHPezqfI)*e2poaQS>Kv zZxt#r?;6s14{?RXlaa6is2b^0>Fn!%SQUGxel*+xBQQK_i+Qkvm?Mlz3KPe$57e zfay?SP|PL|8mQ)bsvD-^_c-g_CXSdj7-CAwP3l7R5pbJ9!U#*1dxKwP2b=3$i6dEK zDG*SKWJb^0NSF$B`W)Y1pD8%>*q*>oP7=lv;Y|X)k_%Ohu+%HU$@ljjgM!ko%-g=y zj1-ihA}303r9hV_`;Nom^CuEfENIswK)G*ipz2`TgVBUXR55D7u`|58*pvTE( zXkh4b>cRRE{!|ij(P%0J11j(fC?*RR_kBP1zRlhVt zcXCLdcb{CVangF)DPqtfIuoVYU>+Bp|3EvhBvb>|w~wA_EEJY`y9qPVUm-kJTNXA0 zidbcgHlHDj*XX?fZj$1ZwFJH0Mg+*^kLv*kTbcBvd4qpay^W;Q3H$ zRVHeWu8;KQZQ!@YFy6cGwWU$4jp{lY9|1E(agTBC*9)cHqOoRU>Pehuwm`jlG}W5f ziux@**(C6W{97_bK?|IrDf`zZY*!1N>%y=yECTP;DCV3hs85&xLby8G2*sURCDINi z+EJf#OI4L@l3m}jVb3#+cHL45oVAN63TL85=25a}k|(hSLXL!w56&-!b0mL5im@Fg z0N1p)ZJtuFg`t>xp>}Qx61!JuRG;X_=8rh!8BytYEWplfE0UVYhQ|KR&Jl`m!kU#I z^r(k!sG@H8D$Xa)3|(-+6Kqf9XRoz_Hezi0cAs7632_&6Up1(@3Ifu2@DQ=3Jt#GA}ttU>^ONopG z1GUlx*IHD#K{=libkZB>B2w3n9qyG=#Y-Q}XD7q7j|}rF4UhTJZ-K1oG(jW6_mb5D zA#!N$H+Eg&6(PcJB{Xx!Hzx@`9M1pcz>6x&9BR&Dlio1}((52VcXJ=(GMCO&DVpO& zqtj9WkGLKIJg0|+cU3TEN@PZK2wn8fZ!-tdosS0bMoZQlLBf{YMkHCWin!eVc`W>~ zmSypFJjZ=b8tcuZbw{)4|DJ+YuvxQ5bdtu~V5Ldv(n0xo@4MW8pyYbNPcSa0!&|Hd z6Yh}f%Lpb!E0cJaX#9E(Cm5MVGxFO5)#nuHr(QA|Ae+3NCPQqK%VZcus@&Ldz8^Bp ze11%(=Kxc}mZC^r2Yhcv{lg$g`A|}6&mJ?Fw8P09|F}npozIapH|r9aRRdC4m)Jk@ z82w?RP>p*pdR-afqErq2xz`cSGBY9TeJ(zDGp2MEKtYA`)|)UB6E;on)}KwG=kTmr zM@kSe_j~^hsHzz|OaAg{|IT3ab}2nSq*5aZhIBq=ZEdgBg^V~H3&iUNms9cUq4-hE zfBllA_r3AG|=j%S{g8oi_qX>k`RO7bVx1M*>vSP&|>M z3_wzZT>}MHvVv<|o>;R$`K`pe=c=a;4bs5!I_KoSsiRUsOr77vKq-+#as^q7jm78A4?qA@IbM(X-dqB_N zx5`%`L7dx+0Eg-=nbydc45A&5GpkroM?Qcw*QmsfGqT@!YVezhc;jk6b>qEMjYc?~ zaW8x)aE{yGZ*RBzFhjO46a1D}!Qlsep=mbGM#xqJ7D(o#U$f`3Wt)uQVU;iy zX4~Gp4$K~w#i7lIwd%lowEz*xJ`@vc{gfv#=%l1Li2`7Qp?WFjisMomlae_cYE&m@=f+s$ z#?Tc*-<`(~`~qak2bF*Dh(0S!xL54Qp$VP~s9~ z`ktwr6p%TYp@yHdV77EC5!$3lW2%0U^L-%y(Pt9(Lqzs@z78;?mY4au3`&LiHbyt+d)c0QU=l|BvqokjQ`h;^2igdJOYt zz_0172Ws#SB+MFhANbly9wDKRuP7TXxvcV3tWs4J+mYV#y~LcURP>1sV+ttpXJZF_ zk-BX$gxQl8Ipv)9@qiwRC?YVEsXb%=JMbQm_B9@Qq&<5Cd3V0K%78P=`jpcZ&7(n` zt@4OTB`oI!tY`%*5Pf?UEqU#SK}W(1ANzx$}y&e8&!H# z@%IU5RuSKLUaJy+P#hc4?(52gu?M8p(gdVDJH3JEWSBTdFdtJ7-i|u5a9|Gzb`zy4 zBr^grO%g1DZ(>i^4r7*0t+HXO8Co)qthG5bI%ngvawT#=P_f|TB#)H`h{0&kBAw9&+k*E!nl_A!!~Z{% z(*bv+!5fzuju!%@n8P_6ndL87J$n3jXljm zGl%%dKyaWJiyDBv5HF_y2+}u$24vDYDj2o87HSu~Ytwd|6h4Sec0z%G@?jKhVwvuG zor;lRRWZj_lqsMopOs3NEkdE)#;NWQtQ*2&GGTGeZ9Q97kAEo?nt!>~NSCYLd8EgL zM;&VV4wx95*ZotA2lK;p%`JUBSmY)&pk}{V_9NY>T3S9ApaRYTCiCx#98w(Gl*=h8 z!!vD{K_;Zo^E{Ek^J6LNwwvG-a^?xkbp(Pda&IiyzXOVjM<~t&ZRJQ)V78FfUcC*- zy;k1qazEguA^u_Zr7n0jm&~__-cO;LUEqkc7Iq8<3#_;P9hT2lh0Sn%Q(CjB$A+CO zL*u{iid*9Q_BN)R!FYAKt?E>oJG=AQXMfI=eT7Qh=4|4+m`5X~SQ{K?C-AKq{-8or z-?W=ZhyZe1OPafVWVBaX+&a2!!2i;2x(UFKrG4o>tlQ-3y}30>>FhxZrhMd}+?St9 zpjTTjuzv^8eudqM;xfm2!p<@q+!!w#!%yRKLTp=Lt-UksUYF|!qz1O^Au{M8=-ZWg z!^IDZzo@ZboY%=DqKWGq{b`8(LFbb})Q8C#WR#k=7bYP@oNkpqd$SuA3j=Qw+;;?x z1OEm{N2>>gi6MMj+rb7i#8EHxTP_^OnA#O#wCw6p92Z@91V-B4^Bm|wS8~}qvod+Q(Bs(HTw&zq;nFbD#Zla{u`%n?;DE=~C^AL2B zRb}Pj|DT@DGAgTXYr}Mx(jX<>B_&c1NOyNhhe(5fJVx z^PcnVAN$uDbFV$d8guS7@4fD8G18hSxKKc)N_ZDjG76r`{I4+m?BoD`1ujr+lQ}L3 z@$1cCbSFj38LkuPY+W=UH<;jdtQ6OdaG(TSrDEJC1hY5CZ5Z=cV^kE{%4&^k#vfu- z8SNo^4L__vDh1|Llgl91Jl`C#jUi%M!pp+!139MD^!{zb><32$@YQyhbhYCqkb{Eu z^-JAk_-mvDql{E_!uTkiM&3X{8W6#=Hq33|^(B7Ls^zNw*%;FsOULT+JOPEdkVctS z#X@lKcrHq>e@$delJ0@6+BAx&h4bJ;#lo7Bx1Vm#kiQBnoivz6MFUYU{8Rh#Bwgx5 z46OPSnxAeyswHUUwd+p-w~5^&mX?mGWn7c_9n@}9&aDt9vrDp_Nr8IZ)0zZ5;86K< zvx1Xua?&LdU6pgot31HCmLE+rWLU)BTQl$HIj~#or`oFY-l;{})gLR|7EeIg{9SZ+ z&DC=H{$cOZOc1o_^JBTgtvU*`#xt}O4os~Q9zbbpKMU~U2}vY`URD9M)lB!*B;G3T zcEw~8_IG#n*&+BRMIg5v)!s_>zcM z)O2kBF&7AY-=E0K`E>p@3rlc;wYKA|;t1~iO;xX~hGzjFs5JVo`U8scc1qGM(kxkH zQ~PZAt`j3rw2hLq29Kj4Y7rCo-G!hy=XQ@oa^`({H*cWjj#P_IVgq)Pbd30G?iC&* zee+;2tuU0Q*1TSP6f?cS4qtq8$EI+cxSzfo%{PbUBXiqW_vN)i+GrXEq^c` zg3}ZKI)=bd$tZSd@wOK|K?n?xVEQEwO)~h|z3}Y*F8w7<^3|P6dUqq)OOgv^FQEZg zTNbXc7Mp$-5B+!D2q)F<*@D{(vGS;7Wj0>|?T)YFNEIN`gS4oKbfYa=hrS_mW;b^{ zjj)y!!$VH}%3Y-HIr4kZgCHiM8EFc!Qs{j*r4i57ZqC_6^ zjyVc;X(~j#LWs7JLelnXS_lyPtE{pP@ZU`p41;`kz59^>`{hf~k5GR^q6wjKS!qtQ zVf#q>=X?Djl;*Im4(z~xHusOY2Ia%{gohkm>m=izie;R__VPuVNxzj73)g>jJe18~ z&0h75;BN4i>}j{WjF<2AOp9=PLeneeT7$m$df9u!y(8*KIf@om24)W!EygD-^X z1xjSsU$R`wowu56;|64u8Ql@@gFz9`76)RnR@*^gz=-c7I^Roz5}p z_iIilyFg;A+7)>6<#^&JT*BAa*aHfe6a(R1^sfJH!p@hz|~n znI0C9JpqrY>H#FU_RKh1gk8Q1h$JSyJu6b*~3&b91p9g-UC}{m0!L+U!?S(KGCth6MzY+QtvvpinF5MjCm$>ilNmF2LU9upNEh!&zLvn*5Tfb1@-$Kp5q82Xr#~5|S z2ylvHfn{lm(CaLS8z*+(T+EA1%(1K*oRG9i=az~@JnSf1YH%~&GvhgmS3tH~cgHp; z!IowFnLpPq>bL2v(mm?N1PT}p)J*!xY%RamZCa5R)g#o)!a-`Qu(IFRkd@xQb%PIT z)_ zh+p}i7oj0zeDx=B( z@#F+sW}Y6e9Br5GNX|_UV}`D2>q7!0ty2LlfF0%ZA1y7X8}C5ocvM44+qAgol?95d z^J^7K_Ps20gfk->2u1?<`1ee)eMqTCpaLn9r#jq#JKiFXBF|Lq_QPpGIJajAQ1hPs zBd5FLHu|I3H?io$giH(nv=Ccp9xDHElKCn=0Teh#QX;-M!7?tCw;ZW|!>-m;iHk2D zkp#UwurpSb*DCvG9xh~Qk+UUM8H7QJF;Pcm->%y22pq>+%=3A8+mD>-XJPIE|H`v( zY~RZZHU1)29TAm`MN5}NZ*S?wCE19`!i(3T2qtC+946>hqa+L2spcFDp~D9Od&YnK zTNxBM`3C|xwu3gcVUp&TkB`(ISPBi#tTxWrIG8zZRToWy6IbyJ2-m1az9)mEJKVZ= zXv-6SpgfsP>*R}Ym`VN;8vK&d?_QRNSL=%YGvQx5j*dCSB}rzMy*31Ub{KTQIG+B` zGI=W4swTg|AGrW;+_{R0Qs3Bak_Vk4%!Ci9r&$#&p+eMN&QxsKvO>;)IiYv*_f6vL z-d|fvw*C@zo@gqg{ZAg7`3l8%rAnEWWy2Vs+&gz)$X z3h5-WA_OHsDGqZRF$~Rh!+*UFqpSx)dulTBOW}mw*Mif`MA_s1r~SZUA!@*YGeb*w zH%D~C=zGNT+YDWlNOWl`237gOry}b;pa`BeVV!mbvq-W}A+9aBJdP$BANgP%;Vvi| z?zY8;9L%@BUkVG`hi^f3vvF9dhsqE9mV6pwSi%y&y}v?fL-^3M4ta`F8ayI~~l$}mnw92WC{TJX?b;pAz>RagN8kEA>3Qx_U-~u3s1sb>Ml%uuVWr!63^?HH&= zlNjLRc`?cQG^WIS{vbWZl}v;goj8xiDNPINaPOc0sfOq~{)%=r5Dzg6rW1-qMCpvk z{YLy3QdK_-_A?x5MUfV7a!$x;Z|KohH$!&?9#5SZCh52w23Naj0d|lm90R+_XbeRz zDI(tG2@kY8*R=74TtTMv?dB5q4sf50HQ&Of5kQiPh(NIu(u^Up8y7I@bltV;D#s<> zO$TAF37+FCE^@8KGYbX3Q8Y2z>2N7E^7@H<_+rM~Fvqi2d_!8)+$K8H>SO48c%ehD3U#W2?I9tIpy$? zw<-h8cVNrkdmMEQ@y($B*ulypA@YhcqNZroD%IXMFZu(&}K<4Z%2m?j*_g|r$s*@?d;ha1-`jMP{b(u3O0D3|8H5eCN>YSIM#^2b#lvj z_(VGk^1eSwgl{$+zPR{HIAz}s`Xfw|FZkC?*6Ih1+Lb%=Tp22%j*M8yw~x2#?)U4Y zAav*b{K5TsG+mCiPx=}Y!q`N9-N()*tmhT$Z`#71XhHHt?ZKoFg;5^I+GG$yh@!bE zEqWK+);4}(tnT%cKPEUvTNYD(29Mvi@SdG);QDR5^t!>)%=@lIwc;1hv0^_ET0&N{ z?{}LjN_i2KlJxk&Q)Z8QKKyE<%x?-J6_ir|b1p0p{}P?YJ%<&ubr zVmU4_)AapzN2!!k_%MekD06@lerYtG+m$r$7jhFJ+36$<6vzf*F8hZaBZFo=7yJvc z((NIIzotp{FhnSJgd><{!5}SB??NN^E?e6efSUH7gKZK&X6{jX;%CoKtu&mwSoGqPNV!S8m5 zaLbF9(7Ed8jLXO~q{0f`zg!twFWPhsY;~UF7 zNws=IN)jMoM6Sb`+Su)@kl^OTcaamQ*lF1mlv^XS`e;Wu^TZ3-ctkbUE~ov0sYA`) zhQDdR7r2Faq&jrB)UTCmHkBG6QC9Nq-IKM$!pG6GF1GtP3|fQ!8YFb633^xBo2QH} zAo+_$=9M`{*I~Wwv*-;yU#~=CXCrG$-kXahQuoFrYrri>JhPJb)Zd##Quo)0v(i_i zgB?q?DD##=0JA4*Q510Y^&^h-LVeljXtuYalqw7*aFozb&-Aq|lrSi&82*CEpxE}=f*8xM>3dZ3 zz0&|nzR>X#N|?1VRr#ibSrsAl|GhT;gz6?PabI!oeRmoXa~646CF-yTCTG275u`-B&FmB@~m8-)pQ4YNgvspj>wa4qXPim^SJ!$O6H|0O7C;~$ z8JGD+pU6%{b0n5VDbOL2kzQ7J=|L6cONxj&F)27|lHS`ps!}jr0lWkr+VxHYAN7eb za`Wc+KUVnpH6eg4jmDrdNwPVb66508nZC;SAG)aqlp)X?m z#W*kHuY(oyzVbc{P>!tFY1+SDlzRDXAE-4stcf49D$4b|mabn$h!@-WC5;&!KZ)nt zn(V^6vjJ@SEmL?buzN}LE3++o>VlQVkU5*FFlJM)nU#pl58JPk~fGcZ%eNA=;n zpDm+F6&QWIOJTOJOMDezfNT#s_GuuU)=0-fhHbX_U+J{Fjmtth(f(o^vz^K)3S;*G z-JO{dYprt6@^^>O{fDsog9j0g7o3JlGR~y;9-jI^V4P*l9fYIUs$iSl%OmzzuRq3W zFO>;@sLMqXDf7MSuL6}pQ2QtWM1F_00 zo{;sbW1ihY=q1MA#pNgTT8b^vG>dNC!j8TF=Qlji4T+yBVwuY7v4oS{Sb473Ao-+T zbVc{aO>70Ls!#hD?E7jX4YCjl*ZPLxV-CedL-gzBJM6lX<2#H{v#^waQhZm?l2QGW z^SmgG|4%e{ROD-6fJ4w=k#bAWA7g|K0LT_=P2YtQIEH@F4p9qSE zycFr%bk~XR$6!OSC}=1{Z4&O}3>I44{b+1Yad-(&vOFlPeQ(L)abWdnRH-!TZE4C$ z@~csdqJ@%!qUbmv#R<8?ER|Z=c2ThbWML0Cw%5+SNb0Ia!%NV$VrUD&NZ7qy&8)Uu zBSdB!3a+0?`W*9Pw@rqKS#?Emme#yb_1>jYR@WKAnU==Rq=t2S{sYZ{O~)~ko?$Ii zGj+5JT3_~n6LOB*ki`FblHKO`M~vs-4l`_sE1^FM8~k)cLu2`f->!9DDFO#uIM0w* zSAZ_pE+g&hfAc%+-g15e+dP3bSwpg8I+DPO{)(?t_F?3ixJKx&AP0-4HSAy$mbYkk3 z+{|8dG-zp$@5^}q1qR)DW@RcBO0;y05aoUFfwG|eJI*0bBg6rx`X{65Aw%2^EC|Ue zBxmcZfK$)Zw)k^uD?03X`%E6U(D;s~sNfVjMkgq6aG#s_6PEhP0ZUH3*e0OM?dChf zg&Wt6z{+ySpc+#%6c|yTS4sYk|I{#DYRPs8$wzI;5wc`sw~gw~YB&t;4EtX>VyOQi z&+b&ksyw_Beu@@lH`Aj2fjj)oxo~ChzHoB449r1&!jUE!xyb-0Mm8sdL024wGVJ5x zhz7%t=@ULWqDSE8L++d76%>Bl!5WZbjsMxmGs)65)4UpKieZjhBX=KURK;_{Xm<>- zjh(FY@SoP0jGYgBH{2G+hf-Cu!8socXHOn z>r&MV(5~a3DvZaF2;!kVAEWZ|JdkPE3t#gI=iRumAn4fZ0#b1!{!vGd_J=fYbGiMZ z-|q||GPrtXH*y3IrB^D!7}KjZA5?xccx|7f0I^gQ>wcx!#?-_{y*puRy@9FhJi|@_mA87@m;|2SlzSZDyiKQ?Ln7Jqwjlpn-P;g2+fjC) zx8ND+W524J9uI>gp!jaCwT4fo+HjUtRN2K04g&hhHO@XrK~0zc#LpnrAH+Wsw08zf zXD6=AzE^|n=X?V8`65~DDGhrw&$uQ{()qaeez-3lXxnEtn*V|TiS06K$fCC7d+!uE z43e3|)GLV{0!YX@qa<=e%B{m6pcb%QN=MT6asDR7`A3H>;0WoziF{=Fwh$$&plesR H1?~Pn*?i0# literal 0 HcmV?d00001 diff --git a/cmd/keeper/README.md b/cmd/keeper/README.md new file mode 100644 index 0000000000..4737a22674 --- /dev/null +++ b/cmd/keeper/README.md @@ -0,0 +1,69 @@ +# Keeper - geth as a zkvm guest + +Keeper command is a specialized tool for validating stateless execution of Ethereum blocks. It's designed to run as a zkvm guest. + +## Overview + +The keeper reads an RLP-encoded payload containing: +- A block to execute +- A witness with the necessary state data +- A chainID + +It then executes the block statelessly and validates that the computed state root and receipt root match the values in the block header. + +## Building Keeper + +The keeper uses build tags to compile platform-specific input methods and chain configurations: + +### Example Implementation + +See `getpayload_example.go` for a complete example with embedded Hoodi block data: + +```bash +# Build example with different chain configurations +go build -tags "example" ./cmd/keeper +``` + +### Ziren zkVM Implementation + +Build for the Ziren zkVM platform, which is a MIPS ISA-based zkvm: + +```bash +GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -tags "ziren" ./cmd/keeper +``` + +As an example runner, refer to https://gist.github.com/gballet/7b669a99eb3ab2b593324e3a76abd23d + +## Creating a Custom Platform Implementation + +To add support for a new platform (e.g., "myplatform"), create a new file with the appropriate build tag: + +### 1. Create `getinput_myplatform.go` + +```go +//go:build myplatform + +package main + +import ( + "github.com/ethereum/go-ethereum/params" + // ... other imports as needed +) + +// getInput returns the RLP-encoded payload +func getInput() []byte { + // Your platform-specific code to retrieve the RLP-encoded payload + // This might read from: + // - Memory-mapped I/O + // - Hardware registers + // - Serial port + // - Network interface + // - File system + + // The payload must be RLP-encoded and contain: + // - Block with transactions + // - Witness with parent headers and state data + + return encodedPayload +} +``` diff --git a/cmd/keeper/chainconfig.go b/cmd/keeper/chainconfig.go new file mode 100644 index 0000000000..c9859d450f --- /dev/null +++ b/cmd/keeper/chainconfig.go @@ -0,0 +1,38 @@ +// Copyright 2025 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 . + +package main + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/params" +) + +// getChainConfig returns the appropriate chain configuration based on the chainID. +// Returns an error for unsupported chain IDs. +func getChainConfig(chainID uint64) (*params.ChainConfig, error) { + switch chainID { + case 0, params.MainnetChainConfig.ChainID.Uint64(): + return params.MainnetChainConfig, nil + case params.SepoliaChainConfig.ChainID.Uint64(): + return params.SepoliaChainConfig, nil + case params.HoodiChainConfig.ChainID.Uint64(): + return params.HoodiChainConfig, nil + default: + return nil, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} diff --git a/cmd/keeper/getpayload_example.go b/cmd/keeper/getpayload_example.go new file mode 100644 index 0000000000..683cc79248 --- /dev/null +++ b/cmd/keeper/getpayload_example.go @@ -0,0 +1,102 @@ +// Copyright 2025 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 . + +//go:build example + +package main + +import ( + _ "embed" + "fmt" + "os" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/stateless" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +// ExtWitness is a witness RLP encoding for transferring across clients. +// This is taken from PR #32216 until it's merged. +// It contains block headers, contract codes, state nodes, and storage keys +// required for stateless execution verification. +type ExtWitness struct { + Headers []*types.Header `json:"headers"` + Codes []hexutil.Bytes `json:"codes"` + State []hexutil.Bytes `json:"state"` + Keys []hexutil.Bytes `json:"keys"` +} + +// This is taken from PR #32216 until it's merged +// fromExtWitness converts the consensus witness format into our internal one. +func fromExtWitness(ext *ExtWitness) (*stateless.Witness, error) { + w := &stateless.Witness{} + w.Headers = ext.Headers + + w.Codes = make(map[string]struct{}, len(ext.Codes)) + for _, code := range ext.Codes { + w.Codes[string(code)] = struct{}{} + } + w.State = make(map[string]struct{}, len(ext.State)) + for _, node := range ext.State { + w.State[string(node)] = struct{}{} + } + return w, nil +} + +//go:embed 1192c3_witness.rlp +var witnessRlp []byte + +//go:embed 1192c3_block.rlp +var blockRlp []byte + +// getInput is a platform-specific function that will recover the input payload +// and returns it as a slice. It is expected to be an RLP-encoded Payload structure +// that contains the witness and the block. +// This is a demo version, that is intended to run on a regular computer, so what +// it does is embed a small Hoodi block, encodes the Payload structure containing +// the block and its witness as RLP, and returns the encoding. +func getInput() []byte { + var block types.Block + err := rlp.DecodeBytes(blockRlp, &block) + if err != nil { + panic(err) + } + + var extwitness ExtWitness + err = rlp.DecodeBytes(witnessRlp, &extwitness) + if err != nil { + panic(err) + } + witness, err := fromExtWitness(&extwitness) + if err != nil { + panic(err) + } + + payload := Payload{ + ChainID: params.HoodiChainConfig.ChainID.Uint64(), + Block: &block, + Witness: witness, + } + + encoded, err := rlp.EncodeToBytes(payload) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to encode payload: %v\n", err) + os.Exit(20) + } + return encoded +} diff --git a/cmd/keeper/getpayload_ziren.go b/cmd/keeper/getpayload_ziren.go new file mode 100644 index 0000000000..11c5845bcc --- /dev/null +++ b/cmd/keeper/getpayload_ziren.go @@ -0,0 +1,31 @@ +// Copyright 2025 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 . + +//go:build ziren + +package main + +import ( + zkruntime "github.com/zkMIPS/zkMIPS/crates/go-runtime/zkm_runtime" +) + +// getInput reads the input payload from the zkVM runtime environment. +// The zkVM host provides the RLP-encoded Payload structure containing +// the block and witness data through the runtime's input mechanism. +func getInput() []byte { + input := zkruntime.Read[[]byte]() + return input +} diff --git a/cmd/keeper/go.mod b/cmd/keeper/go.mod new file mode 100644 index 0000000000..72bec0fef3 --- /dev/null +++ b/cmd/keeper/go.mod @@ -0,0 +1,48 @@ +module github.com/ethereum/go-ethereum/cmd/keeper + +go 1.24.0 + +require ( + github.com/ethereum/go-ethereum v0.0.0-00010101000000-000000000000 + github.com/zkMIPS/zkMIPS/crates/go-runtime/zkm_runtime v0.0.0-20250915074013-fbc07aa2c6f5 +) + +require ( + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.2 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/consensys/gnark-crypto v0.18.0 // indirect + github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.0 // indirect + github.com/ethereum/go-verkle v0.2.2 // indirect + github.com/ferranbt/fastssz v0.1.4 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/supranational/blst v0.3.14 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) + +replace ( + github.com/ethereum/go-ethereum => ../../ + github.com/zkMIPS/zkMIPS/crates/go-runtime/zkm_runtime => github.com/weilzkm/zkMIPS/crates/go-runtime/zkvm_runtime v0.0.0-20250915074013-fbc07aa2c6f5 +) diff --git a/cmd/keeper/go.sum b/cmd/keeper/go.sum new file mode 100644 index 0000000000..036d5ebd4b --- /dev/null +++ b/cmd/keeper/go.sum @@ -0,0 +1,148 @@ +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +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.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +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/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/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/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.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0= +github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI= +github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= +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/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/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/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= +github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= +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/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= +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/go-ole/go-ole v1.2.5/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/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +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/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +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/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/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/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/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/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/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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= +github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +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/weilzkm/zkMIPS/crates/go-runtime/zkvm_runtime v0.0.0-20250915074013-fbc07aa2c6f5 h1:MxKlbmI7Dta6O6Nsc9OAer/rOltjoL11CVLMqCiYnxU= +github.com/weilzkm/zkMIPS/crates/go-runtime/zkvm_runtime v0.0.0-20250915074013-fbc07aa2c6f5/go.mod h1:zk/SUgiiVz2U1ufZ+yM2MHPbD93W25KH5zK3qAxXbT4= +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/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.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.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cmd/keeper/main.go b/cmd/keeper/main.go new file mode 100644 index 0000000000..cfb06f0da0 --- /dev/null +++ b/cmd/keeper/main.go @@ -0,0 +1,63 @@ +// Copyright 2025 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 . + +package main + +import ( + "fmt" + "os" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/stateless" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/rlp" +) + +// Payload represents the input data for stateless execution containing +// a block and its associated witness data for verification. +type Payload struct { + ChainID uint64 + Block *types.Block + Witness *stateless.Witness +} + +func main() { + input := getInput() + var payload Payload + rlp.DecodeBytes(input, &payload) + + chainConfig, err := getChainConfig(payload.ChainID) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to get chain config: %v\n", err) + os.Exit(13) + } + vmConfig := vm.Config{} + + crossStateRoot, crossReceiptRoot, err := core.ExecuteStateless(chainConfig, vmConfig, payload.Block, payload.Witness) + if err != nil { + fmt.Fprintf(os.Stderr, "stateless self-validation failed: %v\n", err) + os.Exit(10) + } + if crossStateRoot != payload.Block.Root() { + fmt.Fprintf(os.Stderr, "stateless self-validation root mismatch (cross: %x local: %x)\n", crossStateRoot, payload.Block.Root()) + os.Exit(11) + } + if crossReceiptRoot != payload.Block.ReceiptHash() { + fmt.Fprintf(os.Stderr, "stateless self-validation receipt root mismatch (cross: %x local: %x)\n", crossReceiptRoot, payload.Block.ReceiptHash()) + os.Exit(12) + } +} diff --git a/cmd/keeper/stubs.go b/cmd/keeper/stubs.go new file mode 100644 index 0000000000..04a3bc735b --- /dev/null +++ b/cmd/keeper/stubs.go @@ -0,0 +1,26 @@ +// Copyright 2025 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 . + +//go:build !example && !ziren + +package main + +// getInput is a stub implementation for when no platform-specific build tags are set. +// This allows golangci-lint to typecheck the code without errors. +// The actual implementations are provided in platform-specific files. +func getInput() []byte { + panic("stub") +} diff --git a/go.work b/go.work new file mode 100644 index 0000000000..54e37dd75a --- /dev/null +++ b/go.work @@ -0,0 +1,6 @@ +go 1.24.0 + +use ( + . + ./cmd/keeper +) From e48242bb69ba9a0148f43a2c3c342f1e1f608574 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 16 Sep 2025 10:09:06 +0200 Subject: [PATCH 059/119] cmd/evm/internal/t8ntool: use ApplyTransaction instead of ApplyMessage (#32618) ApplyTransaction calls the hooks and builds the receipt, so some duplicated code can be removed from t8ntool. Test cases have been changed to add the `blockNumber` and `blockHash` in receipts, since they were previously not filled in. --- cmd/evm/internal/t8ntool/execution.go | 58 +++------------------------ cmd/evm/testdata/1/exp.json | 3 +- cmd/evm/testdata/13/exp2.json | 6 ++- cmd/evm/testdata/23/exp.json | 3 +- cmd/evm/testdata/24/exp.json | 6 ++- cmd/evm/testdata/25/exp.json | 3 +- cmd/evm/testdata/28/exp.json | 5 ++- cmd/evm/testdata/29/exp.json | 3 +- cmd/evm/testdata/3/exp.json | 3 +- cmd/evm/testdata/30/exp.json | 6 ++- cmd/evm/testdata/33/exp.json | 3 +- 11 files changed, 33 insertions(+), 66 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 7ea6e578cc..f595a9bf4d 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -32,7 +32,6 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -152,7 +151,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, gasUsed = uint64(0) blobGasUsed = uint64(0) receipts = make(types.Receipts, 0) - txIndex = 0 ) gaspool.AddGas(pre.Env.GasLimit) vmContext := vm.BlockContext{ @@ -250,24 +248,17 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, continue } } - statedb.SetTxContext(tx.Hash(), txIndex) + statedb.SetTxContext(tx.Hash(), len(receipts)) var ( snapshot = statedb.Snapshot() prevGas = gaspool.Gas() ) - if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil { - evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) - } - // (ret []byte, usedGas uint64, failed bool, err error) - msgResult, err := core.ApplyMessage(evm, msg, gaspool) + receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, &gasUsed, evm) if err != nil { statedb.RevertToSnapshot(snapshot) log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) gaspool.SetGas(prevGas) - if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil { - evm.Config.Tracer.OnTxEnd(nil, err) - } continue } includedTxs = append(includedTxs, tx) @@ -275,50 +266,11 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError) } blobGasUsed += txBlobGas - gasUsed += msgResult.UsedGas - - // Receipt: - { - var root []byte - if chainConfig.IsByzantium(vmContext.BlockNumber) { - statedb.Finalise(true) - } else { - root = statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)).Bytes() - } - - // Create a new receipt for the transaction, storing the intermediate root and - // gas used by the tx. - receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: gasUsed} - if msgResult.Failed() { - receipt.Status = types.ReceiptStatusFailed - } else { - receipt.Status = types.ReceiptStatusSuccessful - } - receipt.TxHash = tx.Hash() - receipt.GasUsed = msgResult.UsedGas - - // If the transaction created a contract, store the creation address in the receipt. - if msg.To == nil { - receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) - } - - // Set the receipt logs and create the bloom filter. - receipt.Logs = statedb.GetLogs(tx.Hash(), vmContext.BlockNumber.Uint64(), blockHash, vmContext.Time) - receipt.Bloom = types.CreateBloom(receipt) - - // These three are non-consensus fields: - //receipt.BlockHash - //receipt.BlockNumber - receipt.TransactionIndex = uint(txIndex) - receipts = append(receipts, receipt) - if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil { - evm.Config.Tracer.OnTxEnd(receipt, nil) - } - } - - txIndex++ + receipts = append(receipts, receipt) } + statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) + // Add mining reward? (-1 means rewards are disabled) if miningReward >= 0 { // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases diff --git a/cmd/evm/testdata/1/exp.json b/cmd/evm/testdata/1/exp.json index 50662f35ea..6537c9517d 100644 --- a/cmd/evm/testdata/1/exp.json +++ b/cmd/evm/testdata/1/exp.json @@ -29,7 +29,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x5208", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x0" } ], diff --git a/cmd/evm/testdata/13/exp2.json b/cmd/evm/testdata/13/exp2.json index 6415a4f1f4..f716289cf7 100644 --- a/cmd/evm/testdata/13/exp2.json +++ b/cmd/evm/testdata/13/exp2.json @@ -17,7 +17,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x84d0", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x0" }, { @@ -31,7 +32,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x84d0", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x1" } ], diff --git a/cmd/evm/testdata/23/exp.json b/cmd/evm/testdata/23/exp.json index 7f36165e35..2d9cd492db 100644 --- a/cmd/evm/testdata/23/exp.json +++ b/cmd/evm/testdata/23/exp.json @@ -16,7 +16,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x520b", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x5", "transactionIndex": "0x0" } ], diff --git a/cmd/evm/testdata/24/exp.json b/cmd/evm/testdata/24/exp.json index 8f380c662b..0dd552e112 100644 --- a/cmd/evm/testdata/24/exp.json +++ b/cmd/evm/testdata/24/exp.json @@ -32,7 +32,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0xa861", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x0" }, { @@ -45,7 +46,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x5aa5", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x1" } ], diff --git a/cmd/evm/testdata/25/exp.json b/cmd/evm/testdata/25/exp.json index a674633762..3dac46aa60 100644 --- a/cmd/evm/testdata/25/exp.json +++ b/cmd/evm/testdata/25/exp.json @@ -28,7 +28,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x5208", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x0" } ], diff --git a/cmd/evm/testdata/28/exp.json b/cmd/evm/testdata/28/exp.json index b86c2d8def..15b29bc0ac 100644 --- a/cmd/evm/testdata/28/exp.json +++ b/cmd/evm/testdata/28/exp.json @@ -33,7 +33,10 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0xa865", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blobGasUsed": "0x20000", + "blobGasPrice": "0x1", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x0" } ], diff --git a/cmd/evm/testdata/29/exp.json b/cmd/evm/testdata/29/exp.json index 7fbdc18283..69c8661aa8 100644 --- a/cmd/evm/testdata/29/exp.json +++ b/cmd/evm/testdata/29/exp.json @@ -31,7 +31,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x5208", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x0" } ], diff --git a/cmd/evm/testdata/3/exp.json b/cmd/evm/testdata/3/exp.json index 831c078591..807cdccfb4 100644 --- a/cmd/evm/testdata/3/exp.json +++ b/cmd/evm/testdata/3/exp.json @@ -29,7 +29,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x521f", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x5", "transactionIndex": "0x0" } ], diff --git a/cmd/evm/testdata/30/exp.json b/cmd/evm/testdata/30/exp.json index a206c3bbdf..9861f5a071 100644 --- a/cmd/evm/testdata/30/exp.json +++ b/cmd/evm/testdata/30/exp.json @@ -30,7 +30,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x5208", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x0" }, { @@ -44,7 +45,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x5208", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x1" } ], diff --git a/cmd/evm/testdata/33/exp.json b/cmd/evm/testdata/33/exp.json index ae82ef3efa..b40ca9fee2 100644 --- a/cmd/evm/testdata/33/exp.json +++ b/cmd/evm/testdata/33/exp.json @@ -48,7 +48,8 @@ "contractAddress": "0x0000000000000000000000000000000000000000", "gasUsed": "0x15fa9", "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1337000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", "transactionIndex": "0x0" } ], From b340103e9d4532b0a8bef4d4491609010344154e Mon Sep 17 00:00:00 2001 From: VolodymyrBg Date: Tue, 16 Sep 2025 14:58:37 +0300 Subject: [PATCH 060/119] core/tracing: remove unnecessary 'copy' field skip in TestAllHooksCalled (#32622) This test iterated over Hooks fields and skipped a field named copy. The Hooks struct has no such field, making the condition dead code. --- core/tracing/journal_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/tracing/journal_test.go b/core/tracing/journal_test.go index cf74d83483..e00447f5f3 100644 --- a/core/tracing/journal_test.go +++ b/core/tracing/journal_test.go @@ -280,10 +280,6 @@ func TestAllHooksCalled(t *testing.T) { if field.Type.Kind() != reflect.Func { continue } - // Skip non-hooks, i.e. Copy - if field.Name == "copy" { - continue - } // Skip if field is not set if wrappedValue.Field(i).IsNil() { continue From 3589c0d59b6121d019ac87bb25a5cfc0c4244ef0 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 16 Sep 2025 14:03:11 +0200 Subject: [PATCH 061/119] p2p/discover: expose timeout in lookupFailed Signed-off-by: Csaba Kiraly # Conflicts: # p2p/discover/lookup.go --- p2p/discover/lookup.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index ff2dc907cd..684067a619 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -189,7 +189,7 @@ func (it *lookupIterator) Next() bool { if it.lookup.empty() { // If the lookup is empty right after creation, it means the local table // is in a degraded state, and we need to wait for it to fill again. - it.lookupFailed(it.lookup.tab) + it.lookupFailed(1 * time.Minute) it.lookup = nil continue } @@ -209,30 +209,30 @@ func (it *lookupIterator) Next() bool { // lookupFailed handles failed lookup attempts. This can be called when the table has // exited, or when it runs out of nodes. -func (it *lookupIterator) lookupFailed(tab *Table) { - timeout, cancel := context.WithTimeout(it.ctx, 1*time.Minute) +func (it *lookupIterator) lookupFailed(timeout time.Duration) { + ctx, cancel := context.WithTimeout(it.ctx, timeout) defer cancel() // Wait for Table initialization to complete, in case it is still in progress. select { - case <-tab.initDone: - case <-timeout.Done(): + case <-it.lookup.tab.initDone: + case <-ctx.Done(): return } // Wait for ongoing refresh operation, or trigger one. if it.tabRefreshing == nil { - it.tabRefreshing = tab.refresh() + it.tabRefreshing = it.lookup.tab.refresh() } select { case <-it.tabRefreshing: it.tabRefreshing = nil - case <-timeout.Done(): + case <-ctx.Done(): return } // Wait for the table to fill. - tab.waitForNodes(timeout, 1) + it.lookup.tab.waitForNodes(ctx, 1) } // Close ends the iterator. From 110b4e13c543f301b7b45144130b153220e67fea Mon Sep 17 00:00:00 2001 From: VolodymyrBg Date: Tue, 16 Sep 2025 15:27:20 +0300 Subject: [PATCH 062/119] core/rawdb: fix typo in TestWriteAncientHeaderChain (#32587) --- core/rawdb/accessors_chain_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 196f3dac8f..5ed65b69b5 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -511,7 +511,7 @@ func TestWriteAncientHeaderChain(t *testing.T) { t.Fatalf("unexpected body returned") } if blob := ReadReceiptsRLP(db, header.Hash(), header.Number.Uint64()); len(blob) != 0 { - t.Fatalf("unexpected body returned") + t.Fatalf("unexpected receipts returned") } } } From 03b77d1a4987ca4100a624086c6ae09fbab795ae Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:46:28 +0200 Subject: [PATCH 063/119] core/stateless: API methods to get execution witness of block This PR adds a new RPC call, which re-executes a block with stateless mode activated, so that the witness data are collected and returned. They are `debug_executionWitnessByHash` which takes in a block hash and `debug_executionWitness` which takes in a block number. --------- Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- core/blockchain.go | 10 +++++++--- core/stateless/encoding.go | 28 ++++++++++++++------------ core/stateless/witness.go | 4 ++++ eth/api_debug.go | 41 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index c97897cd70..939b46634f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1907,7 +1907,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness } // The traced section of block import. start := time.Now() - res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1) + res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1) if err != nil { return nil, it.index, err } @@ -1973,9 +1973,13 @@ type blockProcessingResult struct { witness *stateless.Witness } -// processBlock executes and validates the given block. If there was no error +func (bpr *blockProcessingResult) Witness() *stateless.Witness { + return bpr.witness +} + +// ProcessBlock executes and validates the given block. If there was no error // it writes the block and associated state to database. -func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) { +func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) { var ( err error startTime = time.Now() diff --git a/core/stateless/encoding.go b/core/stateless/encoding.go index 5f4cb0ea3c..5c43159e66 100644 --- a/core/stateless/encoding.go +++ b/core/stateless/encoding.go @@ -19,20 +19,21 @@ package stateless import ( "io" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" ) -// toExtWitness converts our internal witness representation to the consensus one. -func (w *Witness) toExtWitness() *extWitness { - ext := &extWitness{ +// ToExtWitness converts our internal witness representation to the consensus one. +func (w *Witness) ToExtWitness() *ExtWitness { + ext := &ExtWitness{ Headers: w.Headers, } - ext.Codes = make([][]byte, 0, len(w.Codes)) + ext.Codes = make([]hexutil.Bytes, 0, len(w.Codes)) for code := range w.Codes { ext.Codes = append(ext.Codes, []byte(code)) } - ext.State = make([][]byte, 0, len(w.State)) + ext.State = make([]hexutil.Bytes, 0, len(w.State)) for node := range w.State { ext.State = append(ext.State, []byte(node)) } @@ -40,7 +41,7 @@ func (w *Witness) toExtWitness() *extWitness { } // fromExtWitness converts the consensus witness format into our internal one. -func (w *Witness) fromExtWitness(ext *extWitness) error { +func (w *Witness) fromExtWitness(ext *ExtWitness) error { w.Headers = ext.Headers w.Codes = make(map[string]struct{}, len(ext.Codes)) @@ -56,21 +57,22 @@ func (w *Witness) fromExtWitness(ext *extWitness) error { // EncodeRLP serializes a witness as RLP. func (w *Witness) EncodeRLP(wr io.Writer) error { - return rlp.Encode(wr, w.toExtWitness()) + return rlp.Encode(wr, w.ToExtWitness()) } // DecodeRLP decodes a witness from RLP. func (w *Witness) DecodeRLP(s *rlp.Stream) error { - var ext extWitness + var ext ExtWitness if err := s.Decode(&ext); err != nil { return err } return w.fromExtWitness(&ext) } -// extWitness is a witness RLP encoding for transferring across clients. -type extWitness struct { - Headers []*types.Header - Codes [][]byte - State [][]byte +// ExtWitness is a witness RLP encoding for transferring across clients. +type ExtWitness struct { + Headers []*types.Header `json:"headers"` + Codes []hexutil.Bytes `json:"codes"` + State []hexutil.Bytes `json:"state"` + Keys []hexutil.Bytes `json:"keys"` } diff --git a/core/stateless/witness.go b/core/stateless/witness.go index 371a128f48..588c895a2f 100644 --- a/core/stateless/witness.go +++ b/core/stateless/witness.go @@ -100,6 +100,10 @@ func (w *Witness) AddState(nodes map[string][]byte) { } } +func (w *Witness) AddKey() { + panic("not yet implemented") +} + // Copy deep-copies the witness object. Witness.Block isn't deep-copied as it // is never mutated by Witness func (w *Witness) Copy() *Witness { diff --git a/eth/api_debug.go b/eth/api_debug.go index 9cedbcbb2a..892e103213 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -491,3 +492,43 @@ func (api *DebugAPI) StateSize(blockHashOrNumber *rpc.BlockNumberOrHash) (interf "contractCodeBytes": hexutil.Uint64(stats.ContractCodeBytes), }, nil } + +func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumber) (*stateless.ExtWitness, error) { + bc := api.eth.blockchain + block, err := api.eth.APIBackend.BlockByNumber(context.Background(), bn) + if err != nil { + return &stateless.ExtWitness{}, fmt.Errorf("block number %v not found", bn) + } + + parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1) + if parent == nil { + return &stateless.ExtWitness{}, fmt.Errorf("block number %v found, but parent missing", bn) + } + + result, err := bc.ProcessBlock(parent.Root, block, false, true) + if err != nil { + return nil, err + } + + return result.Witness().ToExtWitness(), nil +} + +func (api *DebugAPI) ExecutionWitnessByHash(hash common.Hash) (*stateless.ExtWitness, error) { + bc := api.eth.blockchain + block := bc.GetBlockByHash(hash) + if block == nil { + return &stateless.ExtWitness{}, fmt.Errorf("block hash %x not found", hash) + } + + parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1) + if parent == nil { + return &stateless.ExtWitness{}, fmt.Errorf("block number %x found, but parent missing", hash) + } + + result, err := bc.ProcessBlock(parent.Root, block, false, true) + if err != nil { + return nil, err + } + + return result.Witness().ToExtWitness(), nil +} From 6a7f64e76086a12c3a2272c736e086f9dbe8d144 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 16 Sep 2025 17:08:04 +0200 Subject: [PATCH 064/119] core/vm: use go-bigmodexpfix for modexp (#32576) This changes the modexp precompile to use a fork of math/big with some patches by @GottfriedHerold to improve worst-case performance. --- core/vm/contracts.go | 7 ++++--- go.mod | 3 ++- go.sum | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 34cb766708..cae0be9f2d 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -30,6 +30,7 @@ import ( bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + patched_big "github.com/ethereum/go-bigmodexpfix/src/math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/bitutil" "github.com/ethereum/go-ethereum/core/tracing" @@ -631,9 +632,9 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) { } // Retrieve the operands and execute the exponentiation var ( - base = new(big.Int).SetBytes(getData(input, 0, baseLen)) - exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) - mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) + base = new(patched_big.Int).SetBytes(getData(input, 0, baseLen)) + exp = new(patched_big.Int).SetBytes(getData(input, baseLen, expLen)) + mod = new(patched_big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) v []byte ) switch { diff --git a/go.mod b/go.mod index 71bc6b5a1c..15a920c5ca 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 github.com/ethereum/c-kzg-4844/v2 v2.1.0 + github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab github.com/ethereum/go-verkle v0.2.2 github.com/fatih/color v1.16.0 github.com/ferranbt/fastssz v0.1.4 @@ -67,7 +68,7 @@ require ( golang.org/x/crypto v0.36.0 golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df golang.org/x/sync v0.12.0 - golang.org/x/sys v0.31.0 + golang.org/x/sys v0.36.0 golang.org/x/text v0.23.0 golang.org/x/time v0.9.0 golang.org/x/tools v0.29.0 diff --git a/go.sum b/go.sum index 16518fdf43..6ea07ab007 100644 --- a/go.sum +++ b/go.sum @@ -114,6 +114,8 @@ 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/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= +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-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= @@ -449,8 +451,8 @@ 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.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.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= From de9fb9722bd6448c5657c50e82df821a4dd40ea6 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 17 Sep 2025 09:02:38 +0200 Subject: [PATCH 065/119] revert to using table parameter using it.lookup.tab inside is unsafe Signed-off-by: Csaba Kiraly --- p2p/discover/lookup.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index 684067a619..9cca0118ac 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -189,7 +189,7 @@ func (it *lookupIterator) Next() bool { if it.lookup.empty() { // If the lookup is empty right after creation, it means the local table // is in a degraded state, and we need to wait for it to fill again. - it.lookupFailed(1 * time.Minute) + it.lookupFailed(it.lookup.tab, 1*time.Minute) it.lookup = nil continue } @@ -209,30 +209,30 @@ func (it *lookupIterator) Next() bool { // lookupFailed handles failed lookup attempts. This can be called when the table has // exited, or when it runs out of nodes. -func (it *lookupIterator) lookupFailed(timeout time.Duration) { - ctx, cancel := context.WithTimeout(it.ctx, timeout) +func (it *lookupIterator) lookupFailed(tab *Table, timeout time.Duration) { + tout, cancel := context.WithTimeout(it.ctx, timeout) defer cancel() // Wait for Table initialization to complete, in case it is still in progress. select { - case <-it.lookup.tab.initDone: - case <-ctx.Done(): + case <-tab.initDone: + case <-tout.Done(): return } // Wait for ongoing refresh operation, or trigger one. if it.tabRefreshing == nil { - it.tabRefreshing = it.lookup.tab.refresh() + it.tabRefreshing = tab.refresh() } select { case <-it.tabRefreshing: it.tabRefreshing = nil - case <-ctx.Done(): + case <-tout.Done(): return } // Wait for the table to fill. - it.lookup.tab.waitForNodes(ctx, 1) + tab.waitForNodes(tout, 1) } // Close ends the iterator. From 5ae61f9627aa3f4198a94c2febdfca951d8f08d1 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 17 Sep 2025 11:41:14 +0200 Subject: [PATCH 066/119] go.mod: update c-kzg (#32639) Updates c-kzg to the latest release: https://github.com/ethereum/c-kzg-4844/releases/tag/v2.1.3 --- go.mod | 4 ++-- go.sum | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 15a920c5ca..97673cc7ed 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 - github.com/ethereum/c-kzg-4844/v2 v2.1.0 + github.com/ethereum/c-kzg-4844/v2 v2.1.3 github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab github.com/ethereum/go-verkle v0.2.2 github.com/fatih/color v1.16.0 @@ -60,7 +60,7 @@ require ( github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/status-im/keycard-go v0.2.0 github.com/stretchr/testify v1.10.0 - github.com/supranational/blst v0.3.14 + github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/urfave/cli/v2 v2.27.5 go.uber.org/automaxprocs v1.5.2 diff --git a/go.sum b/go.sum index 6ea07ab007..7107e4f5ec 100644 --- a/go.sum +++ b/go.sum @@ -114,6 +114,8 @@ 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/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= +github.com/ethereum/c-kzg-4844/v2 v2.1.3 h1:DQ21UU0VSsuGy8+pcMJHDS0CV1bKmJmxsJYK8l3MiLU= +github.com/ethereum/c-kzg-4844/v2 v2.1.3/go.mod h1:fyNcYI/yAuLWJxf4uzVtS8VDKeoAaRM8G/+ADz/pRdA= 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-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= @@ -353,6 +355,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +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= From f6ba50bf482eb72043e24a04d1cbd38b744cd120 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 17 Sep 2025 12:33:44 +0200 Subject: [PATCH 067/119] go.mod: update go-eth-kzg (#32640) Updates go-eth-kzg: https://github.com/crate-crypto/go-eth-kzg/releases/tag/v1.4.0 --- go.mod | 2 +- go.sum | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 97673cc7ed..03cdf3bb2d 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/cloudflare/cloudflare-go v0.114.0 github.com/cockroachdb/pebble v1.1.5 github.com/consensys/gnark-crypto v0.18.0 - github.com/crate-crypto/go-eth-kzg v1.3.0 + github.com/crate-crypto/go-eth-kzg v1.4.0 github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a github.com/davecgh/go-spew v1.1.1 github.com/dchest/siphash v1.2.3 diff --git a/go.sum b/go.sum index 7107e4f5ec..764cfdb668 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEf github.com/consensys/gnark-crypto v0.18.0/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/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI= -github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +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= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -112,8 +112,6 @@ github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA 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/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= -github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= github.com/ethereum/c-kzg-4844/v2 v2.1.3 h1:DQ21UU0VSsuGy8+pcMJHDS0CV1bKmJmxsJYK8l3MiLU= github.com/ethereum/c-kzg-4844/v2 v2.1.3/go.mod h1:fyNcYI/yAuLWJxf4uzVtS8VDKeoAaRM8G/+ADz/pRdA= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= @@ -353,8 +351,6 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl 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/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= -github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 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= From 2d3704c4d8332b0663ae3128e1ba472a99927be0 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:06:39 +0200 Subject: [PATCH 068/119] core/stateless: add vmwitnessstats cli flag to report leaf stats + log to console (#32619) The format that is currently reported by the chain isn't very useful, as it gives an average for ALL the nodes, and not only the leaves, which skews the results. Also, until now there was no way to activate the reporting of errors. We also decided that metrics weren't the right tool to report this data, so we decided to dump it to the console if the flag is enabled. A better system should be built, but for now, printing to the logs does the job. --- cmd/geth/main.go | 2 ++ cmd/utils/flags.go | 22 ++++++++++++++ core/blockchain.go | 2 +- core/stateless/stats.go | 16 +++++++++- core/stateless/stats_test.go | 58 ++++++++++++++++++++++++++++++++++++ eth/backend.go | 2 ++ eth/ethconfig/config.go | 6 ++++ eth/ethconfig/gen_config.go | 12 ++++++++ 8 files changed, 118 insertions(+), 2 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 750bf55927..b661228681 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -133,6 +133,8 @@ var ( utils.VMEnableDebugFlag, utils.VMTraceFlag, utils.VMTraceJsonConfigFlag, + utils.VMWitnessStatsFlag, + utils.VMStatelessSelfValidationFlag, utils.NetworkIdFlag, utils.EthStatsURLFlag, utils.GpoBlocksFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a134ea4308..83d1c8bda5 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -571,6 +571,16 @@ var ( Value: "{}", Category: flags.VMCategory, } + VMWitnessStatsFlag = &cli.BoolFlag{ + Name: "vmwitnessstats", + Usage: "Enable collection of witness trie access statistics (automatically enables witness generation)", + Category: flags.VMCategory, + } + VMStatelessSelfValidationFlag = &cli.BoolFlag{ + Name: "stateless-self-validation", + Usage: "Generate execution witnesses and self-check against them (testing purpose)", + Category: flags.VMCategory, + } // API options. RPCGlobalGasCapFlag = &cli.Uint64Flag{ Name: "rpc.gascap", @@ -1707,6 +1717,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(VMEnableDebugFlag.Name) { cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name) } + if ctx.IsSet(VMWitnessStatsFlag.Name) { + cfg.EnableWitnessStats = ctx.Bool(VMWitnessStatsFlag.Name) + } + if ctx.IsSet(VMStatelessSelfValidationFlag.Name) { + cfg.StatelessSelfValidation = ctx.Bool(VMStatelessSelfValidationFlag.Name) + } + // Auto-enable StatelessSelfValidation when witness stats are enabled + if ctx.Bool(VMWitnessStatsFlag.Name) { + cfg.StatelessSelfValidation = true + } if ctx.IsSet(RPCGlobalGasCapFlag.Name) { cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name) @@ -2243,6 +2263,8 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh } vmcfg := vm.Config{ EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name), + EnableWitnessStats: ctx.Bool(VMWitnessStatsFlag.Name), + StatelessSelfValidation: ctx.Bool(VMStatelessSelfValidationFlag.Name) || ctx.Bool(VMWitnessStatsFlag.Name), } if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { diff --git a/core/blockchain.go b/core/blockchain.go index 939b46634f..3466923648 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2157,7 +2157,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s } // Report the collected witness statistics if witnessStats != nil { - witnessStats.ReportMetrics() + witnessStats.ReportMetrics(block.NumberU64()) } // Update the metrics touched during block commit diff --git a/core/stateless/stats.go b/core/stateless/stats.go index 1a6389284c..94f5587f99 100644 --- a/core/stateless/stats.go +++ b/core/stateless/stats.go @@ -17,6 +17,7 @@ package stateless import ( + "encoding/json" "maps" "slices" "sort" @@ -24,6 +25,7 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" ) @@ -70,7 +72,19 @@ func (s *WitnessStats) Add(nodes map[string][]byte, owner common.Hash) { } // ReportMetrics reports the collected statistics to the global metrics registry. -func (s *WitnessStats) ReportMetrics() { +func (s *WitnessStats) ReportMetrics(blockNumber uint64) { + // Encode the metrics as JSON for easier consumption + accountLeavesJson, _ := json.Marshal(s.accountTrieLeaves) + storageLeavesJson, _ := json.Marshal(s.storageTrieLeaves) + + // Log account trie depth statistics + log.Info("Account trie depth stats", + "block", blockNumber, + "leavesAtDepth", string(accountLeavesJson)) + log.Info("Storage trie depth stats", + "block", blockNumber, + "leavesAtDepth", string(storageLeavesJson)) + for i := 0; i < 16; i++ { accountTrieLeavesAtDepth[i].Inc(s.accountTrieLeaves[i]) storageTrieLeavesAtDepth[i].Inc(s.storageTrieLeaves[i]) diff --git a/core/stateless/stats_test.go b/core/stateless/stats_test.go index 6219e622f5..e77084df5d 100644 --- a/core/stateless/stats_test.go +++ b/core/stateless/stats_test.go @@ -140,6 +140,64 @@ func TestWitnessStatsAdd(t *testing.T) { } } +func TestWitnessStatsMinMax(t *testing.T) { + stats := NewWitnessStats() + + // Add some account trie nodes with varying depths + stats.Add(map[string][]byte{ + "a": []byte("data1"), + "ab": []byte("data2"), + "abc": []byte("data3"), + "abcd": []byte("data4"), + "abcde": []byte("data5"), + }, common.Hash{}) + + // Only "abcde" is a leaf (depth 5) + for i, v := range stats.accountTrieLeaves { + if v != 0 && i != 5 { + t.Errorf("leaf found at invalid depth %d", i) + } + } + + // Add more leaves with different depths + stats.Add(map[string][]byte{ + "x": []byte("data6"), + "yz": []byte("data7"), + }, common.Hash{}) + + // Now we have leaves at depths 1, 2, and 5 + for i, v := range stats.accountTrieLeaves { + if v != 0 && (i != 5 && i != 2 && i != 1) { + t.Errorf("leaf found at invalid depth %d", i) + } + } +} + +func TestWitnessStatsAverage(t *testing.T) { + stats := NewWitnessStats() + + // Add nodes that will create leaves at depths 2, 3, and 4 + stats.Add(map[string][]byte{ + "aa": []byte("data1"), + "bb": []byte("data2"), + "ccc": []byte("data3"), + "dddd": []byte("data4"), + }, common.Hash{}) + + // All are leaves: 2 + 2 + 3 + 4 = 11 total, 4 samples + expectedAvg := int64(11) / int64(4) + var actualAvg, totalSamples int64 + for i, c := range stats.accountTrieLeaves { + actualAvg += c * int64(i) + totalSamples += c + } + actualAvg = actualAvg / totalSamples + + if actualAvg != expectedAvg { + t.Errorf("Account trie average depth = %d, want %d", actualAvg, expectedAvg) + } +} + func BenchmarkWitnessStatsAdd(b *testing.B) { // Create a realistic trie node structure nodes := make(map[string][]byte) diff --git a/eth/backend.go b/eth/backend.go index 4356733189..3bfe0765f4 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -235,6 +235,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)), VmConfig: vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, + EnableWitnessStats: config.EnableWitnessStats, + StatelessSelfValidation: config.StatelessSelfValidation, }, // Enables file journaling for the trie database. The journal files will be stored // within the data directory. The corresponding paths will be either: diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index dc77141081..ba0a7762c7 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -144,6 +144,12 @@ type Config struct { // Enables tracking of SHA3 preimages in the VM EnablePreimageRecording bool + // Enables collection of witness trie access statistics + EnableWitnessStats bool + + // Generate execution witnesses and self-check against them (testing purpose) + StatelessSelfValidation bool + // Enables tracking of state size EnableStateSizeTracking bool diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 2fdd219dee..b54ba14d68 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -49,6 +49,8 @@ func (c Config) MarshalTOML() (interface{}, error) { BlobPool blobpool.Config GPO gasprice.Config EnablePreimageRecording bool + EnableWitnessStats bool + StatelessSelfValidation bool EnableStateSizeTracking bool VMTrace string VMTraceJsonConfig string @@ -91,6 +93,8 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.BlobPool = c.BlobPool enc.GPO = c.GPO enc.EnablePreimageRecording = c.EnablePreimageRecording + enc.EnableWitnessStats = c.EnableWitnessStats + enc.StatelessSelfValidation = c.StatelessSelfValidation enc.EnableStateSizeTracking = c.EnableStateSizeTracking enc.VMTrace = c.VMTrace enc.VMTraceJsonConfig = c.VMTraceJsonConfig @@ -137,6 +141,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { BlobPool *blobpool.Config GPO *gasprice.Config EnablePreimageRecording *bool + EnableWitnessStats *bool + StatelessSelfValidation *bool EnableStateSizeTracking *bool VMTrace *string VMTraceJsonConfig *string @@ -246,6 +252,12 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.EnablePreimageRecording != nil { c.EnablePreimageRecording = *dec.EnablePreimageRecording } + if dec.EnableWitnessStats != nil { + c.EnableWitnessStats = *dec.EnableWitnessStats + } + if dec.StatelessSelfValidation != nil { + c.StatelessSelfValidation = *dec.StatelessSelfValidation + } if dec.EnableStateSizeTracking != nil { c.EnableStateSizeTracking = *dec.EnableStateSizeTracking } From 21769f3474908007f51aae322b77c845b30bde28 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Wed, 17 Sep 2025 21:57:16 +0800 Subject: [PATCH 069/119] triedb/pathdb: generalize the history indexer (#32523) This pull request is based on #32306 , is the second part for shipping trienode history. Specifically, this pull request generalize the existing index mechanism, making is usable by both state history and trienode history in the near future. --- triedb/pathdb/database.go | 8 +- triedb/pathdb/disklayer.go | 2 +- triedb/pathdb/history.go | 146 +++++++++++- triedb/pathdb/history_index.go | 179 +++++++------- triedb/pathdb/history_index_test.go | 10 +- triedb/pathdb/history_indexer.go | 329 ++++++++++++++------------ triedb/pathdb/history_indexer_test.go | 2 +- triedb/pathdb/history_reader.go | 88 +------ triedb/pathdb/history_reader_test.go | 2 +- triedb/pathdb/history_state.go | 35 ++- triedb/pathdb/history_state_test.go | 12 +- 11 files changed, 468 insertions(+), 345 deletions(-) diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index ae9574963e..546d2e0301 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -189,7 +189,7 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { } // TODO (rjl493456442) disable the background indexing in read-only mode if db.stateFreezer != nil && db.config.EnableStateIndexing { - db.stateIndexer = newHistoryIndexer(db.diskdb, db.stateFreezer, db.tree.bottom().stateID()) + db.stateIndexer = newHistoryIndexer(db.diskdb, db.stateFreezer, db.tree.bottom().stateID(), typeStateHistory) log.Info("Enabled state history indexing") } fields := config.fields() @@ -245,7 +245,7 @@ func (db *Database) repairHistory() error { } // Truncate the extra state histories above in freezer in case it's not // aligned with the disk layer. It might happen after a unclean shutdown. - pruned, err := truncateFromHead(db.stateFreezer, id) + pruned, err := truncateFromHead(db.stateFreezer, typeStateHistory, id) if err != nil { log.Crit("Failed to truncate extra state histories", "err", err) } @@ -448,7 +448,7 @@ func (db *Database) Enable(root common.Hash) error { // 2. Re-initialize the indexer so it starts indexing from the new state root. if db.stateIndexer != nil && db.stateFreezer != nil && db.config.EnableStateIndexing { db.stateIndexer.close() - db.stateIndexer = newHistoryIndexer(db.diskdb, db.stateFreezer, db.tree.bottom().stateID()) + db.stateIndexer = newHistoryIndexer(db.diskdb, db.stateFreezer, db.tree.bottom().stateID(), typeStateHistory) log.Info("Re-enabled state history indexing") } log.Info("Rebuilt trie database", "root", root) @@ -502,7 +502,7 @@ func (db *Database) Recover(root common.Hash) error { if err := db.diskdb.SyncKeyValue(); err != nil { return err } - _, err := truncateFromHead(db.stateFreezer, dl.stateID()) + _, err := truncateFromHead(db.stateFreezer, typeStateHistory, dl.stateID()) if err != nil { return err } diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index 2042e91611..76f3f5a46e 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -378,7 +378,7 @@ func (dl *diskLayer) writeStateHistory(diff *diffLayer) (bool, error) { log.Debug("Skip tail truncation", "persistentID", persistentID, "tailID", tail+1, "headID", diff.stateID(), "limit", limit) return true, nil } - pruned, err := truncateFromTail(dl.db.stateFreezer, newFirst-1) + pruned, err := truncateFromTail(dl.db.stateFreezer, typeStateHistory, newFirst-1) if err != nil { return false, err } diff --git a/triedb/pathdb/history.go b/triedb/pathdb/history.go index bbedd52f34..ae022236fe 100644 --- a/triedb/pathdb/history.go +++ b/triedb/pathdb/history.go @@ -19,11 +19,147 @@ package pathdb import ( "errors" "fmt" + "iter" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" ) +// historyType represents the category of historical data. +type historyType uint8 + +const ( + // typeStateHistory indicates history data related to account or storage changes. + typeStateHistory historyType = 0 +) + +// String returns the string format representation. +func (h historyType) String() string { + switch h { + case typeStateHistory: + return "state" + default: + return fmt.Sprintf("unknown type: %d", h) + } +} + +// elementType represents the category of state element. +type elementType uint8 + +const ( + typeAccount elementType = 0 // represents the account data + typeStorage elementType = 1 // represents the storage slot data +) + +// String returns the string format representation. +func (e elementType) String() string { + switch e { + case typeAccount: + return "account" + case typeStorage: + return "storage" + default: + return fmt.Sprintf("unknown element type: %d", e) + } +} + +// toHistoryType maps an element type to its corresponding history type. +func toHistoryType(typ elementType) historyType { + if typ == typeAccount || typ == typeStorage { + return typeStateHistory + } + panic(fmt.Sprintf("unknown element type %v", typ)) +} + +// stateIdent represents the identifier of a state element, which can be +// an account or a storage slot. +type stateIdent struct { + typ elementType + + // The hash of the account address. This is used instead of the raw account + // address is to align the traversal order with the Merkle-Patricia-Trie. + addressHash common.Hash + + // The hash of the storage slot key. This is used instead of the raw slot key + // because, in legacy state histories (prior to the Cancun fork), the slot + // identifier is the hash of the key, and the original key (preimage) cannot + // be recovered. To maintain backward compatibility, the key hash is used. + // + // Meanwhile, using the storage key hash also preserve the traversal order + // with Merkle-Patricia-Trie. + // + // This field is null if the identifier refers to an account or a trie node. + storageHash common.Hash +} + +// String returns the string format state identifier. +func (ident stateIdent) String() string { + if ident.typ == typeAccount { + return ident.addressHash.Hex() + } + return ident.addressHash.Hex() + ident.storageHash.Hex() +} + +// newAccountIdent constructs a state identifier for an account. +func newAccountIdent(addressHash common.Hash) stateIdent { + return stateIdent{ + typ: typeAccount, + addressHash: addressHash, + } +} + +// newStorageIdent constructs a state identifier for a storage slot. +// The address denotes the address hash of the associated account; +// the storageHash denotes the hash of the raw storage slot key; +func newStorageIdent(addressHash common.Hash, storageHash common.Hash) stateIdent { + return stateIdent{ + typ: typeStorage, + addressHash: addressHash, + storageHash: storageHash, + } +} + +// stateIdentQuery is the extension of stateIdent by adding the account address +// and raw storage key. +type stateIdentQuery struct { + stateIdent + + address common.Address + storageKey common.Hash +} + +// newAccountIdentQuery constructs a state identifier for an account. +func newAccountIdentQuery(address common.Address, addressHash common.Hash) stateIdentQuery { + return stateIdentQuery{ + stateIdent: newAccountIdent(addressHash), + address: address, + } +} + +// newStorageIdentQuery constructs a state identifier for a storage slot. +// the address denotes the address of the associated account; +// the addressHash denotes the address hash of the associated account; +// the storageKey denotes the raw storage slot key; +// the storageHash denotes the hash of the raw storage slot key; +func newStorageIdentQuery(address common.Address, addressHash common.Hash, storageKey common.Hash, storageHash common.Hash) stateIdentQuery { + return stateIdentQuery{ + stateIdent: newStorageIdent(addressHash, storageHash), + address: address, + storageKey: storageKey, + } +} + +// history defines the interface of historical data, implemented by stateHistory +// and trienodeHistory (in the near future). +type history interface { + // typ returns the historical data type held in the history. + typ() historyType + + // forEach returns an iterator to traverse the state entries in the history. + forEach() iter.Seq[stateIdent] +} + var ( errHeadTruncationOutOfRange = errors.New("history head truncation out of range") errTailTruncationOutOfRange = errors.New("history tail truncation out of range") @@ -31,7 +167,7 @@ var ( // truncateFromHead removes excess elements from the head of the freezer based // on the given parameters. It returns the number of items that were removed. -func truncateFromHead(store ethdb.AncientStore, nhead uint64) (int, error) { +func truncateFromHead(store ethdb.AncientStore, typ historyType, nhead uint64) (int, error) { ohead, err := store.Ancients() if err != nil { return 0, err @@ -40,11 +176,11 @@ func truncateFromHead(store ethdb.AncientStore, nhead uint64) (int, error) { if err != nil { return 0, err } - log.Info("Truncating from head", "ohead", ohead, "tail", otail, "nhead", nhead) + log.Info("Truncating from head", "type", typ.String(), "ohead", ohead, "tail", otail, "nhead", nhead) // Ensure that the truncation target falls within the valid range. if ohead < nhead || nhead < otail { - return 0, fmt.Errorf("%w, tail: %d, head: %d, target: %d", errHeadTruncationOutOfRange, otail, ohead, nhead) + return 0, fmt.Errorf("%w, %s, tail: %d, head: %d, target: %d", errHeadTruncationOutOfRange, typ, otail, ohead, nhead) } // Short circuit if nothing to truncate. if ohead == nhead { @@ -61,7 +197,7 @@ func truncateFromHead(store ethdb.AncientStore, nhead uint64) (int, error) { // truncateFromTail removes excess elements from the end of the freezer based // on the given parameters. It returns the number of items that were removed. -func truncateFromTail(store ethdb.AncientStore, ntail uint64) (int, error) { +func truncateFromTail(store ethdb.AncientStore, typ historyType, ntail uint64) (int, error) { ohead, err := store.Ancients() if err != nil { return 0, err @@ -72,7 +208,7 @@ func truncateFromTail(store ethdb.AncientStore, ntail uint64) (int, error) { } // Ensure that the truncation target falls within the valid range. if otail > ntail || ntail > ohead { - return 0, fmt.Errorf("%w, tail: %d, head: %d, target: %d", errTailTruncationOutOfRange, otail, ohead, ntail) + return 0, fmt.Errorf("%w, %s, tail: %d, head: %d, target: %d", errTailTruncationOutOfRange, typ, otail, ohead, ntail) } // Short circuit if nothing to truncate. if otail == ntail { diff --git a/triedb/pathdb/history_index.go b/triedb/pathdb/history_index.go index e781a898e1..47cee9820d 100644 --- a/triedb/pathdb/history_index.go +++ b/triedb/pathdb/history_index.go @@ -78,12 +78,7 @@ type indexReader struct { // loadIndexData loads the index data associated with the specified state. func loadIndexData(db ethdb.KeyValueReader, state stateIdent) ([]*indexBlockDesc, error) { - var blob []byte - if state.account { - blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash) - } else { - blob = rawdb.ReadStorageHistoryIndex(db, state.addressHash, state.storageHash) - } + blob := readStateIndex(state, db) if len(blob) == 0 { return nil, nil } @@ -137,15 +132,8 @@ func (r *indexReader) readGreaterThan(id uint64) (uint64, error) { br, ok := r.readers[desc.id] if !ok { - var ( - err error - blob []byte - ) - if r.state.account { - blob = rawdb.ReadAccountHistoryIndexBlock(r.db, r.state.addressHash, desc.id) - } else { - blob = rawdb.ReadStorageHistoryIndexBlock(r.db, r.state.addressHash, r.state.storageHash, desc.id) - } + var err error + blob := readStateIndexBlock(r.state, r.db, desc.id) br, err = newBlockReader(blob) if err != nil { return 0, err @@ -174,12 +162,7 @@ type indexWriter struct { // newIndexWriter constructs the index writer for the specified state. func newIndexWriter(db ethdb.KeyValueReader, state stateIdent) (*indexWriter, error) { - var blob []byte - if state.account { - blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash) - } else { - blob = rawdb.ReadStorageHistoryIndex(db, state.addressHash, state.storageHash) - } + blob := readStateIndex(state, db) if len(blob) == 0 { desc := newIndexBlockDesc(0) bw, _ := newBlockWriter(nil, desc) @@ -194,15 +177,8 @@ func newIndexWriter(db ethdb.KeyValueReader, state stateIdent) (*indexWriter, er if err != nil { return nil, err } - var ( - indexBlock []byte - lastDesc = descList[len(descList)-1] - ) - if state.account { - indexBlock = rawdb.ReadAccountHistoryIndexBlock(db, state.addressHash, lastDesc.id) - } else { - indexBlock = rawdb.ReadStorageHistoryIndexBlock(db, state.addressHash, state.storageHash, lastDesc.id) - } + lastDesc := descList[len(descList)-1] + indexBlock := readStateIndexBlock(state, db, lastDesc.id) bw, err := newBlockWriter(indexBlock, lastDesc) if err != nil { return nil, err @@ -270,11 +246,7 @@ func (w *indexWriter) finish(batch ethdb.Batch) { return // nothing to commit } for _, bw := range writers { - if w.state.account { - rawdb.WriteAccountHistoryIndexBlock(batch, w.state.addressHash, bw.desc.id, bw.finish()) - } else { - rawdb.WriteStorageHistoryIndexBlock(batch, w.state.addressHash, w.state.storageHash, bw.desc.id, bw.finish()) - } + writeStateIndexBlock(w.state, batch, bw.desc.id, bw.finish()) } w.frozen = nil // release all the frozen writers @@ -282,11 +254,7 @@ func (w *indexWriter) finish(batch ethdb.Batch) { for _, desc := range descList { buf = append(buf, desc.encode()...) } - if w.state.account { - rawdb.WriteAccountHistoryIndex(batch, w.state.addressHash, buf) - } else { - rawdb.WriteStorageHistoryIndex(batch, w.state.addressHash, w.state.storageHash, buf) - } + writeStateIndex(w.state, batch, buf) } // indexDeleter is responsible for deleting index data for a specific state. @@ -301,12 +269,7 @@ type indexDeleter struct { // newIndexDeleter constructs the index deleter for the specified state. func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent) (*indexDeleter, error) { - var blob []byte - if state.account { - blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash) - } else { - blob = rawdb.ReadStorageHistoryIndex(db, state.addressHash, state.storageHash) - } + blob := readStateIndex(state, db) if len(blob) == 0 { // TODO(rjl493456442) we can probably return an error here, // deleter with no data is meaningless. @@ -323,15 +286,8 @@ func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent) (*indexDeleter, if err != nil { return nil, err } - var ( - indexBlock []byte - lastDesc = descList[len(descList)-1] - ) - if state.account { - indexBlock = rawdb.ReadAccountHistoryIndexBlock(db, state.addressHash, lastDesc.id) - } else { - indexBlock = rawdb.ReadStorageHistoryIndexBlock(db, state.addressHash, state.storageHash, lastDesc.id) - } + lastDesc := descList[len(descList)-1] + indexBlock := readStateIndexBlock(state, db, lastDesc.id) bw, err := newBlockWriter(indexBlock, lastDesc) if err != nil { return nil, err @@ -376,15 +332,8 @@ func (d *indexDeleter) pop(id uint64) error { d.descList = d.descList[:len(d.descList)-1] // Open the previous block writer for deleting - var ( - indexBlock []byte - lastDesc = d.descList[len(d.descList)-1] - ) - if d.state.account { - indexBlock = rawdb.ReadAccountHistoryIndexBlock(d.db, d.state.addressHash, lastDesc.id) - } else { - indexBlock = rawdb.ReadStorageHistoryIndexBlock(d.db, d.state.addressHash, d.state.storageHash, lastDesc.id) - } + lastDesc := d.descList[len(d.descList)-1] + indexBlock := readStateIndexBlock(d.state, d.db, lastDesc.id) bw, err := newBlockWriter(indexBlock, lastDesc) if err != nil { return err @@ -399,38 +348,100 @@ func (d *indexDeleter) pop(id uint64) error { // This function is safe to be called multiple times. func (d *indexDeleter) finish(batch ethdb.Batch) { for _, id := range d.dropped { - if d.state.account { - rawdb.DeleteAccountHistoryIndexBlock(batch, d.state.addressHash, id) - } else { - rawdb.DeleteStorageHistoryIndexBlock(batch, d.state.addressHash, d.state.storageHash, id) - } + deleteStateIndexBlock(d.state, batch, id) } d.dropped = nil // Flush the content of last block writer, regardless it's dirty or not if !d.bw.empty() { - if d.state.account { - rawdb.WriteAccountHistoryIndexBlock(batch, d.state.addressHash, d.bw.desc.id, d.bw.finish()) - } else { - rawdb.WriteStorageHistoryIndexBlock(batch, d.state.addressHash, d.state.storageHash, d.bw.desc.id, d.bw.finish()) - } + writeStateIndexBlock(d.state, batch, d.bw.desc.id, d.bw.finish()) } // Flush the index metadata into the supplied batch if d.empty() { - if d.state.account { - rawdb.DeleteAccountHistoryIndex(batch, d.state.addressHash) - } else { - rawdb.DeleteStorageHistoryIndex(batch, d.state.addressHash, d.state.storageHash) - } + deleteStateIndex(d.state, batch) } else { buf := make([]byte, 0, indexBlockDescSize*len(d.descList)) for _, desc := range d.descList { buf = append(buf, desc.encode()...) } - if d.state.account { - rawdb.WriteAccountHistoryIndex(batch, d.state.addressHash, buf) - } else { - rawdb.WriteStorageHistoryIndex(batch, d.state.addressHash, d.state.storageHash, buf) - } + writeStateIndex(d.state, batch, buf) + } +} + +// readStateIndex retrieves the index metadata for the given state identifier. +// This function is shared by accounts and storage slots. +func readStateIndex(ident stateIdent, db ethdb.KeyValueReader) []byte { + switch ident.typ { + case typeAccount: + return rawdb.ReadAccountHistoryIndex(db, ident.addressHash) + case typeStorage: + return rawdb.ReadStorageHistoryIndex(db, ident.addressHash, ident.storageHash) + default: + panic(fmt.Errorf("unknown type: %v", ident.typ)) + } +} + +// writeStateIndex writes the provided index metadata into database with the +// given state identifier. This function is shared by accounts and storage slots. +func writeStateIndex(ident stateIdent, db ethdb.KeyValueWriter, data []byte) { + switch ident.typ { + case typeAccount: + rawdb.WriteAccountHistoryIndex(db, ident.addressHash, data) + case typeStorage: + rawdb.WriteStorageHistoryIndex(db, ident.addressHash, ident.storageHash, data) + default: + panic(fmt.Errorf("unknown type: %v", ident.typ)) + } +} + +// deleteStateIndex removes the index metadata for the given state identifier. +// This function is shared by accounts and storage slots. +func deleteStateIndex(ident stateIdent, db ethdb.KeyValueWriter) { + switch ident.typ { + case typeAccount: + rawdb.DeleteAccountHistoryIndex(db, ident.addressHash) + case typeStorage: + rawdb.DeleteStorageHistoryIndex(db, ident.addressHash, ident.storageHash) + default: + panic(fmt.Errorf("unknown type: %v", ident.typ)) + } +} + +// readStateIndexBlock retrieves the index block for the given state identifier +// and block ID. This function is shared by accounts and storage slots. +func readStateIndexBlock(ident stateIdent, db ethdb.KeyValueReader, id uint32) []byte { + switch ident.typ { + case typeAccount: + return rawdb.ReadAccountHistoryIndexBlock(db, ident.addressHash, id) + case typeStorage: + return rawdb.ReadStorageHistoryIndexBlock(db, ident.addressHash, ident.storageHash, id) + default: + panic(fmt.Errorf("unknown type: %v", ident.typ)) + } +} + +// writeStateIndexBlock writes the provided index block into database with the +// given state identifier. This function is shared by accounts and storage slots. +func writeStateIndexBlock(ident stateIdent, db ethdb.KeyValueWriter, id uint32, data []byte) { + switch ident.typ { + case typeAccount: + rawdb.WriteAccountHistoryIndexBlock(db, ident.addressHash, id, data) + case typeStorage: + rawdb.WriteStorageHistoryIndexBlock(db, ident.addressHash, ident.storageHash, id, data) + default: + panic(fmt.Errorf("unknown type: %v", ident.typ)) + } +} + +// deleteStateIndexBlock removes the index block from database with the given +// state identifier. This function is shared by accounts and storage slots. +func deleteStateIndexBlock(ident stateIdent, db ethdb.KeyValueWriter, id uint32) { + switch ident.typ { + case typeAccount: + rawdb.DeleteAccountHistoryIndexBlock(db, ident.addressHash, id) + case typeStorage: + rawdb.DeleteStorageHistoryIndexBlock(db, ident.addressHash, ident.storageHash, id) + default: + panic(fmt.Errorf("unknown type: %v", ident.typ)) } } diff --git a/triedb/pathdb/history_index_test.go b/triedb/pathdb/history_index_test.go index c83c33ffbd..be9b7c4049 100644 --- a/triedb/pathdb/history_index_test.go +++ b/triedb/pathdb/history_index_test.go @@ -179,7 +179,7 @@ func TestIndexWriterDelete(t *testing.T) { func TestBatchIndexerWrite(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - batch = newBatchIndexer(db, false) + batch = newBatchIndexer(db, false, typeStateHistory) histories = makeStateHistories(10) ) for i, h := range histories { @@ -190,7 +190,7 @@ func TestBatchIndexerWrite(t *testing.T) { if err := batch.finish(true); err != nil { t.Fatalf("Failed to finish batch indexer, %v", err) } - metadata := loadIndexMetadata(db) + metadata := loadIndexMetadata(db, typeStateHistory) if metadata == nil || metadata.Last != uint64(10) { t.Fatal("Unexpected index position") } @@ -256,7 +256,7 @@ func TestBatchIndexerWrite(t *testing.T) { func TestBatchIndexerDelete(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - bw = newBatchIndexer(db, false) + bw = newBatchIndexer(db, false, typeStateHistory) histories = makeStateHistories(10) ) // Index histories @@ -270,7 +270,7 @@ func TestBatchIndexerDelete(t *testing.T) { } // Unindex histories - bd := newBatchIndexer(db, true) + bd := newBatchIndexer(db, true, typeStateHistory) for i := len(histories) - 1; i >= 0; i-- { if err := bd.process(histories[i], uint64(i+1)); err != nil { t.Fatalf("Failed to process history, %v", err) @@ -280,7 +280,7 @@ func TestBatchIndexerDelete(t *testing.T) { t.Fatalf("Failed to finish batch indexer, %v", err) } - metadata := loadIndexMetadata(db) + metadata := loadIndexMetadata(db, typeStateHistory) if metadata != nil { t.Fatal("Unexpected index position") } diff --git a/triedb/pathdb/history_indexer.go b/triedb/pathdb/history_indexer.go index b4e89c3f17..d618585929 100644 --- a/triedb/pathdb/history_indexer.go +++ b/triedb/pathdb/history_indexer.go @@ -26,7 +26,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" @@ -41,13 +40,32 @@ const ( stateIndexVersion = stateIndexV0 // the current state index version ) +// indexVersion returns the latest index version for the given history type. +// It panics if the history type is unknown. +func indexVersion(typ historyType) uint8 { + switch typ { + case typeStateHistory: + return stateIndexVersion + default: + panic(fmt.Errorf("unknown history type: %d", typ)) + } +} + +// indexMetadata describes the metadata of the historical data index. type indexMetadata struct { Version uint8 Last uint64 } -func loadIndexMetadata(db ethdb.KeyValueReader) *indexMetadata { - blob := rawdb.ReadStateHistoryIndexMetadata(db) +// loadIndexMetadata reads the metadata of the specific history index. +func loadIndexMetadata(db ethdb.KeyValueReader, typ historyType) *indexMetadata { + var blob []byte + switch typ { + case typeStateHistory: + blob = rawdb.ReadStateHistoryIndexMetadata(db) + default: + panic(fmt.Errorf("unknown history type %d", typ)) + } if len(blob) == 0 { return nil } @@ -59,91 +77,94 @@ func loadIndexMetadata(db ethdb.KeyValueReader) *indexMetadata { return &m } -func storeIndexMetadata(db ethdb.KeyValueWriter, last uint64) { - var m indexMetadata - m.Version = stateIndexVersion - m.Last = last +// storeIndexMetadata stores the metadata of the specific history index. +func storeIndexMetadata(db ethdb.KeyValueWriter, typ historyType, last uint64) { + m := indexMetadata{ + Version: indexVersion(typ), + Last: last, + } blob, err := rlp.EncodeToBytes(m) if err != nil { - log.Crit("Failed to encode index metadata", "err", err) + panic(fmt.Errorf("fail to encode index metadata, %v", err)) } - rawdb.WriteStateHistoryIndexMetadata(db, blob) + switch typ { + case typeStateHistory: + rawdb.WriteStateHistoryIndexMetadata(db, blob) + default: + panic(fmt.Errorf("unknown history type %d", typ)) + } + log.Debug("Written index metadata", "type", typ, "last", last) } -// batchIndexer is a structure designed to perform batch indexing or unindexing -// of state histories atomically. +// deleteIndexMetadata deletes the metadata of the specific history index. +func deleteIndexMetadata(db ethdb.KeyValueWriter, typ historyType) { + switch typ { + case typeStateHistory: + rawdb.DeleteStateHistoryIndexMetadata(db) + default: + panic(fmt.Errorf("unknown history type %d", typ)) + } + log.Debug("Deleted index metadata", "type", typ) +} + +// batchIndexer is responsible for performing batch indexing or unindexing +// of historical data (e.g., state or trie node changes) atomically. type batchIndexer struct { - accounts map[common.Hash][]uint64 // History ID list, Keyed by the hash of account address - storages map[common.Hash]map[common.Hash][]uint64 // History ID list, Keyed by the hash of account address and the hash of raw storage key - counter int // The counter of processed states - delete bool // Index or unindex mode - lastID uint64 // The ID of latest processed history - db ethdb.KeyValueStore + index map[stateIdent][]uint64 // List of history IDs for tracked state entry + pending int // Number of entries processed in the current batch. + delete bool // Operation mode: true for unindex, false for index. + lastID uint64 // ID of the most recently processed history. + typ historyType // Type of history being processed (e.g., state or trienode). + db ethdb.KeyValueStore // Key-value database used to store or delete index data. } // newBatchIndexer constructs the batch indexer with the supplied mode. -func newBatchIndexer(db ethdb.KeyValueStore, delete bool) *batchIndexer { +func newBatchIndexer(db ethdb.KeyValueStore, delete bool, typ historyType) *batchIndexer { return &batchIndexer{ - accounts: make(map[common.Hash][]uint64), - storages: make(map[common.Hash]map[common.Hash][]uint64), - delete: delete, - db: db, + index: make(map[stateIdent][]uint64), + delete: delete, + typ: typ, + db: db, } } -// process iterates through the accounts and their associated storage slots in the -// state history, tracking the mapping between state and history IDs. -func (b *batchIndexer) process(h *stateHistory, historyID uint64) error { - for _, address := range h.accountList { - addrHash := crypto.Keccak256Hash(address.Bytes()) - b.counter += 1 - b.accounts[addrHash] = append(b.accounts[addrHash], historyID) - - for _, slotKey := range h.storageList[address] { - b.counter += 1 - if _, ok := b.storages[addrHash]; !ok { - b.storages[addrHash] = make(map[common.Hash][]uint64) - } - // The hash of the storage slot key is used as the identifier because the - // legacy history does not include the raw storage key, therefore, the - // conversion from storage key to hash is necessary for non-v0 histories. - slotHash := slotKey - if h.meta.version != stateHistoryV0 { - slotHash = crypto.Keccak256Hash(slotKey.Bytes()) - } - b.storages[addrHash][slotHash] = append(b.storages[addrHash][slotHash], historyID) - } +// process traverses the state entries within the provided history and tracks the mutation +// records for them. +func (b *batchIndexer) process(h history, id uint64) error { + for ident := range h.forEach() { + b.index[ident] = append(b.index[ident], id) + b.pending++ } - b.lastID = historyID + b.lastID = id + return b.finish(false) } // finish writes the accumulated state indexes into the disk if either the // memory limitation is reached or it's requested forcibly. func (b *batchIndexer) finish(force bool) error { - if b.counter == 0 { + if b.pending == 0 { return nil } - if !force && b.counter < historyIndexBatch { + if !force && b.pending < historyIndexBatch { return nil } var ( - batch = b.db.NewBatch() - batchMu sync.RWMutex - storages int - start = time.Now() - eg errgroup.Group + batch = b.db.NewBatch() + batchMu sync.RWMutex + start = time.Now() + eg errgroup.Group ) eg.SetLimit(runtime.NumCPU()) - for addrHash, idList := range b.accounts { + for ident, list := range b.index { eg.Go(func() error { if !b.delete { - iw, err := newIndexWriter(b.db, newAccountIdent(addrHash)) + iw, err := newIndexWriter(b.db, ident) if err != nil { return err } - for _, n := range idList { + for _, n := range list { if err := iw.append(n); err != nil { return err } @@ -152,11 +173,11 @@ func (b *batchIndexer) finish(force bool) error { iw.finish(batch) batchMu.Unlock() } else { - id, err := newIndexDeleter(b.db, newAccountIdent(addrHash)) + id, err := newIndexDeleter(b.db, ident) if err != nil { return err } - for _, n := range idList { + for _, n := range list { if err := id.pop(n); err != nil { return err } @@ -168,72 +189,36 @@ func (b *batchIndexer) finish(force bool) error { return nil }) } - for addrHash, slots := range b.storages { - storages += len(slots) - for storageHash, idList := range slots { - eg.Go(func() error { - if !b.delete { - iw, err := newIndexWriter(b.db, newStorageIdent(addrHash, storageHash)) - if err != nil { - return err - } - for _, n := range idList { - if err := iw.append(n); err != nil { - return err - } - } - batchMu.Lock() - iw.finish(batch) - batchMu.Unlock() - } else { - id, err := newIndexDeleter(b.db, newStorageIdent(addrHash, storageHash)) - if err != nil { - return err - } - for _, n := range idList { - if err := id.pop(n); err != nil { - return err - } - } - batchMu.Lock() - id.finish(batch) - batchMu.Unlock() - } - return nil - }) - } - } if err := eg.Wait(); err != nil { return err } // Update the position of last indexed state history if !b.delete { - storeIndexMetadata(batch, b.lastID) + storeIndexMetadata(batch, b.typ, b.lastID) } else { if b.lastID == 1 { - rawdb.DeleteStateHistoryIndexMetadata(batch) + deleteIndexMetadata(batch, b.typ) } else { - storeIndexMetadata(batch, b.lastID-1) + storeIndexMetadata(batch, b.typ, b.lastID-1) } } if err := batch.Write(); err != nil { return err } - log.Debug("Committed batch indexer", "accounts", len(b.accounts), "storages", storages, "records", b.counter, "elapsed", common.PrettyDuration(time.Since(start))) - b.counter = 0 - b.accounts = make(map[common.Hash][]uint64) - b.storages = make(map[common.Hash]map[common.Hash][]uint64) + log.Debug("Committed batch indexer", "type", b.typ, "entries", len(b.index), "records", b.pending, "elapsed", common.PrettyDuration(time.Since(start))) + b.pending = 0 + b.index = make(map[stateIdent][]uint64) return nil } // indexSingle processes the state history with the specified ID for indexing. -func indexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.AncientReader) error { +func indexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.AncientReader, typ historyType) error { start := time.Now() defer func() { indexHistoryTimer.UpdateSince(start) }() - metadata := loadIndexMetadata(db) + metadata := loadIndexMetadata(db, typ) if metadata == nil || metadata.Last+1 != historyID { last := "null" if metadata != nil { @@ -241,29 +226,37 @@ func indexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.Ancient } return fmt.Errorf("history indexing is out of order, last: %s, requested: %d", last, historyID) } - h, err := readStateHistory(freezer, historyID) + var ( + err error + h history + b = newBatchIndexer(db, false, typ) + ) + if typ == typeStateHistory { + h, err = readStateHistory(freezer, historyID) + } else { + // h, err = readTrienodeHistory(freezer, historyID) + } if err != nil { return err } - b := newBatchIndexer(db, false) if err := b.process(h, historyID); err != nil { return err } if err := b.finish(true); err != nil { return err } - log.Debug("Indexed state history", "id", historyID, "elapsed", common.PrettyDuration(time.Since(start))) + log.Debug("Indexed history", "type", typ, "id", historyID, "elapsed", common.PrettyDuration(time.Since(start))) return nil } // unindexSingle processes the state history with the specified ID for unindexing. -func unindexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.AncientReader) error { +func unindexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.AncientReader, typ historyType) error { start := time.Now() defer func() { unindexHistoryTimer.UpdateSince(start) }() - metadata := loadIndexMetadata(db) + metadata := loadIndexMetadata(db, typ) if metadata == nil || metadata.Last != historyID { last := "null" if metadata != nil { @@ -271,18 +264,26 @@ func unindexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.Ancie } return fmt.Errorf("history unindexing is out of order, last: %s, requested: %d", last, historyID) } - h, err := readStateHistory(freezer, historyID) + var ( + err error + h history + ) + b := newBatchIndexer(db, true, typ) + if typ == typeStateHistory { + h, err = readStateHistory(freezer, historyID) + } else { + // h, err = readTrienodeHistory(freezer, historyID) + } if err != nil { return err } - b := newBatchIndexer(db, true) if err := b.process(h, historyID); err != nil { return err } if err := b.finish(true); err != nil { return err } - log.Debug("Unindexed state history", "id", historyID, "elapsed", common.PrettyDuration(time.Since(start))) + log.Debug("Unindexed history", "type", typ, "id", historyID, "elapsed", common.PrettyDuration(time.Since(start))) return nil } @@ -305,6 +306,8 @@ type indexIniter struct { interrupt chan *interruptSignal done chan struct{} closed chan struct{} + typ historyType + log log.Logger // Contextual logger with the history type injected // indexing progress indexed atomic.Uint64 // the id of latest indexed state @@ -313,18 +316,20 @@ type indexIniter struct { wg sync.WaitGroup } -func newIndexIniter(disk ethdb.KeyValueStore, freezer ethdb.AncientStore, lastID uint64) *indexIniter { +func newIndexIniter(disk ethdb.KeyValueStore, freezer ethdb.AncientStore, typ historyType, lastID uint64) *indexIniter { initer := &indexIniter{ disk: disk, freezer: freezer, interrupt: make(chan *interruptSignal), done: make(chan struct{}), closed: make(chan struct{}), + typ: typ, + log: log.New("type", typ.String()), } // Load indexing progress var recover bool initer.last.Store(lastID) - metadata := loadIndexMetadata(disk) + metadata := loadIndexMetadata(disk, typ) if metadata != nil { initer.indexed.Store(metadata.Last) recover = metadata.Last > lastID @@ -371,7 +376,7 @@ func (i *indexIniter) remain() uint64 { default: last, indexed := i.last.Load(), i.indexed.Load() if last < indexed { - log.Warn("State indexer is in recovery", "indexed", indexed, "last", last) + i.log.Warn("State indexer is in recovery", "indexed", indexed, "last", last) return indexed - last } return last - indexed @@ -389,7 +394,7 @@ func (i *indexIniter) run(lastID uint64) { // checkDone indicates whether all requested state histories // have been fully indexed. checkDone = func() bool { - metadata := loadIndexMetadata(i.disk) + metadata := loadIndexMetadata(i.disk, i.typ) return metadata != nil && metadata.Last == lastID } ) @@ -411,7 +416,7 @@ func (i *indexIniter) run(lastID uint64) { if newLastID == lastID+1 { lastID = newLastID signal.result <- nil - log.Debug("Extended state history range", "last", lastID) + i.log.Debug("Extended history range", "last", lastID) continue } // The index limit is shortened by one, interrupt the current background @@ -422,14 +427,14 @@ func (i *indexIniter) run(lastID uint64) { // If all state histories, including the one to be reverted, have // been fully indexed, unindex it here and shut down the initializer. if checkDone() { - log.Info("Truncate the extra history", "id", lastID) - if err := unindexSingle(lastID, i.disk, i.freezer); err != nil { + i.log.Info("Truncate the extra history", "id", lastID) + if err := unindexSingle(lastID, i.disk, i.freezer, i.typ); err != nil { signal.result <- err return } close(i.done) signal.result <- nil - log.Info("State histories have been fully indexed", "last", lastID-1) + i.log.Info("Histories have been fully indexed", "last", lastID-1) return } // Adjust the indexing target and relaunch the process @@ -438,12 +443,12 @@ func (i *indexIniter) run(lastID uint64) { done, interrupt = make(chan struct{}), new(atomic.Int32) go i.index(done, interrupt, lastID) - log.Debug("Shortened state history range", "last", lastID) + i.log.Debug("Shortened history range", "last", lastID) case <-done: if checkDone() { close(i.done) - log.Info("State histories have been fully indexed", "last", lastID) + i.log.Info("Histories have been fully indexed", "last", lastID) return } // Relaunch the background runner if some tasks are left @@ -452,7 +457,7 @@ func (i *indexIniter) run(lastID uint64) { case <-i.closed: interrupt.Store(1) - log.Info("Waiting background history index initer to exit") + i.log.Info("Waiting background history index initer to exit") <-done if checkDone() { @@ -472,14 +477,14 @@ func (i *indexIniter) next() (uint64, error) { tailID := tail + 1 // compute the id of the oldest history // Start indexing from scratch if nothing has been indexed - metadata := loadIndexMetadata(i.disk) + metadata := loadIndexMetadata(i.disk, i.typ) if metadata == nil { - log.Debug("Initialize state history indexing from scratch", "id", tailID) + i.log.Debug("Initialize history indexing from scratch", "id", tailID) return tailID, nil } // Resume indexing from the last interrupted position if metadata.Last+1 >= tailID { - log.Debug("Resume state history indexing", "id", metadata.Last+1, "tail", tailID) + i.log.Debug("Resume history indexing", "id", metadata.Last+1, "tail", tailID) return metadata.Last + 1, nil } // History has been shortened without indexing. Discard the gapped segment @@ -487,7 +492,7 @@ func (i *indexIniter) next() (uint64, error) { // // The missing indexes corresponding to the gapped histories won't be visible. // It's fine to leave them unindexed. - log.Info("History gap detected, discard old segment", "oldHead", metadata.Last, "newHead", tailID) + i.log.Info("History gap detected, discard old segment", "oldHead", metadata.Last, "newHead", tailID) return tailID, nil } @@ -496,7 +501,7 @@ func (i *indexIniter) index(done chan struct{}, interrupt *atomic.Int32, lastID beginID, err := i.next() if err != nil { - log.Error("Failed to find next state history for indexing", "err", err) + i.log.Error("Failed to find next history for indexing", "err", err) return } // All available state histories have been indexed, and the last indexed one @@ -511,36 +516,47 @@ func (i *indexIniter) index(done chan struct{}, interrupt *atomic.Int32, lastID // // This step is essential to avoid spinning up indexing thread // endlessly until a history object is produced. - storeIndexMetadata(i.disk, 0) - log.Info("Initialized history indexing flag") + storeIndexMetadata(i.disk, i.typ, 0) + i.log.Info("Initialized history indexing flag") } else { - log.Debug("State history is fully indexed", "last", lastID) + i.log.Debug("History is fully indexed", "last", lastID) } return } - log.Info("Start history indexing", "beginID", beginID, "lastID", lastID) + i.log.Info("Start history indexing", "beginID", beginID, "lastID", lastID) var ( current = beginID start = time.Now() logged = time.Now() - batch = newBatchIndexer(i.disk, false) + batch = newBatchIndexer(i.disk, false, i.typ) ) for current <= lastID { count := lastID - current + 1 if count > historyReadBatch { count = historyReadBatch } - histories, err := readStateHistories(i.freezer, current, count) - if err != nil { - // The history read might fall if the history is truncated from - // head due to revert operation. - log.Error("Failed to read history for indexing", "current", current, "count", count, "err", err) - return + var histories []history + if i.typ == typeStateHistory { + histories, err = readStateHistories(i.freezer, current, count) + if err != nil { + // The history read might fall if the history is truncated from + // head due to revert operation. + i.log.Error("Failed to read history for indexing", "current", current, "count", count, "err", err) + return + } + } else { + // histories, err = readTrienodeHistories(i.freezer, current, count) + // if err != nil { + // // The history read might fall if the history is truncated from + // // head due to revert operation. + // i.log.Error("Failed to read history for indexing", "current", current, "count", count, "err", err) + // return + // } } for _, h := range histories { if err := batch.process(h, current); err != nil { - log.Error("Failed to index history", "err", err) + i.log.Error("Failed to index history", "err", err) return } current += 1 @@ -554,7 +570,7 @@ func (i *indexIniter) index(done chan struct{}, interrupt *atomic.Int32, lastID done = current - beginID ) eta := common.CalculateETA(done, left, time.Since(start)) - log.Info("Indexing state history", "processed", done, "left", left, "elapsed", common.PrettyDuration(time.Since(start)), "eta", common.PrettyDuration(eta)) + i.log.Info("Indexing state history", "processed", done, "left", left, "elapsed", common.PrettyDuration(time.Since(start)), "eta", common.PrettyDuration(eta)) } } i.indexed.Store(current - 1) // update indexing progress @@ -563,7 +579,7 @@ func (i *indexIniter) index(done chan struct{}, interrupt *atomic.Int32, lastID if interrupt != nil { if signal := interrupt.Load(); signal != 0 { if err := batch.finish(true); err != nil { - log.Error("Failed to flush index", "err", err) + i.log.Error("Failed to flush index", "err", err) } log.Info("State indexing interrupted") return @@ -571,9 +587,9 @@ func (i *indexIniter) index(done chan struct{}, interrupt *atomic.Int32, lastID } } if err := batch.finish(true); err != nil { - log.Error("Failed to flush index", "err", err) + i.log.Error("Failed to flush index", "err", err) } - log.Info("Indexed state history", "from", beginID, "to", lastID, "elapsed", common.PrettyDuration(time.Since(start))) + i.log.Info("Indexed history", "from", beginID, "to", lastID, "elapsed", common.PrettyDuration(time.Since(start))) } // recover handles unclean shutdown recovery. After an unclean shutdown, any @@ -602,14 +618,14 @@ func (i *indexIniter) recover(lastID uint64) { lastID = newLastID signal.result <- nil i.last.Store(newLastID) - log.Debug("Updated history index flag", "last", lastID) + i.log.Debug("Updated history index flag", "last", lastID) // Terminate the recovery routine once the histories are fully aligned // with the index data, indicating that index initialization is complete. - metadata := loadIndexMetadata(i.disk) + metadata := loadIndexMetadata(i.disk, i.typ) if metadata != nil && metadata.Last == lastID { close(i.done) - log.Info("History indexer is recovered", "last", lastID) + i.log.Info("History indexer is recovered", "last", lastID) return } @@ -631,21 +647,31 @@ func (i *indexIniter) recover(lastID uint64) { // state history. type historyIndexer struct { initer *indexIniter + typ historyType disk ethdb.KeyValueStore freezer ethdb.AncientStore } // checkVersion checks whether the index data in the database matches the version. -func checkVersion(disk ethdb.KeyValueStore) { - blob := rawdb.ReadStateHistoryIndexMetadata(disk) +func checkVersion(disk ethdb.KeyValueStore, typ historyType) { + var blob []byte + if typ == typeStateHistory { + blob = rawdb.ReadStateHistoryIndexMetadata(disk) + } else { + panic(fmt.Errorf("unknown history type: %v", typ)) + } + // Short circuit if metadata is not found, re-index is required + // from scratch. if len(blob) == 0 { return } + // Short circuit if the metadata is found and the version is matched var m indexMetadata err := rlp.DecodeBytes(blob, &m) if err == nil && m.Version == stateIndexVersion { return } + // Version is not matched, prune the existing data and re-index from scratch version := "unknown" if err == nil { version = fmt.Sprintf("%d", m.Version) @@ -662,10 +688,11 @@ func checkVersion(disk ethdb.KeyValueStore) { // newHistoryIndexer constructs the history indexer and launches the background // initer to complete the indexing of any remaining state histories. -func newHistoryIndexer(disk ethdb.KeyValueStore, freezer ethdb.AncientStore, lastHistoryID uint64) *historyIndexer { - checkVersion(disk) +func newHistoryIndexer(disk ethdb.KeyValueStore, freezer ethdb.AncientStore, lastHistoryID uint64, typ historyType) *historyIndexer { + checkVersion(disk, typ) return &historyIndexer{ - initer: newIndexIniter(disk, freezer, lastHistoryID), + initer: newIndexIniter(disk, freezer, typ, lastHistoryID), + typ: typ, disk: disk, freezer: freezer, } @@ -693,7 +720,7 @@ func (i *historyIndexer) extend(historyID uint64) error { case <-i.initer.closed: return errors.New("indexer is closed") case <-i.initer.done: - return indexSingle(historyID, i.disk, i.freezer) + return indexSingle(historyID, i.disk, i.freezer, i.typ) case i.initer.interrupt <- signal: return <-signal.result } @@ -710,7 +737,7 @@ func (i *historyIndexer) shorten(historyID uint64) error { case <-i.initer.closed: return errors.New("indexer is closed") case <-i.initer.done: - return unindexSingle(historyID, i.disk, i.freezer) + return unindexSingle(historyID, i.disk, i.freezer, i.typ) case i.initer.interrupt <- signal: return <-signal.result } diff --git a/triedb/pathdb/history_indexer_test.go b/triedb/pathdb/history_indexer_test.go index 96c87ccb1b..f333d18d8b 100644 --- a/triedb/pathdb/history_indexer_test.go +++ b/triedb/pathdb/history_indexer_test.go @@ -38,7 +38,7 @@ func TestHistoryIndexerShortenDeadlock(t *testing.T) { rawdb.WriteStateHistory(freezer, uint64(i+1), h.meta.encode(), accountIndex, storageIndex, accountData, storageData) } // As a workaround, assign a future block to keep the initer running indefinitely - indexer := newHistoryIndexer(db, freezer, 200) + indexer := newHistoryIndexer(db, freezer, 200, typeStateHistory) defer indexer.close() done := make(chan error, 1) diff --git a/triedb/pathdb/history_reader.go b/triedb/pathdb/history_reader.go index a11297b3f6..ce6aa693d1 100644 --- a/triedb/pathdb/history_reader.go +++ b/triedb/pathdb/history_reader.go @@ -29,88 +29,6 @@ import ( "github.com/ethereum/go-ethereum/ethdb" ) -// stateIdent represents the identifier of a state element, which can be -// either an account or a storage slot. -type stateIdent struct { - account bool - - // The hash of the account address. This is used instead of the raw account - // address is to align the traversal order with the Merkle-Patricia-Trie. - addressHash common.Hash - - // The hash of the storage slot key. This is used instead of the raw slot key - // because, in legacy state histories (prior to the Cancun fork), the slot - // identifier is the hash of the key, and the original key (preimage) cannot - // be recovered. To maintain backward compatibility, the key hash is used. - // - // Meanwhile, using the storage key hash also preserve the traversal order - // with Merkle-Patricia-Trie. - // - // This field is null if the identifier refers to account data. - storageHash common.Hash -} - -// String returns the string format state identifier. -func (ident stateIdent) String() string { - if ident.account { - return ident.addressHash.Hex() - } - return ident.addressHash.Hex() + ident.storageHash.Hex() -} - -// newAccountIdent constructs a state identifier for an account. -func newAccountIdent(addressHash common.Hash) stateIdent { - return stateIdent{ - account: true, - addressHash: addressHash, - } -} - -// newStorageIdent constructs a state identifier for a storage slot. -// The address denotes the address of the associated account; -// the storageHash denotes the hash of the raw storage slot key; -func newStorageIdent(addressHash common.Hash, storageHash common.Hash) stateIdent { - return stateIdent{ - addressHash: addressHash, - storageHash: storageHash, - } -} - -// stateIdentQuery is the extension of stateIdent by adding the raw storage key. -type stateIdentQuery struct { - stateIdent - - address common.Address - storageKey common.Hash -} - -// newAccountIdentQuery constructs a state identifier for an account. -func newAccountIdentQuery(address common.Address, addressHash common.Hash) stateIdentQuery { - return stateIdentQuery{ - stateIdent: stateIdent{ - account: true, - addressHash: addressHash, - }, - address: address, - } -} - -// newStorageIdentQuery constructs a state identifier for a storage slot. -// the address denotes the address of the associated account; -// the addressHash denotes the address hash of the associated account; -// the storageKey denotes the raw storage slot key; -// the storageHash denotes the hash of the raw storage slot key; -func newStorageIdentQuery(address common.Address, addressHash common.Hash, storageKey common.Hash, storageHash common.Hash) stateIdentQuery { - return stateIdentQuery{ - stateIdent: stateIdent{ - addressHash: addressHash, - storageHash: storageHash, - }, - address: address, - storageKey: storageKey, - } -} - // indexReaderWithLimitTag is a wrapper around indexReader that includes an // additional index position. This position represents the ID of the last // indexed state history at the time the reader was created, implying that @@ -169,7 +87,7 @@ func (r *indexReaderWithLimitTag) readGreaterThan(id uint64, lastID uint64) (uin // Given that it's very unlikely to occur and users try to perform historical // state queries while reverting the states at the same time. Simply returning // an error should be sufficient for now. - metadata := loadIndexMetadata(r.db) + metadata := loadIndexMetadata(r.db, toHistoryType(r.reader.state.typ)) if metadata == nil || metadata.Last < lastID { return 0, errors.New("state history hasn't been indexed yet") } @@ -331,7 +249,7 @@ func (r *historyReader) read(state stateIdentQuery, stateID uint64, lastID uint6 // To serve the request, all state histories from stateID+1 to lastID // must be indexed. It's not supposed to happen unless system is very // wrong. - metadata := loadIndexMetadata(r.disk) + metadata := loadIndexMetadata(r.disk, toHistoryType(state.typ)) if metadata == nil || metadata.Last < lastID { indexed := "null" if metadata != nil { @@ -364,7 +282,7 @@ func (r *historyReader) read(state stateIdentQuery, stateID uint64, lastID uint6 // that the associated state histories are no longer available due to a rollback. // Such truncation should be captured by the state resolver below, rather than returning // invalid data. - if state.account { + if state.typ == typeAccount { return r.readAccount(state.address, historyID) } return r.readStorage(state.address, state.storageKey, state.storageHash, historyID) diff --git a/triedb/pathdb/history_reader_test.go b/triedb/pathdb/history_reader_test.go index 75c5f701f9..3e1a545ff3 100644 --- a/triedb/pathdb/history_reader_test.go +++ b/triedb/pathdb/history_reader_test.go @@ -29,7 +29,7 @@ import ( func waitIndexing(db *Database) { for { - metadata := loadIndexMetadata(db.diskdb) + metadata := loadIndexMetadata(db.diskdb, typeStateHistory) if metadata != nil && metadata.Last >= db.tree.bottom().stateID() { return } diff --git a/triedb/pathdb/history_state.go b/triedb/pathdb/history_state.go index 3bb69a7f4d..9d1e4dfb09 100644 --- a/triedb/pathdb/history_state.go +++ b/triedb/pathdb/history_state.go @@ -21,6 +21,7 @@ import ( "encoding/binary" "errors" "fmt" + "iter" "maps" "slices" "time" @@ -275,6 +276,36 @@ func newStateHistory(root common.Hash, parent common.Hash, block uint64, account } } +// typ implements the history interface, returning the historical data type held. +func (h *stateHistory) typ() historyType { + return typeStateHistory +} + +// forEach implements the history interface, returning an iterator to traverse the +// state entries in the history. +func (h *stateHistory) forEach() iter.Seq[stateIdent] { + return func(yield func(stateIdent) bool) { + for _, addr := range h.accountList { + addrHash := crypto.Keccak256Hash(addr.Bytes()) + if !yield(newAccountIdent(addrHash)) { + return + } + for _, slotKey := range h.storageList[addr] { + // The hash of the storage slot key is used as the identifier because the + // legacy history does not include the raw storage key, therefore, the + // conversion from storage key to hash is necessary for non-v0 histories. + slotHash := slotKey + if h.meta.version != stateHistoryV0 { + slotHash = crypto.Keccak256Hash(slotKey.Bytes()) + } + if !yield(newStorageIdent(addrHash, slotHash)) { + return + } + } + } + } +} + // stateSet returns the state set, keyed by the hash of the account address // and the hash of the storage slot key. func (h *stateHistory) stateSet() (map[common.Hash][]byte, map[common.Hash]map[common.Hash][]byte) { @@ -536,8 +567,8 @@ func readStateHistory(reader ethdb.AncientReader, id uint64) (*stateHistory, err } // readStateHistories reads a list of state history records within the specified range. -func readStateHistories(freezer ethdb.AncientReader, start uint64, count uint64) ([]*stateHistory, error) { - var histories []*stateHistory +func readStateHistories(freezer ethdb.AncientReader, start uint64, count uint64) ([]history, error) { + var histories []history metaList, aIndexList, sIndexList, aDataList, sDataList, err := rawdb.ReadStateHistoryList(freezer, start, count) if err != nil { return nil, err diff --git a/triedb/pathdb/history_state_test.go b/triedb/pathdb/history_state_test.go index 4a777111ea..5718081566 100644 --- a/triedb/pathdb/history_state_test.go +++ b/triedb/pathdb/history_state_test.go @@ -137,7 +137,7 @@ func TestTruncateHeadStateHistory(t *testing.T) { rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData) } for size := len(hs); size > 0; size-- { - pruned, err := truncateFromHead(freezer, uint64(size-1)) + pruned, err := truncateFromHead(freezer, typeStateHistory, uint64(size-1)) if err != nil { t.Fatalf("Failed to truncate from head %v", err) } @@ -161,7 +161,7 @@ func TestTruncateTailStateHistory(t *testing.T) { rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData) } for newTail := 1; newTail < len(hs); newTail++ { - pruned, _ := truncateFromTail(freezer, uint64(newTail)) + pruned, _ := truncateFromTail(freezer, typeStateHistory, uint64(newTail)) if pruned != 1 { t.Error("Unexpected pruned items", "want", 1, "got", pruned) } @@ -209,7 +209,7 @@ func TestTruncateTailStateHistories(t *testing.T) { accountData, storageData, accountIndex, storageIndex := hs[i].encode() rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData) } - pruned, _ := truncateFromTail(freezer, uint64(10)-c.limit) + pruned, _ := truncateFromTail(freezer, typeStateHistory, uint64(10)-c.limit) if pruned != c.expPruned { t.Error("Unexpected pruned items", "want", c.expPruned, "got", pruned) } @@ -233,7 +233,7 @@ func TestTruncateOutOfRange(t *testing.T) { accountData, storageData, accountIndex, storageIndex := hs[i].encode() rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData) } - truncateFromTail(freezer, uint64(len(hs)/2)) + truncateFromTail(freezer, typeStateHistory, uint64(len(hs)/2)) // Ensure of-out-range truncations are rejected correctly. head, _ := freezer.Ancients() @@ -254,9 +254,9 @@ func TestTruncateOutOfRange(t *testing.T) { for _, c := range cases { var gotErr error if c.mode == 0 { - _, gotErr = truncateFromHead(freezer, c.target) + _, gotErr = truncateFromHead(freezer, typeStateHistory, c.target) } else { - _, gotErr = truncateFromTail(freezer, c.target) + _, gotErr = truncateFromTail(freezer, typeStateHistory, c.target) } if !errors.Is(gotErr, c.expErr) { t.Errorf("Unexpected error, want: %v, got: %v", c.expErr, gotErr) From fda09c7b1baa3b9e3f6912f636e2d91337c79a55 Mon Sep 17 00:00:00 2001 From: Samuel Arogbonlo <47984109+samuelarogbonlo@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:07:02 +0100 Subject: [PATCH 070/119] trie: add sub-trie iterator support (#32520) - Adds `NodeIteratorWithPrefix()` method to support iterating only nodes within a specific key prefix - Adds `NodeIteratorWithRange()` method to support iterating only nodes within a specific key range Current `NodeIterator` always traverses the entire remaining trie from a start position. For non-ethereum applications using the trie implementation, there's no way to limit iteration to just a subtree with a specific prefix. **Usage:** ```go // Only iterate nodes with prefix "key1" iter, err := trie.NodeIteratorWithPrefix([]byte("key1")) ``` Testing: Comprehensive test suite covering edge cases and boundary conditions. Closes #32484 --------- Co-authored-by: gballet Co-authored-by: Gary Rong --- trie/iterator.go | 88 +++++++ trie/iterator_test.go | 536 ++++++++++++++++++++++++++++++++++++++++++ trie/trie.go | 28 +++ 3 files changed, 652 insertions(+) diff --git a/trie/iterator.go b/trie/iterator.go index 80298ce48f..3d3191ffba 100644 --- a/trie/iterator.go +++ b/trie/iterator.go @@ -836,3 +836,91 @@ func (it *unionIterator) Error() error { } return nil } + +// subTreeIterator wraps nodeIterator to traverse a trie within a predefined +// start and limit range. +type subtreeIterator struct { + NodeIterator + + stopPath []byte // Precomputed hex path for stopKey (without terminator), nil means no limit + exhausted bool // Flag whether the iterator has been exhausted +} + +// newSubtreeIterator creates an iterator that only traverses nodes within a subtree +// defined by the given startKey and stopKey. This supports general range iteration +// where startKey is inclusive and stopKey is exclusive. +// +// The iterator will only visit nodes whose keys k satisfy: startKey <= k < stopKey, +// where comparisons are performed in lexicographic order of byte keys (internally +// implemented via hex-nibble path comparisons for efficiency). +// +// If startKey is nil, iteration starts from the beginning. If stopKey is nil, +// iteration continues to the end of the trie. +func newSubtreeIterator(trie *Trie, startKey, stopKey []byte) (NodeIterator, error) { + it, err := trie.NodeIterator(startKey) + if err != nil { + return nil, err + } + if startKey == nil && stopKey == nil { + return it, nil + } + // Precompute nibble paths for efficient comparison + var stopPath []byte + if stopKey != nil { + stopPath = keybytesToHex(stopKey) + if hasTerm(stopPath) { + stopPath = stopPath[:len(stopPath)-1] + } + } + return &subtreeIterator{ + NodeIterator: it, + stopPath: stopPath, + }, nil +} + +// nextKey returns the next possible key after the given prefix. +// For example, "abc" -> "abd", "ab\xff" -> "ac", etc. +func nextKey(prefix []byte) []byte { + if len(prefix) == 0 { + return nil + } + // Make a copy to avoid modifying the original + next := make([]byte, len(prefix)) + copy(next, prefix) + + // Increment the last byte that isn't 0xff + for i := len(next) - 1; i >= 0; i-- { + if next[i] < 0xff { + next[i]++ + return next + } + // If it's 0xff, we need to carry over + // Trim trailing 0xff bytes + next = next[:i] + } + // If all bytes were 0xff, return nil (no upper bound) + return nil +} + +// newPrefixIterator creates an iterator that only traverses nodes with the given prefix. +// This ensures that only keys starting with the prefix are visited. +func newPrefixIterator(trie *Trie, prefix []byte) (NodeIterator, error) { + return newSubtreeIterator(trie, prefix, nextKey(prefix)) +} + +// Next moves the iterator to the next node. If the parameter is false, any child +// nodes will be skipped. +func (it *subtreeIterator) Next(descend bool) bool { + if it.exhausted { + return false + } + if !it.NodeIterator.Next(descend) { + it.exhausted = true + return false + } + if it.stopPath != nil && reachedPath(it.NodeIterator.Path(), it.stopPath) { + it.exhausted = true + return false + } + return true +} diff --git a/trie/iterator_test.go b/trie/iterator_test.go index 74a1aa378c..f1451cef90 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -19,7 +19,9 @@ package trie import ( "bytes" "fmt" + "maps" "math/rand" + "slices" "testing" "github.com/ethereum/go-ethereum/common" @@ -624,6 +626,540 @@ func isTrieNode(scheme string, key, val []byte) (bool, []byte, common.Hash) { return true, path, hash } +func TestSubtreeIterator(t *testing.T) { + var ( + db = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) + tr = NewEmpty(db) + ) + vals := []struct{ k, v string }{ + {"do", "verb"}, + {"dog", "puppy"}, + {"doge", "coin"}, + {"dog\xff", "value6"}, + {"dog\xff\xff", "value7"}, + {"horse", "stallion"}, + {"house", "building"}, + {"houses", "multiple"}, + {"xyz", "value"}, + {"xyz\xff", "value"}, + {"xyz\xff\xff", "value"}, + } + all := make(map[string]string) + for _, val := range vals { + all[val.k] = val.v + tr.MustUpdate([]byte(val.k), []byte(val.v)) + } + root, nodes := tr.Commit(false) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) + + allNodes := make(map[string][]byte) + tr, _ = New(TrieID(root), db) + it, err := tr.NodeIterator(nil) + if err != nil { + t.Fatal(err) + } + for it.Next(true) { + allNodes[string(it.Path())] = it.NodeBlob() + } + allKeys := slices.Collect(maps.Keys(all)) + + suites := []struct { + start []byte + end []byte + expected []string + }{ + // entire key range + { + start: nil, + end: nil, + expected: allKeys, + }, + { + start: nil, + end: bytes.Repeat([]byte{0xff}, 32), + expected: allKeys, + }, + { + start: bytes.Repeat([]byte{0x0}, 32), + end: bytes.Repeat([]byte{0xff}, 32), + expected: allKeys, + }, + // key range with start + { + start: []byte("do"), + end: nil, + expected: allKeys, + }, + { + start: []byte("doe"), + end: nil, + expected: allKeys[1:], + }, + { + start: []byte("dog"), + end: nil, + expected: allKeys[1:], + }, + { + start: []byte("doge"), + end: nil, + expected: allKeys[2:], + }, + { + start: []byte("dog\xff"), + end: nil, + expected: allKeys[3:], + }, + { + start: []byte("dog\xff\xff"), + end: nil, + expected: allKeys[4:], + }, + { + start: []byte("dog\xff\xff\xff"), + end: nil, + expected: allKeys[5:], + }, + // key range with limit + { + start: nil, + end: []byte("xyz"), + expected: allKeys[:len(allKeys)-3], + }, + { + start: nil, + end: []byte("xyz\xff"), + expected: allKeys[:len(allKeys)-2], + }, + { + start: nil, + end: []byte("xyz\xff\xff"), + expected: allKeys[:len(allKeys)-1], + }, + { + start: nil, + end: []byte("xyz\xff\xff\xff"), + expected: allKeys, + }, + } + for _, suite := range suites { + // We need to re-open the trie from the committed state + tr, _ = New(TrieID(root), db) + it, err := newSubtreeIterator(tr, suite.start, suite.end) + if err != nil { + t.Fatal(err) + } + + found := make(map[string]string) + for it.Next(true) { + if it.Leaf() { + found[string(it.LeafKey())] = string(it.LeafBlob()) + } + } + if len(found) != len(suite.expected) { + t.Errorf("wrong number of values: got %d, want %d", len(found), len(suite.expected)) + } + for k, v := range found { + if all[k] != v { + t.Errorf("wrong value for %s: got %s, want %s", k, found[k], all[k]) + } + } + + expectedNodes := make(map[string][]byte) + for path, blob := range allNodes { + if suite.start != nil { + hexStart := keybytesToHex(suite.start) + hexStart = hexStart[:len(hexStart)-1] + if !reachedPath([]byte(path), hexStart) { + continue + } + } + if suite.end != nil { + hexEnd := keybytesToHex(suite.end) + hexEnd = hexEnd[:len(hexEnd)-1] + if reachedPath([]byte(path), hexEnd) { + continue + } + } + expectedNodes[path] = bytes.Clone(blob) + } + + // Compare the result yield from the subtree iterator + var ( + subCount int + subIt, _ = newSubtreeIterator(tr, suite.start, suite.end) + ) + for subIt.Next(true) { + blob, ok := expectedNodes[string(subIt.Path())] + if !ok { + t.Errorf("Unexpected node iterated, path: %v", subIt.Path()) + } + subCount++ + + if !bytes.Equal(blob, subIt.NodeBlob()) { + t.Errorf("Unexpected node blob, path: %v, want: %v, got: %v", subIt.Path(), blob, subIt.NodeBlob()) + } + } + if subCount != len(expectedNodes) { + t.Errorf("Unexpected node being iterated, want: %d, got: %d", len(expectedNodes), subCount) + } + } +} + +func TestPrefixIterator(t *testing.T) { + // Create a new trie + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) + + // Insert test data + testData := map[string]string{ + "key1": "value1", + "key2": "value2", + "key10": "value10", + "key11": "value11", + "different": "value_different", + } + + for key, value := range testData { + trie.Update([]byte(key), []byte(value)) + } + + // Test prefix iteration for "key1" prefix + prefix := []byte("key1") + iter, err := trie.NodeIteratorWithPrefix(prefix) + if err != nil { + t.Fatalf("Failed to create prefix iterator: %v", err) + } + + var foundKeys [][]byte + for iter.Next(true) { + if iter.Leaf() { + foundKeys = append(foundKeys, iter.LeafKey()) + } + } + + if err := iter.Error(); err != nil { + t.Fatalf("Iterator error: %v", err) + } + + // Verify only keys starting with "key1" were found + expectedCount := 3 // "key1", "key10", "key11" + if len(foundKeys) != expectedCount { + t.Errorf("Expected %d keys, found %d", expectedCount, len(foundKeys)) + } + + for _, key := range foundKeys { + keyStr := string(key) + if !bytes.HasPrefix(key, prefix) { + t.Errorf("Found key %s doesn't have prefix %s", keyStr, string(prefix)) + } + } +} + +func TestPrefixIteratorVsFullIterator(t *testing.T) { + // Create a new trie with more structured data + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) + + // Insert structured test data + testData := map[string]string{ + "aaa": "value_aaa", + "aab": "value_aab", + "aba": "value_aba", + "bbb": "value_bbb", + } + + for key, value := range testData { + trie.Update([]byte(key), []byte(value)) + } + + // Test that prefix iterator stops at boundary + prefix := []byte("aa") + prefixIter, err := trie.NodeIteratorWithPrefix(prefix) + if err != nil { + t.Fatalf("Failed to create prefix iterator: %v", err) + } + + var prefixKeys [][]byte + for prefixIter.Next(true) { + if prefixIter.Leaf() { + prefixKeys = append(prefixKeys, prefixIter.LeafKey()) + } + } + + // Should only find "aaa" and "aab", not "aba" or "bbb" + if len(prefixKeys) != 2 { + t.Errorf("Expected 2 keys with prefix 'aa', found %d", len(prefixKeys)) + } + + // Verify no keys outside prefix were found + for _, key := range prefixKeys { + if !bytes.HasPrefix(key, prefix) { + t.Errorf("Prefix iterator returned key %s outside prefix %s", string(key), string(prefix)) + } + } +} + +func TestEmptyPrefixIterator(t *testing.T) { + // Test with empty trie + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) + + iter, err := trie.NodeIteratorWithPrefix([]byte("nonexistent")) + if err != nil { + t.Fatalf("Failed to create iterator: %v", err) + } + + if iter.Next(true) { + t.Error("Expected no results from empty trie") + } +} + +// TestPrefixIteratorEdgeCases tests various edge cases for prefix iteration +func TestPrefixIteratorEdgeCases(t *testing.T) { + // Create a trie with test data + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) + testData := map[string]string{ + "abc": "value1", + "abcd": "value2", + "abce": "value3", + "abd": "value4", + "dog": "value5", + "dog\xff": "value6", // Test with 0xff byte + "dog\xff\xff": "value7", // Multiple 0xff bytes + } + for key, value := range testData { + trie.Update([]byte(key), []byte(value)) + } + + // Test 1: Prefix not present in trie + t.Run("NonexistentPrefix", func(t *testing.T) { + iter, err := trie.NodeIteratorWithPrefix([]byte("xyz")) + if err != nil { + t.Fatalf("Failed to create iterator: %v", err) + } + count := 0 + for iter.Next(true) { + if iter.Leaf() { + count++ + } + } + if count != 0 { + t.Errorf("Expected 0 results for nonexistent prefix, got %d", count) + } + }) + + // Test 2: Prefix exactly equals an existing key + t.Run("ExactKeyPrefix", func(t *testing.T) { + iter, err := trie.NodeIteratorWithPrefix([]byte("abc")) + if err != nil { + t.Fatalf("Failed to create iterator: %v", err) + } + found := make(map[string]bool) + for iter.Next(true) { + if iter.Leaf() { + found[string(iter.LeafKey())] = true + } + } + // Should find "abc", "abcd", "abce" but not "abd" + if !found["abc"] || !found["abcd"] || !found["abce"] { + t.Errorf("Missing expected keys: got %v", found) + } + if found["abd"] { + t.Errorf("Found unexpected key 'abd' with prefix 'abc'") + } + }) + + // Test 3: Prefix with trailing 0xff + t.Run("TrailingFFPrefix", func(t *testing.T) { + iter, err := trie.NodeIteratorWithPrefix([]byte("dog\xff")) + if err != nil { + t.Fatalf("Failed to create iterator: %v", err) + } + found := make(map[string]bool) + for iter.Next(true) { + if iter.Leaf() { + found[string(iter.LeafKey())] = true + } + } + // Should find "dog\xff" and "dog\xff\xff" + if !found["dog\xff"] || !found["dog\xff\xff"] { + t.Errorf("Missing expected keys with 0xff: got %v", found) + } + if found["dog"] { + t.Errorf("Found unexpected key 'dog' with prefix 'dog\\xff'") + } + }) + + // Test 4: All 0xff case (edge case for nextKey) + t.Run("AllFFPrefix", func(t *testing.T) { + // Add a key with all 0xff bytes + allFF := []byte{0xff, 0xff} + trie.Update(allFF, []byte("all_ff_value")) + trie.Update(append(allFF, 0x00), []byte("all_ff_plus")) + + iter, err := trie.NodeIteratorWithPrefix(allFF) + if err != nil { + t.Fatalf("Failed to create iterator: %v", err) + } + count := 0 + for iter.Next(true) { + if iter.Leaf() { + count++ + } + } + // Should find at least the allFF key itself + if count != 2 { + t.Errorf("Expected at least 1 result for all-0xff prefix, got %d", count) + } + }) + + // Test 5: Empty prefix (should iterate entire trie) + t.Run("EmptyPrefix", func(t *testing.T) { + iter, err := trie.NodeIteratorWithPrefix([]byte{}) + if err != nil { + t.Fatalf("Failed to create iterator: %v", err) + } + count := 0 + for iter.Next(true) { + if iter.Leaf() { + count++ + } + } + // Should find all keys in the trie + expectedCount := len(testData) + 2 // +2 for the extra keys added in test 4 + if count != expectedCount { + t.Errorf("Expected %d results for empty prefix, got %d", expectedCount, count) + } + }) +} + +// TestGeneralRangeIteration tests NewSubtreeIterator with arbitrary start/stop ranges +func TestGeneralRangeIteration(t *testing.T) { + // Create a trie with test data + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) + testData := map[string]string{ + "apple": "fruit1", + "apricot": "fruit2", + "banana": "fruit3", + "cherry": "fruit4", + "date": "fruit5", + "fig": "fruit6", + "grape": "fruit7", + } + for key, value := range testData { + trie.Update([]byte(key), []byte(value)) + } + + // Test range iteration from "banana" to "fig" (exclusive) + t.Run("RangeIteration", func(t *testing.T) { + iter, _ := newSubtreeIterator(trie, []byte("banana"), []byte("fig")) + found := make(map[string]bool) + for iter.Next(true) { + if iter.Leaf() { + found[string(iter.LeafKey())] = true + } + } + // Should find "banana", "cherry", "date" but not "fig" + if !found["banana"] || !found["cherry"] || !found["date"] { + t.Errorf("Missing expected keys in range: got %v", found) + } + if found["apple"] || found["apricot"] || found["fig"] || found["grape"] { + t.Errorf("Found unexpected keys outside range: got %v", found) + } + }) + + // Test with nil stopKey (iterate to end) + t.Run("NilStopKey", func(t *testing.T) { + iter, _ := newSubtreeIterator(trie, []byte("date"), nil) + found := make(map[string]bool) + for iter.Next(true) { + if iter.Leaf() { + found[string(iter.LeafKey())] = true + } + } + // Should find "date", "fig", "grape" + if !found["date"] || !found["fig"] || !found["grape"] { + t.Errorf("Missing expected keys from 'date' to end: got %v", found) + } + if found["apple"] || found["banana"] || found["cherry"] { + t.Errorf("Found unexpected keys before 'date': got %v", found) + } + }) + + // Test with nil startKey (iterate from beginning) + t.Run("NilStartKey", func(t *testing.T) { + iter, _ := newSubtreeIterator(trie, nil, []byte("cherry")) + found := make(map[string]bool) + for iter.Next(true) { + if iter.Leaf() { + found[string(iter.LeafKey())] = true + } + } + // Should find "apple", "apricot", "banana" but not "cherry" or later + if !found["apple"] || !found["apricot"] || !found["banana"] { + t.Errorf("Missing expected keys before 'cherry': got %v", found) + } + if found["cherry"] || found["date"] || found["fig"] || found["grape"] { + t.Errorf("Found unexpected keys at or after 'cherry': got %v", found) + } + }) +} + +// TestPrefixIteratorWithDescend tests prefix iteration with descend=false +func TestPrefixIteratorWithDescend(t *testing.T) { + // Create a trie with nested structure + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) + testData := map[string]string{ + "a": "value_a", + "a/b": "value_ab", + "a/b/c": "value_abc", + "a/b/d": "value_abd", + "a/e": "value_ae", + "b": "value_b", + } + for key, value := range testData { + trie.Update([]byte(key), []byte(value)) + } + + // Test skipping subtrees with descend=false + t.Run("SkipSubtrees", func(t *testing.T) { + iter, err := trie.NodeIteratorWithPrefix([]byte("a")) + if err != nil { + t.Fatalf("Failed to create iterator: %v", err) + } + + // Count nodes at each level + nodesVisited := 0 + leafsFound := make(map[string]bool) + + // First call with descend=true to enter the "a" subtree + if !iter.Next(true) { + t.Fatal("Expected to find at least one node") + } + nodesVisited++ + + // Continue iteration, sometimes with descend=false + descendPattern := []bool{false, true, false, true, true} + for i := 0; iter.Next(descendPattern[i%len(descendPattern)]); i++ { + nodesVisited++ + if iter.Leaf() { + leafsFound[string(iter.LeafKey())] = true + } + } + + // We should still respect the prefix boundary even when skipping + prefix := []byte("a") + for key := range leafsFound { + if !bytes.HasPrefix([]byte(key), prefix) { + t.Errorf("Found key outside prefix when using descend=false: %s", key) + } + } + + // Should not have found "b" even if we skip some subtrees + if leafsFound["b"] { + t.Error("Iterator leaked outside prefix boundary with descend=false") + } + }) +} + func BenchmarkIterator(b *testing.B) { diskDb, srcDb, tr, _ := makeTestTrie(rawdb.HashScheme) root := tr.Hash() diff --git a/trie/trie.go b/trie/trie.go index 630462f8ca..36cc732ee8 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -134,6 +134,34 @@ func (t *Trie) NodeIterator(start []byte) (NodeIterator, error) { return newNodeIterator(t, start), nil } +// NodeIteratorWithPrefix returns an iterator that returns nodes of the trie +// whose leaf keys start with the given prefix. Iteration includes all keys +// where prefix <= k < nextKey(prefix), effectively returning only keys that +// have the prefix. The iteration stops once it would encounter a key that +// doesn't start with the prefix. +// +// For example, with prefix "dog", the iterator will return "dog", "dogcat", +// "dogfish" but not "dot" or "fog". An empty prefix iterates the entire trie. +func (t *Trie) NodeIteratorWithPrefix(prefix []byte) (NodeIterator, error) { + // Short circuit if the trie is already committed and not usable. + if t.committed { + return nil, ErrCommitted + } + // Use the dedicated prefix iterator which handles prefix checking correctly + return newPrefixIterator(t, prefix) +} + +// NodeIteratorWithRange returns an iterator over trie nodes whose leaf keys +// fall within the specified range. It includes all keys where start <= k < end. +// Iteration stops once a key beyond the end boundary is encountered. +func (t *Trie) NodeIteratorWithRange(start, end []byte) (NodeIterator, error) { + // Short circuit if the trie is already committed and not usable. + if t.committed { + return nil, ErrCommitted + } + return newSubtreeIterator(t, start, end) +} + // MustGet is a wrapper of Get and will omit any encountered error but just // print out an error message. func (t *Trie) MustGet(key []byte) []byte { From 64927513554d418e54734c96df6d588099992596 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:12:16 +0200 Subject: [PATCH 071/119] cmd/keeper: disable GC for zkvm execution (#32638) ZKVMs are constrained environments that liberally allocate memory and never release it. In this context, using the GC is only going to cause issues down the road, and slow things down in any case. --- cmd/keeper/main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/keeper/main.go b/cmd/keeper/main.go index cfb06f0da0..9b459f6f36 100644 --- a/cmd/keeper/main.go +++ b/cmd/keeper/main.go @@ -19,6 +19,7 @@ package main import ( "fmt" "os" + "runtime/debug" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/stateless" @@ -35,6 +36,10 @@ type Payload struct { Witness *stateless.Witness } +func init() { + debug.SetGCPercent(-1) // Disable garbage collection +} + func main() { input := getInput() var payload Payload From 339cae81d8992dfc6cf15c856b565f562fb46af4 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Thu, 18 Sep 2025 06:28:03 +0200 Subject: [PATCH 072/119] core: fix fork readiness log (#32623) When I implemented in #31340 I didn't expect multiple forks to be configured at once, but this is exactly how BPOs are defined. This updates the method to determine the next scheduled fork rather than the last fork. --- core/blockchain.go | 13 +++++-------- params/forks/forks.go | 5 +++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 3466923648..30f3da3004 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" "io" - "math" "math/big" "runtime" "slices" @@ -2663,13 +2662,11 @@ func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err er // logForkReadiness will write a log when a future fork is scheduled, but not // active. This is useful so operators know their client is ready for the fork. func (bc *BlockChain) logForkReadiness(block *types.Block) { - config := bc.Config() - current, last := config.LatestFork(block.Time()), config.LatestFork(math.MaxUint64) + current := bc.Config().LatestFork(block.Time()) - // Short circuit if the timestamp of the last fork is undefined, - // or if the network has already passed the last configured fork. - t := config.Timestamp(last) - if t == nil || current >= last { + // Short circuit if the timestamp of the last fork is undefined. + t := bc.Config().Timestamp(current + 1) + if t == nil { return } at := time.Unix(int64(*t), 0) @@ -2679,7 +2676,7 @@ func (bc *BlockChain) logForkReadiness(block *types.Block) { // - Enough time has passed since last alert now := time.Now() if now.Before(at) && now.After(bc.lastForkReadyAlert.Add(forkReadyInterval)) { - log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822), + log.Info("Ready for fork activation", "fork", current+1, "date", at.Format(time.RFC822), "remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix()) bc.lastForkReadyAlert = time.Now() } diff --git a/params/forks/forks.go b/params/forks/forks.go index aab0a54ab7..adb65c8624 100644 --- a/params/forks/forks.go +++ b/params/forks/forks.go @@ -77,4 +77,9 @@ var forkToString = map[Fork]string{ Cancun: "Cancun", Prague: "Prague", Osaka: "Osaka", + BPO1: "BPO1", + BPO2: "BPO2", + BPO3: "BPO3", + BPO4: "BPO4", + BPO5: "BPO5", } From 8a171dce1fb0e2bcaadf44a2c12922ce1d6a881d Mon Sep 17 00:00:00 2001 From: GarmashAlex Date: Thu, 18 Sep 2025 09:46:51 +0300 Subject: [PATCH 073/119] core/rawdb: report truncateErr in concurrent truncate failure (#32651) --- core/rawdb/freezer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go index b8a3d4a6d2..fab3319a2a 100644 --- a/core/rawdb/freezer_test.go +++ b/core/rawdb/freezer_test.go @@ -239,7 +239,7 @@ func TestFreezerConcurrentModifyTruncate(t *testing.T) { // fails, otherwise it succeeds. In either case, the freezer should be positioned // at 10 after both operations are done. if truncateErr != nil { - t.Fatal("concurrent truncate failed:", err) + t.Fatal("concurrent truncate failed:", truncateErr) } if !(errors.Is(modifyErr, nil) || errors.Is(modifyErr, errOutOrderInsertion)) { t.Fatal("wrong error from concurrent modify:", modifyErr) From ab95477a65387d8eb52ac985d30f594da65b5cfe Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 18 Sep 2025 22:58:40 +0200 Subject: [PATCH 074/119] build: update to execution-spec-tests v5.0.0 (#32592) https://github.com/ethereum/execution-spec-tests/releases/tag/v5.0.0 As of this release, execution-spec-tests also contains all state tests that were previously in ethereum/tests. We can probably remove the tests submodule now. However, this would mean we are missing the pre-cancun tests. Still need to figure out how to resolve this. --------- Co-authored-by: MariusVanDerWijden --- build/checksums.txt | 6 +- build/ci.go | 2 +- core/genesis.go | 5 + params/config.go | 24 ++++ tests/block_test.go | 5 +- tests/init.go | 256 ++++++++++++++++++++++++++++++++++++++ tests/transaction_test.go | 2 +- 7 files changed, 293 insertions(+), 7 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index 6e65fa47fb..ca937d115c 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -1,9 +1,9 @@ # This file contains sha256 checksums of optional build dependencies. -# version:spec-tests fusaka-devnet-3%40v1.0.0 +# version:spec-tests v5.0.0 # https://github.com/ethereum/execution-spec-tests/releases -# https://github.com/ethereum/execution-spec-tests/releases/download/fusaka-devnet-3%40v1.0.0 -576261e1280e5300c458aa9b05eccb2fec5ff80a0005940dc52fa03fdd907249 fixtures_fusaka-devnet-3.tar.gz +# https://github.com/ethereum/execution-spec-tests/releases/download/v5.0.0 +a5ed96800ca1af0d86fe2ee894861c24eea079bfb83b924f565bb86ba70021d5 fixtures_develop.tar.gz # version:golang 1.25.1 # https://go.dev/dl/ diff --git a/build/ci.go b/build/ci.go index 6a9848876d..e145cc1cb5 100644 --- a/build/ci.go +++ b/build/ci.go @@ -333,7 +333,7 @@ func doTest(cmdline []string) { // downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures. func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string { ext := ".tar.gz" - base := "fixtures_fusaka-devnet-3" + base := "fixtures_develop" archivePath := filepath.Join(cachedir, base+ext) if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil { log.Fatal(err) diff --git a/core/genesis.go b/core/genesis.go index 2673334e9e..13d4addd7e 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -514,6 +514,11 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { if head.BlobGasUsed == nil { head.BlobGasUsed = new(uint64) } + } else { + if g.ExcessBlobGas != nil { + log.Warn("Invalid genesis, unexpected ExcessBlobGas set before Cancun, allowing it for testing purposes") + head.ExcessBlobGas = g.ExcessBlobGas + } } if conf.IsPrague(num, g.Timestamp) { head.RequestsHash = &types.EmptyRequestsHash diff --git a/params/config.go b/params/config.go index b1297144c3..f8fc35454c 100644 --- a/params/config.go +++ b/params/config.go @@ -359,6 +359,30 @@ var ( Max: 9, UpdateFraction: 5007716, } + // DefaultBPO1BlobConfig is the default blob configuration for the Osaka fork. + DefaultBPO1BlobConfig = &BlobConfig{ + Target: 9, + Max: 14, + UpdateFraction: 8832827, + } + // DefaultBPO1BlobConfig is the default blob configuration for the Osaka fork. + DefaultBPO2BlobConfig = &BlobConfig{ + Target: 14, + Max: 21, + UpdateFraction: 13739630, + } + // DefaultBPO1BlobConfig is the default blob configuration for the Osaka fork. + DefaultBPO3BlobConfig = &BlobConfig{ + Target: 21, + Max: 32, + UpdateFraction: 20609697, + } + // DefaultBPO1BlobConfig is the default blob configuration for the Osaka fork. + DefaultBPO4BlobConfig = &BlobConfig{ + Target: 14, + Max: 21, + UpdateFraction: 13739630, + } // DefaultBlobSchedule is the latest configured blob schedule for Ethereum mainnet. DefaultBlobSchedule = &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, diff --git a/tests/block_test.go b/tests/block_test.go index 91d9f2e653..c718b304b6 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -81,8 +81,9 @@ func TestExecutionSpecBlocktests(t *testing.T) { } bt := new(testMatcher) - bt.skipLoad(".*prague/eip7251_consolidations/contract_deployment/system_contract_deployment.json") - bt.skipLoad(".*prague/eip7002_el_triggerable_withdrawals/contract_deployment/system_contract_deployment.json") + // These tests require us to handle scenarios where a system contract is not deployed at a fork + bt.skipLoad(".*prague/eip7251_consolidations/test_system_contract_deployment.json") + bt.skipLoad(".*prague/eip7002_el_triggerable_withdrawals/test_system_contract_deployment.json") bt.walk(t, executionSpecBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) { execBlockTest(t, bt, test) diff --git a/tests/init.go b/tests/init.go index a8bc424fa2..71072ac275 100644 --- a/tests/init.go +++ b/tests/init.go @@ -464,6 +464,262 @@ var Forks = map[string]*params.ChainConfig{ Osaka: params.DefaultOsakaBlobConfig, }, }, + "BPO1": { + ChainID: big.NewInt(1), + 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), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + CancunTime: u64(0), + PragueTime: u64(0), + OsakaTime: u64(0), + BPO1Time: u64(0), + DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + BPO1: params.DefaultBPO1BlobConfig, + }, + }, + "OsakaToBPO1AtTime15k": { + ChainID: big.NewInt(1), + 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), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + CancunTime: u64(0), + PragueTime: u64(0), + OsakaTime: u64(0), + BPO1Time: u64(15_000), + DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + BPO1: params.DefaultBPO1BlobConfig, + }, + }, + "BPO2": { + ChainID: big.NewInt(1), + 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), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + CancunTime: u64(0), + PragueTime: u64(0), + OsakaTime: u64(0), + BPO1Time: u64(0), + BPO2Time: u64(0), + DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + BPO1: params.DefaultBPO1BlobConfig, + BPO2: params.DefaultBPO2BlobConfig, + }, + }, + "BPO1ToBPO2AtTime15k": { + ChainID: big.NewInt(1), + 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), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + CancunTime: u64(0), + PragueTime: u64(0), + OsakaTime: u64(0), + BPO1Time: u64(0), + BPO2Time: u64(15_000), + DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + BPO1: params.DefaultBPO1BlobConfig, + BPO2: params.DefaultBPO2BlobConfig, + }, + }, + "BPO3": { + ChainID: big.NewInt(1), + 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), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + CancunTime: u64(0), + PragueTime: u64(0), + OsakaTime: u64(0), + BPO1Time: u64(0), + BPO2Time: u64(0), + BPO3Time: u64(0), + DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + BPO1: params.DefaultBPO1BlobConfig, + BPO2: params.DefaultBPO2BlobConfig, + BPO3: params.DefaultBPO3BlobConfig, + }, + }, + "BPO2ToBPO3AtTime15k": { + ChainID: big.NewInt(1), + 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), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + CancunTime: u64(0), + PragueTime: u64(0), + OsakaTime: u64(0), + BPO1Time: u64(0), + BPO2Time: u64(0), + BPO3Time: u64(15_000), + DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + BPO1: params.DefaultBPO1BlobConfig, + BPO2: params.DefaultBPO2BlobConfig, + BPO3: params.DefaultBPO3BlobConfig, + }, + }, + "BPO4": { + ChainID: big.NewInt(1), + 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), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + CancunTime: u64(0), + PragueTime: u64(0), + OsakaTime: u64(0), + BPO1Time: u64(0), + BPO2Time: u64(0), + BPO3Time: u64(0), + BPO4Time: u64(0), + DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + BPO1: params.DefaultBPO1BlobConfig, + BPO2: params.DefaultBPO2BlobConfig, + BPO3: params.DefaultBPO3BlobConfig, + BPO4: params.DefaultBPO4BlobConfig, + }, + }, + "BPO3ToBPO4AtTime15k": { + ChainID: big.NewInt(1), + 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), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + CancunTime: u64(0), + PragueTime: u64(0), + OsakaTime: u64(0), + BPO1Time: u64(0), + BPO2Time: u64(0), + BPO3Time: u64(0), + BPO4Time: u64(15_000), + DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + BPO1: params.DefaultBPO1BlobConfig, + BPO2: params.DefaultBPO2BlobConfig, + BPO3: params.DefaultBPO3BlobConfig, + BPO4: params.DefaultBPO4BlobConfig, + }, + }, } // AvailableForks returns the set of defined fork names diff --git a/tests/transaction_test.go b/tests/transaction_test.go index 6260df0f3f..73ee3aa16a 100644 --- a/tests/transaction_test.go +++ b/tests/transaction_test.go @@ -70,7 +70,7 @@ func TestExecutionSpecTransaction(t *testing.T) { st := new(testMatcher) // Emptiness of authorization list is only validated during the tx precheck - st.skipLoad("^prague/eip7702_set_code_tx/invalid_tx/empty_authorization_list.json") + st.skipLoad("^prague/eip7702_set_code_tx/test_empty_authorization_list.json") st.walk(t, executionSpecTransactionTestDir, func(t *testing.T, name string, test *TransactionTest) { if err := st.checkFailure(t, test.Run()); err != nil { From 2a82964727530414f91b850ab956a9fa5bbefcba Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 19 Sep 2025 06:16:01 +0800 Subject: [PATCH 075/119] eth/catalyst, beacon/engine: enable BPO and Osaka on stateless APIs (#32636) Addresses https://github.com/ethereum/go-ethereum/issues/32630 This pull request enables the stateless engine APIs for Osaka and the following BPOs. Apart from that, a few more descriptions have been added in the engine APIs, making it easier to follow the spec change. --- beacon/engine/gen_epe.go | 4 ++-- beacon/engine/types.go | 42 +++++++++++++++++++++++++++++++++------- eth/catalyst/api.go | 21 ++++++++++++++++---- eth/catalyst/witness.go | 12 ++++++------ 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/beacon/engine/gen_epe.go b/beacon/engine/gen_epe.go index deada06166..cf7bd9ee3f 100644 --- a/beacon/engine/gen_epe.go +++ b/beacon/engine/gen_epe.go @@ -17,7 +17,7 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) { type ExecutionPayloadEnvelope struct { ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` - BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` + BlobsBundle *BlobsBundle `json:"blobsBundle"` Requests []hexutil.Bytes `json:"executionRequests"` Override bool `json:"shouldOverrideBuilder"` Witness *hexutil.Bytes `json:"witness,omitempty"` @@ -42,7 +42,7 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error { type ExecutionPayloadEnvelope struct { ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` - BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` + BlobsBundle *BlobsBundle `json:"blobsBundle"` Requests []hexutil.Bytes `json:"executionRequests"` Override *bool `json:"shouldOverrideBuilder"` Witness *hexutil.Bytes `json:"witness,omitempty"` diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 76bfd22a23..ddb276ab09 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -33,8 +33,22 @@ import ( type PayloadVersion byte var ( + // PayloadV1 is the identifier of ExecutionPayloadV1 introduced in paris fork. + // https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#executionpayloadv1 PayloadV1 PayloadVersion = 0x1 + + // PayloadV2 is the identifier of ExecutionPayloadV2 introduced in shanghai fork. + // + // https://github.com/ethereum/execution-apis/blob/main/src/engine/shanghai.md#executionpayloadv2 + // ExecutionPayloadV2 has the syntax of ExecutionPayloadV1 and appends a + // single field: withdrawals. PayloadV2 PayloadVersion = 0x2 + + // PayloadV3 is the identifier of ExecutionPayloadV3 introduced in cancun fork. + // + // https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#executionpayloadv3 + // ExecutionPayloadV3 has the syntax of ExecutionPayloadV2 and appends the new + // fields: blobGasUsed and excessBlobGas. PayloadV3 PayloadVersion = 0x3 ) @@ -106,13 +120,18 @@ type StatelessPayloadStatusV1 struct { type ExecutionPayloadEnvelope struct { ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` BlockValue *big.Int `json:"blockValue" gencodec:"required"` - BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` + BlobsBundle *BlobsBundle `json:"blobsBundle"` Requests [][]byte `json:"executionRequests"` Override bool `json:"shouldOverrideBuilder"` Witness *hexutil.Bytes `json:"witness,omitempty"` } -type BlobsBundleV1 struct { +// BlobsBundle includes the marshalled sidecar data. Note this structure is +// shared by BlobsBundleV1 and BlobsBundleV2 for the sake of simplicity. +// +// - BlobsBundleV1: proofs contain exactly len(blobs) kzg proofs. +// - BlobsBundleV2: proofs contain exactly CELLS_PER_EXT_BLOB * len(blobs) cell proofs. +type BlobsBundle struct { Commitments []hexutil.Bytes `json:"commitments"` Proofs []hexutil.Bytes `json:"proofs"` Blobs []hexutil.Bytes `json:"blobs"` @@ -125,7 +144,7 @@ type BlobAndProofV1 struct { type BlobAndProofV2 struct { Blob hexutil.Bytes `json:"blob"` - CellProofs []hexutil.Bytes `json:"proofs"` + CellProofs []hexutil.Bytes `json:"proofs"` // proofs MUST contain exactly CELLS_PER_EXT_BLOB cell proofs. } // JSON type overrides for ExecutionPayloadEnvelope. @@ -327,18 +346,27 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types. } // Add blobs. - bundle := BlobsBundleV1{ + bundle := BlobsBundle{ Commitments: make([]hexutil.Bytes, 0), Blobs: make([]hexutil.Bytes, 0), Proofs: make([]hexutil.Bytes, 0), } for _, sidecar := range sidecars { for j := range sidecar.Blobs { - bundle.Blobs = append(bundle.Blobs, hexutil.Bytes(sidecar.Blobs[j][:])) - bundle.Commitments = append(bundle.Commitments, hexutil.Bytes(sidecar.Commitments[j][:])) + bundle.Blobs = append(bundle.Blobs, sidecar.Blobs[j][:]) + bundle.Commitments = append(bundle.Commitments, sidecar.Commitments[j][:]) } + // - Before the Osaka fork, only version-0 blob transactions should be packed, + // with the proof length equal to len(blobs). + // + // - After the Osaka fork, only version-1 blob transactions should be packed, + // with the proof length equal to CELLS_PER_EXT_BLOB * len(blobs). + // + // Ideally, length validation should be performed based on the bundle version. + // In practice, this is unnecessary because blob transaction filtering is + // already done during payload construction. for _, proof := range sidecar.Proofs { - bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(proof[:])) + bundle.Proofs = append(bundle.Proofs, proof[:]) } } diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index b222470228..17299d9296 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -418,13 +418,21 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.Execu // GetPayloadV2 returns a cached payload by id. func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { + // executionPayload: ExecutionPayloadV1 | ExecutionPayloadV2 where: + // + // - ExecutionPayloadV1 MUST be returned if the payload timestamp is lower + // than the Shanghai timestamp + // + // - ExecutionPayloadV2 MUST be returned if the payload timestamp is greater + // or equal to the Shanghai timestamp if !payloadID.Is(engine.PayloadV1, engine.PayloadV2) { return nil, engine.UnsupportedFork } return api.getPayload(payloadID, false) } -// GetPayloadV3 returns a cached payload by id. +// GetPayloadV3 returns a cached payload by id. This endpoint should only +// be used for the Cancun fork. func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { if !payloadID.Is(engine.PayloadV3) { return nil, engine.UnsupportedFork @@ -432,7 +440,8 @@ func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.Execu return api.getPayload(payloadID, false) } -// GetPayloadV4 returns a cached payload by id. +// GetPayloadV4 returns a cached payload by id. This endpoint should only +// be used for the Prague fork. func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { if !payloadID.Is(engine.PayloadV3) { return nil, engine.UnsupportedFork @@ -440,7 +449,11 @@ func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.Execu return api.getPayload(payloadID, false) } -// GetPayloadV5 returns a cached payload by id. +// GetPayloadV5 returns a cached payload by id. This endpoint should only +// be used after the Osaka fork. +// +// This method follows the same specification as engine_getPayloadV4 with +// changes of returning BlobsBundleV2 with BlobSidecar version 1. func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { if !payloadID.Is(engine.PayloadV3) { return nil, engine.UnsupportedFork @@ -637,7 +650,7 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas case executionRequests == nil: return invalidStatus, paramsErr("nil executionRequests post-prague") case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka, forks.BPO1, forks.BPO2, forks.BPO3, forks.BPO4, forks.BPO5): - return invalidStatus, unsupportedForkErr("newPayloadV4 must only be called for Prague payloads") + return invalidStatus, unsupportedForkErr("newPayloadV4 must only be called for prague/osaka payloads") } requests := convertRequests(executionRequests) if err := validateRequests(requests); err != nil { diff --git a/eth/catalyst/witness.go b/eth/catalyst/witness.go index 703f1b0881..0df612a695 100644 --- a/eth/catalyst/witness.go +++ b/eth/catalyst/witness.go @@ -73,8 +73,8 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.Forkchoice return engine.STATUS_INVALID, attributesErr("missing withdrawals") case params.BeaconRoot == nil: return engine.STATUS_INVALID, attributesErr("missing beacon root") - case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague): - return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads") + case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague, forks.Osaka, forks.BPO1, forks.BPO2, forks.BPO3, forks.BPO4, forks.BPO5): + return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun/prague/osaka payloads") } } // TODO(matt): the spec requires that fcu is applied when called on a valid @@ -151,8 +151,8 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, v return invalidStatus, paramsErr("nil beaconRoot post-cancun") case executionRequests == nil: return invalidStatus, paramsErr("nil executionRequests post-prague") - case !api.checkFork(params.Timestamp, forks.Prague): - return invalidStatus, unsupportedForkErr("newPayloadV4 must only be called for prague payloads") + case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka, forks.BPO1, forks.BPO2, forks.BPO3, forks.BPO4, forks.BPO5): + return invalidStatus, unsupportedForkErr("newPayloadV4 must only be called for prague/osaka payloads") } requests := convertRequests(executionRequests) if err := validateRequests(requests); err != nil { @@ -228,8 +228,8 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun") case executionRequests == nil: return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague") - case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka): - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV4 must only be called for prague payloads") + case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka, forks.BPO1, forks.BPO2, forks.BPO3, forks.BPO4, forks.BPO5): + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV4 must only be called for prague/osaka payloads") } requests := convertRequests(executionRequests) if err := validateRequests(requests); err != nil { From dce511c1e59f0af47482399d40e4c1211573c4cf Mon Sep 17 00:00:00 2001 From: Long Vu Date: Fri, 19 Sep 2025 05:53:40 +0700 Subject: [PATCH 076/119] eth/filters, cmd: add config of eth_getLogs address limit (#32327) Add cli configurable limit for the number of addresses allowed in eth_getLogs filter criteria: https://github.com/ethereum/go-ethereum/issues/32264 Key changes: - Added --rpc.getlogmaxaddrs CLI flag (default: 1000) to configure the maximum number of addresses - Updated ethconfig.Config with FilterMaxAddresses field for configuration management - Modified filter system to use the configurable limit instead of the hardcoded maxAddresses constant - Enhanced test coverage with new test cases for address limit validation - Removed hardcoded validation from JSON unmarshaling, moving it to runtime validation Please notice that I remove the check at FilterCriteria UnmarshalJSON because the runtime config can not pass into this validation. Please help review this change! --------- Co-authored-by: zsfelfoldi Co-authored-by: rjl493456442 --- cmd/geth/main.go | 1 + cmd/utils/flags.go | 12 +++- eth/ethconfig/config.go | 5 ++ eth/ethconfig/gen_config.go | 6 ++ eth/filters/api.go | 38 ++++++------ eth/filters/api_test.go | 12 ---- eth/filters/filter_system.go | 16 +++-- eth/filters/filter_system_test.go | 97 +++++++++++++++++++++++++++++-- 8 files changed, 148 insertions(+), 39 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index b661228681..f380c9f2d4 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -182,6 +182,7 @@ var ( utils.RPCGlobalGasCapFlag, utils.RPCGlobalEVMTimeoutFlag, utils.RPCGlobalTxFeeCapFlag, + utils.RPCGlobalLogQueryLimit, utils.AllowUnprotectedTxs, utils.BatchRequestLimit, utils.BatchResponseMaxSize, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 83d1c8bda5..325f8250f8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -600,6 +600,12 @@ var ( Value: ethconfig.Defaults.RPCTxFeeCap, Category: flags.APICategory, } + RPCGlobalLogQueryLimit = &cli.IntFlag{ + Name: "rpc.logquerylimit", + Usage: "Maximum number of alternative addresses or topics allowed per search position in eth_getLogs filter criteria (0 = no cap)", + Value: ethconfig.Defaults.LogQueryLimit, + Category: flags.APICategory, + } // Authenticated RPC HTTP settings AuthListenFlag = &cli.StringFlag{ Name: "authrpc.addr", @@ -1699,6 +1705,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(CacheLogSizeFlag.Name) { cfg.FilterLogCacheSize = ctx.Int(CacheLogSizeFlag.Name) } + if ctx.IsSet(RPCGlobalLogQueryLimit.Name) { + cfg.LogQueryLimit = ctx.Int(RPCGlobalLogQueryLimit.Name) + } if !ctx.Bool(SnapshotFlag.Name) || cfg.SnapshotCache == 0 { // If snap-sync is requested, this flag is also required if cfg.SyncMode == ethconfig.SnapSync { @@ -2017,7 +2026,8 @@ func RegisterGraphQLService(stack *node.Node, backend ethapi.Backend, filterSyst // RegisterFilterAPI adds the eth log filtering RPC API to the node. func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconfig.Config) *filters.FilterSystem { filterSystem := filters.NewFilterSystem(backend, filters.Config{ - LogCacheSize: ethcfg.FilterLogCacheSize, + LogCacheSize: ethcfg.FilterLogCacheSize, + LogQueryLimit: ethcfg.LogQueryLimit, }) stack.RegisterAPIs([]rpc.API{{ Namespace: "eth", diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index ba0a7762c7..4fe24c0fe0 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -62,6 +62,7 @@ var Defaults = Config{ TrieTimeout: 60 * time.Minute, SnapshotCache: 102, FilterLogCacheSize: 32, + LogQueryLimit: 1000, Miner: miner.DefaultConfig, TxPool: legacypool.DefaultConfig, BlobPool: blobpool.DefaultConfig, @@ -131,6 +132,10 @@ type Config struct { // This is the number of blocks for which logs will be cached in the filter system. FilterLogCacheSize int + // This is the maximum number of addresses or topics allowed in filter criteria + // for eth_getLogs. + LogQueryLimit int + // Mining options Miner miner.Config diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index b54ba14d68..50eb5c4161 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -44,6 +44,7 @@ func (c Config) MarshalTOML() (interface{}, error) { SnapshotCache int Preimages bool FilterLogCacheSize int + LogQueryLimit int Miner miner.Config TxPool legacypool.Config BlobPool blobpool.Config @@ -88,6 +89,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.SnapshotCache = c.SnapshotCache enc.Preimages = c.Preimages enc.FilterLogCacheSize = c.FilterLogCacheSize + enc.LogQueryLimit = c.LogQueryLimit enc.Miner = c.Miner enc.TxPool = c.TxPool enc.BlobPool = c.BlobPool @@ -136,6 +138,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { SnapshotCache *int Preimages *bool FilterLogCacheSize *int + LogQueryLimit *int Miner *miner.Config TxPool *legacypool.Config BlobPool *blobpool.Config @@ -237,6 +240,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.FilterLogCacheSize != nil { c.FilterLogCacheSize = *dec.FilterLogCacheSize } + if dec.LogQueryLimit != nil { + c.LogQueryLimit = *dec.LogQueryLimit + } if dec.Miner != nil { c.Miner = *dec.Miner } diff --git a/eth/filters/api.go b/eth/filters/api.go index c929810a12..d678c40389 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -42,12 +42,10 @@ var ( errBlockHashWithRange = errors.New("can't specify fromBlock/toBlock with blockHash") errPendingLogsUnsupported = errors.New("pending logs are not supported") errExceedMaxTopics = errors.New("exceed max topics") - errExceedMaxAddresses = errors.New("exceed max addresses") + errExceedLogQueryLimit = errors.New("exceed max addresses or topics per search position") ) const ( - // The maximum number of addresses allowed in a filter criteria - maxAddresses = 1000 // The maximum number of topic criteria allowed, vm.LOG4 - vm.LOG0 maxTopics = 4 // The maximum number of allowed topics within a topic criteria @@ -70,20 +68,22 @@ type filter struct { // FilterAPI offers support to create and manage filters. This will allow external clients to retrieve various // information related to the Ethereum protocol such as blocks, transactions and logs. type FilterAPI struct { - sys *FilterSystem - events *EventSystem - filtersMu sync.Mutex - filters map[rpc.ID]*filter - timeout time.Duration + sys *FilterSystem + events *EventSystem + filtersMu sync.Mutex + filters map[rpc.ID]*filter + timeout time.Duration + logQueryLimit int } // NewFilterAPI returns a new FilterAPI instance. func NewFilterAPI(system *FilterSystem) *FilterAPI { api := &FilterAPI{ - sys: system, - events: NewEventSystem(system), - filters: make(map[rpc.ID]*filter), - timeout: system.cfg.Timeout, + sys: system, + events: NewEventSystem(system), + filters: make(map[rpc.ID]*filter), + timeout: system.cfg.Timeout, + logQueryLimit: system.cfg.LogQueryLimit, } go api.timeoutLoop(system.cfg.Timeout) @@ -347,8 +347,15 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type if len(crit.Topics) > maxTopics { return nil, errExceedMaxTopics } - if len(crit.Addresses) > maxAddresses { - return nil, errExceedMaxAddresses + if api.logQueryLimit != 0 { + if len(crit.Addresses) > api.logQueryLimit { + return nil, errExceedLogQueryLimit + } + for _, topics := range crit.Topics { + if len(topics) > api.logQueryLimit { + return nil, errExceedLogQueryLimit + } + } } var filter *Filter @@ -545,9 +552,6 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error { // raw.Address can contain a single address or an array of addresses switch rawAddr := raw.Addresses.(type) { case []interface{}: - if len(rawAddr) > maxAddresses { - return errExceedMaxAddresses - } for i, addr := range rawAddr { if strAddr, ok := addr.(string); ok { addr, err := decodeAddress(strAddr) diff --git a/eth/filters/api_test.go b/eth/filters/api_test.go index 2eb3ee97b3..822bc826f6 100644 --- a/eth/filters/api_test.go +++ b/eth/filters/api_test.go @@ -19,7 +19,6 @@ package filters import ( "encoding/json" "fmt" - "strings" "testing" "github.com/ethereum/go-ethereum/common" @@ -183,15 +182,4 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) { if len(test7.Topics[2]) != 0 { t.Fatalf("expected 0 topics, got %d topics", len(test7.Topics[2])) } - - // multiple address exceeding max - var test8 FilterCriteria - addresses := make([]string, maxAddresses+1) - for i := 0; i < maxAddresses+1; i++ { - addresses[i] = fmt.Sprintf(`"%s"`, common.HexToAddress(fmt.Sprintf("0x%x", i)).Hex()) - } - vector = fmt.Sprintf(`{"address": [%s]}`, strings.Join(addresses, ", ")) - if err := json.Unmarshal([]byte(vector), &test8); err != errExceedMaxAddresses { - t.Fatal("expected errExceedMaxAddresses, got", err) - } } diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 751cd417e8..ecf1c870c1 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -41,8 +41,9 @@ import ( // Config represents the configuration of the filter system. type Config struct { - LogCacheSize int // maximum number of cached blocks (default: 32) - Timeout time.Duration // how long filters stay active (default: 5min) + LogCacheSize int // maximum number of cached blocks (default: 32) + Timeout time.Duration // how long filters stay active (default: 5min) + LogQueryLimit int // maximum number of addresses allowed in filter criteria (default: 1000) } func (cfg Config) withDefaults() Config { @@ -291,8 +292,15 @@ func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*typ if len(crit.Topics) > maxTopics { return nil, errExceedMaxTopics } - if len(crit.Addresses) > maxAddresses { - return nil, errExceedMaxAddresses + if es.sys.cfg.LogQueryLimit != 0 { + if len(crit.Addresses) > es.sys.cfg.LogQueryLimit { + return nil, errExceedLogQueryLimit + } + for _, topics := range crit.Topics { + if len(topics) > es.sys.cfg.LogQueryLimit { + return nil, errExceedLogQueryLimit + } + } } var from, to rpc.BlockNumber if crit.FromBlock == nil { diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 013c1ae527..0048e74995 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/triedb" ) type testBackend struct { @@ -424,7 +425,7 @@ func TestInvalidLogFilterCreation(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - _, sys = newTestFilterSystem(db, Config{}) + _, sys = newTestFilterSystem(db, Config{LogQueryLimit: 1000}) api = NewFilterAPI(sys) ) @@ -435,7 +436,7 @@ func TestInvalidLogFilterCreation(t *testing.T) { 1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, 2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, 3: {Topics: [][]common.Hash{{}, {}, {}, {}, {}}}, - 4: {Addresses: make([]common.Address, maxAddresses+1)}, + 4: {Addresses: make([]common.Address, api.logQueryLimit+1)}, } for i, test := range testCases { @@ -455,7 +456,7 @@ func TestInvalidGetLogsRequest(t *testing.T) { BaseFee: big.NewInt(params.InitialBaseFee), } db, blocks, _ = core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 10, func(i int, gen *core.BlockGen) {}) - _, sys = newTestFilterSystem(db, Config{}) + _, sys = newTestFilterSystem(db, Config{LogQueryLimit: 10}) api = NewFilterAPI(sys) blockHash = blocks[0].Hash() unknownBlockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") @@ -500,8 +501,8 @@ func TestInvalidGetLogsRequest(t *testing.T) { err: errExceedMaxTopics, }, { - f: FilterCriteria{BlockHash: &blockHash, Addresses: make([]common.Address, maxAddresses+1)}, - err: errExceedMaxAddresses, + f: FilterCriteria{BlockHash: &blockHash, Addresses: make([]common.Address, api.logQueryLimit+1)}, + err: errExceedLogQueryLimit, }, } @@ -528,6 +529,92 @@ func TestInvalidGetRangeLogsRequest(t *testing.T) { } } +// TestExceedLogQueryLimit tests getLogs with too many addresses or topics +func TestExceedLogQueryLimit(t *testing.T) { + t.Parallel() + + // Test with custom config (LogQueryLimit = 5 for easier testing) + var ( + db = rawdb.NewMemoryDatabase() + backend, sys = newTestFilterSystem(db, Config{LogQueryLimit: 5}) + api = NewFilterAPI(sys) + gspec = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{}, + BaseFee: big.NewInt(params.InitialBaseFee), + } + ) + + _, err := gspec.Commit(db, triedb.NewDatabase(db, nil)) + if err != nil { + t.Fatal(err) + } + chain, _ := core.GenerateChain(gspec.Config, gspec.ToBlock(), ethash.NewFaker(), db, 1000, func(i int, gen *core.BlockGen) {}) + + options := core.DefaultConfig().WithStateScheme(rawdb.HashScheme) + options.TxLookupLimit = 0 // index all txs + bc, err := core.NewBlockChain(db, gspec, ethash.NewFaker(), options) + if err != nil { + t.Fatal(err) + } + _, err = bc.InsertChain(chain[:600]) + if err != nil { + t.Fatal(err) + } + + backend.startFilterMaps(200, false, filtermaps.RangeTestParams) + defer backend.stopFilterMaps() + + addresses := make([]common.Address, 6) + for i := range addresses { + addresses[i] = common.HexToAddress("0x1234567890123456789012345678901234567890") + } + + topics := make([]common.Hash, 6) + for i := range topics { + topics[i] = common.HexToHash("0x123456789012345678901234567890123456789001234567890012345678901234") + } + + // Test that 5 addresses do not result in error + // Add FromBlock and ToBlock to make it similar to other invalid tests + if _, err := api.GetLogs(context.Background(), FilterCriteria{ + FromBlock: big.NewInt(0), + ToBlock: big.NewInt(100), + Addresses: addresses[:5], + }); err != nil { + t.Errorf("Expected GetLogs with 5 addresses to return with no error, got: %v", err) + } + + // Test that 6 addresses fails with correct error + if _, err := api.GetLogs(context.Background(), FilterCriteria{ + FromBlock: big.NewInt(0), + ToBlock: big.NewInt(100), + Addresses: addresses, + }); err != errExceedLogQueryLimit { + t.Errorf("Expected GetLogs with 6 addresses to return errExceedLogQueryLimit, got: %v", err) + } + + // Test that 5 topics at one position do not result in error + if _, err := api.GetLogs(context.Background(), FilterCriteria{ + FromBlock: big.NewInt(0), + ToBlock: big.NewInt(100), + Addresses: addresses[:1], + Topics: [][]common.Hash{topics[:5]}, + }); err != nil { + t.Errorf("Expected GetLogs with 5 topics at one position to return with no error, got: %v", err) + } + + // Test that 6 topics at one position fails with correct error + if _, err := api.GetLogs(context.Background(), FilterCriteria{ + FromBlock: big.NewInt(0), + ToBlock: big.NewInt(100), + Addresses: addresses[:1], + Topics: [][]common.Hash{topics}, + }); err != errExceedLogQueryLimit { + t.Errorf("Expected GetLogs with 6 topics at one position to return errExceedLogQueryLimit, got: %v", err) + } +} + // TestLogFilter tests whether log filters match the correct logs that are posted to the event feed. func TestLogFilter(t *testing.T) { t.Parallel() From b9e2eb5944bbb63cabdcb541a3b99fc129b821c4 Mon Sep 17 00:00:00 2001 From: Klimov Sergei Date: Fri, 19 Sep 2025 07:30:00 +0800 Subject: [PATCH 077/119] beacon/config: fix LoadForks with non-string values (#32609) Fixes a crash when loading the beacon chain config if new fields like `BLOB_SCHEDULE: []` are present. Previously, the config loader assumed all values were strings, causing errors such as: ``` Fatal: Could not load beacon chain config '/network-configs/config.yaml': failed to parse beacon chain config file: yaml: unmarshal errors: line 242: cannot unmarshal !!seq into string ``` This PR updates the parsing logic to handle non-string values correctly and adds explicit validation for fork fields. --- beacon/params/config.go | 39 +++++++++++++++++++++++++----------- beacon/params/config_test.go | 34 +++++++++++++++++++++++++++++++ cmd/utils/flags.go | 12 +++++++---- 3 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 beacon/params/config_test.go diff --git a/beacon/params/config.go b/beacon/params/config.go index 2f6ba082c5..492ee53308 100644 --- a/beacon/params/config.go +++ b/beacon/params/config.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "fmt" "math" + "math/big" "os" "slices" "sort" @@ -90,12 +91,8 @@ func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainC // LoadForks parses the beacon chain configuration file (config.yaml) and extracts // the list of forks. -func (c *ChainConfig) LoadForks(path string) error { - file, err := os.ReadFile(path) - if err != nil { - return fmt.Errorf("failed to read beacon chain config file: %v", err) - } - config := make(map[string]string) +func (c *ChainConfig) LoadForks(file []byte) error { + config := make(map[string]any) if err := yaml.Unmarshal(file, &config); err != nil { return fmt.Errorf("failed to parse beacon chain config file: %v", err) } @@ -108,18 +105,36 @@ func (c *ChainConfig) LoadForks(path string) error { for key, value := range config { if strings.HasSuffix(key, "_FORK_VERSION") { name := key[:len(key)-len("_FORK_VERSION")] - if v, err := hexutil.Decode(value); err == nil { + switch version := value.(type) { + case int: + versions[name] = new(big.Int).SetUint64(uint64(version)).FillBytes(make([]byte, 4)) + case uint64: + versions[name] = new(big.Int).SetUint64(version).FillBytes(make([]byte, 4)) + case string: + v, err := hexutil.Decode(version) + if err != nil { + return fmt.Errorf("failed to decode hex fork id %q in beacon chain config file: %v", version, err) + } versions[name] = v - } else { - return fmt.Errorf("failed to decode hex fork id %q in beacon chain config file: %v", value, err) + default: + return fmt.Errorf("invalid fork version %q in beacon chain config file", version) } } if strings.HasSuffix(key, "_FORK_EPOCH") { name := key[:len(key)-len("_FORK_EPOCH")] - if v, err := strconv.ParseUint(value, 10, 64); err == nil { + switch epoch := value.(type) { + case int: + epochs[name] = uint64(epoch) + case uint64: + epochs[name] = epoch + case string: + v, err := strconv.ParseUint(epoch, 10, 64) + if err != nil { + return fmt.Errorf("failed to parse epoch number %q in beacon chain config file: %v", epoch, err) + } epochs[name] = v - } else { - return fmt.Errorf("failed to parse epoch number %q in beacon chain config file: %v", value, err) + default: + return fmt.Errorf("invalid fork epoch %q in beacon chain config file", epoch) } } } diff --git a/beacon/params/config_test.go b/beacon/params/config_test.go new file mode 100644 index 0000000000..41e120469b --- /dev/null +++ b/beacon/params/config_test.go @@ -0,0 +1,34 @@ +package params + +import ( + "bytes" + "testing" +) + +func TestChainConfig_LoadForks(t *testing.T) { + const config = ` +GENESIS_FORK_VERSION: 0x00000000 + +ALTAIR_FORK_VERSION: 0x00000001 +ALTAIR_FORK_EPOCH: 1 + +EIP7928_FORK_VERSION: 0xb0000038 +EIP7928_FORK_EPOCH: 18446744073709551615 + +BLOB_SCHEDULE: [] +` + c := &ChainConfig{} + err := c.LoadForks([]byte(config)) + if err != nil { + t.Fatal(err) + } + + for _, fork := range c.Forks { + if fork.Name == "GENESIS" && (fork.Epoch != 0) { + t.Errorf("unexpected genesis fork epoch %d", fork.Epoch) + } + if fork.Name == "ALTAIR" && (fork.Epoch != 1 || !bytes.Equal(fork.Version, []byte{0, 0, 0, 1})) { + t.Errorf("unexpected altair fork epoch %d version %x", fork.Epoch, fork.Version) + } + } +} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 325f8250f8..5e96185dbd 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1931,11 +1931,15 @@ func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig { } else { Fatalf("Could not parse --%s: %v", BeaconGenesisRootFlag.Name, err) } - configFile := ctx.String(BeaconConfigFlag.Name) - if err := config.ChainConfig.LoadForks(configFile); err != nil { - Fatalf("Could not load beacon chain config '%s': %v", configFile, err) + configPath := ctx.String(BeaconConfigFlag.Name) + file, err := os.ReadFile(configPath) + if err != nil { + Fatalf("failed to read beacon chain config file '%s': %v", configPath, err) } - log.Info("Using custom beacon chain config", "file", configFile) + if err := config.ChainConfig.LoadForks(file); err != nil { + Fatalf("Could not load beacon chain config '%s': %v", configPath, err) + } + log.Info("Using custom beacon chain config", "file", configPath) } else { if ctx.IsSet(BeaconGenesisRootFlag.Name) { Fatalf("Genesis root is specified but custom beacon chain config is missing") From 2b3d617e04a45d2aa5bd9e64dc016e68b8200369 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 19 Sep 2025 13:29:17 +0200 Subject: [PATCH 078/119] internal/ethapi: skip tx gas limit check for calls (#32641) This disables the tx gaslimit cap for eth_call and related RPC operations. I don't like how this fix works. Ideally we'd be checking the tx gaslimit somewhere else, like in the block validator, or any other place that considers block transactions. Doing the check in StateTransition means it affects all possible ways of executing a message. The challenge is finding a place for this check that also triggers correctly in tests where it is wanted. So for now, we are just combining this with the EOA sender check for transactions. Both are disabled for call-type messages. --- core/state_transition.go | 22 +++++++++++++--------- eth/tracers/api.go | 2 +- internal/ethapi/api.go | 10 +++++----- internal/ethapi/simulate.go | 2 +- internal/ethapi/transaction_args.go | 4 ++-- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 2cafe4865f..bf5ac07636 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -164,8 +164,12 @@ type Message struct { // or the state prefetching. SkipNonceChecks bool - // When SkipFromEOACheck is true, the message sender is not checked to be an EOA. - SkipFromEOACheck bool + // When set, the message is not treated as a transaction, and certain + // transaction-specific checks are skipped: + // + // - From is not verified to be an EOA + // - GasLimit is not checked against the protocol defined tx gaslimit + SkipTransactionChecks bool } // TransactionToMessage converts a transaction into a Message. @@ -182,7 +186,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In AccessList: tx.AccessList(), SetCodeAuthorizations: tx.SetCodeAuthorizations(), SkipNonceChecks: false, - SkipFromEOACheck: false, + SkipTransactionChecks: false, BlobHashes: tx.BlobHashes(), BlobGasFeeCap: tx.BlobGasFeeCap(), } @@ -320,7 +324,12 @@ func (st *stateTransition) preCheck() error { msg.From.Hex(), stNonce) } } - if !msg.SkipFromEOACheck { + isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time) + if !msg.SkipTransactionChecks { + // Verify tx gas limit does not exceed EIP-7825 cap. + if isOsaka && msg.GasLimit > params.MaxTxGas { + return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit) + } // Make sure the sender is an EOA code := st.state.GetCode(msg.From) _, delegated := types.ParseDelegation(code) @@ -354,7 +363,6 @@ func (st *stateTransition) preCheck() error { } } // Check the blob version validity - isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time) if msg.BlobHashes != nil { // The to field of a blob tx type is mandatory, and a `BlobTx` transaction internally // has it as a non-nillable value, so any msg derived from blob transaction has it non-nil. @@ -398,10 +406,6 @@ func (st *stateTransition) preCheck() error { return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From) } } - // Verify tx gas limit does not exceed EIP-7825 cap. - if isOsaka && msg.GasLimit > params.MaxTxGas { - return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit) - } return st.buyGas() } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index b50a532b60..a05b7a7a4a 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -984,7 +984,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc return nil, err } var ( - msg = args.ToMessage(blockContext.BaseFee, true, true) + msg = args.ToMessage(blockContext.BaseFee, true) tx = args.ToTransaction(types.LegacyTxType) traceConfig *TraceConfig ) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 554f525290..ebb8ece730 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -699,15 +699,15 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S } else { gp.AddGas(globalGasCap) } - return applyMessage(ctx, b, args, state, header, timeout, gp, &blockCtx, &vm.Config{NoBaseFee: true}, precompiles, true) + return applyMessage(ctx, b, args, state, header, timeout, gp, &blockCtx, &vm.Config{NoBaseFee: true}, precompiles) } -func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts, skipChecks bool) (*core.ExecutionResult, error) { +func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts) (*core.ExecutionResult, error) { // Get a new instance of the EVM. if err := args.CallDefaults(gp.Gas(), blockContext.BaseFee, b.ChainConfig().ChainID); err != nil { return nil, err } - msg := args.ToMessage(header.BaseFee, skipChecks, skipChecks) + msg := args.ToMessage(header.BaseFee, true) // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). if msg.GasPrice.Sign() == 0 { @@ -858,7 +858,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr if err := args.CallDefaults(gasCap, header.BaseFee, b.ChainConfig().ChainID); err != nil { return 0, err } - call := args.ToMessage(header.BaseFee, true, true) + call := args.ToMessage(header.BaseFee, true) // Run the gas estimation and wrap any revertals into a custom return estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap) @@ -1301,7 +1301,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH statedb := db.Copy() // Set the accesslist to the last al args.AccessList = &accessList - msg := args.ToMessage(header.BaseFee, true, true) + msg := args.ToMessage(header.BaseFee, true) // Apply the transaction with the access list tracer tracer := logger.NewAccessListTracer(accessList, addressesToExclude) diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 362536bcb5..e26e5bd0e9 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -287,7 +287,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, tracer.reset(txHash, uint(i)) sim.state.SetTxContext(txHash, i) // EoA check is always skipped, even in validation mode. - msg := call.ToMessage(header.BaseFee, !sim.validate, true) + msg := call.ToMessage(header.BaseFee, !sim.validate) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) if err != nil { txErr := txValidationError(err) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index f80ef6d080..23aa8e5947 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -443,7 +443,7 @@ func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int, // core evm. This method is used in calls and traces that do not require a real // live transaction. // Assumes that fields are not nil, i.e. setDefaults or CallDefaults has been called. -func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoACheck bool) *core.Message { +func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck bool) *core.Message { var ( gasPrice *big.Int gasFeeCap *big.Int @@ -491,7 +491,7 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoA BlobHashes: args.BlobHashes, SetCodeAuthorizations: args.AuthorizationList, SkipNonceChecks: skipNonceCheck, - SkipFromEOACheck: skipEoACheck, + SkipTransactionChecks: true, } } From b08b629818bd5725b174d1fdf9254f5d202e76e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Fri, 19 Sep 2025 14:07:10 +0200 Subject: [PATCH 079/119] beacon/blsync: test validated finality (#32633) This PR improves `TestBlockSync` so that it also tests the finality update validation. Note: to this date four long and complex (at least partly AI generated) PRs arrived that did something related to testing finality but honestly we do not need a bloated "comprehensive" test to test a trivial feature because maintaining these tests can also be a pain over the long term. This PR adds some sufficient sanity checks to detect if finality ever gets broken by a future change. --- beacon/blsync/block_sync_test.go | 57 +++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/beacon/blsync/block_sync_test.go b/beacon/blsync/block_sync_test.go index e7c2c4d163..e471766738 100644 --- a/beacon/blsync/block_sync_test.go +++ b/beacon/blsync/block_sync_test.go @@ -32,7 +32,7 @@ var ( testServer2 = testServer("testServer2") testBlock1 = types.NewBeaconBlock(&deneb.BeaconBlock{ - Slot: 123, + Slot: 127, Body: deneb.BeaconBlockBody{ ExecutionPayload: deneb.ExecutionPayload{ BlockNumber: 456, @@ -41,7 +41,7 @@ var ( }, }) testBlock2 = types.NewBeaconBlock(&deneb.BeaconBlock{ - Slot: 124, + Slot: 128, Body: deneb.BeaconBlockBody{ ExecutionPayload: deneb.ExecutionPayload{ BlockNumber: 457, @@ -49,6 +49,14 @@ var ( }, }, }) + testFinal1 = types.NewExecutionHeader(&deneb.ExecutionPayloadHeader{ + BlockNumber: 395, + BlockHash: zrntcommon.Hash32(common.HexToHash("abbe7625624bf8ddd84723709e2758956289465dd23475f02387e0854942666")), + }) + testFinal2 = types.NewExecutionHeader(&deneb.ExecutionPayloadHeader{ + BlockNumber: 420, + BlockHash: zrntcommon.Hash32(common.HexToHash("9182a6ef8723654de174283750932ccc092378549836bf4873657eeec474598")), + }) ) type testServer string @@ -66,9 +74,10 @@ func TestBlockSync(t *testing.T) { ts.AddServer(testServer1, 1) ts.AddServer(testServer2, 1) - expHeadBlock := func(expHead *types.BeaconBlock) { + expHeadEvent := func(expHead *types.BeaconBlock, expFinal *types.ExecutionHeader) { t.Helper() var expNumber, headNumber uint64 + var expFinalHash, finalHash common.Hash if expHead != nil { p, err := expHead.ExecutionPayload() if err != nil { @@ -76,19 +85,26 @@ func TestBlockSync(t *testing.T) { } expNumber = p.NumberU64() } + if expFinal != nil { + expFinalHash = expFinal.BlockHash() + } select { case event := <-headCh: headNumber = event.Block.NumberU64() + finalHash = event.Finalized default: } if headNumber != expNumber { t.Errorf("Wrong head block, expected block number %d, got %d)", expNumber, headNumber) } + if finalHash != expFinalHash { + t.Errorf("Wrong finalized block, expected block hash %064x, got %064x)", expFinalHash[:], finalHash[:]) + } } // no block requests expected until head tracker knows about a head ts.Run(1) - expHeadBlock(nil) + expHeadEvent(nil, nil) // set block 1 as prefetch head, announced by server 2 head1 := blockHeadInfo(testBlock1) @@ -103,12 +119,13 @@ func TestBlockSync(t *testing.T) { ts.AddAllowance(testServer2, 1) ts.Run(3) // head block still not expected as the fetched block is not the validated head yet - expHeadBlock(nil) + expHeadEvent(nil, nil) // set as validated head, expect no further requests but block 1 set as head block ht.validated.Header = testBlock1.Header() + ht.finalized, ht.finalizedPayload = testBlock1.Header(), testFinal1 ts.Run(4) - expHeadBlock(testBlock1) + expHeadEvent(testBlock1, testFinal1) // set block 2 as prefetch head, announced by server 1 head2 := blockHeadInfo(testBlock2) @@ -126,17 +143,26 @@ func TestBlockSync(t *testing.T) { // expect req2 retry to server 2 ts.Run(7, testServer2, sync.ReqBeaconBlock(head2.BlockRoot)) // now head block should be unavailable again - expHeadBlock(nil) + expHeadEvent(nil, nil) // valid response, now head block should be block 2 immediately as it is already validated + // but head event is still not expected because an epoch boundary was crossed and the + // expected finality update has not arrived yet ts.RequestEvent(request.EvResponse, ts.Request(7, 1), testBlock2) ts.Run(8) - expHeadBlock(testBlock2) + expHeadEvent(nil, nil) + + // expected finality update arrived, now a head event is expected + ht.finalized, ht.finalizedPayload = testBlock2.Header(), testFinal2 + ts.Run(9) + expHeadEvent(testBlock2, testFinal2) } type testHeadTracker struct { - prefetch types.HeadInfo - validated types.SignedHeader + prefetch types.HeadInfo + validated types.SignedHeader + finalized types.Header + finalizedPayload *types.ExecutionHeader } func (h *testHeadTracker) PrefetchHead() types.HeadInfo { @@ -151,13 +177,14 @@ func (h *testHeadTracker) ValidatedOptimistic() (types.OptimisticUpdate, bool) { }, h.validated.Header != (types.Header{}) } -// TODO add test case for finality func (h *testHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) { - finalized := types.NewExecutionHeader(new(deneb.ExecutionPayloadHeader)) + if h.validated.Header == (types.Header{}) || h.finalizedPayload == nil { + return types.FinalityUpdate{}, false + } return types.FinalityUpdate{ - Attested: types.HeaderWithExecProof{Header: h.validated.Header}, - Finalized: types.HeaderWithExecProof{PayloadHeader: finalized}, + Attested: types.HeaderWithExecProof{Header: h.finalized}, + Finalized: types.HeaderWithExecProof{Header: h.finalized, PayloadHeader: h.finalizedPayload}, Signature: h.validated.Signature, SignatureSlot: h.validated.SignatureSlot, - }, h.validated.Header != (types.Header{}) + }, true } From 9b730e1997c5f3ae0bcf3bd1e0595e43998dd348 Mon Sep 17 00:00:00 2001 From: Galoretka Date: Fri, 19 Sep 2025 16:13:32 +0300 Subject: [PATCH 080/119] core/state: add missing address key in state_object log (#32676) --- core/state/state_object.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state/state_object.go b/core/state/state_object.go index 767f469bfd..2938750503 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -333,7 +333,7 @@ func (s *stateObject) updateTrie() (Trie, error) { continue } if !exist { - log.Error("Storage slot is not found in pending area", s.address, "slot", key) + log.Error("Storage slot is not found in pending area", "address", s.address, "slot", key) continue } if (value != common.Hash{}) { From 103b8b2ec5150cd7989b19961edf810fbe15697d Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 19 Sep 2025 15:18:42 +0200 Subject: [PATCH 081/119] crypto/bn256: switch to gnark again (#32659) We recently update our default implementation to gnark in https://github.com/ethereum/go-ethereum/pull/32024 Then we found a consensus issue and reverted it in https://github.com/ethereum/go-ethereum/commit/65d77c51295c3b5c237a082af856a6926766a51c We fixed the consensus issue and have been fuzzing it more since then in https://github.com/ethereum/go-ethereum/pull/32055/files https://github.com/ethereum/go-ethereum/pull/32065 https://github.com/ethereum/go-ethereum/pull/32055/files So I think now is the time to update it back to gnark --- crypto/bn256/bn256_fast.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/bn256/bn256_fast.go b/crypto/bn256/bn256_fast.go index e3c9b60518..93a2eef879 100644 --- a/crypto/bn256/bn256_fast.go +++ b/crypto/bn256/bn256_fast.go @@ -9,18 +9,18 @@ package bn256 import ( - bn256cf "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + gnark "github.com/ethereum/go-ethereum/crypto/bn256/gnark" ) // G1 is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. -type G1 = bn256cf.G1 +type G1 = gnark.G1 // G2 is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. -type G2 = bn256cf.G2 +type G2 = gnark.G2 // PairingCheck calculates the Optimal Ate pairing for a set of points. func PairingCheck(a []*G1, b []*G2) bool { - return bn256cf.PairingCheck(a, b) + return gnark.PairingCheck(a, b) } From 1601f398d4558e6c060a158fa10b7570ce58cd6b Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Fri, 19 Sep 2025 16:21:15 +0200 Subject: [PATCH 082/119] core/txpool/blobpool: remove conversion in GetBlobs (#32578) This disables blob proof conversion in `GetBlobs` by default, making it conditional. --------- Co-authored-by: Felix Lange --- core/txpool/blobpool/blobpool.go | 22 +++++++++++---- core/txpool/blobpool/blobpool_test.go | 40 ++++++++++++++++----------- eth/catalyst/api.go | 4 +-- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 55d24c7a93..2229f1544c 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1331,7 +1331,11 @@ func (p *BlobPool) GetMetadata(hash common.Hash) *txpool.TxMetadata { // // This is a utility method for the engine API, enabling consensus clients to // retrieve blobs from the pools directly instead of the network. -func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blob, []kzg4844.Commitment, [][]kzg4844.Proof, error) { +// +// The version argument specifies the type of proofs to return, either the +// blob proofs (version 0) or the cell proofs (version 1). Proofs conversion is +// CPU intensive, so only done if explicitly requested with the convert flag. +func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte, convert bool) ([]*kzg4844.Blob, []kzg4844.Commitment, [][]kzg4844.Proof, error) { var ( blobs = make([]*kzg4844.Blob, len(vhashes)) commitments = make([]kzg4844.Commitment, len(vhashes)) @@ -1343,13 +1347,14 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blo for i, h := range vhashes { indices[h] = append(indices[h], i) } + for _, vhash := range vhashes { - // Skip duplicate vhash that was already resolved in a previous iteration if _, ok := filled[vhash]; ok { + // Skip vhash that was already resolved in a previous iteration continue } - // Retrieve the corresponding blob tx with the vhash, skip blob resolution - // if it's not found locally and place the null instead. + + // Retrieve the corresponding blob tx with the vhash. p.lock.RLock() txID, exists := p.lookup.storeidOfBlob(vhash) p.lock.RUnlock() @@ -1379,6 +1384,14 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blo if !ok { continue // non-interesting blob } + // Mark hash as seen. + filled[hash] = struct{}{} + if sidecar.Version != version && !convert { + // Skip blobs with incompatible version. Note we still track the blob hash + // in `filled` here, ensuring that we do not resolve this tx another time. + continue + } + // Get or convert the proof. var pf []kzg4844.Proof switch version { case types.BlobSidecarVersion0: @@ -1411,7 +1424,6 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blo commitments[index] = sidecar.Commitments[i] proofs[index] = pf } - filled[hash] = struct{}{} } } return blobs, commitments, proofs, nil diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index e46529a241..57d27962ce 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -423,11 +423,11 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) { hashes = append(hashes, tx.vhashes...) } } - blobs1, _, proofs1, err := pool.GetBlobs(hashes, types.BlobSidecarVersion0) + blobs1, _, proofs1, err := pool.GetBlobs(hashes, types.BlobSidecarVersion0, false) if err != nil { t.Fatal(err) } - blobs2, _, proofs2, err := pool.GetBlobs(hashes, types.BlobSidecarVersion1) + blobs2, _, proofs2, err := pool.GetBlobs(hashes, types.BlobSidecarVersion1, false) if err != nil { t.Fatal(err) } @@ -441,22 +441,18 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) { return } for i, hash := range hashes { - // If an item is missing, but shouldn't, error - if blobs1[i] == nil || proofs1[i] == nil { - t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash) - continue - } - if blobs2[i] == nil || proofs2[i] == nil { + // If an item is missing from both, but shouldn't, error + if (blobs1[i] == nil || proofs1[i] == nil) && (blobs2[i] == nil || proofs2[i] == nil) { t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash) continue } // Item retrieved, make sure it matches the expectation index := testBlobIndices[hash] - if *blobs1[i] != *testBlobs[index] || proofs1[i][0] != testBlobProofs[index] { + if blobs1[i] != nil && (*blobs1[i] != *testBlobs[index] || proofs1[i][0] != testBlobProofs[index]) { t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash) continue } - if *blobs2[i] != *testBlobs[index] || !slices.Equal(proofs2[i], testBlobCellProofs[index]) { + if blobs2[i] != nil && (*blobs2[i] != *testBlobs[index] || !slices.Equal(proofs2[i], testBlobCellProofs[index])) { t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash) continue } @@ -1926,8 +1922,9 @@ func TestGetBlobs(t *testing.T) { cases := []struct { start int limit int - fillRandom bool - version byte + fillRandom bool // Whether to randomly fill some of the requested blobs with unknowns + version byte // Blob sidecar version to request + convert bool // Whether to convert version on retrieval }{ { start: 0, limit: 6, @@ -1993,6 +1990,11 @@ func TestGetBlobs(t *testing.T) { start: 0, limit: 18, fillRandom: true, version: types.BlobSidecarVersion1, }, + { + start: 0, limit: 18, fillRandom: true, + version: types.BlobSidecarVersion1, + convert: true, // Convert some version 0 blobs to version 1 while retrieving + }, } for i, c := range cases { var ( @@ -2014,7 +2016,7 @@ func TestGetBlobs(t *testing.T) { filled[len(vhashes)] = struct{}{} vhashes = append(vhashes, testrand.Hash()) } - blobs, _, proofs, err := pool.GetBlobs(vhashes, c.version) + blobs, _, proofs, err := pool.GetBlobs(vhashes, c.version, c.convert) if err != nil { t.Errorf("Unexpected error for case %d, %v", i, err) } @@ -2029,6 +2031,7 @@ func TestGetBlobs(t *testing.T) { var unknown int for j := 0; j < len(blobs); j++ { + testBlobIndex := c.start + j - unknown if _, exist := filled[j]; exist { if blobs[j] != nil || proofs[j] != nil { t.Errorf("Unexpected blob and proof, item %d", j) @@ -2038,17 +2041,22 @@ func TestGetBlobs(t *testing.T) { } // If an item is missing, but shouldn't, error if blobs[j] == nil || proofs[j] == nil { - t.Errorf("tracked blob retrieval failed: item %d, hash %x", j, vhashes[j]) + // This is only an error if there was no version mismatch + if c.convert || + (c.version == types.BlobSidecarVersion1 && 6 <= testBlobIndex && testBlobIndex < 12) || + (c.version == types.BlobSidecarVersion0 && (testBlobIndex < 6 || 12 <= testBlobIndex)) { + t.Errorf("tracked blob retrieval failed: item %d, hash %x", j, vhashes[j]) + } continue } // Item retrieved, make sure the blob matches the expectation - if *blobs[j] != *testBlobs[c.start+j-unknown] { + if *blobs[j] != *testBlobs[testBlobIndex] { t.Errorf("retrieved blob mismatch: item %d, hash %x", j, vhashes[j]) continue } // Item retrieved, make sure the proof matches the expectation if c.version == types.BlobSidecarVersion0 { - if proofs[j][0] != testBlobProofs[c.start+j-unknown] { + if proofs[j][0] != testBlobProofs[testBlobIndex] { t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j]) } } else { diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 17299d9296..b37c26149f 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -495,7 +495,7 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo if len(hashes) > 128 { return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))) } - blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion0) + blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion0, false) if err != nil { return nil, engine.InvalidParams.With(err) } @@ -555,7 +555,7 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo return nil, nil } - blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion1) + blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion1, false) if err != nil { return nil, engine.InvalidParams.With(err) } From 64c6de7747072e72a6df73228c51f751e143ab0c Mon Sep 17 00:00:00 2001 From: cui Date: Sat, 20 Sep 2025 06:38:36 +0800 Subject: [PATCH 083/119] p2p: using testing.B.Loop (#32664) --- p2p/discover/v5wire/encoding_test.go | 6 ++---- p2p/netutil/net_test.go | 2 +- p2p/rlpx/rlpx_test.go | 5 ++--- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/p2p/discover/v5wire/encoding_test.go b/p2p/discover/v5wire/encoding_test.go index 2304d0f132..5774cb3d8c 100644 --- a/p2p/discover/v5wire/encoding_test.go +++ b/p2p/discover/v5wire/encoding_test.go @@ -477,10 +477,9 @@ func BenchmarkV5_DecodeHandshakePingSecp256k1(b *testing.B) { b.Fatal("can't encode handshake packet") } challenge.Node = nil // force ENR signature verification in decoder - b.ResetTimer() input := make([]byte, len(enc)) - for i := 0; i < b.N; i++ { + for b.Loop() { copy(input, enc) net.nodeB.c.sc.storeSentHandshake(idA, "", challenge) _, _, _, err := net.nodeB.c.Decode(input, "") @@ -507,10 +506,9 @@ func BenchmarkV5_DecodePing(b *testing.B) { if err != nil { b.Fatalf("can't encode: %v", err) } - b.ResetTimer() input := make([]byte, len(enc)) - for i := 0; i < b.N; i++ { + for b.Loop() { copy(input, enc) _, _, packet, _ := net.nodeB.c.Decode(input, addrB) if _, ok := packet.(*Ping); !ok { diff --git a/p2p/netutil/net_test.go b/p2p/netutil/net_test.go index 569c7ac454..8c810f494f 100644 --- a/p2p/netutil/net_test.go +++ b/p2p/netutil/net_test.go @@ -187,7 +187,7 @@ func TestCheckRelayIP(t *testing.T) { func BenchmarkCheckRelayIP(b *testing.B) { sender := parseIP("23.55.1.242") addr := parseIP("23.55.1.2") - for i := 0; i < b.N; i++ { + for b.Loop() { CheckRelayIP(sender, addr) } } diff --git a/p2p/rlpx/rlpx_test.go b/p2p/rlpx/rlpx_test.go index 27d51546e7..a02c1dc2cc 100644 --- a/p2p/rlpx/rlpx_test.go +++ b/p2p/rlpx/rlpx_test.go @@ -369,8 +369,7 @@ func TestHandshakeForwardCompatibility(t *testing.T) { func BenchmarkHandshakeRead(b *testing.B) { var input = unhex(eip8HandshakeAuthTests[0].input) - - for i := 0; i < b.N; i++ { + for b.Loop() { var ( h handshakeState r = bytes.NewReader(input) @@ -427,7 +426,7 @@ func BenchmarkThroughput(b *testing.B) { // Read N messages. b.SetBytes(int64(len(msgdata))) b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { _, _, _, err := conn2.Read() if err != nil { b.Fatal("read error:", err) From d41dc92da98b43a1ab5f2e13e456c6768fbc39c4 Mon Sep 17 00:00:00 2001 From: cui Date: Sat, 20 Sep 2025 06:57:43 +0800 Subject: [PATCH 084/119] core/state: using testing.B.Loop (#32658) before: go test -run=^$ -bench=. ./core/state/... 120.85s user 7.96s system 129% cpu 1:39.13 tota after: go test -run=^$ -bench=. ./core/state/... 21.32s user 2.12s system 97% cpu 24.006 total --- core/state/snapshot/difflayer_test.go | 14 ++++---------- core/state/snapshot/iterator_test.go | 16 ++++++++-------- core/state/state_object_test.go | 6 +++--- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/core/state/snapshot/difflayer_test.go b/core/state/snapshot/difflayer_test.go index b212098e75..2c868b3010 100644 --- a/core/state/snapshot/difflayer_test.go +++ b/core/state/snapshot/difflayer_test.go @@ -229,8 +229,7 @@ func BenchmarkSearch(b *testing.B) { layer = fill(layer) } key := crypto.Keccak256Hash([]byte{0x13, 0x38}) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { layer.AccountRLP(key) } } @@ -269,8 +268,7 @@ func BenchmarkSearchSlot(b *testing.B) { for i := 0; i < 128; i++ { layer = fill(layer) } - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { layer.Storage(accountKey, storageKey) } } @@ -300,9 +298,7 @@ func BenchmarkFlatten(b *testing.B) { } return newDiffLayer(parent, common.Hash{}, accounts, storage) } - b.ResetTimer() - for i := 0; i < b.N; i++ { - b.StopTimer() + for b.Loop() { var layer snapshot layer = emptyLayer() for i := 1; i < 128; i++ { @@ -352,9 +348,7 @@ func BenchmarkJournal(b *testing.B) { for i := 1; i < 128; i++ { layer = fill(layer) } - b.ResetTimer() - - for i := 0; i < b.N; i++ { + for b.Loop() { layer.Journal(new(bytes.Buffer)) } } diff --git a/core/state/snapshot/iterator_test.go b/core/state/snapshot/iterator_test.go index 2e882f484e..dd6c4cf968 100644 --- a/core/state/snapshot/iterator_test.go +++ b/core/state/snapshot/iterator_test.go @@ -928,7 +928,7 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) { head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) b.Run("binary iterator keys", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { got := 0 it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { @@ -940,7 +940,7 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) { } }) b.Run("binary iterator values", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { got := 0 it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { @@ -953,7 +953,7 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) { } }) b.Run("fast iterator keys", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { it, _ := snaps.AccountIterator(common.HexToHash("0x65"), common.Hash{}) defer it.Release() @@ -967,7 +967,7 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) { } }) b.Run("fast iterator values", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { it, _ := snaps.AccountIterator(common.HexToHash("0x65"), common.Hash{}) defer it.Release() @@ -1025,7 +1025,7 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) b.Run("binary iterator (keys)", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { got := 0 it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { @@ -1037,7 +1037,7 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { } }) b.Run("binary iterator (values)", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { got := 0 it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { @@ -1051,7 +1051,7 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { } }) b.Run("fast iterator (keys)", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { it, _ := snaps.AccountIterator(common.HexToHash("0x65"), common.Hash{}) defer it.Release() @@ -1065,7 +1065,7 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { } }) b.Run("fast iterator (values)", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { it, _ := snaps.AccountIterator(common.HexToHash("0x65"), common.Hash{}) defer it.Release() diff --git a/core/state/state_object_test.go b/core/state/state_object_test.go index 42fd778025..0237f0580d 100644 --- a/core/state/state_object_test.go +++ b/core/state/state_object_test.go @@ -25,7 +25,7 @@ import ( func BenchmarkCutOriginal(b *testing.B) { value := common.HexToHash("0x01") - for i := 0; i < b.N; i++ { + for b.Loop() { bytes.TrimLeft(value[:], "\x00") } } @@ -33,14 +33,14 @@ func BenchmarkCutOriginal(b *testing.B) { func BenchmarkCutsetterFn(b *testing.B) { value := common.HexToHash("0x01") cutSetFn := func(r rune) bool { return r == 0 } - for i := 0; i < b.N; i++ { + for b.Loop() { bytes.TrimLeftFunc(value[:], cutSetFn) } } func BenchmarkCutCustomTrim(b *testing.B) { value := common.HexToHash("0x01") - for i := 0; i < b.N; i++ { + for b.Loop() { common.TrimLeftZeroes(value[:]) } } From 3ebb1431d36496714cdcb40254a00d3c88bc2f8b Mon Sep 17 00:00:00 2001 From: cui Date: Sat, 20 Sep 2025 07:00:29 +0800 Subject: [PATCH 085/119] eth: using testing.B.Loop (#32657) before: go test -run=^$ -bench=. ./eth/... 827.57s user 23.80s system 361% cpu 3:55.49 total after: go test -run=^$ -bench=. ./eth/... 281.62s user 13.62s system 245% cpu 2:00.49 total --- eth/filters/filter_test.go | 5 +---- eth/protocols/snap/sync_test.go | 4 ++-- eth/tracers/internal/tracetest/calltrace_test.go | 4 +--- eth/tracers/internal/tracetest/flat_calltrace_test.go | 2 +- eth/tracers/tracers_test.go | 4 +--- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index e99b2f6caa..edec3e027f 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -109,11 +109,8 @@ func benchmarkFilters(b *testing.B, history uint64, noHistory bool) { backend.startFilterMaps(history, noHistory, filtermaps.DefaultParams) defer backend.stopFilterMaps() - b.ResetTimer() - filter := sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{addr1, addr2, addr3, addr4}, nil) - - for i := 0; i < b.N; i++ { + for b.Loop() { filter.begin = 0 logs, _ := filter.Logs(context.Background()) if len(logs) != 4 { diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index d599e7ecc3..89a782496f 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -106,13 +106,13 @@ func BenchmarkHashing(b *testing.B) { } b.Run("old", func(b *testing.B) { b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { old() } }) b.Run("new", func(b *testing.B) { b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { new() } }) diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index b454522978..ba0706c598 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -211,11 +211,9 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { defer state.Close() b.ReportAllocs() - b.ResetTimer() evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{}) - - for i := 0; i < b.N; i++ { + for b.Loop() { snap := state.StateDB.Snapshot() tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config) if err != nil { diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index d1fa44e9d8..1882ef315e 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -201,7 +201,7 @@ func BenchmarkFlatCallTracer(b *testing.B) { for _, file := range files { filename := strings.TrimPrefix(file, "testdata/call_tracer_flat/") b.Run(camel(strings.TrimSuffix(filename, ".json")), func(b *testing.B) { - for n := 0; n < b.N; n++ { + for b.Loop() { err := flatCallTracerTestRunner("flatCallTracer", filename, "call_tracer_flat", b) if err != nil { b.Fatal(err) diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index a72dbf6ee6..06edeaf698 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -84,10 +84,8 @@ func BenchmarkTransactionTraceV2(b *testing.B) { if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) } - b.ResetTimer() b.ReportAllocs() - - for i := 0; i < b.N; i++ { + for b.Loop() { tracer := logger.NewStructLogger(&logger.Config{}).Hooks() tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) evm.Config.Tracer = tracer From 9b1896bfb576cb68df7cff801b9b1bf4d79e9d97 Mon Sep 17 00:00:00 2001 From: cui Date: Sat, 20 Sep 2025 07:05:21 +0800 Subject: [PATCH 086/119] log: using testing.B.Loop (#32663) before: go test -run=^$ -bench=. ./log -timeout=1h 12.19s user 2.19s system 89% cpu 16.025 total after: go test -run=^$ -bench=. ./log -timeout=1h 10.64s user 1.53s system 89% cpu 13.607 total --------- Co-authored-by: lightclient --- log/format_test.go | 4 ++-- log/logger_test.go | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/log/format_test.go b/log/format_test.go index d4c1df4abc..bb740ceb84 100644 --- a/log/format_test.go +++ b/log/format_test.go @@ -10,7 +10,7 @@ var sink []byte func BenchmarkPrettyInt64Logfmt(b *testing.B) { buf := make([]byte, 100) b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { sink = appendInt64(buf, rand.Int63()) } } @@ -18,7 +18,7 @@ func BenchmarkPrettyInt64Logfmt(b *testing.B) { func BenchmarkPrettyUint64Logfmt(b *testing.B) { buf := make([]byte, 100) b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { sink = appendUint64(buf, rand.Uint64(), false) } } diff --git a/log/logger_test.go b/log/logger_test.go index 3ec6d2e19c..dae8497204 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -70,9 +70,10 @@ func TestJSONHandler(t *testing.T) { func BenchmarkTraceLogging(b *testing.B) { SetDefault(NewLogger(NewTerminalHandler(io.Discard, true))) - b.ResetTimer() - for i := 0; i < b.N; i++ { + i := 0 + for b.Loop() { Trace("a message", "v", i) + i++ } } @@ -99,8 +100,8 @@ func benchmarkLogger(b *testing.B, l Logger) { err = errors.New("oh nooes it's crap") ) b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { + i := 0 + for b.Loop() { l.Info("This is a message", "foo", int16(i), "bytes", bb, @@ -109,8 +110,8 @@ func benchmarkLogger(b *testing.B, l Logger) { "bigint", bigint, "nilbig", nilbig, "err", err) + i++ } - b.StopTimer() } func TestLoggerOutput(t *testing.T) { @@ -161,18 +162,18 @@ const termTimeFormat = "01-02|15:04:05.000" func BenchmarkAppendFormat(b *testing.B) { var now = time.Now() b.Run("fmt time.Format", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { fmt.Fprintf(io.Discard, "%s", now.Format(termTimeFormat)) } }) b.Run("time.AppendFormat", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { now.AppendFormat(nil, termTimeFormat) } }) var buf = new(bytes.Buffer) b.Run("time.Custom", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { writeTimeTermFormat(buf, now) buf.Reset() } From 0758a561d64c3c7d739a912641ab56adba73e25e Mon Sep 17 00:00:00 2001 From: cui Date: Sat, 20 Sep 2025 07:06:27 +0800 Subject: [PATCH 087/119] core: using testing.B.Loop (#32662) Co-authored-by: lightclient --- core/bench_test.go | 6 ++---- core/rlp_test.go | 9 +++------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/core/bench_test.go b/core/bench_test.go index 2830022662..932188f8e2 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -301,7 +301,7 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin func benchWriteChain(b *testing.B, full bool, count uint64) { genesis := &Genesis{Config: params.AllEthashProtocolChanges} - for i := 0; i < b.N; i++ { + for b.Loop() { pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false) if err != nil { b.Fatalf("error opening database: %v", err) @@ -326,9 +326,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { db.Close() options := DefaultConfig().WithArchive(true) b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { + for b.Loop() { pdb, err = pebble.New(dir, 1024, 128, "", false) if err != nil { b.Fatalf("error opening database: %v", err) diff --git a/core/rlp_test.go b/core/rlp_test.go index bc37408537..69efa82551 100644 --- a/core/rlp_test.go +++ b/core/rlp_test.go @@ -149,8 +149,7 @@ func BenchmarkHashing(b *testing.B) { var got common.Hash var hasher = sha3.NewLegacyKeccak256() b.Run("iteratorhashing", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { var hash common.Hash it, err := rlp.NewListIterator(bodyRlp) if err != nil { @@ -172,8 +171,7 @@ func BenchmarkHashing(b *testing.B) { }) var exp common.Hash b.Run("fullbodyhashing", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { var body types.Body rlp.DecodeBytes(bodyRlp, &body) for _, tx := range body.Transactions { @@ -182,8 +180,7 @@ func BenchmarkHashing(b *testing.B) { } }) b.Run("fullblockhashing", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { var block types.Block rlp.DecodeBytes(blockRlp, &block) for _, tx := range block.Transactions() { From 79a4f76b03827e83d522a74417e31185f8a7a41b Mon Sep 17 00:00:00 2001 From: cui Date: Sat, 20 Sep 2025 07:06:55 +0800 Subject: [PATCH 088/119] core/vm: using testing.B.Loop (#32660) before: go test -run=^$ -bench=. ./core/vm/... -timeout=1h 1841.87s user 40.96s system 124% cpu 25:15.76 total after: go test -run=^$ -bench=. ./core/vm/... -timeout=1h 1588.65s user 33.79s system 123% cpu 21:53.25 total --------- Co-authored-by: lightclient --- core/vm/analysis_legacy_test.go | 11 +++-------- core/vm/contracts_test.go | 4 +--- core/vm/instructions_test.go | 10 +++------- core/vm/interpreter_test.go | 3 +-- core/vm/runtime/runtime_test.go | 15 +++++---------- 5 files changed, 13 insertions(+), 30 deletions(-) diff --git a/core/vm/analysis_legacy_test.go b/core/vm/analysis_legacy_test.go index f84a4abc92..75d8a495fa 100644 --- a/core/vm/analysis_legacy_test.go +++ b/core/vm/analysis_legacy_test.go @@ -65,21 +65,17 @@ func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) { // 1.4 ms code := make([]byte, analysisCodeSize) bench.SetBytes(analysisCodeSize) - bench.ResetTimer() - for i := 0; i < bench.N; i++ { + for bench.Loop() { codeBitmap(code) } - bench.StopTimer() } func BenchmarkJumpdestHashing_1200k(bench *testing.B) { // 4 ms code := make([]byte, analysisCodeSize) bench.SetBytes(analysisCodeSize) - bench.ResetTimer() - for i := 0; i < bench.N; i++ { + for bench.Loop() { crypto.Keccak256Hash(code) } - bench.StopTimer() } func BenchmarkJumpdestOpAnalysis(bench *testing.B) { @@ -91,8 +87,7 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) { code[i] = byte(op) } bits := make(BitVec, len(code)/8+1+4) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { clear(bits) codeBitmapInternal(code, bits) } diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 3200ace5cc..51bd7101ff 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -167,12 +167,10 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, reqGas), func(bench *testing.B) { bench.ReportAllocs() start := time.Now() - bench.ResetTimer() - for i := 0; i < bench.N; i++ { + for bench.Loop() { copy(data, in) res, _, err = RunPrecompiledContract(p, data, reqGas, nil) } - bench.StopTimer() elapsed := uint64(time.Since(start)) if elapsed < 1 { elapsed = 1 diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index cd31829a7e..72f561f4bf 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -291,15 +291,13 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { intArgs[i] = new(uint256.Int).SetBytes(common.Hex2Bytes(arg)) } pc := uint64(0) - bench.ResetTimer() - for i := 0; i < bench.N; i++ { + for bench.Loop() { for _, arg := range intArgs { stack.push(arg) } op(&pc, evm, scope) stack.pop() } - bench.StopTimer() for i, arg := range args { want := new(uint256.Int).SetBytes(common.Hex2Bytes(arg)) @@ -551,8 +549,7 @@ func BenchmarkOpMstore(bench *testing.B) { memStart := new(uint256.Int) value := new(uint256.Int).SetUint64(0x1337) - bench.ResetTimer() - for i := 0; i < bench.N; i++ { + for bench.Loop() { stack.push(value) stack.push(memStart) opMstore(&pc, evm, &ScopeContext{mem, stack, nil}) @@ -609,8 +606,7 @@ func BenchmarkOpKeccak256(bench *testing.B) { pc := uint64(0) start := new(uint256.Int) - bench.ResetTimer() - for i := 0; i < bench.N; i++ { + for bench.Loop() { stack.push(uint256.NewInt(32)) stack.push(start) opKeccak256(&pc, evm, &ScopeContext{mem, stack, nil}) diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index 90eeda34e6..79531f78d2 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -90,8 +90,7 @@ func BenchmarkInterpreter(b *testing.B) { stack.push(uint256.NewInt(123)) stack.push(uint256.NewInt(123)) gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { gasSStoreEIP3529(evm, contract, stack, mem, 1234) } } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index cabc57d1fb..ddd32df039 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -150,8 +150,7 @@ func BenchmarkCall(b *testing.B) { b.Fatal(err) } - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { for j := 0; j < 400; j++ { Execute(code, cpurchase, nil) Execute(code, creceived, nil) @@ -190,11 +189,9 @@ func benchmarkEVM_Create(bench *testing.B, code string) { EVMConfig: vm.Config{}, } // Warm up the intpools and stuff - bench.ResetTimer() - for i := 0; i < bench.N; i++ { + for bench.Loop() { Call(receiver, []byte{}, &runtimeConfig) } - bench.StopTimer() } func BenchmarkEVM_CREATE_500(bench *testing.B) { @@ -233,8 +230,7 @@ func BenchmarkEVM_SWAP1(b *testing.B) { b.Run("10k", func(b *testing.B) { contractCode := swapContract(10_000) state.SetCode(contractAddr, contractCode, tracing.CodeChangeUnspecified) - - for i := 0; i < b.N; i++ { + for b.Loop() { _, _, err := Call(contractAddr, []byte{}, &Config{State: state}) if err != nil { b.Fatal(err) @@ -264,8 +260,7 @@ func BenchmarkEVM_RETURN(b *testing.B) { contractCode := returnContract(n) state.SetCode(contractAddr, contractCode, tracing.CodeChangeUnspecified) - - for i := 0; i < b.N; i++ { + for b.Loop() { ret, _, err := Call(contractAddr, []byte{}, &Config{State: state}) if err != nil { b.Fatal(err) @@ -432,7 +427,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode b.Run(name, func(b *testing.B) { b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { Call(destination, nil, cfg) } }) From e35c628656e438ee77c2fc1b2c3d1280d37321e3 Mon Sep 17 00:00:00 2001 From: cui Date: Sat, 20 Sep 2025 07:11:32 +0800 Subject: [PATCH 089/119] core/types: using testing.B.Loop (#32643) before: go test -run=^$ -bench=. ./core/types 47.80s user 2.18s system 102% cpu 48.936 tota after: go test -run=^$ -bench=. ./core/types 42.42s user 2.27s system 112% cpu 39.593 total --- core/types/block_test.go | 3 +-- core/types/bloom9_test.go | 16 ++++++---------- core/types/hashing_test.go | 5 ++--- core/types/transaction_test.go | 8 ++++---- core/types/types_test.go | 4 ++-- 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/core/types/block_test.go b/core/types/block_test.go index 2130a2fcf3..5fa4756a50 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -263,9 +263,8 @@ var benchBuffer = bytes.NewBuffer(make([]byte, 0, 32000)) func BenchmarkEncodeBlock(b *testing.B) { block := makeBenchBlock() - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { benchBuffer.Reset() if err := rlp.Encode(benchBuffer, block); err != nil { b.Fatal(err) diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go index 07f6446a97..ceb6797e1f 100644 --- a/core/types/bloom9_test.go +++ b/core/types/bloom9_test.go @@ -78,7 +78,7 @@ func TestBloomExtensively(t *testing.T) { func BenchmarkBloom9(b *testing.B) { test := []byte("testestestest") - for i := 0; i < b.N; i++ { + for b.Loop() { Bloom9(test) } } @@ -86,7 +86,7 @@ func BenchmarkBloom9(b *testing.B) { func BenchmarkBloom9Lookup(b *testing.B) { toTest := []byte("testtest") bloom := new(Bloom) - for i := 0; i < b.N; i++ { + for b.Loop() { bloom.Test(toTest) } } @@ -128,7 +128,7 @@ func BenchmarkCreateBloom(b *testing.B) { } b.Run("small-createbloom", func(b *testing.B) { b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { for _, receipt := range rSmall { receipt.Bloom = CreateBloom(receipt) } @@ -144,7 +144,7 @@ func BenchmarkCreateBloom(b *testing.B) { }) b.Run("large-createbloom", func(b *testing.B) { b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { for _, receipt := range rLarge { receipt.Bloom = CreateBloom(receipt) } @@ -163,13 +163,11 @@ func BenchmarkCreateBloom(b *testing.B) { receipt.Bloom = CreateBloom(receipt) } b.ReportAllocs() - b.ResetTimer() var bl Bloom - for i := 0; i < b.N; i++ { + for b.Loop() { bl = MergeBloom(rSmall) } - b.StopTimer() var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") got := crypto.Keccak256Hash(bl.Bytes()) @@ -182,13 +180,11 @@ func BenchmarkCreateBloom(b *testing.B) { receipt.Bloom = CreateBloom(receipt) } b.ReportAllocs() - b.ResetTimer() var bl Bloom - for i := 0; i < b.N; i++ { + for b.Loop() { bl = MergeBloom(rLarge) } - b.StopTimer() var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") got := crypto.Keccak256Hash(bl.Bytes()) diff --git a/core/types/hashing_test.go b/core/types/hashing_test.go index c846ecd0c5..54adbc73e8 100644 --- a/core/types/hashing_test.go +++ b/core/types/hashing_test.go @@ -84,9 +84,8 @@ func BenchmarkDeriveSha200(b *testing.B) { 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++ { + for b.Loop() { exp = types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))) } }) @@ -94,7 +93,7 @@ func BenchmarkDeriveSha200(b *testing.B) { b.Run("stack_trie", func(b *testing.B) { b.ResetTimer() b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { got = types.DeriveSha(txs, trie.NewStackTrie(nil)) } }) diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index cc41674dfd..2d854ae345 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -591,7 +591,7 @@ func BenchmarkHash(b *testing.B) { GasTipCap: big.NewInt(500), GasFeeCap: big.NewInt(500), }) - for i := 0; i < b.N; i++ { + for b.Loop() { signer.Hash(tx) } } @@ -614,7 +614,7 @@ func BenchmarkEffectiveGasTip(b *testing.B) { b.Run("Original", func(b *testing.B) { b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { _, err := tx.EffectiveGasTip(baseFee.ToBig()) if err != nil { b.Fatal(err) @@ -625,7 +625,7 @@ func BenchmarkEffectiveGasTip(b *testing.B) { b.Run("IntoMethod", func(b *testing.B) { b.ReportAllocs() dst := new(uint256.Int) - for i := 0; i < b.N; i++ { + for b.Loop() { err := tx.calcEffectiveGasTip(dst, baseFee) if err != nil { b.Fatal(err) @@ -729,7 +729,7 @@ func BenchmarkEffectiveGasTipCmp(b *testing.B) { b.Run("Original", func(b *testing.B) { b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { tx.EffectiveGasTipCmp(other, baseFee) } }) diff --git a/core/types/types_test.go b/core/types/types_test.go index 1fb386d5de..96d0444994 100644 --- a/core/types/types_test.go +++ b/core/types/types_test.go @@ -126,7 +126,7 @@ func benchRLP(b *testing.B, encode bool) { b.Run(tc.name, func(b *testing.B) { b.ReportAllocs() var null = &devnull{} - for i := 0; i < b.N; i++ { + for b.Loop() { rlp.Encode(null, tc.obj) } b.SetBytes(int64(null.len / b.N)) @@ -136,7 +136,7 @@ func benchRLP(b *testing.B, encode bool) { // Test decoding b.Run(tc.name, func(b *testing.B) { b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { if err := rlp.DecodeBytes(data, tc.obj); err != nil { b.Fatal(err) } From a499a11a16a09c30bb426cc3a0334a0d0ebd3d6c Mon Sep 17 00:00:00 2001 From: cui Date: Sat, 20 Sep 2025 07:12:41 +0800 Subject: [PATCH 090/119] crypto: using testing.B.Loop (#32645) before: go test -run=^$ -bench=. ./crypto/... 94.83s user 2.68s system 138% cpu 1:10.55 tota after: go test -run=^$ -bench=. ./crypto/... 75.43s user 2.58s system 123% cpu 1:03.01 total --- crypto/blake2b/blake2b_test.go | 6 ++---- crypto/crypto_test.go | 6 +++--- crypto/ecies/ecies_test.go | 8 +++----- crypto/kzg4844/kzg4844_test.go | 15 +++++---------- crypto/secp256k1/secp256_test.go | 8 ++------ crypto/signature_test.go | 6 +++--- 6 files changed, 18 insertions(+), 31 deletions(-) diff --git a/crypto/blake2b/blake2b_test.go b/crypto/blake2b/blake2b_test.go index 9d24444a27..8f210445fc 100644 --- a/crypto/blake2b/blake2b_test.go +++ b/crypto/blake2b/blake2b_test.go @@ -303,8 +303,7 @@ func benchmarkSum(b *testing.B, size int, sse4, avx, avx2 bool) { data := make([]byte, size) b.SetBytes(int64(size)) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { Sum512(data) } } @@ -319,8 +318,7 @@ func benchmarkWrite(b *testing.B, size int, sse4, avx, avx2 bool) { data := make([]byte, size) h, _ := New512(nil) b.SetBytes(int64(size)) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { h.Write(data) } } diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index e620d6ee3a..d803ab27c5 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -60,7 +60,7 @@ func TestToECDSAErrors(t *testing.T) { func BenchmarkSha3(b *testing.B) { a := []byte("hello world") - for i := 0; i < b.N; i++ { + for b.Loop() { Keccak256(a) } } @@ -310,7 +310,7 @@ func BenchmarkKeccak256Hash(b *testing.B) { rand.Read(input[:]) b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { Keccak256Hash(input[:]) } } @@ -329,7 +329,7 @@ func BenchmarkHashData(b *testing.B) { rand.Read(input[:]) b.ReportAllocs() - for i := 0; i < b.N; i++ { + for b.Loop() { HashData(buffer, input[:]) } } diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index e3da71010e..e822db2783 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -164,7 +164,7 @@ func TestTooBigSharedKey(t *testing.T) { // Benchmark the generation of P256 keys. func BenchmarkGenerateKeyP256(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { if _, err := GenerateKey(rand.Reader, elliptic.P256(), nil); err != nil { b.Fatal(err) } @@ -177,8 +177,7 @@ func BenchmarkGenSharedKeyP256(b *testing.B) { if err != nil { b.Fatal(err) } - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { _, err := prv.GenerateShared(&prv.PublicKey, 16, 16) if err != nil { b.Fatal(err) @@ -192,8 +191,7 @@ func BenchmarkGenSharedKeyS256(b *testing.B) { if err != nil { b.Fatal(err) } - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { _, err := prv.GenerateShared(&prv.PublicKey, 16, 16) if err != nil { b.Fatal(err) diff --git a/crypto/kzg4844/kzg4844_test.go b/crypto/kzg4844/kzg4844_test.go index 7e73efd850..d398a0f48b 100644 --- a/crypto/kzg4844/kzg4844_test.go +++ b/crypto/kzg4844/kzg4844_test.go @@ -105,8 +105,7 @@ func benchmarkBlobToCommitment(b *testing.B, ckzg bool) { blob := randBlob() - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { BlobToCommitment(blob) } } @@ -125,8 +124,7 @@ func benchmarkComputeProof(b *testing.B, ckzg bool) { point = randFieldElement() ) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { ComputeProof(blob, point) } } @@ -147,8 +145,7 @@ func benchmarkVerifyProof(b *testing.B, ckzg bool) { proof, claim, _ = ComputeProof(blob, point) ) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { VerifyProof(commitment, point, claim, proof) } } @@ -167,8 +164,7 @@ func benchmarkComputeBlobProof(b *testing.B, ckzg bool) { commitment, _ = BlobToCommitment(blob) ) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { ComputeBlobProof(blob, commitment) } } @@ -188,8 +184,7 @@ func benchmarkVerifyBlobProof(b *testing.B, ckzg bool) { proof, _ = ComputeBlobProof(blob, commitment) ) - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { VerifyBlobProof(blob, commitment, proof) } } diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go index 4827cc5b25..c7485bca08 100644 --- a/crypto/secp256k1/secp256_test.go +++ b/crypto/secp256k1/secp256_test.go @@ -221,9 +221,7 @@ func TestRecoverSanity(t *testing.T) { func BenchmarkSign(b *testing.B) { _, seckey := generateKeyPair() msg := csprngEntropy(32) - b.ResetTimer() - - for i := 0; i < b.N; i++ { + for b.Loop() { Sign(msg, seckey) } } @@ -232,9 +230,7 @@ func BenchmarkRecover(b *testing.B) { msg := csprngEntropy(32) _, seckey := generateKeyPair() sig, _ := Sign(msg, seckey) - b.ResetTimer() - - for i := 0; i < b.N; i++ { + for b.Loop() { RecoverPubkey(msg, sig) } } diff --git a/crypto/signature_test.go b/crypto/signature_test.go index 74d683b507..b1f2960254 100644 --- a/crypto/signature_test.go +++ b/crypto/signature_test.go @@ -135,7 +135,7 @@ func TestPubkeyRandom(t *testing.T) { } func BenchmarkEcrecoverSignature(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { if _, err := Ecrecover(testmsg, testsig); err != nil { b.Fatal("ecrecover error", err) } @@ -144,7 +144,7 @@ func BenchmarkEcrecoverSignature(b *testing.B) { func BenchmarkVerifySignature(b *testing.B) { sig := testsig[:len(testsig)-1] // remove recovery id - for i := 0; i < b.N; i++ { + for b.Loop() { if !VerifySignature(testpubkey, testmsg, sig) { b.Fatal("verify error") } @@ -152,7 +152,7 @@ func BenchmarkVerifySignature(b *testing.B) { } func BenchmarkDecompressPubkey(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { if _, err := DecompressPubkey(testpubkeyc); err != nil { b.Fatal(err) } From c7eb37608b02a73d2a4bac959d68e718b273f9b0 Mon Sep 17 00:00:00 2001 From: Gengar Date: Sat, 20 Sep 2025 02:13:58 +0300 Subject: [PATCH 091/119] fix: correct typo in TestMustParseUint64Panic error message (#32648) Fix typo in test error message where "MustParseBig" was incorrectly used instead of "MustParseUint64" in the TestMustParseUint64Panic function. The test still functions correctly, but now the error message accurately reflects the function being tested. --- common/math/integer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/math/integer_test.go b/common/math/integer_test.go index 4643a43f20..feb761c6dc 100644 --- a/common/math/integer_test.go +++ b/common/math/integer_test.go @@ -110,7 +110,7 @@ func TestMustParseUint64(t *testing.T) { func TestMustParseUint64Panic(t *testing.T) { defer func() { if recover() == nil { - t.Error("MustParseBig should've panicked") + t.Error("MustParseUint64 should've panicked") } }() MustParseUint64("ggg") From f770ec1ffcbd3afc08aa8959abba100f3f11fcce Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Sat, 20 Sep 2025 07:20:44 +0800 Subject: [PATCH 092/119] common, eth: remove duplicate test cases (#32624) Remove redundant duplicate test vectors. The two entries were identical and back-to-back, providing no additional coverage while adding noise. Keeping a single instance maintains test intent and clarity without altering behavior. --- common/hexutil/json_test.go | 1 - eth/protocols/snap/gentrie_test.go | 2 -- eth/protocols/snap/sync_test.go | 1 - 3 files changed, 4 deletions(-) diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go index a014438458..3dc19680f9 100644 --- a/common/hexutil/json_test.go +++ b/common/hexutil/json_test.go @@ -408,7 +408,6 @@ func TestUnmarshalFixedUnprefixedText(t *testing.T) { {input: "0x2", wantErr: ErrOddLength}, {input: "2", wantErr: ErrOddLength}, {input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")}, - {input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")}, // check that output is not modified for partially correct input {input: "444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}}, {input: "0x444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}}, diff --git a/eth/protocols/snap/gentrie_test.go b/eth/protocols/snap/gentrie_test.go index 2da4f3c866..5998840b52 100644 --- a/eth/protocols/snap/gentrie_test.go +++ b/eth/protocols/snap/gentrie_test.go @@ -239,7 +239,6 @@ func TestPartialGentree(t *testing.T) { {1, len(entries) - 1}, // no left {2, len(entries) - 1}, // no left {2, len(entries) - 2}, // no left and right - {2, len(entries) - 2}, // no left and right {len(entries) / 2, len(entries) / 2}, // single {0, 0}, // single first {len(entries) - 1, len(entries) - 1}, // single last @@ -348,7 +347,6 @@ func TestGentreeDanglingClearing(t *testing.T) { {1, len(entries) - 1}, // no left {2, len(entries) - 1}, // no left {2, len(entries) - 2}, // no left and right - {2, len(entries) - 2}, // no left and right {len(entries) / 2, len(entries) / 2}, // single {0, 0}, // single first {len(entries) - 1, len(entries) - 1}, // single last diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index 89a782496f..713b358ff8 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -597,7 +597,6 @@ func testSyncBloatedProof(t *testing.T, scheme string) { proof := trienode.NewProofSet() if err := t.accountTrie.Prove(origin[:], proof); err != nil { t.logger.Error("Could not prove origin", "origin", origin, "error", err) - t.logger.Error("Could not prove origin", "origin", origin, "error", err) } // The bloat: add proof of every single element for _, entry := range t.accountValues { From b62e0348e0ea505ae14c654de83af530db2f154f Mon Sep 17 00:00:00 2001 From: MozirDmitriy Date: Sat, 20 Sep 2025 02:23:50 +0300 Subject: [PATCH 093/119] =?UTF-8?q?core/rawdb:=20fix=20bad=20blocks=20sort?= =?UTF-8?q?ed=20failure=20message=20to=20map=20index=E2=86=92number=20corr?= =?UTF-8?q?ectly=20(#32627)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the t.Fatalf format arguments in TestBadBlockStorage to match the intended #index output. Previously, the left number used i+1 and the right index used the block number, producing misleading diagnostics. Correct mapping improves test failure clarity and debuggability. --- core/rawdb/accessors_chain_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 5ed65b69b5..819788b4da 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -246,7 +246,7 @@ func TestBadBlockStorage(t *testing.T) { } for i := 0; i < len(badBlocks)-1; i++ { if badBlocks[i].NumberU64() < badBlocks[i+1].NumberU64() { - t.Fatalf("The bad blocks are not sorted #[%d](%d) < #[%d](%d)", i, i+1, badBlocks[i].NumberU64(), badBlocks[i+1].NumberU64()) + t.Fatalf("The bad blocks are not sorted #[%d](%d) < #[%d](%d)", i, badBlocks[i].NumberU64(), i+1, badBlocks[i+1].NumberU64()) } } From fd65f560318828632627aec6624658b42c689aed Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Fri, 19 Sep 2025 19:45:11 -0600 Subject: [PATCH 094/119] params: update config description links to new format (#32681) A PR in the specs repo broke our existing links: https://github.com/ethereum/execution-specs/pull/1416 This PR fixes it and adds the Prague and Osaka specs. --- params/config.go | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/params/config.go b/params/config.go index f8fc35454c..cf94fb6758 100644 --- a/params/config.go +++ b/params/config.go @@ -509,33 +509,33 @@ func (c *ChainConfig) Description() string { // makes sense for mainnet should be optional at printing to avoid bloating // the output for testnets and private networks. banner += "Pre-Merge hard forks (block based):\n" - banner += fmt.Sprintf(" - Homestead: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/homestead.md)\n", c.HomesteadBlock) + banner += fmt.Sprintf(" - Homestead: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/homestead/__init__.py.html)\n", c.HomesteadBlock) if c.DAOForkBlock != nil { - banner += fmt.Sprintf(" - DAO Fork: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/dao-fork.md)\n", c.DAOForkBlock) + banner += fmt.Sprintf(" - DAO Fork: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/dao_fork/__init__.py.html)\n", c.DAOForkBlock) } - banner += fmt.Sprintf(" - Tangerine Whistle (EIP 150): #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/tangerine-whistle.md)\n", c.EIP150Block) - banner += fmt.Sprintf(" - Spurious Dragon/1 (EIP 155): #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md)\n", c.EIP155Block) - banner += fmt.Sprintf(" - Spurious Dragon/2 (EIP 158): #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md)\n", c.EIP155Block) - banner += fmt.Sprintf(" - Byzantium: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/byzantium.md)\n", c.ByzantiumBlock) - banner += fmt.Sprintf(" - Constantinople: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/constantinople.md)\n", c.ConstantinopleBlock) - banner += fmt.Sprintf(" - Petersburg: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/petersburg.md)\n", c.PetersburgBlock) - banner += fmt.Sprintf(" - Istanbul: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/istanbul.md)\n", c.IstanbulBlock) + banner += fmt.Sprintf(" - Tangerine Whistle (EIP 150): #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/tangerine_whistle/__init__.py.html)\n", c.EIP150Block) + banner += fmt.Sprintf(" - Spurious Dragon/1 (EIP 155): #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/spurious_dragon/__init__.py.html)\n", c.EIP155Block) + banner += fmt.Sprintf(" - Spurious Dragon/2 (EIP 158): #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/spurious_dragon/__init__.py.html)\n", c.EIP155Block) + banner += fmt.Sprintf(" - Byzantium: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/byzantium/__init__.py.html)\n", c.ByzantiumBlock) + banner += fmt.Sprintf(" - Constantinople: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/constantinople/__init__.py.html)\n", c.ConstantinopleBlock) + banner += fmt.Sprintf(" - Petersburg: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/constantinople/__init__.py.html)\n", c.PetersburgBlock) + banner += fmt.Sprintf(" - Istanbul: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/istanbul/__init__.py.html)\n", c.IstanbulBlock) if c.MuirGlacierBlock != nil { - banner += fmt.Sprintf(" - Muir Glacier: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/muir-glacier.md)\n", c.MuirGlacierBlock) + banner += fmt.Sprintf(" - Muir Glacier: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/muir_glacier/__init__.py.html)\n", c.MuirGlacierBlock) } - banner += fmt.Sprintf(" - Berlin: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md)\n", c.BerlinBlock) - banner += fmt.Sprintf(" - London: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md)\n", c.LondonBlock) + banner += fmt.Sprintf(" - Berlin: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/berlin/__init__.py.html)\n", c.BerlinBlock) + banner += fmt.Sprintf(" - London: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/london/__init__.py.html)\n", c.LondonBlock) if c.ArrowGlacierBlock != nil { - banner += fmt.Sprintf(" - Arrow Glacier: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/arrow-glacier.md)\n", c.ArrowGlacierBlock) + banner += fmt.Sprintf(" - Arrow Glacier: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/arrow_glacier/__init__.py.html)\n", c.ArrowGlacierBlock) } if c.GrayGlacierBlock != nil { - banner += fmt.Sprintf(" - Gray Glacier: #%-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/gray-glacier.md)\n", c.GrayGlacierBlock) + banner += fmt.Sprintf(" - Gray Glacier: #%-8v (https://ethereum.github.io/execution-specs/src/ethereum/forks/gray_glacier/__init__.py.html)\n", c.GrayGlacierBlock) } banner += "\n" // Add a special section for the merge as it's non-obvious banner += "Merge configured:\n" - banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md\n" + banner += " - Hard-fork specification: https://ethereum.github.io/execution-specs/src/ethereum/forks/paris/__init__.py.html\n" banner += " - Network known to be merged\n" banner += fmt.Sprintf(" - Total terminal difficulty: %v\n", c.TerminalTotalDifficulty) if c.MergeNetsplitBlock != nil { @@ -546,16 +546,16 @@ func (c *ChainConfig) Description() string { // Create a list of forks post-merge banner += "Post-Merge hard forks (timestamp based):\n" if c.ShanghaiTime != nil { - banner += fmt.Sprintf(" - Shanghai: @%-10v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md)\n", *c.ShanghaiTime) + banner += fmt.Sprintf(" - Shanghai: @%-10v (https://ethereum.github.io/execution-specs/src/ethereum/forks/shanghai/__init__.py.html)\n", *c.ShanghaiTime) } if c.CancunTime != nil { - banner += fmt.Sprintf(" - Cancun: @%-10v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/cancun.md)\n", *c.CancunTime) + banner += fmt.Sprintf(" - Cancun: @%-10v (https://ethereum.github.io/execution-specs/src/ethereum/forks/cancun/__init__.py.html)\n", *c.CancunTime) } if c.PragueTime != nil { - banner += fmt.Sprintf(" - Prague: @%-10v\n", *c.PragueTime) + banner += fmt.Sprintf(" - Prague: @%-10v (https://ethereum.github.io/execution-specs/src/ethereum/forks/prague/__init__.py.html)\n", *c.PragueTime) } if c.OsakaTime != nil { - banner += fmt.Sprintf(" - Osaka: @%-10v\n", *c.OsakaTime) + banner += fmt.Sprintf(" - Osaka: @%-10v (https://ethereum.github.io/execution-specs/src/ethereum/forks/osaka/__init__.py.html)\n", *c.OsakaTime) } if c.VerkleTime != nil { banner += fmt.Sprintf(" - Verkle: @%-10v\n", *c.VerkleTime) From 684f0db4a29b966534302f8f328b29d6906b614f Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Sat, 20 Sep 2025 10:19:55 +0800 Subject: [PATCH 095/119] core/txpool/blobpool: introduce sidecar conversion for legacy blob transactions (#32656) This pull request introduces a queue for legacy sidecar conversion to handle transactions that persist after the Osaka fork. Simply dropping these transactions would significantly harm the user experience. To balance usability with system complexity, we have introduced a conversion time window of two hours post Osaka fork. During this period, the system will accept legacy blob transactions and convert them in a background process. After the window, all legacy transactions will be rejected. Notably, all the blob transactions will be validated statically before the conversion, and also all conversion are performed in a single thread, minimize the risk of being DoS. We believe this two hour window provides sufficient time to process in-flight legacy transactions and allows submitters to migrate to the new format. --------- Co-authored-by: Felix Lange --- core/txpool/blobpool/blobpool.go | 123 ++++++++++++++----- core/txpool/blobpool/blobpool_test.go | 42 +++++-- core/txpool/blobpool/conversion.go | 152 ++++++++++++++++++++++++ core/txpool/blobpool/conversion_test.go | 101 ++++++++++++++++ core/txpool/validation.go | 11 +- 5 files changed, 384 insertions(+), 45 deletions(-) create mode 100644 core/txpool/blobpool/conversion.go create mode 100644 core/txpool/blobpool/conversion_test.go diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 2229f1544c..1bf48cf949 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -27,6 +27,7 @@ import ( "path/filepath" "sort" "sync" + "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" @@ -61,11 +62,11 @@ const ( // small buffer is added to the proof overhead. txBlobOverhead = uint32(kzg4844.CellProofsPerBlob*len(kzg4844.Proof{}) + 64) - // txMaxSize is the maximum size a single transaction can have, outside - // the included blobs. Since blob transactions are pulled instead of pushed, - // and only a small metadata is kept in ram, the rest is on disk, there is - // no critical limit that should be enforced. Still, capping it to some sane - // limit can never hurt. + // txMaxSize is the maximum size a single transaction can have, including the + // blobs. Since blob transactions are pulled instead of pushed, and only a + // small metadata is kept in ram, the rest is on disk, there is no critical + // limit that should be enforced. Still, capping it to some sane limit can + // never hurt, which is aligned with maxBlobsPerTx constraint enforced internally. txMaxSize = 1024 * 1024 // maxBlobsPerTx is the maximum number of blobs that a single transaction can @@ -93,6 +94,11 @@ const ( // storeVersion is the current slotter layout used for the billy.Database // store. storeVersion = 1 + + // conversionTimeWindow defines the period after the Osaka fork during which + // the pool will still accept and convert legacy blob transactions. After this + // window, all legacy blob transactions will be rejected. + conversionTimeWindow = time.Hour * 2 ) // blobTxMeta is the minimal subset of types.BlobTx necessary to validate and @@ -329,12 +335,13 @@ type BlobPool struct { stored uint64 // Useful data size of all transactions on disk limbo *limbo // Persistent data store for the non-finalized blobs - signer types.Signer // Transaction signer to use for sender recovery - chain BlockChain // Chain object to access the state through + signer types.Signer // Transaction signer to use for sender recovery + chain BlockChain // Chain object to access the state through + cQueue *conversionQueue // The queue for performing legacy sidecar conversion - head *types.Header // Current head of the chain - state *state.StateDB // Current state at the head of the chain - gasTip *uint256.Int // Currently accepted minimum gas tip + head atomic.Pointer[types.Header] // Current head of the chain + state *state.StateDB // Current state at the head of the chain + gasTip atomic.Pointer[uint256.Int] // Currently accepted minimum gas tip lookup *lookup // Lookup table mapping blobs to txs and txs to billy entries index map[common.Address][]*blobTxMeta // Blob transactions grouped by accounts, sorted by nonce @@ -359,6 +366,7 @@ func New(config Config, chain BlockChain, hasPendingAuth func(common.Address) bo hasPendingAuth: hasPendingAuth, signer: types.LatestSigner(chain.Config()), chain: chain, + cQueue: newConversionQueue(), // Deprecate it after the osaka fork lookup: newLookup(), index: make(map[common.Address][]*blobTxMeta), spent: make(map[common.Address]*uint256.Int), @@ -400,7 +408,8 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser if err != nil { return err } - p.head, p.state = head, state + p.head.Store(head) + p.state = state // Create new slotter for pre-Osaka blob configuration. slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) @@ -440,11 +449,11 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser p.recheck(addr, nil) } var ( - basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), p.head)) + basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), head)) blobfee = uint256.NewInt(params.BlobTxMinBlobGasprice) ) - if p.head.ExcessBlobGas != nil { - blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(p.chain.Config(), p.head)) + if head.ExcessBlobGas != nil { + blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(p.chain.Config(), head)) } p.evict = newPriceHeap(basefee, blobfee, p.index) @@ -474,6 +483,9 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser // Close closes down the underlying persistent store. func (p *BlobPool) Close() error { + // Terminate the conversion queue + p.cQueue.close() + var errs []error if p.limbo != nil { // Close might be invoked due to error in constructor, before p,limbo is set if err := p.limbo.Close(); err != nil { @@ -832,7 +844,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) { log.Error("Failed to reset blobpool state", "err", err) return } - p.head = newHead + p.head.Store(newHead) p.state = statedb // Run the reorg between the old and new head and figure out which accounts @@ -855,7 +867,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) { } } // Flush out any blobs from limbo that are older than the latest finality - if p.chain.Config().IsCancun(p.head.Number, p.head.Time) { + if p.chain.Config().IsCancun(newHead.Number, newHead.Time) { p.limbo.finalize(p.chain.CurrentFinalBlock()) } // Reset the price heap for the new set of basefee/blobfee pairs @@ -1056,14 +1068,15 @@ func (p *BlobPool) SetGasTip(tip *big.Int) { defer p.lock.Unlock() // Store the new minimum gas tip - old := p.gasTip - p.gasTip = uint256.MustFromBig(tip) + old := p.gasTip.Load() + newTip := uint256.MustFromBig(tip) + p.gasTip.Store(newTip) // If the min miner fee increased, remove transactions below the new threshold - if old == nil || p.gasTip.Cmp(old) > 0 { + if old == nil || newTip.Cmp(old) > 0 { for addr, txs := range p.index { for i, tx := range txs { - if tx.execTipCap.Cmp(p.gasTip) < 0 { + if tx.execTipCap.Cmp(newTip) < 0 { // Drop the offending transaction var ( ids = []uint64{tx.id} @@ -1123,10 +1136,10 @@ func (p *BlobPool) ValidateTxBasics(tx *types.Transaction) error { Config: p.chain.Config(), Accept: 1 << types.BlobTxType, MaxSize: txMaxSize, - MinTip: p.gasTip.ToBig(), + MinTip: p.gasTip.Load().ToBig(), MaxBlobCount: maxBlobsPerTx, } - return txpool.ValidateTransaction(tx, p.head, p.signer, opts) + return txpool.ValidateTransaction(tx, p.head.Load(), p.signer, opts) } // checkDelegationLimit determines if the tx sender is delegated or has a @@ -1164,10 +1177,10 @@ func (p *BlobPool) checkDelegationLimit(tx *types.Transaction) error { // validateTx checks whether a transaction is valid according to the consensus // rules and adheres to some heuristic limits of the local node (price and size). +// +// This function assumes the static validation has been performed already and +// only runs the stateful checks with lock protection. func (p *BlobPool) validateTx(tx *types.Transaction) error { - if err := p.ValidateTxBasics(tx); err != nil { - return err - } // Ensure the transaction adheres to the stateful pool filters (nonce, balance) stateOpts := &txpool.ValidationOptionsWithState{ State: p.state, @@ -1444,17 +1457,67 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int { return available } +// preCheck performs the static validation upon the provided txs and converts +// the legacy sidecars if Osaka fork has been activated with a short time window. +// +// This function is pure static and lock free. +func (p *BlobPool) preCheck(txs []*types.Transaction) ([]*types.Transaction, []error) { + var ( + head = p.head.Load() + isOsaka = p.chain.Config().IsOsaka(head.Number, head.Time) + deadline time.Time + ) + if isOsaka { + deadline = time.Unix(int64(*p.chain.Config().OsakaTime), 0).Add(conversionTimeWindow) + } + var errs []error + for _, tx := range txs { + // Validate the transaction statically at first to avoid unnecessary + // conversion. This step doesn't require lock protection. + if err := p.ValidateTxBasics(tx); err != nil { + errs = append(errs, err) + continue + } + // Before the Osaka fork, reject the blob txs with cell proofs + if !isOsaka { + if tx.BlobTxSidecar().Version == types.BlobSidecarVersion0 { + errs = append(errs, nil) + } else { + errs = append(errs, errors.New("cell proof is not supported yet")) + } + continue + } + // After the Osaka fork, reject the legacy blob txs if the conversion + // time window is passed. + if tx.BlobTxSidecar().Version == types.BlobSidecarVersion1 { + errs = append(errs, nil) + continue + } + if head.Time > uint64(deadline.Unix()) { + errs = append(errs, errors.New("legacy blob tx is not supported")) + continue + } + // Convert the legacy sidecar after Osaka fork. This could be a long + // procedure which takes a few seconds, even minutes if there is a long + // queue. Fortunately it will only block the routine of the source peer + // announcing the tx, without affecting other parts. + errs = append(errs, p.cQueue.convert(tx)) + } + return txs, errs +} + // Add inserts a set of blob transactions into the pool if they pass validation (both // consensus validity and pool restrictions). -// -// Note, if sync is set the method will block until all internal maintenance -// related to the add is finished. Only use this during tests for determinism. func (p *BlobPool) Add(txs []*types.Transaction, sync bool) []error { var ( - errs = make([]error, len(txs)) + errs []error adds = make([]*types.Transaction, 0, len(txs)) ) + txs, errs = p.preCheck(txs) for i, tx := range txs { + if errs[i] != nil { + continue + } errs[i] = p.add(tx) if errs[i] == nil { adds = append(adds, tx.WithoutBlobTxSidecar()) @@ -1949,7 +2012,7 @@ func (p *BlobPool) Clear() { p.spent = make(map[common.Address]*uint256.Int) var ( - basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), p.head)) + basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), p.head.Load())) blobfee = uint256.NewInt(params.BlobTxMinBlobGasprice) ) p.evict = newPriceHeap(basefee, blobfee, p.index) diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 57d27962ce..75a87940bd 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -88,6 +88,12 @@ type testBlockChain struct { statedb *state.StateDB blocks map[uint64]*types.Block + + blockTime *uint64 +} + +func (bc *testBlockChain) setHeadTime(time uint64) { + bc.blockTime = &time } func (bc *testBlockChain) Config() *params.ChainConfig { @@ -105,6 +111,10 @@ func (bc *testBlockChain) CurrentBlock() *types.Header { blockTime = *bc.config.CancunTime + 1 gasLimit = uint64(30_000_000) ) + if bc.blockTime != nil { + blockTime = *bc.blockTime + } + lo := new(big.Int) hi := new(big.Int).Mul(big.NewInt(5714), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) @@ -1748,8 +1758,8 @@ func TestAdd(t *testing.T) { // Add each transaction one by one, verifying the pool internals in between for j, add := range tt.adds { signed, _ := types.SignNewTx(keys[add.from], types.LatestSigner(params.MainnetChainConfig), add.tx) - if err := pool.add(signed); !errors.Is(err, add.err) { - t.Errorf("test %d, tx %d: adding transaction error mismatch: have %v, want %v", i, j, err, add.err) + if errs := pool.Add([]*types.Transaction{signed}, true); !errors.Is(errs[0], add.err) { + t.Errorf("test %d, tx %d: adding transaction error mismatch: have %v, want %v", i, j, errs[0], add.err) } if add.err == nil { size, exist := pool.lookup.sizeOfTx(signed.Hash()) @@ -1796,8 +1806,14 @@ func TestAdd(t *testing.T) { } } -// Tests adding transactions with legacy sidecars are correctly rejected. +// Tests that transactions with legacy sidecars are accepted within the +// conversion window but rejected after it has passed. func TestAddLegacyBlobTx(t *testing.T) { + testAddLegacyBlobTx(t, true) // conversion window has not yet passed + testAddLegacyBlobTx(t, false) // conversion window passed +} + +func testAddLegacyBlobTx(t *testing.T, accept bool) { var ( key1, _ = crypto.GenerateKey() key2, _ = crypto.GenerateKey() @@ -1817,6 +1833,15 @@ func TestAddLegacyBlobTx(t *testing.T) { blobfee: uint256.NewInt(105), statedb: statedb, } + var timeDiff uint64 + if accept { + timeDiff = uint64(conversionTimeWindow.Seconds()) - 1 + } else { + timeDiff = uint64(conversionTimeWindow.Seconds()) + 1 + } + time := *params.MergedTestChainConfig.OsakaTime + timeDiff + chain.setHeadTime(time) + pool := New(Config{Datadir: t.TempDir()}, chain, nil) if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) @@ -1826,12 +1851,15 @@ func TestAddLegacyBlobTx(t *testing.T) { var ( tx1 = makeMultiBlobTx(0, 1, 1000, 100, 6, 0, key1, types.BlobSidecarVersion0) tx2 = makeMultiBlobTx(0, 1, 800, 70, 6, 6, key2, types.BlobSidecarVersion0) - tx3 = makeMultiBlobTx(1, 1, 800, 70, 6, 12, key2, types.BlobSidecarVersion1) + txs = []*types.Transaction{tx1, tx2} ) - errs := pool.Add([]*types.Transaction{tx1, tx2, tx3}, true) + errs := pool.Add(txs, true) for _, err := range errs { - if err == nil { - t.Fatalf("expected tx add to fail") + if accept && err != nil { + t.Fatalf("expected tx add to succeed, %v", err) + } + if !accept && err == nil { + t.Fatal("expected tx add to fail") } } verifyPoolInternals(t, pool) diff --git a/core/txpool/blobpool/conversion.go b/core/txpool/blobpool/conversion.go new file mode 100644 index 0000000000..5026892fc8 --- /dev/null +++ b/core/txpool/blobpool/conversion.go @@ -0,0 +1,152 @@ +// Copyright 2025 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 . + +package blobpool + +import ( + "errors" + "slices" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +// maxPendingConversionTasks caps the number of pending conversion tasks. This +// prevents excessive memory usage; the worst-case scenario (2k transactions +// with 6 blobs each) would consume approximately 1.5GB of memory. +const maxPendingConversionTasks = 2048 + +// cTask represents a conversion task with an attached legacy blob transaction. +type cTask struct { + tx *types.Transaction // Legacy blob transaction + done chan error // Channel for signaling back if the conversion succeeds +} + +// conversionQueue is a dedicated queue for converting legacy blob transactions +// received from the network after the Osaka fork. Since conversion is expensive, +// it is performed in the background by a single thread, ensuring the main Geth +// process is not overloaded. +type conversionQueue struct { + tasks chan *cTask + quit chan struct{} + closed chan struct{} +} + +// newConversionQueue constructs the conversion queue. +func newConversionQueue() *conversionQueue { + q := &conversionQueue{ + tasks: make(chan *cTask), + quit: make(chan struct{}), + closed: make(chan struct{}), + } + go q.loop() + return q +} + +// convert accepts a legacy blob transaction with version-0 blobs and queues it +// for conversion. +// +// This function may block for a long time until the transaction is processed. +func (q *conversionQueue) convert(tx *types.Transaction) error { + done := make(chan error, 1) + select { + case q.tasks <- &cTask{tx: tx, done: done}: + return <-done + case <-q.closed: + return errors.New("conversion queue closed") + } +} + +// close terminates the conversion queue. +func (q *conversionQueue) close() { + select { + case <-q.closed: + return + default: + close(q.quit) + <-q.closed + } +} + +// run converts a batch of legacy blob txs to the new cell proof format. +func (q *conversionQueue) run(tasks []*cTask, done chan struct{}, interrupt *atomic.Int32) { + defer close(done) + + for _, t := range tasks { + if interrupt != nil && interrupt.Load() != 0 { + t.done <- errors.New("conversion is interrupted") + continue + } + sidecar := t.tx.BlobTxSidecar() + if sidecar == nil { + t.done <- errors.New("tx without sidecar") + continue + } + // Run the conversion, the original sidecar will be mutated in place + start := time.Now() + err := sidecar.ToV1() + t.done <- err + log.Trace("Converted legacy blob tx", "hash", t.tx.Hash(), "err", err, "elapsed", common.PrettyDuration(time.Since(start))) + } +} + +func (q *conversionQueue) loop() { + defer close(q.closed) + + var ( + done chan struct{} // Non-nil if background routine is active + interrupt *atomic.Int32 // Flag to signal conversion interruption + + // The pending tasks for sidecar conversion. We assume the number of legacy + // blob transactions requiring conversion will not be excessive. However, + // a hard cap is applied as a protective measure. + cTasks []*cTask + ) + for { + select { + case t := <-q.tasks: + if len(cTasks) >= maxPendingConversionTasks { + t.done <- errors.New("conversion queue is overloaded") + continue + } + cTasks = append(cTasks, t) + + // Launch the background conversion thread if it's idle + if done == nil { + done, interrupt = make(chan struct{}), new(atomic.Int32) + + tasks := slices.Clone(cTasks) + cTasks = cTasks[:0] + go q.run(tasks, done, interrupt) + } + + case <-done: + done, interrupt = nil, nil + + case <-q.quit: + if done == nil { + return + } + interrupt.Store(1) + log.Debug("Waiting for blob proof conversion to exit") + <-done + return + } + } +} diff --git a/core/txpool/blobpool/conversion_test.go b/core/txpool/blobpool/conversion_test.go new file mode 100644 index 0000000000..a9fd26dbaf --- /dev/null +++ b/core/txpool/blobpool/conversion_test.go @@ -0,0 +1,101 @@ +// Copyright 2025 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 . + +package blobpool + +import ( + "crypto/ecdsa" + "crypto/sha256" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +// createV1BlobTx creates a blob transaction with version 1 sidecar for testing. +func createV1BlobTx(nonce uint64, key *ecdsa.PrivateKey) *types.Transaction { + blob := &kzg4844.Blob{byte(nonce)} + commitment, _ := kzg4844.BlobToCommitment(blob) + cellProofs, _ := kzg4844.ComputeCellProofs(blob) + + blobtx := &types.BlobTx{ + ChainID: uint256.MustFromBig(params.MainnetChainConfig.ChainID), + Nonce: nonce, + GasTipCap: uint256.NewInt(1), + GasFeeCap: uint256.NewInt(1000), + Gas: 21000, + BlobFeeCap: uint256.NewInt(100), + BlobHashes: []common.Hash{kzg4844.CalcBlobHashV1(sha256.New(), &commitment)}, + Value: uint256.NewInt(100), + Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion1, []kzg4844.Blob{*blob}, []kzg4844.Commitment{commitment}, cellProofs), + } + return types.MustSignNewTx(key, types.LatestSigner(params.MainnetChainConfig), blobtx) +} + +func TestConversionQueueBasic(t *testing.T) { + queue := newConversionQueue() + defer queue.close() + + key, _ := crypto.GenerateKey() + tx := makeTx(0, 1, 1, 1, key) + if err := queue.convert(tx); err != nil { + t.Fatalf("Expected successful conversion, got error: %v", err) + } + if tx.BlobTxSidecar().Version != types.BlobSidecarVersion1 { + t.Errorf("Expected sidecar version to be %d, got %d", types.BlobSidecarVersion1, tx.BlobTxSidecar().Version) + } +} + +func TestConversionQueueV1BlobTx(t *testing.T) { + queue := newConversionQueue() + defer queue.close() + + key, _ := crypto.GenerateKey() + tx := createV1BlobTx(0, key) + version := tx.BlobTxSidecar().Version + + err := queue.convert(tx) + if err != nil { + t.Fatalf("Expected successful conversion, got error: %v", err) + } + if tx.BlobTxSidecar().Version != version { + t.Errorf("Expected sidecar version to remain %d, got %d", version, tx.BlobTxSidecar().Version) + } +} + +func TestConversionQueueClosed(t *testing.T) { + queue := newConversionQueue() + + // Close the queue first + queue.close() + key, _ := crypto.GenerateKey() + tx := makeTx(0, 1, 1, 1, key) + + err := queue.convert(tx) + if err == nil { + t.Fatal("Expected error when converting on closed queue, got nil") + } +} + +func TestConversionQueueDoubleClose(t *testing.T) { + queue := newConversionQueue() + queue.close() + queue.close() // Should not panic +} diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 46974fad3c..df53f30a86 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -176,16 +176,14 @@ func validateBlobTx(tx *types.Transaction, head *types.Header, opts *ValidationO return err } // Fork-specific sidecar checks, including proof verification. - if opts.Config.IsOsaka(head.Number, head.Time) { + if sidecar.Version == types.BlobSidecarVersion1 { return validateBlobSidecarOsaka(sidecar, hashes) + } else { + return validateBlobSidecarLegacy(sidecar, hashes) } - return validateBlobSidecarLegacy(sidecar, hashes) } func validateBlobSidecarLegacy(sidecar *types.BlobTxSidecar, hashes []common.Hash) error { - if sidecar.Version != types.BlobSidecarVersion0 { - return fmt.Errorf("invalid sidecar version pre-osaka: %v", sidecar.Version) - } if len(sidecar.Proofs) != len(hashes) { return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)) } @@ -198,9 +196,6 @@ func validateBlobSidecarLegacy(sidecar *types.BlobTxSidecar, hashes []common.Has } func validateBlobSidecarOsaka(sidecar *types.BlobTxSidecar, hashes []common.Hash) error { - if sidecar.Version != types.BlobSidecarVersion1 { - return fmt.Errorf("invalid sidecar version post-osaka: %v", sidecar.Version) - } if len(sidecar.Proofs) != len(hashes)*kzg4844.CellProofsPerBlob { return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)*kzg4844.CellProofsPerBlob) } From 4414e2833f92f437d0a68b53ed95ac5756a90a16 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Sat, 20 Sep 2025 01:32:21 -0600 Subject: [PATCH 096/119] go.work.sum: add to repo (#32677) --- go.work.sum | 515 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 go.work.sum diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000000..7413bba03f --- /dev/null +++ b/go.work.sum @@ -0,0 +1,515 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/consensys/bavard v0.1.27/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs= +github.com/consensys/bavard v0.1.31-0.20250406004941-2db259e4b582/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs= +github.com/consensys/gnark-crypto v0.16.0/go.mod h1:Ke3j06ndtPTVvo++PhGNgvm+lgpLvzbcE2MqljY7diU= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +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= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From 479b8031dc61ba01001d3cf086d04ea4d4980a17 Mon Sep 17 00:00:00 2001 From: GarmashAlex Date: Mon, 22 Sep 2025 05:30:29 +0300 Subject: [PATCH 097/119] core/state: fix committed-state expectations in StateDB tests (#32678) --- core/state/statedb_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 147546a3c7..661d17bb7b 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -772,7 +772,7 @@ func TestCopyCommitCopy(t *testing.T) { t.Fatalf("second copy non-committed storage slot mismatch: have %x, want %x", val, sval) } if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) { - t.Fatalf("second copy committed storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("second copy committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Commit state, ensure states can be loaded from disk root, _ := state.Commit(0, false, false) @@ -859,7 +859,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { t.Fatalf("third copy non-committed storage slot mismatch: have %x, want %x", val, sval) } if val := copyThree.GetCommittedState(addr, skey); val != (common.Hash{}) { - t.Fatalf("third copy committed storage slot mismatch: have %x, want %x", val, sval) + t.Fatalf("third copy committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } } @@ -912,10 +912,10 @@ func TestCommitCopy(t *testing.T) { } // Slots cached in the stateDB, available after commit if val := copied.GetState(addr, skey2); val != sval2 { - t.Fatalf("unexpected storage slot: have %x", sval1) + t.Fatalf("unexpected storage slot: have %x, want %x", val, sval2) } if val := copied.GetCommittedState(addr, skey2); val != sval2 { - t.Fatalf("unexpected storage slot: have %x", val) + t.Fatalf("unexpected storage slot: have %x, want %x", val, sval2) } } From ada2db430454d9669fa0415074261f7a2369997e Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 22 Sep 2025 14:45:15 +0800 Subject: [PATCH 098/119] triedb/pathdb: move head truncation log (#32649) Print the `Truncating from head` log only if head truncation is needed. --- triedb/pathdb/history.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/triedb/pathdb/history.go b/triedb/pathdb/history.go index ae022236fe..81b843d9f1 100644 --- a/triedb/pathdb/history.go +++ b/triedb/pathdb/history.go @@ -176,8 +176,6 @@ func truncateFromHead(store ethdb.AncientStore, typ historyType, nhead uint64) ( if err != nil { return 0, err } - log.Info("Truncating from head", "type", typ.String(), "ohead", ohead, "tail", otail, "nhead", nhead) - // Ensure that the truncation target falls within the valid range. if ohead < nhead || nhead < otail { return 0, fmt.Errorf("%w, %s, tail: %d, head: %d, target: %d", errHeadTruncationOutOfRange, typ, otail, ohead, nhead) @@ -186,6 +184,8 @@ func truncateFromHead(store ethdb.AncientStore, typ historyType, nhead uint64) ( if ohead == nhead { return 0, nil } + log.Info("Truncating from head", "type", typ.String(), "ohead", ohead, "tail", otail, "nhead", nhead) + ohead, err = store.TruncateHead(nhead) if err != nil { return 0, err From aa37bd063dd82edad32072171d8e34faf6694194 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 22 Sep 2025 14:48:18 +0800 Subject: [PATCH 099/119] cmd, tests: fix snapshot dump and export-preimages (#32650) Address #32646 --- cmd/geth/snapshot.go | 22 ++++---------- cmd/utils/cmd.go | 62 ++++++++++++++++++++++++++++++++++++++-- tests/state_test.go | 4 +-- tests/state_test_util.go | 2 +- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 994cb149ce..7621dfa93c 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -561,17 +561,11 @@ func dumpState(ctx *cli.Context) error { triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false) defer triedb.Close() - snapConfig := snapshot.Config{ - CacheSize: 256, - Recovery: false, - NoBuild: true, - AsyncBuild: false, - } - snaptree, err := snapshot.New(snapConfig, db, triedb, root) + stateIt, err := utils.NewStateIterator(triedb, db, root) if err != nil { return err } - accIt, err := snaptree.AccountIterator(root, common.BytesToHash(conf.Start)) + accIt, err := stateIt.AccountIterator(root, common.BytesToHash(conf.Start)) if err != nil { return err } @@ -605,7 +599,7 @@ func dumpState(ctx *cli.Context) error { if !conf.SkipStorage { da.Storage = make(map[common.Hash]string) - stIt, err := snaptree.StorageIterator(root, accIt.Hash(), common.Hash{}) + stIt, err := stateIt.StorageIterator(root, accIt.Hash(), common.Hash{}) if err != nil { return err } @@ -658,17 +652,11 @@ func snapshotExportPreimages(ctx *cli.Context) error { } root = headBlock.Root() } - snapConfig := snapshot.Config{ - CacheSize: 256, - Recovery: false, - NoBuild: true, - AsyncBuild: false, - } - snaptree, err := snapshot.New(snapConfig, chaindb, triedb, root) + stateIt, err := utils.NewStateIterator(triedb, chaindb, root) if err != nil { return err } - return utils.ExportSnapshotPreimages(chaindb, snaptree, ctx.Args().First(), root) + return utils.ExportSnapshotPreimages(chaindb, stateIt, ctx.Args().First(), root) } // checkAccount iterates the snap data layers, and looks up the given account diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index b332a060de..db7bd691d8 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -48,6 +48,7 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/triedb" "github.com/urfave/cli/v2" ) @@ -567,9 +568,64 @@ func ExportPreimages(db ethdb.Database, fn string) error { return nil } +// StateIterator is a temporary structure for traversing state in order. It serves +// as an aggregator for both path scheme and hash scheme implementations and should +// be removed once the hash scheme is fully deprecated. +type StateIterator struct { + scheme string + root common.Hash + triedb *triedb.Database + snapshots *snapshot.Tree +} + +// NewStateIterator constructs the state iterator with the specific root. +func NewStateIterator(triedb *triedb.Database, db ethdb.Database, root common.Hash) (*StateIterator, error) { + if triedb.Scheme() == rawdb.PathScheme { + return &StateIterator{ + scheme: rawdb.PathScheme, + root: root, + triedb: triedb, + }, nil + } + config := snapshot.Config{ + CacheSize: 256, + Recovery: false, + NoBuild: true, + AsyncBuild: false, + } + snapshots, err := snapshot.New(config, db, triedb, root) + if err != nil { + return nil, err + } + return &StateIterator{ + scheme: rawdb.HashScheme, + root: root, + triedb: triedb, + snapshots: snapshots, + }, nil +} + +// AccountIterator creates a new account iterator for the specified root hash and +// seeks to a starting account hash. +func (it *StateIterator) AccountIterator(root common.Hash, start common.Hash) (snapshot.AccountIterator, error) { + if it.scheme == rawdb.PathScheme { + return it.triedb.AccountIterator(root, start) + } + return it.snapshots.AccountIterator(root, start) +} + +// StorageIterator creates a new storage iterator for the specified root hash and +// account. The iterator will be moved to the specific start position. +func (it *StateIterator) StorageIterator(root common.Hash, accountHash common.Hash, start common.Hash) (snapshot.StorageIterator, error) { + if it.scheme == rawdb.PathScheme { + return it.triedb.StorageIterator(root, accountHash, start) + } + return it.snapshots.StorageIterator(root, accountHash, start) +} + // ExportSnapshotPreimages exports the preimages corresponding to the enumeration of // the snapshot for a given root. -func ExportSnapshotPreimages(chaindb ethdb.Database, snaptree *snapshot.Tree, fn string, root common.Hash) error { +func ExportSnapshotPreimages(chaindb ethdb.Database, stateIt *StateIterator, fn string, root common.Hash) error { log.Info("Exporting preimages", "file", fn) fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) @@ -602,7 +658,7 @@ func ExportSnapshotPreimages(chaindb ethdb.Database, snaptree *snapshot.Tree, fn ) go func() { defer close(hashCh) - accIt, err := snaptree.AccountIterator(root, common.Hash{}) + accIt, err := stateIt.AccountIterator(root, common.Hash{}) if err != nil { log.Error("Failed to create account iterator", "error", err) return @@ -619,7 +675,7 @@ func ExportSnapshotPreimages(chaindb ethdb.Database, snaptree *snapshot.Tree, fn hashCh <- hashAndPreimageSize{Hash: accIt.Hash(), Size: common.AddressLength} if acc.Root != (common.Hash{}) && acc.Root != types.EmptyRootHash { - stIt, err := snaptree.StorageIterator(root, accIt.Hash(), common.Hash{}) + stIt, err := stateIt.StorageIterator(root, accIt.Hash(), common.Hash{}) if err != nil { log.Error("Failed to create storage iterator", "error", err) return diff --git a/tests/state_test.go b/tests/state_test.go index 301bc3a7a9..f80bda4372 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -156,8 +156,8 @@ func execStateTest(t *testing.T, st *testMatcher, test *StateTest) { withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error { var result error test.Run(subtest, vmconfig, true, rawdb.PathScheme, func(err error, state *StateTestState) { - if state.Snapshots != nil && state.StateDB != nil { - if _, err := state.Snapshots.Journal(state.StateDB.IntermediateRoot(false)); err != nil { + if state.TrieDB != nil && state.StateDB != nil { + if err := state.TrieDB.Journal(state.StateDB.IntermediateRoot(false)); err != nil { result = err return } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index ec7eec1f39..b8d3c4fb92 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -523,7 +523,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bo // If snapshot is requested, initialize the snapshotter and use it in state. var snaps *snapshot.Tree - if snapshotter { + if snapshotter && scheme == rawdb.HashScheme { snapconfig := snapshot.Config{ CacheSize: 1, Recovery: false, From 1597d58fae760859ae344f7d403235b4e39befa5 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 22 Sep 2025 22:20:47 +0200 Subject: [PATCH 100/119] go.work, build: remove workspace file (#32699) https://go.dev/ref/mod#go-work-file advises against checking `go.work` files because they can interfere with local development. We added the workspace file in order to make `go test` and other tools work across multiple modules. But it seems to cause weird issues with the `go.work.sum` file being modified, etc. So with this PR, we instead run all the `ci.go` commands for all modules in the workspace manually. --- build/ci.go | 133 +++++------ cmd/keeper/go.mod | 9 +- cmd/keeper/go.sum | 18 +- go.work | 6 - go.work.sum | 515 ----------------------------------------- internal/build/util.go | 12 +- 6 files changed, 93 insertions(+), 600 deletions(-) delete mode 100644 go.work delete mode 100644 go.work.sum diff --git a/build/ci.go b/build/ci.go index e145cc1cb5..c5d522c0e6 100644 --- a/build/ci.go +++ b/build/ci.go @@ -64,6 +64,11 @@ import ( ) var ( + goModules = []string{ + ".", + "./cmd/keeper", + } + // Files that end up in the geth*.zip archive. gethArchiveFiles = []string{ "COPYING", @@ -295,6 +300,7 @@ func doTest(cmdline []string) { if *dlgo { tc.Root = build.DownloadGo(csdb) } + gotest := tc.Go("test") // CI needs a bit more time for the statetests (default 45m). @@ -323,11 +329,19 @@ func doTest(cmdline []string) { } packages := flag.CommandLine.Args() - if len(packages) == 0 { - packages = workspacePackagePatterns() + if len(packages) > 0 { + gotest.Args = append(gotest.Args, packages...) + build.MustRun(gotest) + return + } + + // No packages specified, run all tests for all modules. + gotest.Args = append(gotest.Args, "./...") + for _, mod := range goModules { + test := *gotest + test.Dir = mod + build.MustRun(&test) } - gotest.Args = append(gotest.Args, packages...) - build.MustRun(gotest) } // downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures. @@ -351,40 +365,46 @@ func doCheckGenerate() { cachedir = flag.String("cachedir", "./build/cache", "directory for caching binaries.") tc = new(build.GoToolchain) ) - // Compute the origin hashes of all the files - var hashes map[string][32]byte - var err error - hashes, err = build.HashFolder(".", []string{"tests/testdata", "build/cache", ".git"}) - if err != nil { - log.Fatal("Error computing hashes", "err", err) - } // Run any go generate steps we might be missing var ( protocPath = downloadProtoc(*cachedir) protocGenGoPath = downloadProtocGenGo(*cachedir) ) - c := tc.Go("generate", workspacePackagePatterns()...) pathList := []string{filepath.Join(protocPath, "bin"), protocGenGoPath, os.Getenv("PATH")} - c.Env = append(c.Env, "PATH="+strings.Join(pathList, string(os.PathListSeparator))) - build.MustRun(c) - // Check if generate file hashes have changed - generated, err := build.HashFolder(".", []string{"tests/testdata", "build/cache", ".git"}) - if err != nil { - log.Fatalf("Error re-computing hashes: %v", err) - } - updates := build.DiffHashes(hashes, generated) - for _, file := range updates { - log.Printf("File changed: %s", file) - } - if len(updates) != 0 { - log.Fatal("One or more generated files were updated by running 'go generate ./...'") + for _, mod := range goModules { + // Compute the origin hashes of all the files + hashes, err := build.HashFolder(mod, []string{"tests/testdata", "build/cache", ".git"}) + if err != nil { + log.Fatal("Error computing hashes", "err", err) + } + + c := tc.Go("generate", "./...") + c.Env = append(c.Env, "PATH="+strings.Join(pathList, string(os.PathListSeparator))) + c.Dir = mod + build.MustRun(c) + // Check if generate file hashes have changed + generated, err := build.HashFolder(mod, []string{"tests/testdata", "build/cache", ".git"}) + if err != nil { + log.Fatalf("Error re-computing hashes: %v", err) + } + updates := build.DiffHashes(hashes, generated) + for _, file := range updates { + log.Printf("File changed: %s", file) + } + if len(updates) != 0 { + log.Fatal("One or more generated files were updated by running 'go generate ./...'") + } } fmt.Println("No stale files detected.") // Run go mod tidy check. - build.MustRun(tc.Go("mod", "tidy", "-diff")) + for _, mod := range goModules { + tidy := tc.Go("mod", "tidy", "-diff") + tidy.Dir = mod + build.MustRun(tidy) + } fmt.Println("No untidy module files detected.") } @@ -425,20 +445,29 @@ func doLint(cmdline []string) { ) flag.CommandLine.Parse(cmdline) - packages := flag.CommandLine.Args() - if len(packages) == 0 { - // Get module directories in workspace. - packages = []string{"./..."} - modules := workspaceModules() - for _, m := range modules[1:] { - dir := strings.TrimPrefix(m, modules[0]) - packages = append(packages, "."+dir+"/...") - } + linter := downloadLinter(*cachedir) + linter, err := filepath.Abs(linter) + if err != nil { + log.Fatal(err) + } + config, err := filepath.Abs(".golangci.yml") + if err != nil { + log.Fatal(err) } - linter := downloadLinter(*cachedir) - lflags := []string{"run", "--config", ".golangci.yml"} - build.MustRunCommandWithOutput(linter, append(lflags, packages...)...) + lflags := []string{"run", "--config", config} + packages := flag.CommandLine.Args() + if len(packages) > 0 { + build.MustRunCommandWithOutput(linter, append(lflags, packages...)...) + } else { + // Run for all modules in workspace. + for _, mod := range goModules { + args := append(lflags, "./...") + lintcmd := exec.Command(linter, args...) + lintcmd.Dir = mod + build.MustRunWithOutput(lintcmd) + } + } fmt.Println("You have achieved perfection.") } @@ -1176,31 +1205,3 @@ func doSanityCheck() { csdb := download.MustLoadChecksums("build/checksums.txt") csdb.DownloadAndVerifyAll() } - -// workspaceModules lists the module paths in the current work. -func workspaceModules() []string { - listing, err := new(build.GoToolchain).Go("list", "-m").Output() - if err != nil { - log.Fatalf("go list failed:", err) - } - var modules []string - for _, m := range bytes.Split(listing, []byte("\n")) { - m = bytes.TrimSpace(m) - if len(m) > 0 { - modules = append(modules, string(m)) - } - } - if len(modules) == 0 { - panic("no modules found") - } - return modules -} - -func workspacePackagePatterns() []string { - modules := workspaceModules() - patterns := make([]string, len(modules)) - for i, m := range modules { - patterns[i] = m + "/..." - } - return patterns -} diff --git a/cmd/keeper/go.mod b/cmd/keeper/go.mod index 72bec0fef3..d1649da43f 100644 --- a/cmd/keeper/go.mod +++ b/cmd/keeper/go.mod @@ -13,12 +13,13 @@ require ( github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/consensys/gnark-crypto v0.18.0 // indirect - github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect + github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/emicklei/dot v1.6.2 // indirect - github.com/ethereum/c-kzg-4844/v2 v2.1.0 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.3 // indirect + github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/ethereum/go-verkle v0.2.2 // indirect github.com/ferranbt/fastssz v0.1.4 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -33,12 +34,12 @@ require ( github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect - github.com/supranational/blst v0.3.14 // indirect + github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.31.0 // indirect + golang.org/x/sys v0.36.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/cmd/keeper/go.sum b/cmd/keeper/go.sum index 036d5ebd4b..e3bc204ba8 100644 --- a/cmd/keeper/go.sum +++ b/cmd/keeper/go.sum @@ -27,8 +27,8 @@ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAK github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0= github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= -github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI= -github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +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= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -42,8 +42,10 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= 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/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= -github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= +github.com/ethereum/c-kzg-4844/v2 v2.1.3 h1:DQ21UU0VSsuGy8+pcMJHDS0CV1bKmJmxsJYK8l3MiLU= +github.com/ethereum/c-kzg-4844/v2 v2.1.3/go.mod h1:fyNcYI/yAuLWJxf4uzVtS8VDKeoAaRM8G/+ADz/pRdA= +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-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= @@ -114,8 +116,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= -github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +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/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= @@ -133,8 +135,8 @@ golang.org/x/sys v0.1.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.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= diff --git a/go.work b/go.work deleted file mode 100644 index 54e37dd75a..0000000000 --- a/go.work +++ /dev/null @@ -1,6 +0,0 @@ -go 1.24.0 - -use ( - . - ./cmd/keeper -) diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index 7413bba03f..0000000000 --- a/go.work.sum +++ /dev/null @@ -1,515 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/consensys/bavard v0.1.27/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs= -github.com/consensys/bavard v0.1.31-0.20250406004941-2db259e4b582/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs= -github.com/consensys/gnark-crypto v0.16.0/go.mod h1:Ke3j06ndtPTVvo++PhGNgvm+lgpLvzbcE2MqljY7diU= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= -github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -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= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/internal/build/util.go b/internal/build/util.go index aee8bf0fc8..27531d3d95 100644 --- a/internal/build/util.go +++ b/internal/build/util.go @@ -39,6 +39,9 @@ var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands") // MustRun executes the given command and exits the host process for // any error. func MustRun(cmd *exec.Cmd) { + if cmd.Dir != "" && cmd.Dir != "." { + fmt.Printf("(in %s) ", cmd.Dir) + } fmt.Println(">>>", printArgs(cmd.Args)) if !*DryRunFlag { cmd.Stderr = os.Stderr @@ -71,6 +74,13 @@ func MustRunCommand(cmd string, args ...string) { // printed while it runs. This is useful for CI builds where the process will be stopped // when there is no output. func MustRunCommandWithOutput(cmd string, args ...string) { + MustRunWithOutput(exec.Command(cmd, args...)) +} + +// MustRunWithOutput runs the given command, and ensures that some output will be printed +// while it runs. This is useful for CI builds where the process will be stopped when +// there is no output. +func MustRunWithOutput(cmd *exec.Cmd) { interval := time.NewTicker(time.Minute) done := make(chan struct{}) defer interval.Stop() @@ -85,7 +95,7 @@ func MustRunCommandWithOutput(cmd string, args ...string) { } } }() - MustRun(exec.Command(cmd, args...)) + MustRun(cmd) } var warnedAboutGit bool From 2872242045377abe1ec9a54b8bc874dc2bb4febd Mon Sep 17 00:00:00 2001 From: MozirDmitriy Date: Mon, 22 Sep 2025 23:27:54 +0300 Subject: [PATCH 101/119] cmd/era: fix iterator error source handling in checkAccumulator (#32698) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change replaces wrapping a stale outer err with the iterator’s own error after Next(), and switches the post-BlockAndReceipts() check to use the returned err. According to internal/era iterator contract, Error() should be consulted immediately after Next() to surface iteration errors, while decoding errors from Block/Receipts are returned directly. The previous code could hide the real failure (using nil or unrelated err), leading to misleading diagnostics and missed iteration errors. --------- Co-authored-by: lightclient --- cmd/era/main.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/era/main.go b/cmd/era/main.go index 8b57fd695c..35a889d4dc 100644 --- a/cmd/era/main.go +++ b/cmd/era/main.go @@ -274,10 +274,10 @@ func checkAccumulator(e *era.Era) error { for it.Next() { // 1) next() walks the block index, so we're able to implicitly verify it. if it.Error() != nil { - return fmt.Errorf("error reading block %d: %w", it.Number(), err) + return fmt.Errorf("error reading block %d: %w", it.Number(), it.Error()) } block, receipts, err := it.BlockAndReceipts() - if it.Error() != nil { + if err != nil { return fmt.Errorf("error reading block %d: %w", it.Number(), err) } // 2) recompute tx root and verify against header. @@ -294,6 +294,9 @@ func checkAccumulator(e *era.Era) error { td.Add(td, block.Difficulty()) tds = append(tds, new(big.Int).Set(td)) } + if it.Error() != nil { + return fmt.Errorf("error reading block %d: %w", it.Number(), it.Error()) + } // 4+5) Verify accumulator and total difficulty. got, err := era.ComputeAccumulator(hashes, tds) if err != nil { From 2b5718fe9248fd9869a27408d31e8b59ef747315 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Tue, 23 Sep 2025 14:39:44 +0200 Subject: [PATCH 102/119] cmd/evm/internal/t8ntool: fix nil pointer dereference in Osaka blob gas calculation (#32714) The parent header was missing the BaseFee field when calculating the reserve price for EIP-7918 in the Osaka fork, causing a nil pointer dereference. This fix ensures BaseFee is properly set from ParentBaseFee in the environment. Added regression test case 34 to verify Osaka fork blob gas calculation works correctly with parent base fee. --- cmd/evm/internal/t8ntool/execution.go | 1 + cmd/evm/t8n_test.go | 8 ++++++++ cmd/evm/testdata/34/README.md | 6 ++++++ cmd/evm/testdata/34/alloc.json | 6 ++++++ cmd/evm/testdata/34/env.json | 18 ++++++++++++++++++ cmd/evm/testdata/34/exp.json | 23 +++++++++++++++++++++++ cmd/evm/testdata/34/txs.json | 1 + 7 files changed, 63 insertions(+) create mode 100644 cmd/evm/testdata/34/README.md create mode 100644 cmd/evm/testdata/34/alloc.json create mode 100644 cmd/evm/testdata/34/env.json create mode 100644 cmd/evm/testdata/34/exp.json create mode 100644 cmd/evm/testdata/34/txs.json diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index f595a9bf4d..f1e65afe9c 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -191,6 +191,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, Time: pre.Env.ParentTimestamp, ExcessBlobGas: pre.Env.ParentExcessBlobGas, BlobGasUsed: pre.Env.ParentBlobGasUsed, + BaseFee: pre.Env.ParentBaseFee, } header := &types.Header{ Time: pre.Env.Timestamp, diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index 46146be787..3c6fd90b47 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -296,6 +296,14 @@ func TestT8n(t *testing.T) { output: t8nOutput{alloc: true, result: true}, expOut: "exp.json", }, + { // Osaka test, EIP-7918 blob gas with parent base fee + base: "./testdata/34", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Osaka", "", + }, + output: t8nOutput{alloc: true, result: true}, + expOut: "exp.json", + }, } { args := []string{"t8n"} args = append(args, tc.output.get()...) diff --git a/cmd/evm/testdata/34/README.md b/cmd/evm/testdata/34/README.md new file mode 100644 index 0000000000..a18f85ca14 --- /dev/null +++ b/cmd/evm/testdata/34/README.md @@ -0,0 +1,6 @@ +This test verifies that Osaka fork blob gas calculation works correctly when +parentBaseFee is provided. It tests the EIP-7918 reserve price calculation +which requires parent.BaseFee to be properly set. + +Regression test for: nil pointer dereference when parent.BaseFee was not +included in the parent header during Osaka fork blob gas calculations. \ No newline at end of file diff --git a/cmd/evm/testdata/34/alloc.json b/cmd/evm/testdata/34/alloc.json new file mode 100644 index 0000000000..199de13285 --- /dev/null +++ b/cmd/evm/testdata/34/alloc.json @@ -0,0 +1,6 @@ +{ + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x1000000000000000000", + "nonce": "0x0" + } +} \ No newline at end of file diff --git a/cmd/evm/testdata/34/env.json b/cmd/evm/testdata/34/env.json new file mode 100644 index 0000000000..ae2bde5ef3 --- /dev/null +++ b/cmd/evm/testdata/34/env.json @@ -0,0 +1,18 @@ +{ + "currentCoinbase": "0x0000000000000000000000000000000000000000", + "currentDifficulty": "0x0", + "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000000000", + "currentGasLimit": "0x5f5e100", + "currentNumber": "0x1", + "currentTimestamp": "0x1000", + "parentTimestamp": "0x0", + "currentBaseFee": "0x10", + "parentBaseFee": "0x0a", + "parentGasUsed": "0x0", + "parentGasLimit": "0x5f5e100", + "currentExcessBlobGas": "0x0", + "parentExcessBlobGas": "0x0", + "parentBlobGasUsed": "0x20000", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "withdrawals": [] +} \ No newline at end of file diff --git a/cmd/evm/testdata/34/exp.json b/cmd/evm/testdata/34/exp.json new file mode 100644 index 0000000000..56d24a532e --- /dev/null +++ b/cmd/evm/testdata/34/exp.json @@ -0,0 +1,23 @@ +{ + "alloc": { + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x1000000000000000000" + } + }, + "result": { + "stateRoot": "0x01c28492482a1a1f66224726ef1059a7036fce69d1d2c991b65cd013725d5742", + "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "currentDifficulty": null, + "receipts": [], + "gasUsed": "0x0", + "currentBaseFee": "0x10", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "currentExcessBlobGas": "0x0", + "blobGasUsed": "0x0", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "requests": [] + } +} \ No newline at end of file diff --git a/cmd/evm/testdata/34/txs.json b/cmd/evm/testdata/34/txs.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/cmd/evm/testdata/34/txs.json @@ -0,0 +1 @@ +[] \ No newline at end of file From 8dfd30fdd18a55222f5ca6cc823ee986c5a8ed93 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 23 Sep 2025 20:45:36 +0800 Subject: [PATCH 103/119] core/txpool/blobpool: add legacy sidecar conversion in reinject (#32688) This adds the conversion for the legacy sidecar if these transactions are reorged out after the osaka. --- core/txpool/blobpool/blobpool.go | 15 +++++++++++++++ crypto/kzg4844/kzg4844_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 1bf48cf949..db965bc71a 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1030,6 +1030,21 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error { // TODO: seems like an easy optimization here would be getting the serialized tx // from limbo instead of re-serializing it here. + // Converts reorged-out legacy blob transactions to the new format to prevent + // them from becoming stuck in the pool until eviction. + // + // Performance note: Conversion takes ~140ms (Mac M1 Pro). Since a maximum of + // 9 legacy blob transactions are allowed in a block pre-Osaka, an adversary + // could theoretically halt a Geth node for ~1.2s by reorging per block. However, + // this attack is financially inefficient to execute. + head := p.head.Load() + if p.chain.Config().IsOsaka(head.Number, head.Time) && tx.BlobTxSidecar().Version == types.BlobSidecarVersion0 { + if err := tx.BlobTxSidecar().ToV1(); err != nil { + log.Error("Failed to convert the legacy sidecar", "err", err) + return err + } + log.Info("Legacy blob transaction is reorged", "hash", tx.Hash()) + } // Serialize the transaction back into the primary datastore. blob, err := rlp.EncodeToBytes(tx) if err != nil { diff --git a/crypto/kzg4844/kzg4844_test.go b/crypto/kzg4844/kzg4844_test.go index d398a0f48b..743a277199 100644 --- a/crypto/kzg4844/kzg4844_test.go +++ b/crypto/kzg4844/kzg4844_test.go @@ -225,3 +225,31 @@ func testKZGCells(t *testing.T, ckzg bool) { t.Fatalf("failed to verify KZG proof at point: %v", err) } } + +// goos: darwin +// goarch: arm64 +// pkg: github.com/ethereum/go-ethereum/crypto/kzg4844 +// cpu: Apple M1 Pro +// BenchmarkGOKZGComputeCellProofs +// BenchmarkGOKZGComputeCellProofs-8 8 139012286 ns/op +func BenchmarkGOKZGComputeCellProofs(b *testing.B) { benchmarkComputeCellProofs(b, false) } +func BenchmarkCKZGComputeCellProofs(b *testing.B) { benchmarkComputeCellProofs(b, true) } + +func benchmarkComputeCellProofs(b *testing.B, ckzg bool) { + if ckzg && !ckzgAvailable { + b.Skip("CKZG unavailable in this test build") + } + defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load()) + useCKZG.Store(ckzg) + + blob := randBlob() + _, _ = ComputeCellProofs(blob) // for kzg initialization + b.ResetTimer() + + for b.Loop() { + _, err := ComputeCellProofs(blob) + if err != nil { + b.Fatalf("failed to create KZG proof at point: %v", err) + } + } +} From 1cd004871bbc6726abc8020a1c4b7e533088c27c Mon Sep 17 00:00:00 2001 From: cui Date: Tue, 23 Sep 2025 20:49:15 +0800 Subject: [PATCH 104/119] accounts/keystore: use runtime.AddCleanup (#32610) --- accounts/keystore/keystore.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go index fefba026ae..f9cc3edac8 100644 --- a/accounts/keystore/keystore.go +++ b/accounts/keystore/keystore.go @@ -99,9 +99,10 @@ func (ks *KeyStore) init(keydir string) { // TODO: In order for this finalizer to work, there must be no references // to ks. addressCache doesn't keep a reference but unlocked keys do, // so the finalizer will not trigger until all timed unlocks have expired. - runtime.SetFinalizer(ks, func(m *KeyStore) { - m.cache.close() - }) + runtime.AddCleanup(ks, func(c *accountCache) { + c.close() + }, ks.cache) + // Create the initial list of wallets from the cache accs := ks.cache.accounts() ks.wallets = make([]accounts.Wallet, len(accs)) From 4074f745bd42ea7f407c6e6976753ede916c54e4 Mon Sep 17 00:00:00 2001 From: Nikita Mescheryakov Date: Tue, 23 Sep 2025 23:00:16 +0500 Subject: [PATCH 105/119] internal/ethapi: fix merge transition in eth_simulate (#32616) This PR fixes the fork detection of `eth_simulateV1`, particularly for networks that are post-merge since genesis, like Hoodi. --- consensus/beacon/consensus.go | 14 ++------------ internal/ethapi/simulate.go | 14 +++++++++++--- params/config.go | 10 ++++++++++ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 196cbc857c..84926c3d0b 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -71,16 +71,6 @@ func New(ethone consensus.Engine) *Beacon { return &Beacon{ethone: ethone} } -// isPostMerge reports whether the given block number is assumed to be post-merge. -// Here we check the MergeNetsplitBlock to allow configuring networks with a PoW or -// PoA chain for unit testing purposes. -func isPostMerge(config *params.ChainConfig, blockNum uint64, timestamp uint64) bool { - mergedAtGenesis := config.TerminalTotalDifficulty != nil && config.TerminalTotalDifficulty.Sign() == 0 - return mergedAtGenesis || - config.MergeNetsplitBlock != nil && blockNum >= config.MergeNetsplitBlock.Uint64() || - config.ShanghaiTime != nil && timestamp >= *config.ShanghaiTime -} - // Author implements consensus.Engine, returning the verified author of the block. func (beacon *Beacon) Author(header *types.Header) (common.Address, error) { if !beacon.IsPoSHeader(header) { @@ -328,7 +318,7 @@ func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers [ // Prepare implements consensus.Engine, initializing the difficulty field of a // header to conform to the beacon protocol. The changes are done inline. func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { - if !isPostMerge(chain.Config(), header.Number.Uint64(), header.Time) { + if !chain.Config().IsPostMerge(header.Number.Uint64(), header.Time) { return beacon.ethone.Prepare(chain, header) } header.Difficulty = beaconDifficulty @@ -442,7 +432,7 @@ func (beacon *Beacon) SealHash(header *types.Header) common.Hash { // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { - if !isPostMerge(chain.Config(), parent.Number.Uint64()+1, time) { + if !chain.Config().IsPostMerge(parent.Number.Uint64()+1, time) { return beacon.ethone.CalcDifficulty(chain, time, parent) } return beaconDifficulty diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index e26e5bd0e9..75b5c5ffa8 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -474,22 +474,30 @@ func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) { overrides := block.BlockOverrides var withdrawalsHash *common.Hash - if sim.chainConfig.IsShanghai(overrides.Number.ToInt(), (uint64)(*overrides.Time)) { + number := overrides.Number.ToInt() + timestamp := (uint64)(*overrides.Time) + if sim.chainConfig.IsShanghai(number, timestamp) { withdrawalsHash = &types.EmptyWithdrawalsHash } var parentBeaconRoot *common.Hash - if sim.chainConfig.IsCancun(overrides.Number.ToInt(), (uint64)(*overrides.Time)) { + if sim.chainConfig.IsCancun(number, timestamp) { parentBeaconRoot = &common.Hash{} if overrides.BeaconRoot != nil { parentBeaconRoot = overrides.BeaconRoot } } + // Set difficulty to zero if the given block is post-merge. Without this, all post-merge hardforks would remain inactive. + // For example, calling eth_simulateV1(..., blockParameter: 0x0) on hoodi network will cause all blocks to have a difficulty of 1 and be treated as pre-merge. + difficulty := header.Difficulty + if sim.chainConfig.IsPostMerge(number.Uint64(), timestamp) { + difficulty = big.NewInt(0) + } header = overrides.MakeHeader(&types.Header{ UncleHash: types.EmptyUncleHash, ReceiptHash: types.EmptyReceiptsHash, TxHash: types.EmptyTxsHash, Coinbase: header.Coinbase, - Difficulty: header.Difficulty, + Difficulty: difficulty, GasLimit: header.GasLimit, WithdrawalsHash: withdrawalsHash, ParentBeaconRoot: parentBeaconRoot, diff --git a/params/config.go b/params/config.go index cf94fb6758..3788325d5c 100644 --- a/params/config.go +++ b/params/config.go @@ -678,6 +678,16 @@ func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *bi return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0 } +// IsPostMerge reports whether the given block number is assumed to be post-merge. +// Here we check the MergeNetsplitBlock to allow configuring networks with a PoW or +// PoA chain for unit testing purposes. +func (c *ChainConfig) IsPostMerge(blockNum uint64, timestamp uint64) bool { + mergedAtGenesis := c.TerminalTotalDifficulty != nil && c.TerminalTotalDifficulty.Sign() == 0 + return mergedAtGenesis || + c.MergeNetsplitBlock != nil && blockNum >= c.MergeNetsplitBlock.Uint64() || + c.ShanghaiTime != nil && timestamp >= *c.ShanghaiTime +} + // IsShanghai returns whether time is either equal to the Shanghai fork time or greater. func (c *ChainConfig) IsShanghai(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.ShanghaiTime, time) From 48c74f45939c5bf748d62ad9166d9a9d1c3677f3 Mon Sep 17 00:00:00 2001 From: VolodymyrBg Date: Wed, 24 Sep 2025 05:36:56 +0300 Subject: [PATCH 106/119] trie: align AllFFPrefix test assertion and message (#32719) --- trie/iterator_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie/iterator_test.go b/trie/iterator_test.go index f1451cef90..3e0ef126be 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -1005,9 +1005,9 @@ func TestPrefixIteratorEdgeCases(t *testing.T) { count++ } } - // Should find at least the allFF key itself + // Should find exactly the two keys with the all-0xff prefix if count != 2 { - t.Errorf("Expected at least 1 result for all-0xff prefix, got %d", count) + t.Errorf("Expected 2 results for all-0xff prefix, got %d", count) } }) From 15a94b4331c8aecd493b05617564df531881908d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 24 Sep 2025 19:40:49 +0200 Subject: [PATCH 107/119] build: module-aware FindMainPackages (#32736) This fixes `go run build/ci.go install`. It was failing because we resolved all main packages by parsing sources, which fails when the source directory contains multiple modules. --- build/ci.go | 2 +- internal/build/util.go | 32 ++++++++++---------------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/build/ci.go b/build/ci.go index c5d522c0e6..c6f4f28c87 100644 --- a/build/ci.go +++ b/build/ci.go @@ -221,7 +221,7 @@ func doInstall(cmdline []string) { // Default: collect all 'main' packages in cmd/ and build those. packages := flag.Args() if len(packages) == 0 { - packages = build.FindMainPackages("./cmd") + packages = build.FindMainPackages(&tc, "./cmd/...") } // Do the build! diff --git a/internal/build/util.go b/internal/build/util.go index 27531d3d95..6e6632c750 100644 --- a/internal/build/util.go +++ b/internal/build/util.go @@ -21,8 +21,6 @@ import ( "bytes" "flag" "fmt" - "go/parser" - "go/token" "io" "log" "os" @@ -219,28 +217,18 @@ func UploadSFTP(identityFile, host, dir string, files []string) error { // FindMainPackages finds all 'main' packages in the given directory and returns their // package paths. -func FindMainPackages(dir string) []string { - var commands []string - cmds, err := os.ReadDir(dir) +func FindMainPackages(tc *GoToolchain, pattern string) []string { + list := tc.Go("list", "-f", `{{if eq .Name "main"}}{{.ImportPath}}{{end}}`, pattern) + output, err := list.Output() if err != nil { - log.Fatal(err) + log.Fatal("go list failed:", err) } - for _, cmd := range cmds { - pkgdir := filepath.Join(dir, cmd.Name()) - if !cmd.IsDir() { - continue - } - pkgs, err := parser.ParseDir(token.NewFileSet(), pkgdir, nil, parser.PackageClauseOnly) - if err != nil { - log.Fatal(err) - } - for name := range pkgs { - if name == "main" { - path := "./" + filepath.ToSlash(pkgdir) - commands = append(commands, path) - break - } + var result []string + for l := range bytes.Lines(output) { + l = bytes.TrimSpace(l) + if len(l) > 0 { + result = append(result, string(l)) } } - return commands + return result } From bc451546ae4ab5e1553de738d1944788173a5371 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 24 Sep 2025 19:41:33 +0200 Subject: [PATCH 108/119] miner: default gaslimit 60M (#32734) --- miner/miner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miner/miner.go b/miner/miner.go index 20845b5036..810cc20a6c 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -52,7 +52,7 @@ type Config struct { // DefaultConfig contains default settings for miner. var DefaultConfig = Config{ - GasCeil: 45_000_000, + GasCeil: 60_000_000, GasPrice: big.NewInt(params.GWei / 1000), // The default recommit time is chosen as two seconds since From ad55a3e07fe3854aa6582cca612f2fdf09915f67 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 24 Sep 2025 20:03:17 +0200 Subject: [PATCH 109/119] core/txpool/blobpool: fork boundary conversion 3 (#32716) This implements the conversion of existing blob transactions to the new proof version. Conversion is triggered at the Osaka fork boundary. The conversion is designed to be idempotent, and may be triggered multiple times in case of a reorg around the fork boundary. This change is the last missing piece that completes our strategy for the blobpool conversion. After the Osaka fork, - new transactions will be converted on-the-fly upon entry to the pool - reorged transactions will be converted while being reinjected - (this change) existing transactions will be converted in the background --------- Co-authored-by: Gary Rong Co-authored-by: lightclient --- core/txpool/blobpool/blobpool.go | 170 +++++++++++++++++++++++- core/txpool/blobpool/blobpool_test.go | 181 ++++++++++++++++++++++++++ core/txpool/blobpool/conversion.go | 93 ++++++++++--- core/txpool/blobpool/lookup.go | 10 ++ 4 files changed, 432 insertions(+), 22 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index db965bc71a..f4aa406e2a 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -21,10 +21,12 @@ import ( "container/heap" "errors" "fmt" + "maps" "math" "math/big" "os" "path/filepath" + "slices" "sort" "sync" "sync/atomic" @@ -337,7 +339,7 @@ type BlobPool struct { signer types.Signer // Transaction signer to use for sender recovery chain BlockChain // Chain object to access the state through - cQueue *conversionQueue // The queue for performing legacy sidecar conversion + cQueue *conversionQueue // The queue for performing legacy sidecar conversion (TODO: remove after Osaka) head atomic.Pointer[types.Header] // Current head of the chain state *state.StateDB // Current state at the head of the chain @@ -883,6 +885,172 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) { basefeeGauge.Update(int64(basefee.Uint64())) blobfeeGauge.Update(int64(blobfee.Uint64())) p.updateStorageMetrics() + + // Perform the conversion logic at the fork boundary + if !p.chain.Config().IsOsaka(oldHead.Number, oldHead.Time) && p.chain.Config().IsOsaka(newHead.Number, newHead.Time) { + // Deep copy all indexed transaction metadata. + var ( + ids = make(map[common.Address]map[uint64]uint64) + txs = make(map[common.Address]map[uint64]common.Hash) + ) + for sender, list := range p.index { + ids[sender] = make(map[uint64]uint64) + txs[sender] = make(map[uint64]common.Hash) + for _, m := range list { + ids[sender][m.nonce] = m.id + txs[sender][m.nonce] = m.hash + } + } + // Initiate the background conversion thread. + p.cQueue.launchBillyConversion(func() { + p.convertLegacySidecars(ids, txs) + }) + } +} + +// compareAndSwap checks if the specified transaction is still tracked in the pool +// and replace the metadata accordingly. It should only be used in the fork boundary +// bulk conversion. If it fails for some reason, the subsequent txs won't be dropped +// for simplicity which we assume it's very likely to happen. +// +// The returned flag indicates whether the replacement succeeded. +func (p *BlobPool) compareAndSwap(address common.Address, hash common.Hash, blob []byte, oldID uint64, oldStorageSize uint32) bool { + p.lock.Lock() + defer p.lock.Unlock() + + newId, err := p.store.Put(blob) + if err != nil { + log.Error("Failed to store transaction", "hash", hash, "err", err) + return false + } + newSize := uint64(len(blob)) + newStorageSize := p.store.Size(newId) + + // Terminate the procedure if the transaction was already evicted. The + // newly added blob should be removed before return. + if !p.lookup.update(hash, newId, newSize) { + if derr := p.store.Delete(newId); derr != nil { + log.Error("Failed to delete the dangling blob tx", "err", derr) + } else { + log.Warn("Deleted the dangling blob tx", "id", newId) + } + return false + } + // Update the metadata of blob transaction + for _, meta := range p.index[address] { + if meta.hash == hash { + meta.id = newId + meta.version = types.BlobSidecarVersion1 + meta.storageSize = newStorageSize + meta.size = newSize + + p.stored += uint64(newStorageSize) + p.stored -= uint64(oldStorageSize) + break + } + } + if err := p.store.Delete(oldID); err != nil { + log.Error("Failed to delete the legacy transaction", "hash", hash, "id", oldID, "err", err) + } + return true +} + +// convertLegacySidecar fetches transaction data from the store, performs an +// on-the-fly conversion. This function is intended for use only during the +// Osaka fork transition period. +// +// The returned flag indicates whether the replacement succeeds or not. +func (p *BlobPool) convertLegacySidecar(sender common.Address, hash common.Hash, id uint64) bool { + start := time.Now() + + // Retrieves the legacy blob transaction from the underlying store with + // read lock held, preventing any potential data race around the slot + // specified by the id. + p.lock.RLock() + data, err := p.store.Get(id) + if err != nil { + p.lock.RUnlock() + // The transaction may have been evicted simultaneously, safe to skip conversion. + log.Debug("Blob transaction is missing", "hash", hash, "id", id, "err", err) + return false + } + oldStorageSize := p.store.Size(id) + p.lock.RUnlock() + + // Decode the transaction, the failure is not expected and report the error + // loudly if possible. If the blob transaction in this slot is corrupted, + // leave it in the store, it will be dropped during the next pool + // initialization. + var tx types.Transaction + if err = rlp.DecodeBytes(data, &tx); err != nil { + log.Error("Blob transaction is corrupted", "hash", hash, "id", id, "err", err) + return false + } + + // Skip conversion if the transaction does not match the expected hash, or if it was + // already converted. This can occur if the original transaction was evicted from the + // pool and the slot was reused by a new one. + if tx.Hash() != hash { + log.Warn("Blob transaction was replaced", "hash", hash, "id", id, "stored", tx.Hash()) + return false + } + sc := tx.BlobTxSidecar() + if sc.Version >= types.BlobSidecarVersion1 { + log.Debug("Skipping conversion of blob tx", "hash", hash, "id", id) + return false + } + + // Perform the sidecar conversion, the failure is not expected and report the error + // loudly if possible. + if err := tx.BlobTxSidecar().ToV1(); err != nil { + log.Error("Failed to convert blob transaction", "hash", hash, "err", err) + return false + } + + // Encode the converted transaction, the failure is not expected and report + // the error loudly if possible. + blob, err := rlp.EncodeToBytes(&tx) + if err != nil { + log.Error("Failed to encode blob transaction", "hash", tx.Hash(), "err", err) + return false + } + + // Replace the legacy blob transaction with the converted format. + if !p.compareAndSwap(sender, hash, blob, id, oldStorageSize) { + log.Error("Failed to replace the legacy transaction", "hash", hash) + return false + } + log.Debug("Converted legacy blob transaction", "hash", hash, "elapsed", common.PrettyDuration(time.Since(start))) + return true +} + +// convertLegacySidecars converts all given transactions to sidecar version 1. +// +// If any of them fails to be converted, the subsequent transactions will still +// be processed, as we assume the failure is very unlikely to happen. If happens, +// these transactions will be stuck in the pool until eviction. +func (p *BlobPool) convertLegacySidecars(ids map[common.Address]map[uint64]uint64, txs map[common.Address]map[uint64]common.Hash) { + var ( + start = time.Now() + success int + failure int + ) + for addr, list := range txs { + // Transactions evicted from the pool must be contiguous, if in any case, + // the transactions are gapped with each other, they will be discarded. + nonces := slices.Collect(maps.Keys(list)) + slices.Sort(nonces) + + // Convert the txs with nonce order + for _, nonce := range nonces { + if p.convertLegacySidecar(addr, list[nonce], ids[addr][nonce]) { + success++ + } else { + failure++ + } + } + } + log.Info("Completed blob transaction conversion", "discarded", failure, "injected", success, "elapsed", common.PrettyDuration(time.Since(start))) } // reorg assembles all the transactors and missing transactions between an old diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 75a87940bd..f0f00c8055 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -2098,6 +2098,185 @@ func TestGetBlobs(t *testing.T) { pool.Close() } +// TestSidecarConversion will verify that after the Osaka fork, all legacy +// sidecars in the pool are successfully convert to v1 sidecars. +func TestSidecarConversion(t *testing.T) { + // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true))) + + // Create a temporary folder for the persistent backend + storage := t.TempDir() + os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) + + var ( + preOsakaTxs = make(types.Transactions, 10) + postOsakaTxs = make(types.Transactions, 3) + keys = make([]*ecdsa.PrivateKey, len(preOsakaTxs)+len(postOsakaTxs)) + addrs = make([]common.Address, len(preOsakaTxs)+len(postOsakaTxs)) + statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + ) + for i := range keys { + keys[i], _ = crypto.GenerateKey() + addrs[i] = crypto.PubkeyToAddress(keys[i].PublicKey) + statedb.AddBalance(addrs[i], uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + } + for i := range preOsakaTxs { + preOsakaTxs[i] = makeMultiBlobTx(0, 1, 1000, 100, 2, 0, keys[i], types.BlobSidecarVersion0) + } + for i := range postOsakaTxs { + if i == 0 { + // First has a v0 sidecar. + postOsakaTxs[i] = makeMultiBlobTx(0, 1, 1000, 100, 1, 0, keys[len(preOsakaTxs)+i], types.BlobSidecarVersion0) + } + postOsakaTxs[i] = makeMultiBlobTx(0, 1, 1000, 100, 1, 0, keys[len(preOsakaTxs)+i], types.BlobSidecarVersion1) + } + statedb.Commit(0, true, false) + + // Test plan: + // 1) Create a bunch v0 sidecar txs and add to pool before Osaka. + // 2) Pass in new Osaka header to activate the conversion thread. + // 3) Continue adding both v0 and v1 transactions to the pool. + // 4) Verify that as additional blocks come in, transactions involved in the + // migration are correctly discarded. + + config := ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + LondonBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + CancunTime: newUint64(0), + PragueTime: newUint64(0), + OsakaTime: newUint64(1), + BlobScheduleConfig: params.DefaultBlobSchedule, + } + chain := &testBlockChain{ + config: config, + basefee: uint256.NewInt(1050), + blobfee: uint256.NewInt(105), + statedb: statedb, + blocks: make(map[uint64]*types.Block), + } + + // Create 3 blocks: + // - the current block, before Osaka + // - the first block after Osaka + // - another post-Osaka block with several transactions in it + header0 := chain.CurrentBlock() + header0.Time = 0 + chain.blocks[0] = types.NewBlockWithHeader(header0) + + header1 := chain.CurrentBlock() + header1.Number = big.NewInt(1) + header1.Time = 1 + chain.blocks[1] = types.NewBlockWithHeader(header1) + + header2 := chain.CurrentBlock() + header2.Time = 2 + header2.Number = big.NewInt(2) + + // Make a copy of one of the pre-Osaka transactions and convert it to v1 here + // so that we can add it to the pool later and ensure a duplicate is not added + // by the conversion queue. + tx := preOsakaTxs[len(preOsakaTxs)-1] + sc := *tx.BlobTxSidecar() // copy sidecar + sc.ToV1() + tx.WithBlobTxSidecar(&sc) + + block2 := types.NewBlockWithHeader(header2).WithBody(types.Body{Transactions: append(postOsakaTxs, tx)}) + chain.blocks[2] = block2 + + pool := New(Config{Datadir: storage}, chain, nil) + if err := pool.Init(1, header0, newReserver()); err != nil { + t.Fatalf("failed to create blob pool: %v", err) + } + + errs := pool.Add(preOsakaTxs, true) + for i, err := range errs { + if err != nil { + t.Errorf("failed to insert blob tx from %s: %s", addrs[i], errs[i]) + } + } + + // Kick off migration. + pool.Reset(header0, header1) + + // Add the v0 sidecar tx, but don't block so we can keep doing other stuff + // while it converts the sidecar. + addDone := make(chan struct{}) + go func() { + pool.Add(types.Transactions{postOsakaTxs[0]}, false) + close(addDone) + }() + + // Add the post-Osaka v1 sidecar txs. + errs = pool.Add(postOsakaTxs[1:], false) + for _, err := range errs { + if err != nil { + t.Fatalf("expected tx add to succeed: %v", err) + } + } + + // Wait for the first tx's conversion to complete, then check that all + // transactions added after Osaka can be accounted for in the pool. + <-addDone + pending := pool.Pending(txpool.PendingFilter{BlobTxs: true, BlobVersion: types.BlobSidecarVersion1}) + for _, tx := range postOsakaTxs { + from, _ := pool.signer.Sender(tx) + if len(pending[from]) != 1 || pending[from][0].Hash != tx.Hash() { + t.Fatalf("expected post-Osaka txs to be pending") + } + } + + // Now update the pool with the next block. This should cause the pool to + // clear out the post-Osaka txs since they were included in block 2. Since the + // test blockchain doesn't manage nonces, we'll just do that manually before + // the reset is called. Don't forget about the pre-Osaka transaction we also + // added to block 2! + for i := range postOsakaTxs { + statedb.SetNonce(addrs[len(preOsakaTxs)+i], 1, tracing.NonceChangeEoACall) + } + statedb.SetNonce(addrs[len(preOsakaTxs)-1], 1, tracing.NonceChangeEoACall) + pool.Reset(header1, block2.Header()) + + // Now verify no post-Osaka transactions are tracked by the pool. + for i, tx := range postOsakaTxs { + if pool.Get(tx.Hash()) != nil { + t.Fatalf("expected txs added post-osaka to have been placed in limbo due to inclusion in a block: index %d, hash %s", i, tx.Hash()) + } + } + + // Wait for the pool migration to complete. + <-pool.cQueue.anyBillyConversionDone + + // Verify all transactions in the pool were converted and verify the + // subsequent cell proofs. + count, _ := pool.Stats() + if count != len(preOsakaTxs)-1 { + t.Errorf("expected pending count to match initial tx count: pending=%d, expected=%d", count, len(preOsakaTxs)-1) + } + for addr, acc := range pool.index { + for _, m := range acc { + if m.version != types.BlobSidecarVersion1 { + t.Errorf("expected sidecar to have been converted: from %s, hash %s", addr, m.hash) + } + tx := pool.Get(m.hash) + if tx == nil { + t.Errorf("failed to get tx by hash: %s", m.hash) + } + sc := tx.BlobTxSidecar() + if err := kzg4844.VerifyCellProofs(sc.Blobs, sc.Commitments, sc.Proofs); err != nil { + t.Errorf("failed to verify cell proofs for tx %s after conversion: %s", m.hash, err) + } + } + } + + verifyPoolInternals(t, pool) + + // Launch conversion a second time. + // This is just a sanity check to ensure we can handle it. + pool.Reset(header0, header1) + + pool.Close() +} + // fakeBilly is a billy.Database implementation which just drops data on the floor. type fakeBilly struct { billy.Database @@ -2180,3 +2359,5 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) { } } } + +func newUint64(val uint64) *uint64 { return &val } diff --git a/core/txpool/blobpool/conversion.go b/core/txpool/blobpool/conversion.go index 5026892fc8..95828d83b2 100644 --- a/core/txpool/blobpool/conversion.go +++ b/core/txpool/blobpool/conversion.go @@ -32,8 +32,8 @@ import ( // with 6 blobs each) would consume approximately 1.5GB of memory. const maxPendingConversionTasks = 2048 -// cTask represents a conversion task with an attached legacy blob transaction. -type cTask struct { +// txConvert represents a conversion task with an attached legacy blob transaction. +type txConvert struct { tx *types.Transaction // Legacy blob transaction done chan error // Channel for signaling back if the conversion succeeds } @@ -43,17 +43,27 @@ type cTask struct { // it is performed in the background by a single thread, ensuring the main Geth // process is not overloaded. type conversionQueue struct { - tasks chan *cTask - quit chan struct{} - closed chan struct{} + tasks chan *txConvert + startBilly chan func() + quit chan struct{} + closed chan struct{} + + billyQueue []func() + billyTaskDone chan struct{} + + // This channel will be closed when the first billy conversion finishes. + // It's added for unit tests to synchronize with the conversion progress. + anyBillyConversionDone chan struct{} } // newConversionQueue constructs the conversion queue. func newConversionQueue() *conversionQueue { q := &conversionQueue{ - tasks: make(chan *cTask), - quit: make(chan struct{}), - closed: make(chan struct{}), + tasks: make(chan *txConvert), + startBilly: make(chan func()), + quit: make(chan struct{}), + closed: make(chan struct{}), + anyBillyConversionDone: make(chan struct{}), } go q.loop() return q @@ -66,13 +76,23 @@ func newConversionQueue() *conversionQueue { func (q *conversionQueue) convert(tx *types.Transaction) error { done := make(chan error, 1) select { - case q.tasks <- &cTask{tx: tx, done: done}: + case q.tasks <- &txConvert{tx: tx, done: done}: return <-done case <-q.closed: return errors.New("conversion queue closed") } } +// launchBillyConversion starts a conversion task in the background. +func (q *conversionQueue) launchBillyConversion(fn func()) error { + select { + case q.startBilly <- fn: + return nil + case <-q.closed: + return errors.New("conversion queue closed") + } +} + // close terminates the conversion queue. func (q *conversionQueue) close() { select { @@ -85,7 +105,7 @@ func (q *conversionQueue) close() { } // run converts a batch of legacy blob txs to the new cell proof format. -func (q *conversionQueue) run(tasks []*cTask, done chan struct{}, interrupt *atomic.Int32) { +func (q *conversionQueue) run(tasks []*txConvert, done chan struct{}, interrupt *atomic.Int32) { defer close(done) for _, t := range tasks { @@ -116,37 +136,68 @@ func (q *conversionQueue) loop() { // The pending tasks for sidecar conversion. We assume the number of legacy // blob transactions requiring conversion will not be excessive. However, // a hard cap is applied as a protective measure. - cTasks []*cTask + txTasks []*txConvert + + firstBilly = true ) + for { select { case t := <-q.tasks: - if len(cTasks) >= maxPendingConversionTasks { + if len(txTasks) >= maxPendingConversionTasks { t.done <- errors.New("conversion queue is overloaded") continue } - cTasks = append(cTasks, t) + txTasks = append(txTasks, t) // Launch the background conversion thread if it's idle if done == nil { done, interrupt = make(chan struct{}), new(atomic.Int32) - tasks := slices.Clone(cTasks) - cTasks = cTasks[:0] + tasks := slices.Clone(txTasks) + txTasks = txTasks[:0] go q.run(tasks, done, interrupt) } case <-done: done, interrupt = nil, nil - case <-q.quit: - if done == nil { - return + case fn := <-q.startBilly: + q.billyQueue = append(q.billyQueue, fn) + q.runNextBillyTask() + + case <-q.billyTaskDone: + if firstBilly { + close(q.anyBillyConversionDone) + firstBilly = false + } + q.runNextBillyTask() + + case <-q.quit: + if done != nil { + log.Debug("Waiting for blob proof conversion to exit") + interrupt.Store(1) + <-done + } + if q.billyTaskDone != nil { + log.Debug("Waiting for blobpool billy conversion to exit") + <-q.billyTaskDone } - interrupt.Store(1) - log.Debug("Waiting for blob proof conversion to exit") - <-done return } } } + +func (q *conversionQueue) runNextBillyTask() { + if len(q.billyQueue) == 0 { + q.billyTaskDone = nil + return + } + + fn := q.billyQueue[0] + q.billyQueue = append(q.billyQueue[:0], q.billyQueue[1:]...) + + done := make(chan struct{}) + go func() { defer close(done); fn() }() + q.billyTaskDone = done +} diff --git a/core/txpool/blobpool/lookup.go b/core/txpool/blobpool/lookup.go index 7607cd487a..874ca85b8c 100644 --- a/core/txpool/blobpool/lookup.go +++ b/core/txpool/blobpool/lookup.go @@ -110,3 +110,13 @@ func (l *lookup) untrack(tx *blobTxMeta) { } } } + +// update updates the transaction index. It should only be used in the conversion. +func (l *lookup) update(hash common.Hash, id uint64, size uint64) bool { + meta, exists := l.txIndex[hash] + if !exists { + return false + } + meta.id, meta.size = id, size + return true +} From 7ed17f1933bccd835d474def98e1b34830b424fe Mon Sep 17 00:00:00 2001 From: radik878 Date: Thu, 25 Sep 2025 03:57:01 +0300 Subject: [PATCH 110/119] trie: fix TestOneElementProof expected value message (#32738) - Correct the error message in TestOneElementProof to expect 'v' instead of 'k'. - The trie is updated with key "k" and value "v"; on mismatch the expected value must be 'v'. - Aligns the message with the actual test logic and other similar checks in this file, reducing confusion during test failures. No behavioral changes. --- trie/proof_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie/proof_test.go b/trie/proof_test.go index b3c9dd753c..40c49f358b 100644 --- a/trie/proof_test.go +++ b/trie/proof_test.go @@ -109,7 +109,7 @@ func TestOneElementProof(t *testing.T) { t.Fatalf("prover %d: failed to verify proof: %v\nraw proof: %x", i, err, proof) } if !bytes.Equal(val, []byte("v")) { - t.Fatalf("prover %d: verified value mismatch: have %x, want 'k'", i, val) + t.Fatalf("prover %d: verified value mismatch: have %x, want 'v'", i, val) } } } From 1c706d1571d6e61754631ae6f0ae73d54ed4d44a Mon Sep 17 00:00:00 2001 From: viktorking7 <140458814+viktorking7@users.noreply.github.com> Date: Thu, 25 Sep 2025 03:47:55 +0200 Subject: [PATCH 111/119] accounts/keystore: use ticker to avoid timer allocations (#32732) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace time.After with a long‑lived time.Ticker in KeyStore.updater, preventing per‑iteration timer allocations and potential timer buildup. Co-authored-by: lightclient --- accounts/keystore/keystore.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go index f9cc3edac8..3e4266924f 100644 --- a/accounts/keystore/keystore.go +++ b/accounts/keystore/keystore.go @@ -196,11 +196,14 @@ func (ks *KeyStore) Subscribe(sink chan<- accounts.WalletEvent) event.Subscripti // forces a manual refresh (only triggers for systems where the filesystem notifier // is not running). func (ks *KeyStore) updater() { + ticker := time.NewTicker(walletRefreshCycle) + defer ticker.Stop() + for { // Wait for an account update or a refresh timeout select { case <-ks.changes: - case <-time.After(walletRefreshCycle): + case <-ticker.C: } // Run the wallet refresher ks.refreshWallets() From 7611f351c18de983c49544f09aa042bd0403243b Mon Sep 17 00:00:00 2001 From: hero5512 Date: Wed, 24 Sep 2025 22:05:14 -0400 Subject: [PATCH 112/119] accounts/abi/bind: fix data race in TestWaitDeployedCornerCases (#32740) Fixes race in WaitDeploy test where the backend is closed before goroutine using it wraps up. --------- Co-authored-by: lightclient --- accounts/abi/bind/v2/util_test.go | 61 +++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/accounts/abi/bind/v2/util_test.go b/accounts/abi/bind/v2/util_test.go index b1b647a7b9..a9f5b4035c 100644 --- a/accounts/abi/bind/v2/util_test.go +++ b/accounts/abi/bind/v2/util_test.go @@ -100,22 +100,29 @@ func TestWaitDeployed(t *testing.T) { } func TestWaitDeployedCornerCases(t *testing.T) { - backend := simulated.NewBackend( - types.GenesisAlloc{ - crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}, - }, + var ( + backend = simulated.NewBackend( + types.GenesisAlloc{ + crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}, + }, + ) + head, _ = backend.Client().HeaderByNumber(t.Context(), nil) // Should be child's, good enough + gasPrice = new(big.Int).Add(head.BaseFee, big.NewInt(1)) + signer = types.LatestSigner(params.AllDevChainProtocolChanges) + code = common.FromHex("6060604052600a8060106000396000f360606040526008565b00") + ctx, cancel = context.WithCancel(t.Context()) ) defer backend.Close() - head, _ := backend.Client().HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - - // Create a transaction to an account. - code := "6060604052600a8060106000396000f360606040526008565b00" - tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code)) - tx, _ = types.SignTx(tx, types.LatestSigner(params.AllDevChainProtocolChanges), testKey) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + // 1. WaitDeploy on a transaction that does not deploy a contract, verify it + // returns an error. + tx := types.MustSignNewTx(testKey, signer, &types.LegacyTx{ + Nonce: 0, + To: &common.Address{0x01}, + Gas: 300000, + GasPrice: gasPrice, + Data: code, + }) if err := backend.Client().SendTransaction(ctx, tx); err != nil { t.Errorf("failed to send transaction: %q", err) } @@ -124,14 +131,23 @@ func TestWaitDeployedCornerCases(t *testing.T) { t.Errorf("error mismatch: want %q, got %q, ", bind.ErrNoAddressInReceipt, err) } - // Create a transaction that is not mined. - tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, common.FromHex(code)) - tx, _ = types.SignTx(tx, types.LatestSigner(params.AllDevChainProtocolChanges), testKey) + // 2. Create a contract, but cancel the WaitDeploy before it is mined. + tx = types.MustSignNewTx(testKey, signer, &types.LegacyTx{ + Nonce: 1, + Gas: 300000, + GasPrice: gasPrice, + Data: code, + }) + // Wait in another thread so that we can quickly cancel it after submitting + // the transaction. + done := make(chan struct{}) go func() { - contextCanceled := errors.New("context canceled") - if _, err := bind.WaitDeployed(ctx, backend.Client(), tx.Hash()); err.Error() != contextCanceled.Error() { - t.Errorf("error mismatch: want %q, got %q, ", contextCanceled, err) + defer close(done) + want := errors.New("context canceled") + _, err := bind.WaitDeployed(ctx, backend.Client(), tx.Hash()) + if err == nil || errors.Is(want, err) { + t.Errorf("error mismatch: want %v, got %v", want, err) } }() @@ -139,4 +155,11 @@ func TestWaitDeployedCornerCases(t *testing.T) { t.Errorf("failed to send transaction: %q", err) } cancel() + + // Wait for goroutine to exit or for a timeout. + select { + case <-done: + case <-time.After(time.Second * 2): + t.Fatalf("failed to cancel wait deploy") + } } From 965ffff9ac4fee6169df8b5fd0a6c097475b9fb9 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Thu, 25 Sep 2025 02:21:41 -0600 Subject: [PATCH 113/119] all: add bpo1 and bpo2 overrides (#32737) This adds overrides to the cli for BPO1 and BPO2. --------- Co-authored-by: Gary Rong --- cmd/geth/chaincmd.go | 10 ++++++++++ cmd/geth/config.go | 8 ++++++++ cmd/geth/main.go | 2 ++ cmd/utils/flags.go | 10 ++++++++++ core/genesis.go | 8 ++++++++ eth/backend.go | 6 ++++++ eth/ethconfig/config.go | 6 ++++++ eth/ethconfig/gen_config.go | 12 ++++++++++++ 8 files changed, 62 insertions(+) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 71ff821bb9..c5145bbfb7 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -59,6 +59,8 @@ var ( Flags: slices.Concat([]cli.Flag{ utils.CachePreimagesFlag, utils.OverrideOsaka, + utils.OverrideBPO1, + utils.OverrideBPO2, utils.OverrideVerkle, }, utils.DatabaseFlags), Description: ` @@ -274,6 +276,14 @@ func initGenesis(ctx *cli.Context) error { v := ctx.Uint64(utils.OverrideOsaka.Name) overrides.OverrideOsaka = &v } + if ctx.IsSet(utils.OverrideBPO1.Name) { + v := ctx.Uint64(utils.OverrideBPO1.Name) + overrides.OverrideBPO1 = &v + } + if ctx.IsSet(utils.OverrideBPO2.Name) { + v := ctx.Uint64(utils.OverrideBPO2.Name) + overrides.OverrideBPO2 = &v + } if ctx.IsSet(utils.OverrideVerkle.Name) { v := ctx.Uint64(utils.OverrideVerkle.Name) overrides.OverrideVerkle = &v diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 33a3eadea8..fcb315af97 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -227,6 +227,14 @@ func makeFullNode(ctx *cli.Context) *node.Node { v := ctx.Uint64(utils.OverrideOsaka.Name) cfg.Eth.OverrideOsaka = &v } + if ctx.IsSet(utils.OverrideBPO1.Name) { + v := ctx.Uint64(utils.OverrideBPO1.Name) + cfg.Eth.OverrideBPO1 = &v + } + if ctx.IsSet(utils.OverrideBPO2.Name) { + v := ctx.Uint64(utils.OverrideBPO2.Name) + cfg.Eth.OverrideBPO2 = &v + } if ctx.IsSet(utils.OverrideVerkle.Name) { v := ctx.Uint64(utils.OverrideVerkle.Name) cfg.Eth.OverrideVerkle = &v diff --git a/cmd/geth/main.go b/cmd/geth/main.go index f380c9f2d4..2465b52ad1 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -63,6 +63,8 @@ var ( utils.USBFlag, utils.SmartCardDaemonPathFlag, utils.OverrideOsaka, + utils.OverrideBPO1, + utils.OverrideBPO2, utils.OverrideVerkle, utils.EnablePersonal, // deprecated utils.TxPoolLocalsFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5e96185dbd..c7775adb53 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -248,6 +248,16 @@ var ( Usage: "Manually specify the Osaka fork timestamp, overriding the bundled setting", Category: flags.EthCategory, } + OverrideBPO1 = &cli.Uint64Flag{ + Name: "override.bpo1", + Usage: "Manually specify the bpo1 fork timestamp, overriding the bundled setting", + Category: flags.EthCategory, + } + OverrideBPO2 = &cli.Uint64Flag{ + Name: "override.bpo2", + Usage: "Manually specify the bpo2 fork timestamp, overriding the bundled setting", + Category: flags.EthCategory, + } OverrideVerkle = &cli.Uint64Flag{ Name: "override.verkle", Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting", diff --git a/core/genesis.go b/core/genesis.go index 13d4addd7e..2fd044c70a 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -259,6 +259,8 @@ func (e *GenesisMismatchError) Error() string { // ChainOverrides contains the changes to chain config. type ChainOverrides struct { OverrideOsaka *uint64 + OverrideBPO1 *uint64 + OverrideBPO2 *uint64 OverrideVerkle *uint64 } @@ -270,6 +272,12 @@ func (o *ChainOverrides) apply(cfg *params.ChainConfig) error { if o.OverrideOsaka != nil { cfg.OsakaTime = o.OverrideOsaka } + if o.OverrideBPO1 != nil { + cfg.BPO1Time = o.OverrideBPO1 + } + if o.OverrideBPO2 != nil { + cfg.BPO2Time = o.OverrideBPO2 + } if o.OverrideVerkle != nil { cfg.VerkleTime = o.OverrideVerkle } diff --git a/eth/backend.go b/eth/backend.go index 3bfe0765f4..8509561822 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -262,6 +262,12 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if config.OverrideOsaka != nil { overrides.OverrideOsaka = config.OverrideOsaka } + if config.OverrideBPO1 != nil { + overrides.OverrideBPO1 = config.OverrideBPO1 + } + if config.OverrideBPO2 != nil { + overrides.OverrideBPO2 = config.OverrideBPO2 + } if config.OverrideVerkle != nil { overrides.OverrideVerkle = config.OverrideVerkle } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 4fe24c0fe0..6020387bcd 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -175,6 +175,12 @@ type Config struct { // OverrideOsaka (TODO: remove after the fork) OverrideOsaka *uint64 `toml:",omitempty"` + // OverrideBPO1 (TODO: remove after the fork) + OverrideBPO1 *uint64 `toml:",omitempty"` + + // OverrideBPO2 (TODO: remove after the fork) + OverrideBPO2 *uint64 `toml:",omitempty"` + // OverrideVerkle (TODO: remove after the fork) OverrideVerkle *uint64 `toml:",omitempty"` } diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 50eb5c4161..6f6e541368 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -59,6 +59,8 @@ func (c Config) MarshalTOML() (interface{}, error) { RPCEVMTimeout time.Duration RPCTxFeeCap float64 OverrideOsaka *uint64 `toml:",omitempty"` + OverrideBPO1 *uint64 `toml:",omitempty"` + OverrideBPO2 *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"` } var enc Config @@ -104,6 +106,8 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.RPCEVMTimeout = c.RPCEVMTimeout enc.RPCTxFeeCap = c.RPCTxFeeCap enc.OverrideOsaka = c.OverrideOsaka + enc.OverrideBPO1 = c.OverrideBPO1 + enc.OverrideBPO2 = c.OverrideBPO2 enc.OverrideVerkle = c.OverrideVerkle return &enc, nil } @@ -153,6 +157,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { RPCEVMTimeout *time.Duration RPCTxFeeCap *float64 OverrideOsaka *uint64 `toml:",omitempty"` + OverrideBPO1 *uint64 `toml:",omitempty"` + OverrideBPO2 *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"` } var dec Config @@ -285,6 +291,12 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.OverrideOsaka != nil { c.OverrideOsaka = dec.OverrideOsaka } + if dec.OverrideBPO1 != nil { + c.OverrideBPO1 = dec.OverrideBPO1 + } + if dec.OverrideBPO2 != nil { + c.OverrideBPO2 = dec.OverrideBPO2 + } if dec.OverrideVerkle != nil { c.OverrideVerkle = dec.OverrideVerkle } From 89158aa64e09c91f8fd56e05afaab4e28edc6be1 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Thu, 25 Sep 2025 10:27:23 +0200 Subject: [PATCH 114/119] core/txpool/blobpool: convert and add one-by-one (#32718) This is a small improvement on #32656 in case Add was called with multiple type 3 transactions, adding transactions to the pool one-by-one as they are converted. Announcement to peers is still done in a batch. Signed-off-by: Csaba Kiraly --- core/txpool/blobpool/blobpool.go | 72 ++++++++++++++------------------ 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index f4aa406e2a..bfaf4d5b8e 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1640,11 +1640,11 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int { return available } -// preCheck performs the static validation upon the provided txs and converts +// preCheck performs the static validation upon the provided tx and converts // the legacy sidecars if Osaka fork has been activated with a short time window. // // This function is pure static and lock free. -func (p *BlobPool) preCheck(txs []*types.Transaction) ([]*types.Transaction, []error) { +func (p *BlobPool) preCheck(tx *types.Transaction) error { var ( head = p.head.Load() isOsaka = p.chain.Config().IsOsaka(head.Number, head.Time) @@ -1653,56 +1653,46 @@ func (p *BlobPool) preCheck(txs []*types.Transaction) ([]*types.Transaction, []e if isOsaka { deadline = time.Unix(int64(*p.chain.Config().OsakaTime), 0).Add(conversionTimeWindow) } - var errs []error - for _, tx := range txs { - // Validate the transaction statically at first to avoid unnecessary - // conversion. This step doesn't require lock protection. - if err := p.ValidateTxBasics(tx); err != nil { - errs = append(errs, err) - continue - } - // Before the Osaka fork, reject the blob txs with cell proofs - if !isOsaka { - if tx.BlobTxSidecar().Version == types.BlobSidecarVersion0 { - errs = append(errs, nil) - } else { - errs = append(errs, errors.New("cell proof is not supported yet")) - } - continue - } - // After the Osaka fork, reject the legacy blob txs if the conversion - // time window is passed. - if tx.BlobTxSidecar().Version == types.BlobSidecarVersion1 { - errs = append(errs, nil) - continue - } - if head.Time > uint64(deadline.Unix()) { - errs = append(errs, errors.New("legacy blob tx is not supported")) - continue - } - // Convert the legacy sidecar after Osaka fork. This could be a long - // procedure which takes a few seconds, even minutes if there is a long - // queue. Fortunately it will only block the routine of the source peer - // announcing the tx, without affecting other parts. - errs = append(errs, p.cQueue.convert(tx)) + // Validate the transaction statically at first to avoid unnecessary + // conversion. This step doesn't require lock protection. + if err := p.ValidateTxBasics(tx); err != nil { + return err } - return txs, errs + // Before the Osaka fork, reject the blob txs with cell proofs + if !isOsaka { + if tx.BlobTxSidecar().Version == types.BlobSidecarVersion0 { + return nil + } else { + return errors.New("cell proof is not supported yet") + } + } + // After the Osaka fork, reject the legacy blob txs if the conversion + // time window is passed. + if tx.BlobTxSidecar().Version == types.BlobSidecarVersion1 { + return nil + } + if head.Time > uint64(deadline.Unix()) { + return errors.New("legacy blob tx is not supported") + } + // Convert the legacy sidecar after Osaka fork. This could be a long + // procedure which takes a few seconds, even minutes if there is a long + // queue. Fortunately it will only block the routine of the source peer + // announcing the tx, without affecting other parts. + return p.cQueue.convert(tx) } // Add inserts a set of blob transactions into the pool if they pass validation (both // consensus validity and pool restrictions). func (p *BlobPool) Add(txs []*types.Transaction, sync bool) []error { var ( - errs []error - adds = make([]*types.Transaction, 0, len(txs)) + errs []error = make([]error, len(txs)) + adds = make([]*types.Transaction, 0, len(txs)) ) - txs, errs = p.preCheck(txs) for i, tx := range txs { - if errs[i] != nil { + if errs[i] = p.preCheck(tx); errs[i] != nil { continue } - errs[i] = p.add(tx) - if errs[i] == nil { + if errs[i] = p.add(tx); errs[i] == nil { adds = append(adds, tx.WithoutBlobTxSidecar()) } } From ad484fcbd0f513d09d20de616528efba9bba9d71 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 25 Sep 2025 13:14:28 +0200 Subject: [PATCH 115/119] build: upgrade to execution-spec-tests v5.1.0 (#32742) https://github.com/ethereum/execution-spec-tests/releases/tag/v5.1.0 --- build/checksums.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index ca937d115c..98ee3a91ef 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -1,9 +1,9 @@ # This file contains sha256 checksums of optional build dependencies. -# version:spec-tests v5.0.0 +# version:spec-tests v5.1.0 # https://github.com/ethereum/execution-spec-tests/releases -# https://github.com/ethereum/execution-spec-tests/releases/download/v5.0.0 -a5ed96800ca1af0d86fe2ee894861c24eea079bfb83b924f565bb86ba70021d5 fixtures_develop.tar.gz +# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0 +a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz # version:golang 1.25.1 # https://go.dev/dl/ From bacc1504baa6ede16e8541d74b141d4dac763e3a Mon Sep 17 00:00:00 2001 From: Delweng Date: Thu, 25 Sep 2025 19:15:12 +0800 Subject: [PATCH 116/119] core/txpool: add eip2681 check for incoming transactions (#32726) --- core/txpool/validation.go | 4 ++ core/txpool/validation_test.go | 115 +++++++++++++++++++++++++++++++++ eth/api_backend_test.go | 22 +++++++ 3 files changed, 141 insertions(+) create mode 100644 core/txpool/validation_test.go diff --git a/core/txpool/validation.go b/core/txpool/validation.go index df53f30a86..4b54eac50d 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -116,6 +116,10 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types if _, err := types.Sender(signer, tx); err != nil { return fmt.Errorf("%w: %v", ErrInvalidSender, err) } + // Limit nonce to 2^64-1 per EIP-2681 + if tx.Nonce()+1 < tx.Nonce() { + return core.ErrNonceMax + } // Ensure the transaction has more gas than the bare minimum needed to cover // the transaction metadata intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, rules.IsIstanbul, rules.IsShanghai) diff --git a/core/txpool/validation_test.go b/core/txpool/validation_test.go new file mode 100644 index 0000000000..3945b548c1 --- /dev/null +++ b/core/txpool/validation_test.go @@ -0,0 +1,115 @@ +// Copyright 2025 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 . + +package txpool + +import ( + "crypto/ecdsa" + "errors" + "math" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +func TestValidateTransactionEIP2681(t *testing.T) { + key, err := crypto.GenerateKey() + if err != nil { + t.Fatal(err) + } + + head := &types.Header{ + Number: big.NewInt(1), + GasLimit: 5000000, + Time: 1, + Difficulty: big.NewInt(1), + } + + signer := types.LatestSigner(params.TestChainConfig) + + // Create validation options + opts := &ValidationOptions{ + Config: params.TestChainConfig, + Accept: 0xFF, // Accept all transaction types + MaxSize: 32 * 1024, + MaxBlobCount: 6, + MinTip: big.NewInt(0), + } + + tests := []struct { + name string + nonce uint64 + wantErr error + }{ + { + name: "normal nonce", + nonce: 42, + wantErr: nil, + }, + { + name: "max allowed nonce (2^64-2)", + nonce: math.MaxUint64 - 1, + wantErr: nil, + }, + { + name: "EIP-2681 nonce overflow (2^64-1)", + nonce: math.MaxUint64, + wantErr: core.ErrNonceMax, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tx := createTestTransaction(key, tt.nonce) + err := ValidateTransaction(tx, head, signer, opts) + + if tt.wantErr == nil { + if err != nil { + t.Errorf("ValidateTransaction() error = %v, wantErr nil", err) + } + } else { + if err == nil { + t.Errorf("ValidateTransaction() error = nil, wantErr %v", tt.wantErr) + } else if !errors.Is(err, tt.wantErr) { + t.Errorf("ValidateTransaction() error = %v, wantErr %v", err, tt.wantErr) + } + } + }) + } +} + +// createTestTransaction creates a basic transaction for testing +func createTestTransaction(key *ecdsa.PrivateKey, nonce uint64) *types.Transaction { + to := common.HexToAddress("0x0000000000000000000000000000000000000001") + + txdata := &types.LegacyTx{ + Nonce: nonce, + To: &to, + Value: big.NewInt(1000), + Gas: 21000, + GasPrice: big.NewInt(1), + Data: nil, + } + + tx := types.NewTx(txdata) + signedTx, _ := types.SignTx(tx, types.HomesteadSigner{}, key) + return signedTx +} diff --git a/eth/api_backend_test.go b/eth/api_backend_test.go index d0718ede1c..aa0539511b 100644 --- a/eth/api_backend_test.go +++ b/eth/api_backend_test.go @@ -20,6 +20,7 @@ import ( "context" "crypto/ecdsa" "errors" + "math" "math/big" "testing" "time" @@ -130,6 +131,27 @@ func TestSendTx(t *testing.T) { testSendTx(t, true) } +func TestSendTxEIP2681(t *testing.T) { + b := initBackend(false) + + // Test EIP-2681: nonce overflow should be rejected + tx := makeTx(uint64(math.MaxUint64), nil, nil, key) // max uint64 nonce + err := b.SendTx(context.Background(), tx) + if err == nil { + t.Fatal("Expected EIP-2681 nonce overflow error, but transaction was accepted") + } + if !errors.Is(err, core.ErrNonceMax) { + t.Errorf("Expected core.ErrNonceMax, got: %v", err) + } + + // Test normal case: should succeed + normalTx := makeTx(0, nil, nil, key) + err = b.SendTx(context.Background(), normalTx) + if err != nil { + t.Errorf("Normal transaction should succeed, got error: %v", err) + } +} + func testSendTx(t *testing.T, withLocal bool) { b := initBackend(withLocal) From 7d8ccddaaccb4f9ebeaa317ad82aee93ab891399 Mon Sep 17 00:00:00 2001 From: cui Date: Thu, 25 Sep 2025 21:05:27 +0800 Subject: [PATCH 117/119] all: refactor to use builtin max/min (#32694) Replaces the last few instances of `math.Max` and `math.Min` with go builtins. --- cmd/utils/flags.go | 3 +-- metrics/runtimehistogram.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c7775adb53..c9da08578c 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -24,7 +24,6 @@ import ( "encoding/json" "errors" "fmt" - "math" "math/big" "net" "net/http" @@ -1620,7 +1619,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } // Ensure Go's GC ignores the database cache for trigger percentage cache := ctx.Int(CacheFlag.Name) - gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024))) + gogc := max(20, min(100, 100/(float64(cache)/1024))) log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc)) godebug.SetGCPercent(int(gogc)) diff --git a/metrics/runtimehistogram.go b/metrics/runtimehistogram.go index 0ab8914602..efbed498af 100644 --- a/metrics/runtimehistogram.go +++ b/metrics/runtimehistogram.go @@ -204,7 +204,7 @@ func (h *runtimeHistogramSnapshot) Percentiles(ps []float64) []float64 { thresholds := make([]float64, len(ps)) indexes := make([]int, len(ps)) for i, percentile := range ps { - thresholds[i] = count * math.Max(0, math.Min(1.0, percentile)) + thresholds[i] = count * max(0, min(1.0, percentile)) indexes[i] = i } sort.Sort(floatsAscendingKeepingIndex{thresholds, indexes}) From 0977a02ec1eec2d0d85236c33b0e8878b703f890 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 25 Sep 2025 18:07:33 +0200 Subject: [PATCH 118/119] params: schedule Osaka/BPO1/BPO2 for testnets (#32735) Timestamps taken from: - Holesky: https://github.com/eth-clients/holesky/blob/main/metadata/genesis.json - Sepolia: https://github.com/eth-clients/sepolia/blob/main/metadata/genesis.json - Hoodi: https://github.com/eth-clients/hoodi/blob/main/metadata/genesis.json --- core/forkid/forkid_test.go | 30 ++++++++++++++++++++++------ params/config.go | 26 +++++++++++++++++++++---- tests/init.go | 40 +++++++++++++++++++++++++------------- 3 files changed, 72 insertions(+), 24 deletions(-) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index 413e4d77a8..dc6e6fe817 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -95,8 +95,14 @@ func TestCreation(t *testing.T) { {1735372, 1706655071, ID{Hash: checksumToBytes(0xf7f9bc08), Next: 1706655072}}, // Last Shanghai block {1735372, 1706655072, ID{Hash: checksumToBytes(0x88cf81d9), Next: 1741159776}}, // First Cancun block {1735372, 1741159775, ID{Hash: checksumToBytes(0x88cf81d9), Next: 1741159776}}, // Last Cancun block - {1735372, 1741159776, ID{Hash: checksumToBytes(0xed88b5fd), Next: 0}}, // First Prague block - {1735372, 2741159776, ID{Hash: checksumToBytes(0xed88b5fd), Next: 0}}, // Future Prague block + {1735372, 1741159776, ID{Hash: checksumToBytes(0xed88b5fd), Next: 1760427360}}, // First Prague block + {1735372, 1760427359, ID{Hash: checksumToBytes(0xed88b5fd), Next: 1760427360}}, // Last Prague block + {1735372, 1760427360, ID{Hash: checksumToBytes(0xe2ae4999), Next: 1761017184}}, // First Osaka block + {1735372, 1761017183, ID{Hash: checksumToBytes(0xe2ae4999), Next: 1761017184}}, // Last Osaka block + {1735372, 1761017184, ID{Hash: checksumToBytes(0x56078a1e), Next: 1761607008}}, // First BPO1 block + {1735372, 1761607007, ID{Hash: checksumToBytes(0x56078a1e), Next: 1761607008}}, // Last BPO1 block + {1735372, 1761607008, ID{Hash: checksumToBytes(0x268956b6), Next: 0}}, // First BPO2 block + {1735372, 2000000000, ID{Hash: checksumToBytes(0x268956b6), Next: 0}}, // Future BPO2 block }, }, // Holesky test cases @@ -110,8 +116,14 @@ func TestCreation(t *testing.T) { {123, 1707305663, ID{Hash: checksumToBytes(0xfd4f016b), Next: 1707305664}}, // Last Shanghai block {123, 1707305664, ID{Hash: checksumToBytes(0x9b192ad0), Next: 1740434112}}, // First Cancun block {123, 1740434111, ID{Hash: checksumToBytes(0x9b192ad0), Next: 1740434112}}, // Last Cancun block - {123, 1740434112, ID{Hash: checksumToBytes(0xdfbd9bed), Next: 0}}, // First Prague block - {123, 2740434112, ID{Hash: checksumToBytes(0xdfbd9bed), Next: 0}}, // Future Prague block + {123, 1740434112, ID{Hash: checksumToBytes(0xdfbd9bed), Next: 1759308480}}, // First Prague block + {123, 1759308479, ID{Hash: checksumToBytes(0xdfbd9bed), Next: 1759308480}}, // Last Prague block + {123, 1759308480, ID{Hash: checksumToBytes(0x783def52), Next: 1759800000}}, // First Osaka block + {123, 1759799999, ID{Hash: checksumToBytes(0x783def52), Next: 1759800000}}, // Last Osaka block + {123, 1759800000, ID{Hash: checksumToBytes(0xa280a45c), Next: 1760389824}}, // First BPO1 block + {123, 1760389823, ID{Hash: checksumToBytes(0xa280a45c), Next: 1760389824}}, // Last BPO1 block + {123, 1760389824, ID{Hash: checksumToBytes(0x9bc6cb31), Next: 0}}, // First BPO2 block + {123, 2000000000, ID{Hash: checksumToBytes(0x9bc6cb31), Next: 0}}, // Future BPO1 block }, }, // Hoodi test cases @@ -121,8 +133,14 @@ func TestCreation(t *testing.T) { []testcase{ {0, 0, ID{Hash: checksumToBytes(0xbef71d30), Next: 1742999832}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris, Shanghai, Cancun block {123, 1742999831, ID{Hash: checksumToBytes(0xbef71d30), Next: 1742999832}}, // Last Cancun block - {123, 1742999832, ID{Hash: checksumToBytes(0x0929e24e), Next: 0}}, // First Prague block - {123, 2740434112, ID{Hash: checksumToBytes(0x0929e24e), Next: 0}}, // Future Prague block + {123, 1742999832, ID{Hash: checksumToBytes(0x0929e24e), Next: 1761677592}}, // First Prague block + {123, 1761677591, ID{Hash: checksumToBytes(0x0929e24e), Next: 1761677592}}, // Last Prague block + {123, 1761677592, ID{Hash: checksumToBytes(0xe7e0e7ff), Next: 1762365720}}, // First Osaka block + {123, 1762365719, ID{Hash: checksumToBytes(0xe7e0e7ff), Next: 1762365720}}, // Last Osaka block + {123, 1762365720, ID{Hash: checksumToBytes(0x3893353e), Next: 1762955544}}, // First BPO1 block + {123, 1762955543, ID{Hash: checksumToBytes(0x3893353e), Next: 1762955544}}, // Last BPO1 block + {123, 1762955544, ID{Hash: checksumToBytes(0x23aa1351), Next: 0}}, // First BPO2 block + {123, 2000000000, ID{Hash: checksumToBytes(0x23aa1351), Next: 0}}, // Future BPO2 block }, }, } diff --git a/params/config.go b/params/config.go index 3788325d5c..42a2c61ab5 100644 --- a/params/config.go +++ b/params/config.go @@ -91,11 +91,17 @@ var ( ShanghaiTime: newUint64(1696000704), CancunTime: newUint64(1707305664), PragueTime: newUint64(1740434112), + OsakaTime: newUint64(1759308480), + BPO1Time: newUint64(1759800000), + BPO2Time: newUint64(1760389824), DepositContractAddress: common.HexToAddress("0x4242424242424242424242424242424242424242"), Ethash: new(EthashConfig), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, + Osaka: DefaultOsakaBlobConfig, + BPO1: DefaultBPO1BlobConfig, + BPO2: DefaultBPO2BlobConfig, }, } // SepoliaChainConfig contains the chain parameters to run a node on the Sepolia test network. @@ -121,11 +127,17 @@ var ( ShanghaiTime: newUint64(1677557088), CancunTime: newUint64(1706655072), PragueTime: newUint64(1741159776), + OsakaTime: newUint64(1760427360), + BPO1Time: newUint64(1761017184), + BPO2Time: newUint64(1761607008), DepositContractAddress: common.HexToAddress("0x7f02c3e3c98b133055b8b348b2ac625669ed295d"), Ethash: new(EthashConfig), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, + Osaka: DefaultOsakaBlobConfig, + BPO1: DefaultBPO1BlobConfig, + BPO2: DefaultBPO2BlobConfig, }, } // HoodiChainConfig contains the chain parameters to run a node on the Hoodi test network. @@ -151,11 +163,17 @@ var ( ShanghaiTime: newUint64(0), CancunTime: newUint64(0), PragueTime: newUint64(1742999832), + OsakaTime: newUint64(1761677592), + BPO1Time: newUint64(1762365720), + BPO2Time: newUint64(1762955544), DepositContractAddress: common.HexToAddress("0x00000000219ab540356cBB839Cbe05303d7705Fa"), Ethash: new(EthashConfig), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, + Osaka: DefaultOsakaBlobConfig, + BPO1: DefaultBPO1BlobConfig, + BPO2: DefaultBPO2BlobConfig, }, } // AllEthashProtocolChanges contains every protocol change (EIPs) introduced @@ -361,15 +379,15 @@ var ( } // DefaultBPO1BlobConfig is the default blob configuration for the Osaka fork. DefaultBPO1BlobConfig = &BlobConfig{ - Target: 9, - Max: 14, - UpdateFraction: 8832827, + Target: 10, + Max: 15, + UpdateFraction: 8346193, } // DefaultBPO1BlobConfig is the default blob configuration for the Osaka fork. DefaultBPO2BlobConfig = &BlobConfig{ Target: 14, Max: 21, - UpdateFraction: 13739630, + UpdateFraction: 11684671, } // DefaultBPO1BlobConfig is the default blob configuration for the Osaka fork. DefaultBPO3BlobConfig = &BlobConfig{ diff --git a/tests/init.go b/tests/init.go index 71072ac275..705e929ae9 100644 --- a/tests/init.go +++ b/tests/init.go @@ -490,7 +490,7 @@ var Forks = map[string]*params.ChainConfig{ Cancun: params.DefaultCancunBlobConfig, Prague: params.DefaultPragueBlobConfig, Osaka: params.DefaultOsakaBlobConfig, - BPO1: params.DefaultBPO1BlobConfig, + BPO1: bpo1BlobConfig, }, }, "OsakaToBPO1AtTime15k": { @@ -519,7 +519,7 @@ var Forks = map[string]*params.ChainConfig{ Cancun: params.DefaultCancunBlobConfig, Prague: params.DefaultPragueBlobConfig, Osaka: params.DefaultOsakaBlobConfig, - BPO1: params.DefaultBPO1BlobConfig, + BPO1: bpo1BlobConfig, }, }, "BPO2": { @@ -549,8 +549,8 @@ var Forks = map[string]*params.ChainConfig{ Cancun: params.DefaultCancunBlobConfig, Prague: params.DefaultPragueBlobConfig, Osaka: params.DefaultOsakaBlobConfig, - BPO1: params.DefaultBPO1BlobConfig, - BPO2: params.DefaultBPO2BlobConfig, + BPO1: bpo1BlobConfig, + BPO2: bpo2BlobConfig, }, }, "BPO1ToBPO2AtTime15k": { @@ -580,8 +580,8 @@ var Forks = map[string]*params.ChainConfig{ Cancun: params.DefaultCancunBlobConfig, Prague: params.DefaultPragueBlobConfig, Osaka: params.DefaultOsakaBlobConfig, - BPO1: params.DefaultBPO1BlobConfig, - BPO2: params.DefaultBPO2BlobConfig, + BPO1: bpo1BlobConfig, + BPO2: bpo2BlobConfig, }, }, "BPO3": { @@ -612,8 +612,8 @@ var Forks = map[string]*params.ChainConfig{ Cancun: params.DefaultCancunBlobConfig, Prague: params.DefaultPragueBlobConfig, Osaka: params.DefaultOsakaBlobConfig, - BPO1: params.DefaultBPO1BlobConfig, - BPO2: params.DefaultBPO2BlobConfig, + BPO1: bpo1BlobConfig, + BPO2: bpo2BlobConfig, BPO3: params.DefaultBPO3BlobConfig, }, }, @@ -645,8 +645,8 @@ var Forks = map[string]*params.ChainConfig{ Cancun: params.DefaultCancunBlobConfig, Prague: params.DefaultPragueBlobConfig, Osaka: params.DefaultOsakaBlobConfig, - BPO1: params.DefaultBPO1BlobConfig, - BPO2: params.DefaultBPO2BlobConfig, + BPO1: bpo1BlobConfig, + BPO2: bpo2BlobConfig, BPO3: params.DefaultBPO3BlobConfig, }, }, @@ -679,8 +679,8 @@ var Forks = map[string]*params.ChainConfig{ Cancun: params.DefaultCancunBlobConfig, Prague: params.DefaultPragueBlobConfig, Osaka: params.DefaultOsakaBlobConfig, - BPO1: params.DefaultBPO1BlobConfig, - BPO2: params.DefaultBPO2BlobConfig, + BPO1: bpo1BlobConfig, + BPO2: bpo2BlobConfig, BPO3: params.DefaultBPO3BlobConfig, BPO4: params.DefaultBPO4BlobConfig, }, @@ -714,14 +714,26 @@ var Forks = map[string]*params.ChainConfig{ Cancun: params.DefaultCancunBlobConfig, Prague: params.DefaultPragueBlobConfig, Osaka: params.DefaultOsakaBlobConfig, - BPO1: params.DefaultBPO1BlobConfig, - BPO2: params.DefaultBPO2BlobConfig, + BPO1: bpo1BlobConfig, + BPO2: bpo2BlobConfig, BPO3: params.DefaultBPO3BlobConfig, BPO4: params.DefaultBPO4BlobConfig, }, }, } +var bpo1BlobConfig = ¶ms.BlobConfig{ + Target: 9, + Max: 14, + UpdateFraction: 8832827, +} + +var bpo2BlobConfig = ¶ms.BlobConfig{ + Target: 14, + Max: 21, + UpdateFraction: 13739630, +} + // AvailableForks returns the set of defined fork names func AvailableForks() []string { var availableForks []string From b964b6574f118cc23fc46a2353008a7a97b74fc1 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 25 Sep 2025 18:19:00 +0200 Subject: [PATCH 119/119] version: release go-ethereum v1.16.4 stable --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 092735ff1f..6430140631 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 16 // Minor version component of the current release - Patch = 4 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 16 // Minor version component of the current release + Patch = 4 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string )