mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 20:56:42 +00:00
core/state, core/types, trie: implement EIP-8032
This commit is contained in:
parent
ee309827c6
commit
e09e1aa791
18 changed files with 269 additions and 45 deletions
|
|
@ -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
|
||||
|
|
|
|||
168
core/state/maxdepth_test.go
Normal file
168
core/state/maxdepth_test.go
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
59
trie/trie.go
59
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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue