diff --git a/core/state/database.go b/core/state/database.go index 3a0ac422ee..8ae55267aa 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -103,7 +103,8 @@ type Trie interface { // any existing value is deleted from the trie. The value bytes must not be modified // by the caller while they are stored in the trie. If a node was not found in the // database, a trie.MissingNodeError is returned. - UpdateStorage(addr common.Address, key, value []byte) error + // Returns the depth at which the value was inserted in the trie. + UpdateStorage(addr common.Address, key, value []byte) (int, error) // DeleteAccount abstracts an account deletion from the trie. DeleteAccount(address common.Address) error diff --git a/core/state/maxdepth_test.go b/core/state/maxdepth_test.go new file mode 100644 index 0000000000..a5ac149baa --- /dev/null +++ b/core/state/maxdepth_test.go @@ -0,0 +1,168 @@ +// 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 ( + "math/big" + "testing" + + "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/holiman/uint256" +) + +// TestMaxDepthInitialization tests that MaxDepth starts at 0 for new accounts +func TestMaxDepthInitialization(t *testing.T) { + db := rawdb.NewMemoryDatabase() + tdb := triedb.NewDatabase(db, nil) + sdb := NewDatabase(tdb, nil) + state, _ := New(types.EmptyRootHash, sdb) + + addr := common.HexToAddress("0x1234") + + state.AddBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified) + + if maxDepth := state.GetMaxDepth(addr); maxDepth != 0 { + t.Errorf("Initial MaxDepth should be 0, got %d", maxDepth) + } +} + +// TestMaxDepthMultipleStorage tests MaxDepth tracking with multiple storage writes +func TestMaxDepthMultipleStorage(t *testing.T) { + db := rawdb.NewMemoryDatabase() + tdb := triedb.NewDatabase(db, nil) + sdb := NewDatabase(tdb, nil) + state, _ := New(types.EmptyRootHash, sdb) + + addr := common.HexToAddress("0x1234") + + for i := 0; i < 10; i++ { + key := common.BigToHash(big.NewInt(int64(i))) + val := common.BigToHash(big.NewInt(int64(i * 100))) + state.SetState(addr, key, val) + } + + root, err := state.Commit(0, false, false) + if err != nil { + t.Fatalf("Failed to commit state: %v", err) + } + + state, _ = New(root, sdb) + + maxDepth := state.GetMaxDepth(addr) + if maxDepth == 0 { + t.Error("MaxDepth should be > 0 after storage writes") + } + t.Logf("MaxDepth after 10 storage writes: %d", maxDepth) +} + +// TestMaxDepthEmptyAccount tests that non-existent accounts return 0 for MaxDepth +func TestMaxDepthEmptyAccount(t *testing.T) { + db := rawdb.NewMemoryDatabase() + tdb := triedb.NewDatabase(db, nil) + sdb := NewDatabase(tdb, nil) + state, _ := New(types.EmptyRootHash, sdb) + + addr := common.HexToAddress("0x9999") + + // Check MaxDepth for non-existent account + maxDepth := state.GetMaxDepth(addr) + if maxDepth != 0 { + t.Errorf("Non-existent account should have MaxDepth 0, got %d", maxDepth) + } +} + +// TestMaxDepthIncrementalIncrease tests that MaxDepth increases progressively +// as more keys are added to the storage trie. With carefully chosen keys that +// create hash collisions at specific depths (2-3), we can observe incremental increases. +func TestMaxDepthIncrementalIncrease(t *testing.T) { + db := rawdb.NewMemoryDatabase() + tdb := triedb.NewDatabase(db, nil) + sdb := NewDatabase(tdb, nil) + + addr := common.HexToAddress("0x1234") + + // Insert keys progressively and track MaxDepth changes. + // With enough keys, we'll naturally see depth increases. + // We insert in multiple "blocks" to simulate incremental growth. + + var previousMaxDepth uint64 = 0 + var depthIncreases []int // Track which blocks caused depth increases + + // We'll insert keys in batches, starting from a fresh state each time + // This simulates blocks where storage grows incrementally + // Use larger, more spread-out values to increase hash collision chances + keyBatches := []int{1, 200, 200, 200, 200, 200} // Number of keys to insert in each "block" + + for blockNum, totalKeys := range keyBatches { + state, _ := New(types.EmptyRootHash, sdb) + state.AddBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified) + + // Insert keys using larger, more spread-out values to increase + // the chance of hash collisions that create deeper trie structures + for i := range totalKeys { + // Use a larger multiplier to spread keys across hash space + key := common.BigToHash(big.NewInt(int64(i*1000 + 1))) + val := common.BigToHash(big.NewInt(int64(i*100 + 1))) + state.SetState(addr, key, val) + } + + root, err := state.Commit(0, false, false) + if err != nil { + t.Fatalf("Block %d: Failed to commit state: %v", blockNum, err) + } + if err := tdb.Commit(root, false); err != nil { + t.Fatalf("Block %d: Failed to commit trie: %v", blockNum, err) + } + + // Reload state and get MaxDepth + state, _ = New(root, sdb) + currentMaxDepth := state.GetMaxDepth(addr) + + t.Logf("Block %d: Inserted %d keys, MaxDepth = %d (previous = %d)", + blockNum, totalKeys, currentMaxDepth, previousMaxDepth) + + // Track if depth increased + if currentMaxDepth > previousMaxDepth { + depthIncreases = append(depthIncreases, blockNum) + } + + // MaxDepth should never decrease + if currentMaxDepth < previousMaxDepth { + t.Errorf("Block %d: MaxDepth decreased from %d to %d", blockNum, previousMaxDepth, currentMaxDepth) + } + + previousMaxDepth = currentMaxDepth + } + + // Verify we saw at least some depth increases + if len(depthIncreases) == 0 { + t.Error("Expected to see at least one MaxDepth increase across blocks") + } else { + t.Logf("MaxDepth increased in %d blocks: %v", len(depthIncreases), depthIncreases) + } + + // Final verification: with 31 keys, we should have decent depth + if previousMaxDepth < 2 { + t.Logf("Note: Final MaxDepth is %d with 31 keys - hash distribution may need more keys for deeper collisions", previousMaxDepth) + } + + t.Logf("Final MaxDepth: %d", previousMaxDepth) +} diff --git a/core/state/state_object.go b/core/state/state_object.go index 2938750503..b883762de8 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -325,6 +325,7 @@ func (s *stateObject) updateTrie() (Trie, error) { deletions []common.Hash used = make([]common.Hash, 0, len(s.uncommittedStorage)) ) + var maxDepth int for key, origin := range s.uncommittedStorage { // Skip noop changes, persist actual changes value, exist := s.pendingStorage[key] @@ -337,10 +338,14 @@ func (s *stateObject) updateTrie() (Trie, error) { continue } if (value != common.Hash{}) { - if err := tr.UpdateStorage(s.address, key[:], common.TrimLeftZeroes(value[:])); err != nil { + depth, err := tr.UpdateStorage(s.address, key[:], common.TrimLeftZeroes(value[:])) + if err != nil { s.db.setError(err) return nil, err } + if depth > maxDepth { + maxDepth = depth + } s.db.StorageUpdated.Add(1) } else { deletions = append(deletions, key) @@ -358,6 +363,9 @@ func (s *stateObject) updateTrie() (Trie, error) { if s.db.prefetcher != nil { s.db.prefetcher.used(s.addrHash, s.data.Root, nil, used) } + if uint64(maxDepth) > s.data.MaxDepth { + s.data.MaxDepth = s.data.MaxDepth + 1 + } s.uncommittedStorage = make(Storage) // empties the commit markers return tr, nil } @@ -585,3 +593,7 @@ func (s *stateObject) Nonce() uint64 { func (s *stateObject) Root() common.Hash { return s.data.Root } + +func (s *stateObject) MaxDepth() uint64 { + return s.data.MaxDepth +} diff --git a/core/state/statedb.go b/core/state/statedb.go index b770698255..f179700e05 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -327,6 +327,15 @@ func (s *StateDB) GetNonce(addr common.Address) uint64 { return 0 } +// GetMaxDepth retrieves the max depth from the given address or 0 if object not found +func (s *StateDB) GetMaxDepth(addr common.Address) uint64 { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.MaxDepth() + } + return 0 +} + // GetStorageRoot retrieves the storage root from the given address or empty // if object not found. func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash { diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index d2595bcefe..65ba95ad62 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -61,6 +61,10 @@ func (s *hookedStateDB) GetNonce(addr common.Address) uint64 { return s.inner.GetNonce(addr) } +func (s *hookedStateDB) GetMaxDepth(addr common.Address) uint64 { + return s.inner.GetMaxDepth(addr) +} + func (s *hookedStateDB) GetCodeHash(addr common.Address) common.Hash { return s.inner.GetCodeHash(addr) } diff --git a/core/types/gen_account_rlp.go b/core/types/gen_account_rlp.go index 8b424493af..a785451236 100644 --- a/core/types/gen_account_rlp.go +++ b/core/types/gen_account_rlp.go @@ -16,6 +16,10 @@ func (obj *StateAccount) EncodeRLP(_w io.Writer) error { } w.WriteBytes(obj.Root[:]) w.WriteBytes(obj.CodeHash) + _tmp1 := obj.MaxDepth != 0 + if _tmp1 { + w.WriteUint64(obj.MaxDepth) + } w.ListEnd(_tmp0) return w.Flush() } diff --git a/core/types/state_account.go b/core/types/state_account.go index 52ef843b35..8b6f515c3b 100644 --- a/core/types/state_account.go +++ b/core/types/state_account.go @@ -33,6 +33,7 @@ type StateAccount struct { Balance *uint256.Int Root common.Hash // merkle root of the storage trie CodeHash []byte + MaxDepth uint64 `rlp:"optional"` } // NewEmptyStateAccount constructs an empty state account. @@ -55,6 +56,7 @@ func (acct *StateAccount) Copy() *StateAccount { Balance: balance, Root: acct.Root, CodeHash: common.CopyBytes(acct.CodeHash), + MaxDepth: acct.MaxDepth, } } @@ -66,6 +68,7 @@ type SlimAccount struct { Balance *uint256.Int Root []byte // Nil if root equals to types.EmptyRootHash CodeHash []byte // Nil if hash equals to types.EmptyCodeHash + MaxDepth uint64 `rlp:"optional"` } // SlimAccountRLP encodes the state account in 'slim RLP' format. @@ -80,6 +83,7 @@ func SlimAccountRLP(account StateAccount) []byte { if !bytes.Equal(account.CodeHash, EmptyCodeHash[:]) { slim.CodeHash = account.CodeHash } + slim.MaxDepth = account.MaxDepth data, err := rlp.EncodeToBytes(slim) if err != nil { panic(err) @@ -108,6 +112,7 @@ func FullAccount(data []byte) (*StateAccount, error) { } else { account.CodeHash = slim.CodeHash } + account.MaxDepth = slim.MaxDepth return &account, nil } diff --git a/core/vm/interface.go b/core/vm/interface.go index d7f4c10e1f..e580eb112d 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -39,6 +39,8 @@ type StateDB interface { GetNonce(common.Address) uint64 SetNonce(common.Address, uint64, tracing.NonceChangeReason) + GetMaxDepth(common.Address) uint64 + GetCodeHash(common.Address) common.Hash GetCode(common.Address) []byte diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 085b018e4c..5f12ed2017 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -42,6 +42,11 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { // Check slot presence in the access list if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { cost = params.ColdSloadCostEIP2929 + // Add MaxDepth cost for cold writes if depth exceeds threshold + maxDepth := evm.StateDB.GetMaxDepth(contract.Address()) + if maxDepth >= params.MaxDepthGasThreshold { + cost += params.MaxDepthGasFactor * maxDepth + } // If the caller cannot afford the cost, this change will be rolled back evm.StateDB.AddSlotToAccessList(contract.Address(), slot) } diff --git a/params/protocol_params.go b/params/protocol_params.go index e8b044f450..a2478ce98a 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -69,6 +69,10 @@ const ( ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST + // MaxDepthGasFactor is the gas cost per unit of MaxDepth for cold SSTORE operations + MaxDepthGasFactor = uint64(200) // Gas cost multiplier for MaxDepth in cold SSTORE + MaxDepthGasThreshold = uint64(9) // Threshold depth - no extra cost charged until depth reaches this value + // In EIP-2200: SstoreResetGas was 5000. // In EIP-2929: SstoreResetGas was changed to '5000 - COLD_SLOAD_COST'. // In EIP-3529: SSTORE_CLEARS_SCHEDULE is defined as SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST diff --git a/trie/list_hasher.go b/trie/list_hasher.go index 8f334f9901..d87a8a5534 100644 --- a/trie/list_hasher.go +++ b/trie/list_hasher.go @@ -47,7 +47,8 @@ func (h *ListHasher) Reset() { // Update inserts a key-value pair into the trie. func (h *ListHasher) Update(key []byte, value []byte) error { key, value = bytes.Clone(key), bytes.Clone(value) - return h.tr.Update(key, value) + _, err := h.tr.Update(key, value) + return err } // Hash computes the root hash of all inserted key-value pairs. diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 7c7bd184bf..e3e5c5438a 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -197,17 +197,18 @@ func (t *StateTrie) MustUpdate(key, value []byte) { // stored in the trie. // // If a node is not found in the database, a MissingNodeError is returned. -func (t *StateTrie) UpdateStorage(_ common.Address, key, value []byte) error { +// Returns the depth at which the value was inserted in the trie. +func (t *StateTrie) UpdateStorage(_ common.Address, key, value []byte) (int, error) { hk := crypto.Keccak256(key) v, _ := rlp.EncodeToBytes(value) - err := t.trie.Update(hk, v) + depth, err := t.trie.Update(hk, v) if err != nil { - return err + return 0, err } if t.preimages != nil { t.secKeyCache[common.Hash(hk)] = common.CopyBytes(key) } - return nil + return depth, nil } // UpdateAccount will abstract the write of an account to the secure trie. @@ -217,7 +218,7 @@ func (t *StateTrie) UpdateAccount(address common.Address, acc *types.StateAccoun if err != nil { return err } - if err := t.trie.Update(hk, data); err != nil { + if _, err := t.trie.Update(hk, data); err != nil { return err } if t.preimages != nil { diff --git a/trie/transition.go b/trie/transition.go index da49c6cdc2..77d9c09ef5 100644 --- a/trie/transition.go +++ b/trie/transition.go @@ -119,7 +119,8 @@ func (t *TransitionTrie) PrefetchAccount(addresses []common.Address) error { // UpdateStorage associates key with value in the trie. If value has length zero, any // existing value is deleted from the trie. The value bytes must not be modified // by the caller while they are stored in the trie. -func (t *TransitionTrie) UpdateStorage(address common.Address, key []byte, value []byte) error { +// Returns the depth at which the value was inserted in the trie. +func (t *TransitionTrie) UpdateStorage(address common.Address, key []byte, value []byte) (int, error) { var v []byte if len(value) >= 32 { v = value[:32] diff --git a/trie/trie.go b/trie/trie.go index 1ef2c2f1a6..0c75f577a7 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -360,7 +360,7 @@ func (t *Trie) getNode(origNode node, path []byte, pos int) (item []byte, newnod // MustUpdate is a wrapper of Update and will omit any encountered error but // just print out an error message. func (t *Trie) MustUpdate(key, value []byte) { - if err := t.Update(key, value); err != nil { + if _, err := t.Update(key, value); err != nil { log.Error("Unhandled trie error in Trie.Update", "err", err) } } @@ -374,40 +374,43 @@ func (t *Trie) MustUpdate(key, value []byte) { // // If the requested node is not present in trie, no error will be returned. // If the trie is corrupted, a MissingNodeError is returned. -func (t *Trie) Update(key, value []byte) error { +// Returns the depth at which the value was inserted. +func (t *Trie) Update(key, value []byte) (int, error) { // Short circuit if the trie is already committed and not usable. if t.committed { - return ErrCommitted + return 0, ErrCommitted } return t.update(key, value) } -func (t *Trie) update(key, value []byte) error { +func (t *Trie) update(key, value []byte) (int, error) { t.unhashed++ t.uncommitted++ k := keybytesToHex(key) + var depth int if len(value) != 0 { - _, n, err := t.insert(t.root, nil, k, valueNode(value)) + _, n, d, err := t.insert(t.root, nil, k, valueNode(value), 0) if err != nil { - return err + return 0, err } t.root = n + depth = d } else { _, n, err := t.delete(t.root, nil, k) if err != nil { - return err + return 0, err } t.root = n } - return nil + return depth, nil } -func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error) { +func (t *Trie) insert(n node, prefix, key []byte, value node, depth int) (bool, node, int, error) { if len(key) == 0 { if v, ok := n.(valueNode); ok { - return !bytes.Equal(v, value.(valueNode)), value, nil + return !bytes.Equal(v, value.(valueNode)), value, depth, nil } - return true, value, nil + return true, value, depth, nil } switch n := n.(type) { case *shortNode: @@ -415,26 +418,26 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error // If the whole key matches, keep this short node as is // and only update the value. if matchlen == len(n.Key) { - dirty, nn, err := t.insert(n.Val, append(prefix, key[:matchlen]...), key[matchlen:], value) + dirty, nn, d, err := t.insert(n.Val, append(prefix, key[:matchlen]...), key[matchlen:], value, depth+1) if !dirty || err != nil { - return false, n, err + return false, n, depth, err } - return true, &shortNode{n.Key, nn, t.newFlag()}, nil + return true, &shortNode{n.Key, nn, t.newFlag()}, d, nil } // Otherwise branch out at the index where they differ. branch := &fullNode{flags: t.newFlag()} var err error - _, branch.Children[n.Key[matchlen]], err = t.insert(nil, append(prefix, n.Key[:matchlen+1]...), n.Key[matchlen+1:], n.Val) + _, branch.Children[n.Key[matchlen]], _, err = t.insert(nil, append(prefix, n.Key[:matchlen+1]...), n.Key[matchlen+1:], n.Val, depth+1) if err != nil { - return false, nil, err + return false, nil, depth, err } - _, branch.Children[key[matchlen]], err = t.insert(nil, append(prefix, key[:matchlen+1]...), key[matchlen+1:], value) + _, branch.Children[key[matchlen]], _, err = t.insert(nil, append(prefix, key[:matchlen+1]...), key[matchlen+1:], value, depth+1) if err != nil { - return false, nil, err + return false, nil, depth, err } // Replace this shortNode with the branch if it occurs at index 0. if matchlen == 0 { - return true, branch, nil + return true, branch, depth+1, nil } // New branch node is created as a child of the original short node. // Track the newly inserted node in the tracer. The node identifier @@ -442,16 +445,16 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error t.opTracer.onInsert(append(prefix, key[:matchlen]...)) // Replace it with a short node leading up to the branch. - return true, &shortNode{key[:matchlen], branch, t.newFlag()}, nil + return true, &shortNode{key[:matchlen], branch, t.newFlag()}, depth+1, nil case *fullNode: - dirty, nn, err := t.insert(n.Children[key[0]], append(prefix, key[0]), key[1:], value) + dirty, nn, d, err := t.insert(n.Children[key[0]], append(prefix, key[0]), key[1:], value, depth+1) if !dirty || err != nil { - return false, n, err + return false, n, depth, err } n.flags = t.newFlag() n.Children[key[0]] = nn - return true, n, nil + return true, n, d, nil case nil: // New short node is created and track it in the tracer. The node identifier @@ -459,7 +462,7 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error // since it's always embedded in its parent. t.opTracer.onInsert(prefix) - return true, &shortNode{key, value, t.newFlag()}, nil + return true, &shortNode{key, value, t.newFlag()}, depth, nil case hashNode: // We've hit a part of the trie that isn't loaded yet. Load @@ -467,13 +470,13 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error // the path to the value in the trie. rn, err := t.resolveAndTrack(n, prefix) if err != nil { - return false, nil, err + return false, nil, depth, err } - dirty, nn, err := t.insert(rn, prefix, key, value) + dirty, nn, d, err := t.insert(rn, prefix, key, value, depth) if !dirty || err != nil { - return false, rn, err + return false, rn, depth, err } - return true, nn, nil + return true, nn, d, nil default: panic(fmt.Sprintf("%T: invalid node: %v", n, n)) diff --git a/trie/trie_test.go b/trie/trie_test.go index b8b8edb33e..f84543e8b7 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -120,7 +120,7 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) { t.Errorf("Unexpected error: %v", err) } trie, _ = New(TrieID(root), triedb) - err = trie.Update([]byte("120099"), []byte("zxcvzxcvzxcvzxcvzxcvzxcvzxcvzxcv")) + _, err = trie.Update([]byte("120099"), []byte("zxcvzxcvzxcvzxcvzxcvzxcvzxcvzxcv")) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -159,7 +159,7 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) { if err != nil { t.Errorf("Unexpected error: %v", err) } - err = trie.Update([]byte("120099"), []byte("zxcv")) + _, err = trie.Update([]byte("120099"), []byte("zxcv")) if _, ok := err.(*MissingNodeError); !ok { t.Errorf("Wrong error: %v", err) } diff --git a/trie/verkle.go b/trie/verkle.go index 186ac1f642..cadb950653 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -179,7 +179,8 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount, // UpdateStorage implements state.Trie, writing the provided storage slot into // the tree. If the tree is corrupted, an error will be returned. -func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) error { +// Returns the depth at which the value was inserted in the trie (always 0 for verkle trees). +func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) (int, error) { // Left padding the slot value to 32 bytes. var v [32]byte if len(value) >= 32 { @@ -188,7 +189,9 @@ func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) er copy(v[32-len(value):], value[:]) } k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(address.Bytes()), key) - return t.root.Insert(k, v[:], t.nodeResolver) + err := t.root.Insert(k, v[:], t.nodeResolver) + // TODO: when eip is schedule, also implement this for verkle + return 0, err } // DeleteAccount leaves the account untouched, as no account deletion can happen diff --git a/trie/verkle_test.go b/trie/verkle_test.go index 1832e3db13..710dc7015c 100644 --- a/trie/verkle_test.go +++ b/trie/verkle_test.go @@ -65,7 +65,7 @@ func TestVerkleTreeReadWrite(t *testing.T) { t.Fatalf("Failed to update account, %v", err) } for key, val := range storages[addr] { - if err := tr.UpdateStorage(addr, key.Bytes(), val); err != nil { + if _, err := tr.UpdateStorage(addr, key.Bytes(), val); err != nil { t.Fatalf("Failed to update storage, %v", err) } } @@ -106,7 +106,7 @@ func TestVerkleRollBack(t *testing.T) { t.Fatalf("Failed to update account, %v", err) } for key, val := range storages[addr] { - if err := tr.UpdateStorage(addr, key.Bytes(), val); err != nil { + if _, err := tr.UpdateStorage(addr, key.Bytes(), val); err != nil { t.Fatalf("Failed to update storage, %v", err) } } diff --git a/triedb/pathdb/execute.go b/triedb/pathdb/execute.go index 4c1cafec12..f4d20b28df 100644 --- a/triedb/pathdb/execute.go +++ b/triedb/pathdb/execute.go @@ -123,7 +123,7 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) if len(val) == 0 { deletes = append(deletes, tkey) } else { - err := st.Update(tkey.Bytes(), val) + _, err := st.Update(tkey.Bytes(), val) if err != nil { return err } @@ -151,7 +151,8 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) if err != nil { return err } - return ctx.accountTrie.Update(addrHash.Bytes(), full) + _, err = ctx.accountTrie.Update(addrHash.Bytes(), full) + return err } // deleteAccount the account was not present in prev-state, and is expected