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"
2023-06-23 12:42:55 +00:00
"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"
2023-06-23 12:42:55 +00:00
// "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 . Keccak256Hash ( [ ] byte { } ) . Bytes ( )
2023-06-23 12:42:55 +00:00
zeroHashBytes = utils . Hex2Bytes ( "0000000000000000000000000000000000000000000000000000000000000000" )
// zeroHash = core.HexToHash("0000000000000000000000000000000000000000000000000000000000000000")
2023-06-14 12:43:34 +00:00
)
// Builder interface exposes the method for building a state diff between two blocks
type Builder interface {
2023-06-23 12:42:55 +00:00
BuildStateDiffObject ( args Args , params Params ) ( sdtypes . StateObject , error )
WriteStateDiffObject ( args Args , params Params , output sdtypes . StateNodeSink , ipldOutput sdtypes . IPLDSink ) error
2023-06-14 12:43:34 +00:00
}
type StateDiffBuilder struct {
2023-06-23 12:42:55 +00:00
// StateCache state.Database
StateCache StateView
2023-06-14 12:43:34 +00:00
}
type IterPair struct {
Older , Newer trie . NodeIterator
}
2023-06-23 12:42:55 +00:00
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
}
}
2023-06-23 12:42:55 +00:00
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
}
}
2023-06-23 12:42:55 +00:00
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
2023-06-23 12:42:55 +00:00
func NewBuilder ( stateCache StateView ) Builder {
2023-06-14 12:43:34 +00:00
return & StateDiffBuilder {
StateCache : stateCache , // state cache is safe for concurrent reads
}
}
// BuildStateDiffObject builds a statediff object from two blocks and the provided parameters
2023-06-23 12:42:55 +00:00
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
2023-06-14 12:43:34 +00:00
err := sdb . WriteStateDiffObject ( args , params , StateNodeAppender ( & stateNodes ) , IPLDMappingAppender ( & iplds ) )
if err != nil {
2023-06-23 12:42:55 +00:00
return sdtypes . StateObject { } , err
2023-06-14 12:43:34 +00:00
}
2023-06-23 12:42:55 +00:00
return sdtypes . StateObject {
2023-06-14 12:43:34 +00:00
BlockHash : args . BlockHash ,
BlockNumber : args . BlockNumber ,
Nodes : stateNodes ,
IPLDs : iplds ,
} , nil
}
// WriteStateDiffObject writes a statediff object to output sinks
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) WriteStateDiffObject ( args Args , params Params , output sdtypes . StateNodeSink ,
ipldOutput sdtypes . IPLDSink ) error {
defer metrics . UpdateDuration ( time . Now ( ) , metrics . IndexerMetrics . WriteStateDiffObjectTimer )
2023-06-14 12:43:34 +00:00
// 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: %v" , err )
}
newTrie , err := sdb . StateCache . OpenTrie ( args . NewStateRoot )
if err != nil {
return fmt . Errorf ( "error creating trie for newStateRoot: %v" , err )
}
// we do two state trie iterations:
// one for new/updated nodes,
// one for deleted/updated nodes;
// prepare 2 iterator instances for each task
iterPairs := [ ] IterPair {
{
Older : oldTrie . NodeIterator ( [ ] byte { } ) ,
Newer : newTrie . NodeIterator ( [ ] byte { } ) ,
} ,
{
Older : oldTrie . NodeIterator ( [ ] byte { } ) ,
Newer : newTrie . NodeIterator ( [ ] byte { } ) ,
} ,
}
2023-06-23 12:42:55 +00:00
logger := log . New ( "hash" , args . BlockHash . String ( ) , "number" , args . BlockNumber )
2023-06-14 12:43:34 +00:00
return sdb . BuildStateDiffWithIntermediateStateNodes ( iterPairs , params , output , ipldOutput , logger , nil )
}
func ( sdb * StateDiffBuilder ) BuildStateDiffWithIntermediateStateNodes ( iterPairs [ ] IterPair , params Params ,
2023-06-23 12:42:55 +00:00
output sdtypes . StateNodeSink , ipldOutput sdtypes . IPLDSink , logger log . Logger , prefixPath [ ] byte ) error {
2023-06-14 12:43:34 +00:00
logger . Debug ( "statediff BEGIN BuildStateDiffWithIntermediateStateNodes" )
2023-06-23 12:42:55 +00:00
defer metrics . ReportAndUpdateDuration ( "statediff END BuildStateDiffWithIntermediateStateNodes" , time . Now ( ) , logger , metrics . IndexerMetrics . BuildStateDiffWithIntermediateStateNodesTimer )
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
diffAccountsAtB , err := sdb . createdAndUpdatedState (
iterPairs [ 0 ] . Older , iterPairs [ 0 ] . Newer , params . watchedAddressesLeafPaths , ipldOutput , logger , prefixPath )
if err != nil {
return fmt . Errorf ( "error collecting createdAndUpdatedNodes: %v" , err )
}
// collect a slice of all the nodes that existed at a path in A that doesn't exist in B
// a map of their leafkey to all the accounts that were touched and exist at A
diffAccountsAtA , err := sdb . deletedOrUpdatedState (
iterPairs [ 1 ] . Older , iterPairs [ 1 ] . Newer , diffAccountsAtB ,
params . watchedAddressesLeafPaths , output , logger , prefixPath )
if err != nil {
return fmt . Errorf ( "error collecting deletedOrUpdatedNodes: %v" , 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 ( fmt . Sprintf ( "statediff BuildStateDiffWithIntermediateStateNodes sort duration=%dms" , time . Since ( t ) . Milliseconds ( ) ) )
// 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 ( fmt . Sprintf ( "statediff BuildStateDiffWithIntermediateStateNodes intersection count=%d duration=%dms" ,
len ( updatedKeys ) ,
time . Since ( t ) . Milliseconds ( ) ) )
// 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 , output , ipldOutput , logger )
if err != nil {
return fmt . Errorf ( "error building diff for updated accounts: %v" , err )
}
// build the diff nodes for created accounts
err = sdb . buildAccountCreations ( diffAccountsAtB , output , ipldOutput , logger )
if err != nil {
return fmt . Errorf ( "error building diff for created accounts: %v" , 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 ,
2023-06-23 12:42:55 +00:00
watchedAddressesLeafPaths [ ] [ ] byte , output sdtypes . IPLDSink , logger log . Logger , prefixPath [ ] byte ) ( sdtypes . AccountMap , error ) {
2023-06-14 12:43:34 +00:00
logger . Debug ( "statediff BEGIN createdAndUpdatedState" )
2023-06-23 12:42:55 +00:00
defer metrics . ReportAndUpdateDuration ( "statediff END createdAndUpdatedState" , time . Now ( ) , logger , metrics . IndexerMetrics . CreatedAndUpdatedStateTimer )
diffAccountsAtB := make ( sdtypes . AccountMap )
2023-06-14 12:43:34 +00:00
2023-06-23 12:42:55 +00:00
// cache the RLP of the previous node, so when we hit a leaf we have the parent (containing) node
var prevBlob [ ] byte
2023-06-14 12:43:34 +00:00
it , itCount := trie . NewDifferenceIterator ( a , b )
for it . Next ( true ) {
// ignore node if it is not along paths of interest
2023-06-23 12:42:55 +00:00
if ! isWatchedPathPrefix ( watchedAddressesLeafPaths , it . Path ( ) ) {
2023-06-14 12:43:34 +00:00
continue
}
// index values by leaf key
if it . Leaf ( ) {
// if it is a "value" node, we will index the value by leaf key
2023-06-23 12:42:55 +00:00
accountW , err := sdb . processStateValueNode ( it , prevBlob )
2023-06-14 12:43:34 +00:00
if err != nil {
return nil , err
}
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
2023-06-23 12:42:55 +00:00
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
// TODO: use Hash type
if bytes . Equal ( it . Hash ( ) . Bytes ( ) , zeroHashBytes ) {
2023-06-14 12:43:34 +00:00
continue
}
nodeVal := make ( [ ] byte , len ( it . NodeBlob ( ) ) )
copy ( nodeVal , it . NodeBlob ( ) )
2023-06-23 12:42:55 +00:00
// 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 {
return nil , err
}
ok , err := isLeaf ( elements )
if err != nil {
return nil , err
}
2023-06-23 12:42:55 +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
}
}
2023-06-23 12:42:55 +00:00
if err := output ( sdtypes . IPLD {
CID : ipld . Keccak256ToCid ( ipld . MEthStateTrie , it . Hash ( ) . Bytes ( ) ) . String ( ) ,
2023-06-14 12:43:34 +00:00
Content : nodeVal ,
} ) ; err != nil {
return nil , err
}
2023-06-23 12:42:55 +00:00
prevBlob = nodeVal
2023-06-14 12:43:34 +00:00
}
}
logger . Debug ( "statediff COUNTS createdAndUpdatedStateWithIntermediateNodes" , "it" , itCount , "diffAccountsAtB" , len ( diffAccountsAtB ) )
2023-06-23 12:42:55 +00:00
metrics . IndexerMetrics . DifferenceIteratorCounter . Inc ( int64 ( * itCount ) )
2023-06-14 12:43:34 +00:00
return diffAccountsAtB , it . Error ( )
}
2023-06-23 12:42:55 +00:00
// reminder: it.Leaf() == true when the iterator is positioned at a "value node" which is not something
// that actually exists in an MMPT
func ( sdb * StateDiffBuilder ) processStateValueNode ( it trie . NodeIterator , parentBlob [ ] byte ) ( * sdtypes . AccountWrapper , error ) {
encodedPath := utils . HexToCompact ( it . Path ( ) )
2023-06-14 12:43:34 +00:00
leafKey := encodedPath [ 1 : ]
var account types . StateAccount
2023-06-23 12:42:55 +00:00
if err := rlp . DecodeBytes ( it . LeafBlob ( ) , & account ) ; err != nil {
2023-06-14 12:43:34 +00:00
return nil , fmt . Errorf ( "error decoding account for leaf value at leaf key %x\nerror: %v" , leafKey , err )
}
2023-06-23 12:42:55 +00:00
return & sdtypes . AccountWrapper {
2023-06-14 12:43:34 +00:00
LeafKey : leafKey ,
Account : & account ,
2023-06-23 12:42:55 +00:00
CID : ipld . Keccak256ToCid ( ipld . MEthStateTrie , crypto . Keccak256 ( parentBlob ) ) . String ( ) ,
2023-06-14 12:43:34 +00:00
} , nil
}
// deletedOrUpdatedState returns a slice of all the pathes that are emptied at B
// and a mapping of their leafkeys to all the accounts that exist in a different state at A than B
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) deletedOrUpdatedState ( a , b trie . NodeIterator , diffAccountsAtB sdtypes . AccountMap ,
watchedAddressesLeafPaths [ ] [ ] byte , output sdtypes . StateNodeSink , logger log . Logger , prefixPath [ ] byte ) ( sdtypes . AccountMap , error ) {
2023-06-14 12:43:34 +00:00
logger . Debug ( "statediff BEGIN deletedOrUpdatedState" )
2023-06-23 12:42:55 +00:00
defer metrics . ReportAndUpdateDuration ( "statediff END deletedOrUpdatedState" , time . Now ( ) , logger , metrics . IndexerMetrics . DeletedOrUpdatedStateTimer )
diffAccountAtA := make ( sdtypes . AccountMap )
2023-06-14 12:43:34 +00:00
2023-06-23 12:42:55 +00:00
var prevBlob [ ] byte
2023-06-14 12:43:34 +00:00
it , _ := trie . NewDifferenceIterator ( b , a )
for it . Next ( true ) {
2023-06-23 12:42:55 +00:00
if ! isWatchedPathPrefix ( watchedAddressesLeafPaths , it . Path ( ) ) {
2023-06-14 12:43:34 +00:00
continue
}
if it . Leaf ( ) {
2023-06-23 12:42:55 +00:00
accountW , err := sdb . processStateValueNode ( it , prevBlob )
2023-06-14 12:43:34 +00:00
if err != nil {
return nil , err
}
if accountW == nil {
continue
}
2023-06-23 12:42:55 +00:00
leafKey := hex . EncodeToString ( accountW . LeafKey )
2023-06-14 12:43:34 +00:00
diffAccountAtA [ leafKey ] = * accountW
// if this node's leaf key did not show up in diffAccountsAtB
// that means the account was deleted
// in that case, emit an empty "removed" diff state node
// include empty "removed" diff storage nodes for all the storage slots
if _ , ok := diffAccountsAtB [ leafKey ] ; ! ok {
2023-06-23 12:42:55 +00:00
diff := sdtypes . StateLeafNode {
AccountWrapper : sdtypes . AccountWrapper {
2023-06-14 12:43:34 +00:00
Account : nil ,
LeafKey : accountW . LeafKey ,
CID : shared . RemovedNodeStateCID ,
} ,
Removed : true ,
}
2023-06-23 12:42:55 +00:00
storageDiff := make ( [ ] sdtypes . StorageLeafNode , 0 )
2023-06-14 12:43:34 +00:00
err := sdb . buildRemovedAccountStorageNodes ( accountW . Account . Root , StorageNodeAppender ( & storageDiff ) )
if err != nil {
return nil , fmt . Errorf ( "failed building storage diffs for removed state account with key %x\r\nerror: %v" , leafKey , err )
}
diff . StorageDiff = storageDiff
if err := output ( diff ) ; err != nil {
return nil , err
}
}
2023-06-23 12:42:55 +00:00
} else {
prevBlob = make ( [ ] byte , len ( it . NodeBlob ( ) ) )
copy ( prevBlob , it . NodeBlob ( ) )
2023-06-14 12:43:34 +00:00
}
}
return diffAccountAtA , it . Error ( )
}
// 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
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) buildAccountUpdates ( creations , deletions sdtypes . AccountMap , updatedKeys [ ] string ,
output sdtypes . StateNodeSink , ipldOutput sdtypes . IPLDSink , logger log . Logger ) error {
2023-06-14 12:43:34 +00:00
logger . Debug ( "statediff BEGIN buildAccountUpdates" , "creations" , len ( creations ) , "deletions" , len ( deletions ) , "updatedKeys" , len ( updatedKeys ) )
2023-06-23 12:42:55 +00:00
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-06-23 12:42:55 +00:00
storageDiff := make ( [ ] sdtypes . StorageLeafNode , 0 )
2023-06-14 12:43:34 +00:00
if deletedAcc . Account != nil && createdAcc . Account != nil {
err = sdb . buildStorageNodesIncremental (
2023-06-23 12:42:55 +00:00
deletedAcc . Account . Root , createdAcc . Account . Root ,
StorageNodeAppender ( & storageDiff ) , ipldOutput ,
)
2023-06-14 12:43:34 +00:00
if err != nil {
return fmt . Errorf ( "failed building incremental storage diffs for account with leafkey %s\r\nerror: %v" , key , err )
}
}
2023-06-23 12:42:55 +00:00
if err = output ( sdtypes . StateLeafNode {
2023-06-14 12:43:34 +00:00
AccountWrapper : createdAcc ,
Removed : false ,
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
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) buildAccountCreations ( accounts sdtypes . AccountMap , output sdtypes . StateNodeSink ,
ipldOutput sdtypes . IPLDSink , logger log . Logger ) error {
2023-06-14 12:43:34 +00:00
logger . Debug ( "statediff BEGIN buildAccountCreations" )
2023-06-23 12:42:55 +00:00
defer metrics . ReportAndUpdateDuration ( "statediff END buildAccountCreations" , time . Now ( ) , logger , metrics . IndexerMetrics . BuildAccountCreationsTimer )
2023-06-14 12:43:34 +00:00
for _ , val := range accounts {
2023-06-23 12:42:55 +00:00
diff := sdtypes . StateLeafNode {
2023-06-14 12:43:34 +00:00
AccountWrapper : val ,
Removed : false ,
}
if ! bytes . Equal ( val . Account . CodeHash , nullCodeHash ) {
// For contract creations, any storage node contained is a diff
2023-06-23 12:42:55 +00:00
storageDiff := make ( [ ] sdtypes . StorageLeafNode , 0 )
2023-06-14 12:43:34 +00:00
err := sdb . buildStorageNodesEventual ( val . Account . Root , StorageNodeAppender ( & storageDiff ) , ipldOutput )
if err != nil {
return fmt . Errorf ( "failed building eventual storage diffs for node with leaf key %x\r\nerror: %v" , val . LeafKey , err )
}
diff . StorageDiff = storageDiff
// emit codehash => code mappings for contract
codeHash := common . BytesToHash ( val . Account . CodeHash )
2023-06-23 12:42:55 +00:00
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: %v" , codeHash . String ( ) , err )
}
2023-06-23 12:42:55 +00:00
if err := ipldOutput ( 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 := output ( 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
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) buildStorageNodesEventual ( sr common . Hash , output sdtypes . StorageNodeSink ,
ipldOutput sdtypes . IPLDSink ) error {
defer metrics . UpdateDuration ( time . Now ( ) , metrics . IndexerMetrics . BuildStorageNodesEventualTimer )
2023-06-14 12:43:34 +00:00
if bytes . Equal ( sr . Bytes ( ) , emptyContractRoot . Bytes ( ) ) {
return nil
}
2023-06-23 12:42:55 +00:00
log . Debug ( "Storage Root For Eventual Diff" , "root" , sr . String ( ) )
2023-06-14 12:43:34 +00:00
sTrie , err := sdb . StateCache . OpenTrie ( sr )
if err != nil {
log . Info ( "error in build storage diff eventual" , "error" , err )
return err
}
it := sTrie . NodeIterator ( make ( [ ] byte , 0 ) )
err = sdb . buildStorageNodesFromTrie ( it , output , ipldOutput )
if err != nil {
return err
}
return nil
}
// buildStorageNodesFromTrie returns all the storage diff node objects in the provided node interator
// including intermediate nodes can be turned on or off
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) buildStorageNodesFromTrie ( it trie . NodeIterator , output sdtypes . StorageNodeSink ,
ipldOutput sdtypes . IPLDSink ) error {
defer metrics . UpdateDuration ( time . Now ( ) , metrics . IndexerMetrics . BuildStorageNodesFromTrieTimer )
var prevBlob [ ] byte
2023-06-14 12:43:34 +00:00
for it . Next ( true ) {
if it . Leaf ( ) {
2023-06-23 12:42:55 +00:00
storageLeafNode , err := sdb . processStorageValueNode ( it , prevBlob )
2023-06-14 12:43:34 +00:00
if err != nil {
return err
}
if err := output ( storageLeafNode ) ; err != nil {
return err
}
} else {
nodeVal := make ( [ ] byte , len ( it . NodeBlob ( ) ) )
copy ( nodeVal , it . NodeBlob ( ) )
nodeHash := make ( [ ] byte , len ( it . Hash ( ) . Bytes ( ) ) )
copy ( nodeHash , it . Hash ( ) . Bytes ( ) )
2023-06-23 12:42:55 +00:00
if err := ipldOutput ( sdtypes . IPLD {
CID : ipld . Keccak256ToCid ( ipld . MEthStorageTrie , nodeHash ) . String ( ) ,
2023-06-14 12:43:34 +00:00
Content : nodeVal ,
} ) ; err != nil {
return err
}
2023-06-23 12:42:55 +00:00
prevBlob = nodeVal
2023-06-14 12:43:34 +00:00
}
}
return it . Error ( )
}
2023-06-23 12:42:55 +00:00
// reminder: it.Leaf() == true when the iterator is positioned at a "value node" which is not something
// that actually exists in an MMPT
func ( sdb * StateDiffBuilder ) processStorageValueNode ( it trie . NodeIterator , parentBlob [ ] byte ) ( sdtypes . StorageLeafNode , error ) {
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 ( ) )
2023-06-23 12:42:55 +00:00
// // since this is a "value node", we need to move up to the "parent" node which is the actual leaf node
// // it should be in the fastcache since it necessarily was recently accessed to reach the current node
// parentNodeRLP, err := sdb.StateCache.TrieDB().Node(it.Parent())
// if err != nil {
// return sdtypes.StorageLeafNode{}, err
// }
2023-06-14 12:43:34 +00:00
2023-06-23 12:42:55 +00:00
return sdtypes . StorageLeafNode {
2023-06-14 12:43:34 +00:00
LeafKey : leafKey ,
Value : value ,
2023-06-23 12:42:55 +00:00
CID : ipld . Keccak256ToCid ( ipld . MEthStorageTrie , crypto . Keccak256 ( parentBlob ) ) . String ( ) ,
2023-06-14 12:43:34 +00:00
} , nil
}
// buildRemovedAccountStorageNodes builds the "removed" diffs for all the storage nodes for a destroyed account
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) buildRemovedAccountStorageNodes ( sr common . Hash , output sdtypes . StorageNodeSink ) error {
defer metrics . UpdateDuration ( time . Now ( ) , metrics . IndexerMetrics . BuildRemovedAccountStorageNodesTimer )
2023-06-14 12:43:34 +00:00
if bytes . Equal ( sr . Bytes ( ) , emptyContractRoot . Bytes ( ) ) {
return nil
}
2023-06-23 12:42:55 +00:00
log . Debug ( "Storage Root For Removed Diffs" , "root" , sr . String ( ) )
2023-06-14 12:43:34 +00:00
sTrie , err := sdb . StateCache . OpenTrie ( sr )
if err != nil {
log . Info ( "error in build removed account storage diffs" , "error" , err )
return err
}
it := sTrie . NodeIterator ( make ( [ ] byte , 0 ) )
err = sdb . buildRemovedStorageNodesFromTrie ( it , output )
if err != nil {
return err
}
return nil
}
// buildRemovedStorageNodesFromTrie returns diffs for all the storage nodes in the provided node interator
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) buildRemovedStorageNodesFromTrie ( it trie . NodeIterator , output sdtypes . StorageNodeSink ) error {
defer metrics . UpdateDuration ( time . Now ( ) , metrics . IndexerMetrics . BuildRemovedStorageNodesFromTrieTimer )
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 ( ) )
2023-06-23 12:42:55 +00:00
if err := output ( 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
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) buildStorageNodesIncremental ( oldroot common . Hash , newroot common . Hash , output sdtypes . StorageNodeSink ,
ipldOutput sdtypes . IPLDSink ) error {
defer metrics . UpdateDuration ( time . Now ( ) , metrics . IndexerMetrics . BuildStorageNodesIncrementalTimer )
if bytes . Equal ( newroot . Bytes ( ) , oldroot . Bytes ( ) ) {
2023-06-14 12:43:34 +00:00
return nil
}
2023-06-23 12:42:55 +00:00
log . Trace ( "Storage Roots for Incremental Diff" , "old" , oldroot . String ( ) , "new" , newroot . String ( ) )
oldTrie , err := sdb . StateCache . OpenTrie ( oldroot )
2023-06-14 12:43:34 +00:00
if err != nil {
return err
}
2023-06-23 12:42:55 +00:00
newTrie , err := sdb . StateCache . OpenTrie ( newroot )
2023-06-14 12:43:34 +00:00
if err != nil {
return err
}
diffSlotsAtB , err := sdb . createdAndUpdatedStorage (
oldTrie . NodeIterator ( [ ] byte { } ) , newTrie . NodeIterator ( [ ] byte { } ) , output , ipldOutput )
if err != nil {
return err
}
err = sdb . deletedOrUpdatedStorage ( oldTrie . NodeIterator ( [ ] byte { } ) , newTrie . NodeIterator ( [ ] byte { } ) ,
diffSlotsAtB , output )
if err != nil {
return err
}
return nil
}
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) createdAndUpdatedStorage ( a , b trie . NodeIterator , output sdtypes . StorageNodeSink ,
ipldOutput sdtypes . IPLDSink ) ( map [ string ] bool , error ) {
defer metrics . UpdateDuration ( time . Now ( ) , metrics . IndexerMetrics . CreatedAndUpdatedStorageTimer )
2023-06-14 12:43:34 +00:00
diffSlotsAtB := make ( map [ string ] bool )
2023-06-23 12:42:55 +00:00
var prevBlob [ ] byte
2023-06-14 12:43:34 +00:00
it , _ := trie . NewDifferenceIterator ( a , b )
for it . Next ( true ) {
if it . Leaf ( ) {
2023-06-23 12:42:55 +00:00
storageLeafNode , err := sdb . processStorageValueNode ( it , prevBlob )
2023-06-14 12:43:34 +00:00
if err != nil {
return nil , err
}
if err := output ( storageLeafNode ) ; err != nil {
return nil , err
}
2023-06-23 12:42:55 +00:00
diffSlotsAtB [ hex . EncodeToString ( storageLeafNode . LeafKey ) ] = true
2023-06-14 12:43:34 +00:00
} else {
2023-06-23 12:42:55 +00:00
if bytes . Equal ( it . Hash ( ) . Bytes ( ) , zeroHashBytes ) {
2023-06-14 12:43:34 +00:00
continue
}
nodeVal := make ( [ ] byte , len ( it . NodeBlob ( ) ) )
copy ( nodeVal , it . NodeBlob ( ) )
nodeHash := make ( [ ] byte , len ( it . Hash ( ) . Bytes ( ) ) )
copy ( nodeHash , it . Hash ( ) . Bytes ( ) )
2023-06-23 12:42:55 +00:00
if err := ipldOutput ( sdtypes . IPLD {
CID : ipld . Keccak256ToCid ( ipld . MEthStorageTrie , nodeHash ) . String ( ) ,
2023-06-14 12:43:34 +00:00
Content : nodeVal ,
} ) ; err != nil {
return nil , err
}
2023-06-23 12:42:55 +00:00
prevBlob = nodeVal
2023-06-14 12:43:34 +00:00
}
}
return diffSlotsAtB , it . Error ( )
}
2023-06-23 12:42:55 +00:00
func ( sdb * StateDiffBuilder ) deletedOrUpdatedStorage ( a , b trie . NodeIterator , diffSlotsAtB map [ string ] bool , output sdtypes . StorageNodeSink ) error {
defer metrics . UpdateDuration ( time . Now ( ) , metrics . IndexerMetrics . DeletedOrUpdatedStorageTimer )
2023-06-14 12:43:34 +00:00
it , _ := trie . NewDifferenceIterator ( b , a )
for it . Next ( true ) {
if it . Leaf ( ) {
leafKey := make ( [ ] byte , len ( it . LeafKey ( ) ) )
copy ( leafKey , it . LeafKey ( ) )
// if this node's leaf key did not show up in diffSlotsAtB
// that means the storage slot was vacated
// in that case, emit an empty "removed" diff storage node
2023-06-23 12:42:55 +00:00
if _ , ok := diffSlotsAtB [ hex . EncodeToString ( leafKey ) ] ; ! ok {
if err := output ( 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 ( )
}
2023-06-23 12:42:55 +00:00
// 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
}
2023-06-23 12:42:55 +00:00
// 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" )
}
}