From 2ad905ff75c6d0a4fd7369ddfff045e0ae023a4a Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Thu, 14 May 2020 16:03:36 -0500 Subject: [PATCH] comments; explain logic --- statediff/builder.go | 94 +++++++++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 28 deletions(-) diff --git a/statediff/builder.go b/statediff/builder.go index f8c9ce61b..c3b27e0b9 100644 --- a/statediff/builder.go +++ b/statediff/builder.go @@ -58,7 +58,7 @@ func (sdb *builder) BuildStateDiff(args Args, params Params) (StateDiff, error) } func (sdb *builder) buildStateDiffWithIntermediateStateNodes(args Args, params Params) (StateDiff, error) { - // Generate tries for old and new states + // Load tries for old and new states oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot) if err != nil { return StateDiff{}, fmt.Errorf("error creating trie for oldStateRoot: %v", err) @@ -68,35 +68,48 @@ func (sdb *builder) buildStateDiffWithIntermediateStateNodes(args Args, params P return StateDiff{}, fmt.Errorf("error creating trie for newStateRoot: %v", err) } + // collect a slice of all the intermediate nodes that were touched and exist at B + // 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 createdOrUpdatedIntermediateNodes, diffAccountsAtB, diffPathsAtB, err := sdb.createdAndUpdatedNodes(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{})) if err != nil { return StateDiff{}, fmt.Errorf("error collecting createdAndUpdatedNodes: %v", err) } + // collect a slice of all the intermediate nodes that existed at A but don't at B + // a map of their leafkey to all the accounts that were touched and exist at A deletedIntermediateNodes, diffAccountsAtA, err := sdb.deletedOrUpdatedNodes(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), diffPathsAtB) if err != nil { return StateDiff{}, fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err) } - // Find all the diffed keys + // collect and sort the leafkey keys for both account mappings into a slice createKeys := sortKeys(diffAccountsAtB) deleteKeys := sortKeys(diffAccountsAtA) + + // 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 in intersection keys + // and leaving the truly created or deleted keys in place updatedKeys := findIntersection(createKeys, deleteKeys) - // Build and return the statediff + // 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 updatedAccounts, err := sdb.buildAccountUpdates(diffAccountsAtB, diffAccountsAtA, updatedKeys, params.WatchedStorageSlots, params.IntermediateStorageNodes) if err != nil { return StateDiff{}, fmt.Errorf("error building diff for updated accounts: %v", err) } + // build the diff nodes for created accounts createdAccounts, err := sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes) if err != nil { return StateDiff{}, fmt.Errorf("error building diff for created accounts: %v", err) } + // build the diff nodes for deleted accounts deletedAccounts, err := sdb.buildAccountDeletions(diffAccountsAtA) if err != nil { return StateDiff{}, fmt.Errorf("error building diff for deleted accounts: %v", err) } + // assemble all of the nodes into the statediff object, including the intermediate nodes return StateDiff{ BlockNumber: args.BlockNumber, BlockHash: args.BlockHash, @@ -105,7 +118,7 @@ func (sdb *builder) buildStateDiffWithIntermediateStateNodes(args Args, params P } func (sdb *builder) buildStateDiffWithoutIntermediateStateNodes(args Args, params Params) (StateDiff, error) { - // Generate tries for old and new states + // Load tries for old (A) and new (B) states oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot) if err != nil { return StateDiff{}, fmt.Errorf("error creating trie for oldStateRoot: %v", err) @@ -115,35 +128,45 @@ func (sdb *builder) buildStateDiffWithoutIntermediateStateNodes(args Args, param return StateDiff{}, fmt.Errorf("error creating trie for newStateRoot: %v", err) } + // collect a map of their leafkey to all the accounts that were touched and exist at B diffAccountsAtB, err := sdb.collectDiffAccounts(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), params.WatchedAddresses) if err != nil { return StateDiff{}, fmt.Errorf("error collecting createdAndUpdatedNodes: %v", err) } + // collect a map of their leafkey to all the accounts that were touched and exist at A diffAccountsAtA, err := sdb.collectDiffAccounts(newTrie.NodeIterator([]byte{}), oldTrie.NodeIterator([]byte{}), params.WatchedAddresses) if err != nil { return StateDiff{}, fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err) } - // Find all the diffed keys + // collect and sort the leafkeys for both account mappings into a slice createKeys := sortKeys(diffAccountsAtB) deleteKeys := sortKeys(diffAccountsAtA) + + // 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 in intersection keys + // and leaving the truly created or deleted keys in place updatedKeys := findIntersection(createKeys, deleteKeys) - // Build and return the statediff + // 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 updatedAccounts, err := sdb.buildAccountUpdates(diffAccountsAtB, diffAccountsAtA, updatedKeys, params.WatchedStorageSlots, params.IntermediateStorageNodes) if err != nil { return StateDiff{}, fmt.Errorf("error building diff for updated accounts: %v", err) } + // build the diff nodes for created accounts createdAccounts, err := sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes) if err != nil { return StateDiff{}, fmt.Errorf("error building diff for created accounts: %v", err) } + // build the diff nodes for deleted accounts deletedAccounts, err := sdb.buildAccountDeletions(diffAccountsAtA) if err != nil { return StateDiff{}, fmt.Errorf("error building diff for deleted accounts: %v", err) } + // assemble all of the nodes into the statediff object return StateDiff{ BlockNumber: args.BlockNumber, BlockHash: args.BlockHash, @@ -151,6 +174,8 @@ func (sdb *builder) buildStateDiffWithoutIntermediateStateNodes(args Args, param }, nil } +// collectDiffAccounts returns a mapping of their leafkeys to all the accounts that exist in a different state at B than A +// restricts the returned set to the watchedAddresses if any are provided func (sdb *builder) collectDiffAccounts(a, b trie.NodeIterator, watchedAddresses []common.Address) (AccountMap, error) { diffAcountsAtB := make(AccountMap) it, _ := trie.NewDifferenceIterator(a, b) @@ -206,26 +231,9 @@ func (sdb *builder) collectDiffAccounts(a, b trie.NodeIterator, watchedAddresses return diffAcountsAtB, nil } -// isWatchedAddress is used to check if a state account corresponds to one of the addresses the builder is configured to watch -func isWatchedAddress(watchedAddresses []common.Address, stateLeafKey []byte) bool { - // If we aren't watching any specific addresses, we are watching everything - if len(watchedAddresses) == 0 { - return true - } - for _, addr := range watchedAddresses { - addrHashKey := crypto.Keccak256(addr.Bytes()) - if bytes.Equal(addrHashKey, stateLeafKey) { - return true - } - } - return false -} - -// createdAndUpdatedNodes returns a slice of all the intermediate nodes that are different in b than they are in a -// it also returns a map of LEAFKEY to all the leaf nodes (accounts) that are different in b than they are in a -// it also returns a list of the intermediate node paths that were touched and the leaf node PATHs that were touched -// this function should only be called b = the NodeIterator for the state trie at a.blockheight + 1 -// make version for leaf nodes only +// createdAndUpdatedNodes 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 *builder) createdAndUpdatedNodes(a, b trie.NodeIterator) ([]StateNode, AccountMap, map[string]bool, error) { createdOrUpdatedIntermediateNodes := make([]StateNode, 0) diffPathsAtB := make(map[string]bool) @@ -289,6 +297,8 @@ func (sdb *builder) createdAndUpdatedNodes(a, b trie.NodeIterator) ([]StateNode, return createdOrUpdatedIntermediateNodes, diffAcountsAtB, diffPathsAtB, nil } +// deletedOrUpdatedNodes returns a slice of all the intermediate nodes that exist at A but not at B +// a mapping of their leafkeys to all the accounts that exist in a different state at A than B func (sdb *builder) deletedOrUpdatedNodes(a, b trie.NodeIterator, diffPathsAtB map[string]bool) ([]StateNode, AccountMap, error) { deletedIntermediateNodes := make([]StateNode, 0) diffAccountAtA := make(AccountMap) @@ -335,7 +345,8 @@ func (sdb *builder) deletedOrUpdatedNodes(a, b trie.NodeIterator, diffPathsAtB m } case Extension, Branch: // if this nodePath did not show up in diffPathsAtB - // that means the node at this path at A was deleted in B + // that means the node at this path was deleted in B + // emit an empty "removed" diff to signify as such if _, ok := diffPathsAtB[common.Bytes2Hex(nodePath)]; !ok { deletedIntermediateNodes = append(deletedIntermediateNodes, StateNode{ NodeType: Removed, @@ -350,7 +361,9 @@ func (sdb *builder) deletedOrUpdatedNodes(a, b trie.NodeIterator, diffPathsAtB m return deletedIntermediateNodes, diffAccountAtA, nil } -// needs to be called before building account creations and deletions as this mutattes +// 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 *builder) buildAccountUpdates(creations, deletions AccountMap, updatedKeys []string, watchedStorageKeys []common.Hash, intermediateStorageNodes bool) ([]StateNode, error) { updatedAccounts := make([]StateNode, 0, len(updatedKeys)) @@ -381,6 +394,7 @@ func (sdb *builder) buildAccountUpdates(creations, deletions AccountMap, updated return updatedAccounts, nil } +// buildAccountCreations returns the statediff node objects for all the accounts that exist at B but not at A func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKeys []common.Hash, intermediateStorageNodes bool) ([]StateNode, error) { accountDiffs := make([]StateNode, 0, len(accounts)) for _, val := range accounts { @@ -401,6 +415,8 @@ func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKey return accountDiffs, nil } +// buildAccountDeletions returns the state diff "removed" empty node objects +// to represent when an account exists at A but not at B func (sdb *builder) buildAccountDeletions(accounts AccountMap) ([]StateNode, error) { accountDiffs := make([]StateNode, 0, len(accounts)) for _, val := range accounts { @@ -415,6 +431,8 @@ func (sdb *builder) buildAccountDeletions(accounts AccountMap) ([]StateNode, err return accountDiffs, 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 *builder) buildStorageNodesEventual(sr common.Hash, watchedStorageKeys []common.Hash, intermediateNodes bool) ([]StorageNode, error) { log.Debug("Storage Root For Eventual Diff", "root", sr.Hex()) sTrie, err := sdb.stateCache.OpenTrie(sr) @@ -426,6 +444,8 @@ func (sdb *builder) buildStorageNodesEventual(sr common.Hash, watchedStorageKeys return sdb.buildStorageNodesFromTrie(it, watchedStorageKeys, intermediateNodes) } +// buildStorageNodesIncremental builds the storage diff node objects for all nodes that exist in a different state at B than A +// this assumes storage leafs cannot be deleted like accounts can be due to EIP-158 func (sdb *builder) buildStorageNodesIncremental(oldSR common.Hash, newSR common.Hash, watchedStorageKeys []common.Hash, intermediateNodes bool) ([]StorageNode, error) { log.Debug("Storage Roots for Incremental Diff", "old", oldSR.Hex(), "new", newSR.Hex()) oldTrie, err := sdb.stateCache.OpenTrie(oldSR) @@ -443,6 +463,9 @@ func (sdb *builder) buildStorageNodesIncremental(oldSR common.Hash, newSR common return sdb.buildStorageNodesFromTrie(it, watchedStorageKeys, intermediateNodes) } +// buildStorageNodesFromTrie returns all the storage diff node objects in the provided node interator +// if any storage keys are provided it will only return those leaf nodes +// including intermediate nodes can be turned on or off func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, watchedStorageKeys []common.Hash, intermediateNodes bool) ([]StorageNode, error) { storageDiffs := make([]StorageNode, 0) for it.Next(true) { @@ -496,6 +519,21 @@ func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, watchedStora return storageDiffs, nil } +// isWatchedAddress is used to check if a state account corresponds to one of the addresses the builder is configured to watch +func isWatchedAddress(watchedAddresses []common.Address, stateLeafKey []byte) bool { + // If we aren't watching any specific addresses, we are watching everything + if len(watchedAddresses) == 0 { + return true + } + for _, addr := range watchedAddresses { + addrHashKey := crypto.Keccak256(addr.Bytes()) + if bytes.Equal(addrHashKey, stateLeafKey) { + return true + } + } + return false +} + // isWatchedStorageKey is used to check if a storage leaf corresponds to one of the storage slots the builder is configured to watch func isWatchedStorageKey(watchedKeys []common.Hash, storageLeafKey []byte) bool { // If we aren't watching any specific addresses, we are watching everything