diff --git a/builder.go b/builder.go index 2054f60..bccbeee 100644 --- a/builder.go +++ b/builder.go @@ -21,7 +21,6 @@ package statediff import ( "bytes" - "encoding/hex" "fmt" "time" @@ -35,7 +34,6 @@ import ( "github.com/cerc-io/plugeth-statediff/indexer/database/metrics" "github.com/cerc-io/plugeth-statediff/indexer/ipld" "github.com/cerc-io/plugeth-statediff/indexer/shared" - "github.com/cerc-io/plugeth-statediff/trie_helpers" sdtypes "github.com/cerc-io/plugeth-statediff/types" "github.com/cerc-io/plugeth-statediff/utils" "github.com/cerc-io/plugeth-statediff/utils/log" @@ -59,25 +57,19 @@ type StateDiffBuilder struct { stateCache adapt.StateView } -type IterPair struct { +type iterPair struct { Older, Newer trie.NodeIterator } -func StateNodeAppender(nodes *[]sdtypes.StateLeafNode) sdtypes.StateNodeSink { - return func(node sdtypes.StateLeafNode) error { - *nodes = append(*nodes, node) - return nil - } +type accountUpdate struct { + new sdtypes.AccountWrapper + oldRoot common.Hash } -func StorageNodeAppender(nodes *[]sdtypes.StorageLeafNode) sdtypes.StorageNodeSink { - return func(node sdtypes.StorageLeafNode) error { - *nodes = append(*nodes, node) - return nil - } -} -func IPLDMappingAppender(iplds *[]sdtypes.IPLD) sdtypes.IPLDSink { - return func(c sdtypes.IPLD) error { - *iplds = append(*iplds, c) +type accountUpdateMap map[string]*accountUpdate + +func appender[T any](to *[]T) func(T) error { + return func(a T) error { + *to = append(*to, a) return nil } } @@ -94,7 +86,7 @@ func (sdb *StateDiffBuilder) BuildStateDiffObject(args Args, params Params) (sdt defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.BuildStateDiffObjectTimer) var stateNodes []sdtypes.StateLeafNode var iplds []sdtypes.IPLD - err := sdb.WriteStateDiff(args, params, StateNodeAppender(&stateNodes), IPLDMappingAppender(&iplds)) + err := sdb.WriteStateDiff(args, params, appender(&stateNodes), appender(&iplds)) if err != nil { return sdtypes.StateObject{}, err } @@ -107,147 +99,107 @@ func (sdb *StateDiffBuilder) BuildStateDiffObject(args Args, params Params) (sdt } // WriteStateDiff writes a statediff object to output sinks -func (sdb *StateDiffBuilder) WriteStateDiff(args Args, params Params, nodeSink sdtypes.StateNodeSink, - ipldSink sdtypes.IPLDSink) error { +func (sdb *StateDiffBuilder) WriteStateDiff( + args Args, params Params, + nodeSink sdtypes.StateNodeSink, + ipldSink sdtypes.IPLDSink, +) error { defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.WriteStateDiffTimer) // Load tries for old and new states oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot) if err != nil { - return fmt.Errorf("error creating trie for oldStateRoot: %w", err) + return fmt.Errorf("error opening old state trie: %w", err) } newTrie, err := sdb.stateCache.OpenTrie(args.NewStateRoot) if err != nil { - return fmt.Errorf("error creating trie for newStateRoot: %w", err) + return fmt.Errorf("error opening new state trie: %w", err) } - iters := IterPair{ + iters := iterPair{ Older: oldTrie.NodeIterator(nil), Newer: newTrie.NodeIterator(nil), } logger := log.New("hash", args.BlockHash, "number", args.BlockNumber) - return sdb.BuildStateDiff(iters, params, nodeSink, ipldSink, logger) -} -func (sdb *StateDiffBuilder) BuildStateDiff(iterPair IterPair, params Params, - nodeSink sdtypes.StateNodeSink, ipldSink sdtypes.IPLDSink, logger log.Logger) error { - logger.Trace("statediff BEGIN BuildStateDiff") - defer metrics.ReportAndUpdateDuration("statediff END BuildStateDiff", time.Now(), logger, metrics.IndexerMetrics.BuildStateDiffTimer) - // collect a slice of all the nodes that were touched and exist at B (B-A) - // a map of their leafkey to all the accounts that were touched and exist at B - // and a slice of all the paths for the nodes in both of the above sets - diffAccountsAtB, diffAccountsAtA, err := sdb.createdAndUpdatedState( - iterPair.Older, iterPair.Newer, params.watchedAddressesLeafPaths, nodeSink, ipldSink, logger) + err = sdb.processAccounts( + iters.Older, iters.Newer, + params.watchedAddressesLeafPaths, + nodeSink, ipldSink, logger) if err != nil { return fmt.Errorf("error collecting createdAndUpdatedNodes: %w", err) } - - // collect and sort the leafkey keys for both account mappings into a slice - t := time.Now() - createKeys := trie_helpers.SortKeys(diffAccountsAtB) - deleteKeys := trie_helpers.SortKeys(diffAccountsAtA) - logger.Debug("statediff BuildStateDiff sort", "duration", time.Since(t)) - - // and then find the intersection of these keys - // these are the leafkeys for the accounts which exist at both A and B but are different - // this also mutates the passed in createKeys and deleteKeys, removing the intersection keys - // and leaving the truly created or deleted keys in place - t = time.Now() - updatedKeys := trie_helpers.FindIntersection(createKeys, deleteKeys) - logger.Debug("statediff BuildStateDiff intersection", - "count", len(updatedKeys), - "duration", time.Since(t)) - - // build the diff nodes for the updated accounts using the mappings at both A and B as directed by the keys found as the intersection of the two - err = sdb.buildAccountUpdates(diffAccountsAtB, diffAccountsAtA, updatedKeys, nodeSink, ipldSink, logger) - if err != nil { - return fmt.Errorf("error building diff for updated accounts: %w", err) - } - // build the diff nodes for created accounts - err = sdb.buildAccountCreations(diffAccountsAtB, nodeSink, ipldSink, logger) - if err != nil { - return fmt.Errorf("error building diff for created accounts: %w", err) - } return nil } -// createdAndUpdatedState returns -// a slice of all the intermediate nodes that exist in a different state at B than A -// a mapping of their leafkeys to all the accounts that exist in a different state at B than A -// and a slice of the paths for all of the nodes included in both -func (sdb *StateDiffBuilder) createdAndUpdatedState(a, b trie.NodeIterator, +// processAccounts processes account creations and deletions, and returns a set of updated +// existing accounts, indexed by leaf key. +func (sdb *StateDiffBuilder) processAccounts(a, b trie.NodeIterator, watchedAddressesLeafPaths [][]byte, nodeSink sdtypes.StateNodeSink, ipldSink sdtypes.IPLDSink, logger log.Logger, -) (sdtypes.AccountMap, sdtypes.AccountMap, error) { - logger.Trace("statediff BEGIN createdAndUpdatedState") - defer metrics.ReportAndUpdateDuration("statediff END createdAndUpdatedState", time.Now(), logger, metrics.IndexerMetrics.CreatedAndUpdatedStateTimer) - diffAccountsAtB := make(sdtypes.AccountMap) - diffAccountsAtA := make(sdtypes.AccountMap) +) error { + defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.CreatedAndUpdatedStateTimer) - // Cache the RLP of the previous node on A and B. When we hit a value node this will be the parent blob. - var prevBlobFromA, prevBlobFromB []byte + updates := make(accountUpdateMap) + // Cache the RLP of the previous node. When we hit a value node this will be the parent blob. + var prevBlob []byte it, itCount := utils.NewSymmetricDifferenceIterator(a, b) for it.Next(true) { // ignore node if it is not along paths of interest if !isWatchedPathPrefix(watchedAddressesLeafPaths, it.Path()) { continue } - if it.FromA() { + if it.FromA() { // Node exists in the old trie if it.Leaf() { - accountW, err := sdb.processStateValueNode(it, prevBlobFromB) - if err != nil { - return nil, nil, err + var account types.StateAccount + if err := rlp.DecodeBytes(it.LeafBlob(), &account); err != nil { + return err } - if accountW == nil { - continue - } - leafKey := hex.EncodeToString(accountW.LeafKey) - diffAccountsAtA[leafKey] = *accountW + leafKey := make([]byte, len(it.LeafKey())) + copy(leafKey, it.LeafKey()) - if !it.CommonPath() { - // if this node is removed, not updated, that means the account was - // deleted. in that case, emit an empty "removed" state node record and - // "removed" storage node records for all the storage slots. - diff := sdtypes.StateLeafNode{ - AccountWrapper: sdtypes.AccountWrapper{ - LeafKey: accountW.LeafKey, - CID: shared.RemovedNodeStateCID, - }, - Removed: true, + if it.CommonPath() { + // If B also contains this leaf node, this is the old state of an updated account. + if update, ok := updates[string(leafKey)]; ok { + update.oldRoot = account.Root + } else { + updates[string(leafKey)] = &accountUpdate{oldRoot: account.Root} } - - err = sdb.buildRemovedAccountStorageNodes(accountW.Account.Root, StorageNodeAppender(&diff.StorageDiff)) + } else { + // This node was removed, meaning the account was deleted. Emit empty + // "removed" records for the state node and all storage all storage slots. + err := sdb.processAccountDeletion(leafKey, account, nodeSink) if err != nil { - return nil, nil, fmt.Errorf("failed building storage diffs for removed state account with key %x\r\nerror: %w", leafKey, err) - } - if err := nodeSink(diff); err != nil { - return nil, nil, err + return err } } - } else { - prevBlobFromB = make([]byte, len(it.NodeBlob())) - copy(prevBlobFromB, it.NodeBlob()) } continue } - // index values by leaf key + // Node exists in the new trie if it.Leaf() { - // if it is a "value" node, we will index the value by leaf key - accountW, err := sdb.processStateValueNode(it, prevBlobFromA) + accountW, err := sdb.decodeStateLeaf(it, prevBlob) if err != nil { - return nil, nil, err + return err } - if accountW == nil { - continue + + if it.CommonPath() { + // If A also contains this leaf node, this is the new state of an updated account. + if update, ok := updates[string(accountW.LeafKey)]; ok { + update.new = *accountW + } else { + updates[string(accountW.LeafKey)] = &accountUpdate{new: *accountW} + } + } else { // account was created + err := sdb.processAccountCreation(accountW, ipldSink, nodeSink) + if err != nil { + return err + } } - // for now, just add it to diffAccountsAtB - // we will compare to diffAccountsAtA to determine which diffAccountsAtB - // were creations and which were updates and also identify accounts that were removed going A->B - diffAccountsAtB[hex.EncodeToString(accountW.LeafKey)] = *accountW } else { - // trie nodes will be written to blockstore only - // reminder that this includes leaf nodes, since the geth iterator.Leaf() actually - // signifies a "value" node + // New trie nodes will be written to blockstore only. + // Reminder: this includes leaf nodes, since the geth iterator.Leaf() actually + // signifies a "value" node. if it.Hash() == zeroHash { continue } @@ -257,42 +209,104 @@ func (sdb *StateDiffBuilder) createdAndUpdatedState(a, b trie.NodeIterator, if len(watchedAddressesLeafPaths) > 0 { var elements []interface{} if err := rlp.DecodeBytes(nodeVal, &elements); err != nil { - return nil, nil, err + return err } ok, err := isLeaf(elements) if err != nil { - return nil, nil, err + return err } - partialPath := utils.CompactToHex(elements[0].([]byte)) - valueNodePath := append(it.Path(), partialPath...) - if ok && !isWatchedPath(watchedAddressesLeafPaths, valueNodePath) { - continue + if ok { + partialPath := utils.CompactToHex(elements[0].([]byte)) + valueNodePath := append(it.Path(), partialPath...) + if !isWatchedPath(watchedAddressesLeafPaths, valueNodePath) { + continue + } } } if err := ipldSink(sdtypes.IPLD{ CID: ipld.Keccak256ToCid(ipld.MEthStateTrie, it.Hash().Bytes()).String(), Content: nodeVal, }); err != nil { - return nil, nil, err + return err } - prevBlobFromA = nodeVal + prevBlob = nodeVal + } + } + + for key, update := range updates { + var storageDiff []sdtypes.StorageLeafNode + err := sdb.processUpdatedAccountStorage( + update.oldRoot, update.new.Account.Root, + appender(&storageDiff), ipldSink, + ) + if err != nil { + return fmt.Errorf("error processing incremental storage diffs for account with leafkey %x\r\nerror: %w", key, err) + } + + if err = nodeSink(sdtypes.StateLeafNode{ + AccountWrapper: update.new, + StorageDiff: storageDiff, + }); err != nil { + return err } } - logger.Debug("statediff COUNTS createdAndUpdatedState", "it", itCount, "diffAccountsAtB", len(diffAccountsAtB)) metrics.IndexerMetrics.DifferenceIteratorCounter.Inc(int64(*itCount)) - return diffAccountsAtB, diffAccountsAtA, it.Error() + return it.Error() +} + +func (sdb *StateDiffBuilder) processAccountDeletion(leafKey []byte, account types.StateAccount, nodeSink sdtypes.StateNodeSink) error { + diff := sdtypes.StateLeafNode{ + AccountWrapper: sdtypes.AccountWrapper{ + LeafKey: leafKey, + CID: shared.RemovedNodeStateCID, + }, + Removed: true, + } + err := sdb.processRemovedAccountStorage(account.Root, appender(&diff.StorageDiff)) + if err != nil { + return fmt.Errorf("failed building storage diffs for removed state account with key %x\r\nerror: %w", leafKey, err) + } + return nodeSink(diff) +} + +func (sdb *StateDiffBuilder) processAccountCreation(accountW *sdtypes.AccountWrapper, ipldSink sdtypes.IPLDSink, nodeSink sdtypes.StateNodeSink) error { + diff := sdtypes.StateLeafNode{ + AccountWrapper: *accountW, + } + if !bytes.Equal(accountW.Account.CodeHash, nullCodeHash) { + // For contract creations, any storage node contained is a diff + err := sdb.processCreatedAccountStorage(accountW.Account.Root, appender(&diff.StorageDiff), ipldSink) + if err != nil { + return fmt.Errorf("failed building eventual storage diffs for node with leaf key %x\r\nerror: %w", accountW.LeafKey, err) + } + // emit codehash => code mappings for contract + codeHash := common.BytesToHash(accountW.Account.CodeHash) + code, err := sdb.stateCache.ContractCode(codeHash) + if err != nil { + return fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %w", codeHash, err) + } + if err := ipldSink(sdtypes.IPLD{ + CID: ipld.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes()).String(), + Content: code, + }); err != nil { + return err + } + } + return nodeSink(diff) } // decodes account at leaf and encodes RLP data to CID // reminder: it.Leaf() == true when the iterator is positioned at a "value node" (which is not something // that actually exists in an MMPT), therefore we pass the parent node blob as the leaf RLP. -func (sdb *StateDiffBuilder) processStateValueNode(it trie.NodeIterator, parentBlob []byte) (*sdtypes.AccountWrapper, error) { +func (sdb *StateDiffBuilder) decodeStateLeaf(it trie.NodeIterator, parentBlob []byte) (*sdtypes.AccountWrapper, error) { var account types.StateAccount if err := rlp.DecodeBytes(it.LeafBlob(), &account); err != nil { return nil, fmt.Errorf("error decoding account at leaf key %x: %w", it.LeafKey(), err) } + leafKey := make([]byte, len(it.LeafKey())) + copy(leafKey, it.LeafKey()) return &sdtypes.AccountWrapper{ LeafKey: it.LeafKey(), Account: &account, @@ -300,85 +314,23 @@ func (sdb *StateDiffBuilder) processStateValueNode(it trie.NodeIterator, parentB }, nil } -// buildAccountUpdates uses the account diffs maps for A => B and B => A and the known intersection of their leafkeys -// to generate the statediff node objects for all of the accounts that existed at both A and B but in different states -// needs to be called before building account creations and deletions as this mutates -// those account maps to remove the accounts which were updated -func (sdb *StateDiffBuilder) buildAccountUpdates(creations, deletions sdtypes.AccountMap, updatedKeys []string, - nodeSink sdtypes.StateNodeSink, ipldSink sdtypes.IPLDSink, logger log.Logger) error { - logger.Trace("statediff BEGIN buildAccountUpdates", - "creations", len(creations), "deletions", len(deletions), "updated", len(updatedKeys)) - defer metrics.ReportAndUpdateDuration("statediff END buildAccountUpdates ", - time.Now(), logger, metrics.IndexerMetrics.BuildAccountUpdatesTimer) - var err error - for _, key := range updatedKeys { - createdAcc := creations[key] - deletedAcc := deletions[key] - var storageDiff []sdtypes.StorageLeafNode - if deletedAcc.Account != nil && createdAcc.Account != nil { - err = sdb.buildStorageNodesIncremental( - deletedAcc.Account.Root, createdAcc.Account.Root, - StorageNodeAppender(&storageDiff), ipldSink, - ) - if err != nil { - return fmt.Errorf("failed building incremental storage diffs for account with leafkey %x\r\nerror: %w", key, err) - } - } - if err = nodeSink(sdtypes.StateLeafNode{ - AccountWrapper: createdAcc, - StorageDiff: storageDiff, - }); err != nil { - return err - } - delete(creations, key) - delete(deletions, key) - } +// processAccountUpdates emits state and storage node records for all updated existing accounts. +func (sdb *StateDiffBuilder) processAccountUpdates( + updates accountUpdateMap, + nodeSink sdtypes.StateNodeSink, + ipldSink sdtypes.IPLDSink, + logger log.Logger, +) error { + defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.BuildAccountUpdatesTimer) return nil } -// buildAccountCreations returns the statediff node objects for all the accounts that exist at B but not at A -// it also returns the code and codehash for created contract accounts -func (sdb *StateDiffBuilder) buildAccountCreations(accounts sdtypes.AccountMap, nodeSink sdtypes.StateNodeSink, - ipldSink sdtypes.IPLDSink, logger log.Logger) error { - logger.Trace("statediff BEGIN buildAccountCreations") - defer metrics.ReportAndUpdateDuration("statediff END buildAccountCreations", - time.Now(), logger, metrics.IndexerMetrics.BuildAccountCreationsTimer) - for _, val := range accounts { - diff := sdtypes.StateLeafNode{ - AccountWrapper: val, - } - if !bytes.Equal(val.Account.CodeHash, nullCodeHash) { - // For contract creations, any storage node contained is a diff - err := sdb.buildStorageNodesEventual(val.Account.Root, StorageNodeAppender(&diff.StorageDiff), ipldSink) - if err != nil { - return fmt.Errorf("failed building eventual storage diffs for node with leaf key %x\r\nerror: %w", val.LeafKey, err) - } - // emit codehash => code mappings for contract - codeHash := common.BytesToHash(val.Account.CodeHash) - code, err := sdb.stateCache.ContractCode(codeHash) - if err != nil { - return fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %w", codeHash, err) - } - if err := ipldSink(sdtypes.IPLD{ - CID: ipld.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes()).String(), - Content: code, - }); err != nil { - return err - } - } - if err := nodeSink(diff); err != nil { - return err - } - } - - return nil -} - -// buildStorageNodesEventual builds the storage diff node objects for a created account -// i.e. it returns all the storage nodes at this state, since there is no previous state -func (sdb *StateDiffBuilder) buildStorageNodesEventual(sr common.Hash, storageSink sdtypes.StorageNodeSink, - ipldSink sdtypes.IPLDSink) error { +// processCreatedAccountStorage processes the storage node records for a newly created account +// i.e. it returns all the storage nodes at this state, since there is no previous state. +func (sdb *StateDiffBuilder) processCreatedAccountStorage( + sr common.Hash, storageSink sdtypes.StorageNodeSink, ipldSink sdtypes.IPLDSink, +) error { defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.BuildStorageNodesEventualTimer) if sr == emptyContractRoot { return nil @@ -390,11 +342,11 @@ func (sdb *StateDiffBuilder) buildStorageNodesEventual(sr common.Hash, storageSi return err } - it := sTrie.NodeIterator(make([]byte, 0)) var prevBlob []byte + it := sTrie.NodeIterator(make([]byte, 0)) for it.Next(true) { if it.Leaf() { - storageLeafNode := sdb.processStorageValueNode(it, prevBlob) + storageLeafNode := sdb.decodeStorageLeaf(it, prevBlob) if err := storageSink(storageLeafNode); err != nil { return err } @@ -413,10 +365,10 @@ func (sdb *StateDiffBuilder) buildStorageNodesEventual(sr common.Hash, storageSi return it.Error() } -// decodes account at leaf and encodes RLP data to CID +// decodes slot at leaf and encodes RLP data to CID // reminder: it.Leaf() == true when the iterator is positioned at a "value node" (which is not something // that actually exists in an MMPT), therefore we pass the parent node blob as the leaf RLP. -func (sdb *StateDiffBuilder) processStorageValueNode(it trie.NodeIterator, parentBlob []byte) sdtypes.StorageLeafNode { +func (sdb *StateDiffBuilder) decodeStorageLeaf(it trie.NodeIterator, parentBlob []byte) sdtypes.StorageLeafNode { leafKey := make([]byte, len(it.LeafKey())) copy(leafKey, it.LeafKey()) value := make([]byte, len(it.LeafBlob())) @@ -429,8 +381,10 @@ func (sdb *StateDiffBuilder) processStorageValueNode(it trie.NodeIterator, paren } } -// buildRemovedAccountStorageNodes builds the "removed" diffs for all the storage nodes for a destroyed account -func (sdb *StateDiffBuilder) buildRemovedAccountStorageNodes(sr common.Hash, storageSink sdtypes.StorageNodeSink) error { +// processRemovedAccountStorage builds the "removed" diffs for all the storage nodes for a destroyed account +func (sdb *StateDiffBuilder) processRemovedAccountStorage( + sr common.Hash, storageSink sdtypes.StorageNodeSink, +) error { defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.BuildRemovedAccountStorageNodesTimer) if sr == emptyContractRoot { return nil @@ -442,13 +396,6 @@ func (sdb *StateDiffBuilder) buildRemovedAccountStorageNodes(sr common.Hash, sto return err } it := sTrie.NodeIterator(nil) - return sdb.buildRemovedStorageNodesFromTrie(it, storageSink) -} - -// buildRemovedStorageNodesFromTrie returns diffs for all the storage nodes in the provided node interator -func (sdb *StateDiffBuilder) buildRemovedStorageNodesFromTrie(it trie.NodeIterator, storageSink sdtypes.StorageNodeSink) error { - defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.BuildRemovedStorageNodesFromTrieTimer) - // it := trie.NewIterator(it) for it.Next(true) { if it.Leaf() { // only leaf values are indexed, don't need to demarcate removed intermediate nodes leafKey := make([]byte, len(it.LeafKey())) @@ -466,9 +413,12 @@ func (sdb *StateDiffBuilder) buildRemovedStorageNodesFromTrie(it trie.NodeIterat return it.Error() } -// buildStorageNodesIncremental builds the storage diff node objects for all nodes that exist in a different state at B than A -func (sdb *StateDiffBuilder) buildStorageNodesIncremental(oldroot common.Hash, newroot common.Hash, storageSink sdtypes.StorageNodeSink, - ipldSink sdtypes.IPLDSink) error { +// processUpdatedAccountStorage builds the storage diff node objects for all nodes that exist in a different state at B than A +func (sdb *StateDiffBuilder) processUpdatedAccountStorage( + oldroot common.Hash, newroot common.Hash, + storageSink sdtypes.StorageNodeSink, + ipldSink sdtypes.IPLDSink, +) error { defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.BuildStorageNodesIncrementalTimer) if newroot == oldroot { return nil @@ -483,8 +433,8 @@ func (sdb *StateDiffBuilder) buildStorageNodesIncremental(oldroot common.Hash, n return err } - a, b := oldTrie.NodeIterator(nil), newTrie.NodeIterator(nil) var prevBlob []byte + a, b := oldTrie.NodeIterator(nil), newTrie.NodeIterator(nil) it, _ := utils.NewSymmetricDifferenceIterator(a, b) for it.Next(true) { if it.FromA() { @@ -503,7 +453,7 @@ func (sdb *StateDiffBuilder) buildStorageNodesIncremental(oldroot common.Hash, n continue } if it.Leaf() { - storageLeafNode := sdb.processStorageValueNode(it, prevBlob) + storageLeafNode := sdb.decodeStorageLeaf(it, prevBlob) if err := storageSink(storageLeafNode); err != nil { return err }