plugeth-statediff/builder.go

574 lines
21 KiB
Go
Raw Normal View History

2023-06-14 12:43:34 +00:00
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains a batch of utility type declarations used by the tests. As the node
// operates on unique types, a lot of them are needed to check various features.
package statediff
import (
"bytes"
"encoding/hex"
2023-06-14 12:43:34 +00:00
"fmt"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/cerc-io/plugeth-statediff/adapt"
"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"
2023-06-14 12:43:34 +00:00
)
var (
emptyNode, _ = rlp.EncodeToBytes(&[]byte{})
emptyContractRoot = crypto.Keccak256Hash(emptyNode)
nullCodeHash = crypto.Keccak256([]byte{})
zeroHash common.Hash
2023-06-14 12:43:34 +00:00
)
// Builder interface exposes the method for building a state diff between two blocks
type Builder interface {
BuildStateDiffObject(Args, Params) (sdtypes.StateObject, error)
WriteStateDiff(Args, Params, sdtypes.StateNodeSink, sdtypes.IPLDSink) error
2023-06-14 12:43:34 +00:00
}
type StateDiffBuilder struct {
// state cache is safe for concurrent reads
stateCache adapt.StateView
2023-06-14 12:43:34 +00:00
}
type IterPair struct {
Older, Newer trie.NodeIterator
}
func StateNodeAppender(nodes *[]sdtypes.StateLeafNode) sdtypes.StateNodeSink {
return func(node sdtypes.StateLeafNode) error {
2023-06-14 12:43:34 +00:00
*nodes = append(*nodes, node)
return nil
}
}
func StorageNodeAppender(nodes *[]sdtypes.StorageLeafNode) sdtypes.StorageNodeSink {
return func(node sdtypes.StorageLeafNode) error {
2023-06-14 12:43:34 +00:00
*nodes = append(*nodes, node)
return nil
}
}
func IPLDMappingAppender(iplds *[]sdtypes.IPLD) sdtypes.IPLDSink {
return func(c sdtypes.IPLD) error {
2023-06-14 12:43:34 +00:00
*iplds = append(*iplds, c)
return nil
}
}
// NewBuilder is used to create a statediff builder
func NewBuilder(stateCache adapt.StateView) Builder {
2023-06-14 12:43:34 +00:00
return &StateDiffBuilder{
stateCache: stateCache,
2023-06-14 12:43:34 +00:00
}
}
// BuildStateDiffObject builds a statediff object from two blocks and the provided parameters
func (sdb *StateDiffBuilder) BuildStateDiffObject(args Args, params Params) (sdtypes.StateObject, error) {
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))
2023-06-14 12:43:34 +00:00
if err != nil {
return sdtypes.StateObject{}, err
2023-06-14 12:43:34 +00:00
}
return sdtypes.StateObject{
2023-06-14 12:43:34 +00:00
BlockHash: args.BlockHash,
BlockNumber: args.BlockNumber,
Nodes: stateNodes,
IPLDs: iplds,
}, nil
}
// WriteStateDiff writes a statediff object to output sinks
func (sdb *StateDiffBuilder) WriteStateDiff(args Args, params Params, nodeSink sdtypes.StateNodeSink,
ipldSink sdtypes.IPLDSink) error {
defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.WriteStateDiffTimer)
2023-06-14 12:43:34 +00:00
// Load tries for old and new states
oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot)
2023-06-14 12:43:34 +00:00
if err != nil {
return fmt.Errorf("error creating trie for oldStateRoot: %w", err)
2023-06-14 12:43:34 +00:00
}
newTrie, err := sdb.stateCache.OpenTrie(args.NewStateRoot)
2023-06-14 12:43:34 +00:00
if err != nil {
return fmt.Errorf("error creating trie for newStateRoot: %w", err)
2023-06-14 12:43:34 +00:00
}
2023-07-23 17:00:37 +00:00
iters := IterPair{
Older: oldTrie.NodeIterator(nil),
Newer: newTrie.NodeIterator(nil),
2023-06-14 12:43:34 +00:00
}
logger := log.New("hash", args.BlockHash, "number", args.BlockNumber)
2023-07-23 17:00:37 +00:00
return sdb.BuildStateDiff(iters, params, nodeSink, ipldSink, logger)
2023-06-14 12:43:34 +00:00
}
2023-07-23 17:00:37 +00:00
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)
2023-06-14 12:43:34 +00:00
// 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
2023-07-23 17:00:37 +00:00
diffAccountsAtB, diffAccountsAtA, err := sdb.createdAndUpdatedState(
iterPair.Older, iterPair.Newer, params.watchedAddressesLeafPaths, nodeSink, ipldSink, logger)
2023-06-14 12:43:34 +00:00
if err != nil {
return fmt.Errorf("error collecting createdAndUpdatedNodes: %w", err)
2023-06-14 12:43:34 +00:00
}
// 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))
2023-06-14 12:43:34 +00:00
// 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))
2023-06-14 12:43:34 +00:00
// 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
2023-07-23 17:00:37 +00:00
err = sdb.buildAccountUpdates(diffAccountsAtB, diffAccountsAtA, updatedKeys, nodeSink, ipldSink, logger)
2023-06-14 12:43:34 +00:00
if err != nil {
return fmt.Errorf("error building diff for updated accounts: %w", err)
2023-06-14 12:43:34 +00:00
}
// build the diff nodes for created accounts
2023-07-23 17:00:37 +00:00
err = sdb.buildAccountCreations(diffAccountsAtB, nodeSink, ipldSink, logger)
2023-06-14 12:43:34 +00:00
if err != nil {
return fmt.Errorf("error building diff for created accounts: %w", err)
2023-06-14 12:43:34 +00:00
}
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,
2023-07-23 17:00:37 +00:00
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)
2023-07-23 17:00:37 +00:00
diffAccountsAtA := make(sdtypes.AccountMap)
2023-06-14 12:43:34 +00:00
2023-07-23 17:00:37 +00:00
// 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
it, itCount := utils.NewSymmetricDifferenceIterator(a, b)
2023-06-14 12:43:34 +00:00
for it.Next(true) {
// ignore node if it is not along paths of interest
if !isWatchedPathPrefix(watchedAddressesLeafPaths, it.Path()) {
2023-06-14 12:43:34 +00:00
continue
}
2023-07-23 17:00:37 +00:00
if it.FromA() {
if it.Leaf() {
accountW, err := sdb.processStateValueNode(it, prevBlobFromB)
if err != nil {
return nil, nil, err
}
if accountW == nil {
continue
}
leafKey := hex.EncodeToString(accountW.LeafKey)
diffAccountsAtA[leafKey] = *accountW
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,
}
err = sdb.buildRemovedAccountStorageNodes(accountW.Account.Root, StorageNodeAppender(&diff.StorageDiff))
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
}
}
} else {
prevBlobFromB = make([]byte, len(it.NodeBlob()))
copy(prevBlobFromB, it.NodeBlob())
}
continue
}
2023-06-14 12:43:34 +00:00
// index values by leaf key
if it.Leaf() {
// if it is a "value" node, we will index the value by leaf key
2023-07-23 17:00:37 +00:00
accountW, err := sdb.processStateValueNode(it, prevBlobFromA)
2023-06-14 12:43:34 +00:00
if err != nil {
2023-07-23 17:00:37 +00:00
return nil, nil, err
2023-06-14 12:43:34 +00:00
}
if accountW == nil {
continue
}
// 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
if it.Hash() == zeroHash {
2023-06-14 12:43:34 +00:00
continue
}
nodeVal := make([]byte, len(it.NodeBlob()))
copy(nodeVal, it.NodeBlob())
// if doing a selective diff, we need to ensure this is a watched path
2023-06-14 12:43:34 +00:00
if len(watchedAddressesLeafPaths) > 0 {
var elements []interface{}
if err := rlp.DecodeBytes(nodeVal, &elements); err != nil {
2023-07-23 17:00:37 +00:00
return nil, nil, err
2023-06-14 12:43:34 +00:00
}
ok, err := isLeaf(elements)
if err != nil {
2023-07-23 17:00:37 +00:00
return nil, nil, err
2023-06-14 12:43:34 +00:00
}
partialPath := utils.CompactToHex(elements[0].([]byte))
valueNodePath := append(it.Path(), partialPath...)
if ok && !isWatchedPath(watchedAddressesLeafPaths, valueNodePath) {
continue
2023-06-14 12:43:34 +00:00
}
}
if err := ipldSink(sdtypes.IPLD{
CID: ipld.Keccak256ToCid(ipld.MEthStateTrie, it.Hash().Bytes()).String(),
2023-06-14 12:43:34 +00:00
Content: nodeVal,
}); err != nil {
2023-07-23 17:00:37 +00:00
return nil, nil, err
2023-06-14 12:43:34 +00:00
}
2023-07-23 17:00:37 +00:00
prevBlobFromA = nodeVal
2023-06-14 12:43:34 +00:00
}
}
2023-07-23 17:00:37 +00:00
logger.Debug("statediff COUNTS createdAndUpdatedState", "it", itCount, "diffAccountsAtB", len(diffAccountsAtB))
metrics.IndexerMetrics.DifferenceIteratorCounter.Inc(int64(*itCount))
2023-07-23 17:00:37 +00:00
return diffAccountsAtB, diffAccountsAtA, it.Error()
2023-06-14 12:43:34 +00:00
}
// 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) {
2023-06-14 12:43:34 +00:00
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)
2023-06-14 12:43:34 +00:00
}
return &sdtypes.AccountWrapper{
LeafKey: it.LeafKey(),
2023-06-14 12:43:34 +00:00
Account: &account,
CID: ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(parentBlob)).String(),
2023-06-14 12:43:34 +00:00
}, 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)
2023-06-14 12:43:34 +00:00
var err error
for _, key := range updatedKeys {
createdAcc := creations[key]
deletedAcc := deletions[key]
2023-07-23 17:00:37 +00:00
var storageDiff []sdtypes.StorageLeafNode
2023-06-14 12:43:34 +00:00
if deletedAcc.Account != nil && createdAcc.Account != nil {
err = sdb.buildStorageNodesIncremental(
deletedAcc.Account.Root, createdAcc.Account.Root,
StorageNodeAppender(&storageDiff), ipldSink,
)
2023-06-14 12:43:34 +00:00
if err != nil {
return fmt.Errorf("failed building incremental storage diffs for account with leafkey %x\r\nerror: %w", key, err)
2023-06-14 12:43:34 +00:00
}
}
if err = nodeSink(sdtypes.StateLeafNode{
2023-06-14 12:43:34 +00:00
AccountWrapper: createdAcc,
StorageDiff: storageDiff,
}); err != nil {
return err
}
delete(creations, key)
delete(deletions, key)
}
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)
2023-06-14 12:43:34 +00:00
for _, val := range accounts {
diff := sdtypes.StateLeafNode{
2023-06-14 12:43:34 +00:00
AccountWrapper: val,
}
if !bytes.Equal(val.Account.CodeHash, nullCodeHash) {
// For contract creations, any storage node contained is a diff
2023-07-23 17:00:37 +00:00
err := sdb.buildStorageNodesEventual(val.Account.Root, StorageNodeAppender(&diff.StorageDiff), ipldSink)
2023-06-14 12:43:34 +00:00
if err != nil {
return fmt.Errorf("failed building eventual storage diffs for node with leaf key %x\r\nerror: %w", val.LeafKey, err)
2023-06-14 12:43:34 +00:00
}
// emit codehash => code mappings for contract
codeHash := common.BytesToHash(val.Account.CodeHash)
code, err := sdb.stateCache.ContractCode(codeHash)
2023-06-14 12:43:34 +00:00
if err != nil {
return fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %w", codeHash, err)
2023-06-14 12:43:34 +00:00
}
if err := ipldSink(sdtypes.IPLD{
CID: ipld.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes()).String(),
2023-06-14 12:43:34 +00:00
Content: code,
}); err != nil {
return err
}
}
if err := nodeSink(diff); err != nil {
2023-06-14 12:43:34 +00:00
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 {
defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.BuildStorageNodesEventualTimer)
if sr == emptyContractRoot {
2023-06-14 12:43:34 +00:00
return nil
}
log.Debug("Storage root for eventual diff", "root", sr)
sTrie, err := sdb.stateCache.OpenTrie(sr)
2023-06-14 12:43:34 +00:00
if err != nil {
log.Info("error in build storage diff eventual", "error", err)
return err
}
it := sTrie.NodeIterator(make([]byte, 0))
var prevBlob []byte
2023-06-14 12:43:34 +00:00
for it.Next(true) {
if it.Leaf() {
storageLeafNode := sdb.processStorageValueNode(it, prevBlob)
if err := storageSink(storageLeafNode); err != nil {
2023-06-14 12:43:34 +00:00
return err
}
} else {
nodeVal := make([]byte, len(it.NodeBlob()))
copy(nodeVal, it.NodeBlob())
if err := ipldSink(sdtypes.IPLD{
CID: ipld.Keccak256ToCid(ipld.MEthStorageTrie, it.Hash().Bytes()).String(),
2023-06-14 12:43:34 +00:00
Content: nodeVal,
}); err != nil {
return err
}
prevBlob = nodeVal
2023-06-14 12:43:34 +00:00
}
}
return it.Error()
}
// 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) processStorageValueNode(it trie.NodeIterator, parentBlob []byte) sdtypes.StorageLeafNode {
2023-06-14 12:43:34 +00:00
leafKey := make([]byte, len(it.LeafKey()))
copy(leafKey, it.LeafKey())
value := make([]byte, len(it.LeafBlob()))
copy(value, it.LeafBlob())
return sdtypes.StorageLeafNode{
2023-06-14 12:43:34 +00:00
LeafKey: leafKey,
Value: value,
CID: ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(parentBlob)).String(),
}
2023-06-14 12:43:34 +00:00
}
// 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 {
defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.BuildRemovedAccountStorageNodesTimer)
if sr == emptyContractRoot {
2023-06-14 12:43:34 +00:00
return nil
}
log.Debug("Storage root for removed diffs", "root", sr)
sTrie, err := sdb.stateCache.OpenTrie(sr)
2023-06-14 12:43:34 +00:00
if err != nil {
log.Info("error in build removed account storage diffs", "error", err)
return err
}
it := sTrie.NodeIterator(nil)
return sdb.buildRemovedStorageNodesFromTrie(it, storageSink)
2023-06-14 12:43:34 +00:00
}
// 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)
2023-07-23 17:00:37 +00:00
// it := trie.NewIterator(it)
2023-06-14 12:43:34 +00:00
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()))
copy(leafKey, it.LeafKey())
if err := storageSink(sdtypes.StorageLeafNode{
2023-06-14 12:43:34 +00:00
CID: shared.RemovedNodeStorageCID,
Removed: true,
LeafKey: leafKey,
Value: []byte{},
}); err != nil {
return err
}
}
}
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 {
defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.BuildStorageNodesIncrementalTimer)
if newroot == oldroot {
2023-06-14 12:43:34 +00:00
return nil
}
log.Trace("Storage roots for incremental diff", "old", oldroot, "new", newroot)
oldTrie, err := sdb.stateCache.OpenTrie(oldroot)
2023-06-14 12:43:34 +00:00
if err != nil {
return err
}
newTrie, err := sdb.stateCache.OpenTrie(newroot)
2023-06-14 12:43:34 +00:00
if err != nil {
return err
}
2023-07-23 17:00:37 +00:00
a, b := oldTrie.NodeIterator(nil), newTrie.NodeIterator(nil)
var prevBlob []byte
2023-07-23 17:00:37 +00:00
it, _ := utils.NewSymmetricDifferenceIterator(a, b)
2023-06-14 12:43:34 +00:00
for it.Next(true) {
2023-07-23 17:00:37 +00:00
if it.FromA() {
if it.Leaf() && !it.CommonPath() {
// If this node's leaf key is absent from B, the storage slot was vacated.
// In that case, emit an empty "removed" storage node record.
if err := storageSink(sdtypes.StorageLeafNode{
CID: shared.RemovedNodeStorageCID,
Removed: true,
LeafKey: []byte(it.LeafKey()),
Value: []byte{},
}); err != nil {
return err
}
}
continue
}
2023-06-14 12:43:34 +00:00
if it.Leaf() {
storageLeafNode := sdb.processStorageValueNode(it, prevBlob)
if err := storageSink(storageLeafNode); err != nil {
2023-07-23 17:00:37 +00:00
return err
2023-06-14 12:43:34 +00:00
}
} else {
if it.Hash() == zeroHash {
2023-06-14 12:43:34 +00:00
continue
}
nodeVal := make([]byte, len(it.NodeBlob()))
copy(nodeVal, it.NodeBlob())
if err := ipldSink(sdtypes.IPLD{
CID: ipld.Keccak256ToCid(ipld.MEthStorageTrie, it.Hash().Bytes()).String(),
2023-06-14 12:43:34 +00:00
Content: nodeVal,
}); err != nil {
2023-07-23 17:00:37 +00:00
return err
2023-06-14 12:43:34 +00:00
}
prevBlob = nodeVal
2023-06-14 12:43:34 +00:00
}
}
return it.Error()
}
// isWatchedPathPrefix checks if a node path is a prefix (ancestor) to one of the watched addresses.
// An empty watch list means all paths are watched.
func isWatchedPathPrefix(watchedLeafPaths [][]byte, path []byte) bool {
if len(watchedLeafPaths) == 0 {
return true
}
for _, watched := range watchedLeafPaths {
if bytes.HasPrefix(watched, path) {
2023-06-14 12:43:34 +00:00
return true
}
}
return false
}
// isWatchedPath checks if a node path corresponds to one of the watched addresses
func isWatchedPath(watchedLeafPaths [][]byte, leafPath []byte) bool {
defer metrics.UpdateDuration(time.Now(), metrics.IndexerMetrics.IsWatchedAddressTimer)
for _, watched := range watchedLeafPaths {
if bytes.Equal(watched, leafPath) {
2023-06-14 12:43:34 +00:00
return true
}
}
return false
}
// isLeaf checks if the node we are at is a leaf
func isLeaf(elements []interface{}) (bool, error) {
if len(elements) > 2 {
return false, nil
}
if len(elements) < 2 {
return false, fmt.Errorf("node cannot be less than two elements in length")
}
switch elements[0].([]byte)[0] / 16 {
case '\x00':
return false, nil
case '\x01':
return false, nil
case '\x02':
return true, nil
case '\x03':
return true, nil
default:
return false, fmt.Errorf("unknown hex prefix")
}
}