Skip to content

Commit bf4bba2

Browse files
authored
Merge pull request #247 from deep-stack/pm-account-selective-statediffing
Use prefix comparison for account selective statediffing
2 parents c265fdc + 0438809 commit bf4bba2

File tree

7 files changed

+257
-116
lines changed

7 files changed

+257
-116
lines changed

statediff/builder.go

Lines changed: 115 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,7 @@ func (sdb *StateDiffBuilder) WriteStateDiffObject(args types2.StateRoots, params
198198
},
199199
}
200200

201-
if !params.IntermediateStateNodes || len(params.WatchedAddresses) > 0 {
202-
// if we are watching only specific accounts then we are only diffing leaf nodes
201+
if !params.IntermediateStateNodes {
203202
return sdb.BuildStateDiffWithoutIntermediateStateNodes(iterPairs, params, output, codeOutput)
204203
} else {
205204
return sdb.BuildStateDiffWithIntermediateStateNodes(iterPairs, params, output, codeOutput)
@@ -211,7 +210,7 @@ func (sdb *StateDiffBuilder) BuildStateDiffWithIntermediateStateNodes(iterPairs
211210
// a map of their leafkey to all the accounts that were touched and exist at B
212211
// and a slice of all the paths for the nodes in both of the above sets
213212
diffAccountsAtB, diffPathsAtB, err := sdb.createdAndUpdatedStateWithIntermediateNodes(
214-
iterPairs[0].Older, iterPairs[0].Newer, output)
213+
iterPairs[0].Older, iterPairs[0].Newer, params.watchedAddressesLeafPaths, output)
215214
if err != nil {
216215
return fmt.Errorf("error collecting createdAndUpdatedNodes: %v", err)
217216
}
@@ -220,7 +219,7 @@ func (sdb *StateDiffBuilder) BuildStateDiffWithIntermediateStateNodes(iterPairs
220219
// a map of their leafkey to all the accounts that were touched and exist at A
221220
diffAccountsAtA, err := sdb.deletedOrUpdatedState(
222221
iterPairs[1].Older, iterPairs[1].Newer,
223-
diffAccountsAtB, diffPathsAtB, params.watchedAddressesLeafKeys,
222+
diffAccountsAtB, diffPathsAtB, params.watchedAddressesLeafPaths,
224223
params.IntermediateStateNodes, params.IntermediateStorageNodes, output)
225224
if err != nil {
226225
return fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err)
@@ -256,7 +255,7 @@ func (sdb *StateDiffBuilder) BuildStateDiffWithoutIntermediateStateNodes(iterPai
256255
// and a slice of all the paths for the nodes in both of the above sets
257256
diffAccountsAtB, diffPathsAtB, err := sdb.createdAndUpdatedState(
258257
iterPairs[0].Older, iterPairs[0].Newer,
259-
params.watchedAddressesLeafKeys)
258+
params.watchedAddressesLeafPaths)
260259
if err != nil {
261260
return fmt.Errorf("error collecting createdAndUpdatedNodes: %v", err)
262261
}
@@ -265,7 +264,7 @@ func (sdb *StateDiffBuilder) BuildStateDiffWithoutIntermediateStateNodes(iterPai
265264
// a map of their leafkey to all the accounts that were touched and exist at A
266265
diffAccountsAtA, err := sdb.deletedOrUpdatedState(
267266
iterPairs[1].Older, iterPairs[1].Newer,
268-
diffAccountsAtB, diffPathsAtB, params.watchedAddressesLeafKeys,
267+
diffAccountsAtB, diffPathsAtB, params.watchedAddressesLeafPaths,
269268
params.IntermediateStateNodes, params.IntermediateStorageNodes, output)
270269
if err != nil {
271270
return fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err)
@@ -299,11 +298,18 @@ func (sdb *StateDiffBuilder) BuildStateDiffWithoutIntermediateStateNodes(iterPai
299298
// createdAndUpdatedState returns
300299
// a mapping of their leafkeys to all the accounts that exist in a different state at B than A
301300
// and a slice of the paths for all of the nodes included in both
302-
func (sdb *StateDiffBuilder) createdAndUpdatedState(a, b trie.NodeIterator, watchedAddressesLeafKeys map[common.Hash]struct{}) (types2.AccountMap, map[string]bool, error) {
301+
func (sdb *StateDiffBuilder) createdAndUpdatedState(a, b trie.NodeIterator, watchedAddressesLeafPaths [][]byte) (types2.AccountMap, map[string]bool, error) {
303302
diffPathsAtB := make(map[string]bool)
304-
diffAcountsAtB := make(types2.AccountMap)
303+
diffAccountsAtB := make(types2.AccountMap)
304+
watchingAddresses := len(watchedAddressesLeafPaths) > 0
305+
305306
it, _ := trie.NewDifferenceIterator(a, b)
306307
for it.Next(true) {
308+
// ignore node if it is not along paths of interest
309+
if watchingAddresses && !isValidPrefixPath(watchedAddressesLeafPaths, it.Path()) {
310+
continue
311+
}
312+
307313
// skip value nodes
308314
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
309315
continue
@@ -322,33 +328,44 @@ func (sdb *StateDiffBuilder) createdAndUpdatedState(a, b trie.NodeIterator, watc
322328
}
323329
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
324330
valueNodePath := append(node.Path, partialPath...)
331+
332+
// ignore leaf node if it is not a watched address
333+
if !isWatchedAddress(watchedAddressesLeafPaths, valueNodePath) {
334+
continue
335+
}
336+
325337
encodedPath := trie.HexToCompact(valueNodePath)
326338
leafKey := encodedPath[1:]
327-
if isWatchedAddress(watchedAddressesLeafKeys, leafKey) {
328-
diffAcountsAtB[common.Bytes2Hex(leafKey)] = types2.AccountWrapper{
329-
NodeType: node.NodeType,
330-
Path: node.Path,
331-
NodeValue: node.NodeValue,
332-
LeafKey: leafKey,
333-
Account: &account,
334-
}
339+
diffAccountsAtB[common.Bytes2Hex(leafKey)] = types2.AccountWrapper{
340+
NodeType: node.NodeType,
341+
Path: node.Path,
342+
NodeValue: node.NodeValue,
343+
LeafKey: leafKey,
344+
Account: &account,
335345
}
336346
}
337347
// add both intermediate and leaf node paths to the list of diffPathsAtB
338348
diffPathsAtB[common.Bytes2Hex(node.Path)] = true
339349
}
340-
return diffAcountsAtB, diffPathsAtB, it.Error()
350+
return diffAccountsAtB, diffPathsAtB, it.Error()
341351
}
342352

343353
// createdAndUpdatedStateWithIntermediateNodes returns
344354
// a slice of all the intermediate nodes that exist in a different state at B than A
345355
// a mapping of their leafkeys to all the accounts that exist in a different state at B than A
346356
// and a slice of the paths for all of the nodes included in both
347-
func (sdb *StateDiffBuilder) createdAndUpdatedStateWithIntermediateNodes(a, b trie.NodeIterator, output types2.StateNodeSink) (types2.AccountMap, map[string]bool, error) {
357+
func (sdb *StateDiffBuilder) createdAndUpdatedStateWithIntermediateNodes(a, b trie.NodeIterator, watchedAddressesLeafPaths [][]byte, output types2.StateNodeSink) (types2.AccountMap, map[string]bool, error) {
348358
diffPathsAtB := make(map[string]bool)
349-
diffAcountsAtB := make(types2.AccountMap)
359+
diffAccountsAtB := make(types2.AccountMap)
360+
watchingAddresses := len(watchedAddressesLeafPaths) > 0
361+
350362
it, _ := trie.NewDifferenceIterator(a, b)
351363
for it.Next(true) {
364+
// ignore node if it is not along paths of interest
365+
if watchingAddresses && !isValidPrefixPath(watchedAddressesLeafPaths, it.Path()) {
366+
continue
367+
}
368+
352369
// skip value nodes
353370
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
354371
continue
@@ -367,9 +384,15 @@ func (sdb *StateDiffBuilder) createdAndUpdatedStateWithIntermediateNodes(a, b tr
367384
}
368385
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
369386
valueNodePath := append(node.Path, partialPath...)
387+
388+
// ignore leaf node if it is not a watched address
389+
if !isWatchedAddress(watchedAddressesLeafPaths, valueNodePath) {
390+
continue
391+
}
392+
370393
encodedPath := trie.HexToCompact(valueNodePath)
371394
leafKey := encodedPath[1:]
372-
diffAcountsAtB[common.Bytes2Hex(leafKey)] = types2.AccountWrapper{
395+
diffAccountsAtB[common.Bytes2Hex(leafKey)] = types2.AccountWrapper{
373396
NodeType: node.NodeType,
374397
Path: node.Path,
375398
NodeValue: node.NodeValue,
@@ -392,15 +415,22 @@ func (sdb *StateDiffBuilder) createdAndUpdatedStateWithIntermediateNodes(a, b tr
392415
// add both intermediate and leaf node paths to the list of diffPathsAtB
393416
diffPathsAtB[common.Bytes2Hex(node.Path)] = true
394417
}
395-
return diffAcountsAtB, diffPathsAtB, it.Error()
418+
return diffAccountsAtB, diffPathsAtB, it.Error()
396419
}
397420

398421
// deletedOrUpdatedState returns a slice of all the pathes that are emptied at B
399422
// and a mapping of their leafkeys to all the accounts that exist in a different state at A than B
400-
func (sdb *StateDiffBuilder) deletedOrUpdatedState(a, b trie.NodeIterator, diffAccountsAtB types2.AccountMap, diffPathsAtB map[string]bool, watchedAddressesLeafKeys map[common.Hash]struct{}, intermediateStateNodes, intermediateStorageNodes bool, output types2.StateNodeSink) (types2.AccountMap, error) {
423+
func (sdb *StateDiffBuilder) deletedOrUpdatedState(a, b trie.NodeIterator, diffAccountsAtB types2.AccountMap, diffPathsAtB map[string]bool, watchedAddressesLeafPaths [][]byte, intermediateStateNodes, intermediateStorageNodes bool, output types2.StateNodeSink) (types2.AccountMap, error) {
401424
diffAccountAtA := make(types2.AccountMap)
425+
watchingAddresses := len(watchedAddressesLeafPaths) > 0
426+
402427
it, _ := trie.NewDifferenceIterator(b, a)
403428
for it.Next(true) {
429+
// ignore node if it is not along paths of interest
430+
if watchingAddresses && !isValidPrefixPath(watchedAddressesLeafPaths, it.Path()) {
431+
continue
432+
}
433+
404434
// skip value nodes
405435
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
406436
continue
@@ -419,50 +449,54 @@ func (sdb *StateDiffBuilder) deletedOrUpdatedState(a, b trie.NodeIterator, diffA
419449
}
420450
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
421451
valueNodePath := append(node.Path, partialPath...)
452+
453+
// ignore leaf node if it is not a watched address
454+
if !isWatchedAddress(watchedAddressesLeafPaths, valueNodePath) {
455+
continue
456+
}
457+
422458
encodedPath := trie.HexToCompact(valueNodePath)
423459
leafKey := encodedPath[1:]
424-
if isWatchedAddress(watchedAddressesLeafKeys, leafKey) {
425-
diffAccountAtA[common.Bytes2Hex(leafKey)] = types2.AccountWrapper{
426-
NodeType: node.NodeType,
427-
Path: node.Path,
428-
NodeValue: node.NodeValue,
429-
LeafKey: leafKey,
430-
Account: &account,
431-
}
432-
// if this node's path did not show up in diffPathsAtB
433-
// that means the node at this path was deleted (or moved) in B
434-
if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; !ok {
435-
var diff types2.StateNode
436-
// if this node's leaf key also did not show up in diffAccountsAtB
437-
// that means the node was deleted
438-
// in that case, emit an empty "removed" diff state node
439-
// include empty "removed" diff storage nodes for all the storage slots
440-
if _, ok := diffAccountsAtB[common.Bytes2Hex(leafKey)]; !ok {
441-
diff = types2.StateNode{
442-
NodeType: types2.Removed,
443-
Path: node.Path,
444-
LeafKey: leafKey,
445-
NodeValue: []byte{},
446-
}
447-
448-
var storageDiffs []types2.StorageNode
449-
err := sdb.buildRemovedAccountStorageNodes(account.Root, intermediateStorageNodes, StorageNodeAppender(&storageDiffs))
450-
if err != nil {
451-
return nil, fmt.Errorf("failed building storage diffs for removed node %x\r\nerror: %v", node.Path, err)
452-
}
453-
diff.StorageNodes = storageDiffs
454-
} else {
455-
// emit an empty "removed" diff with empty leaf key if the account was moved
456-
diff = types2.StateNode{
457-
NodeType: types2.Removed,
458-
Path: node.Path,
459-
NodeValue: []byte{},
460-
}
460+
diffAccountAtA[common.Bytes2Hex(leafKey)] = types2.AccountWrapper{
461+
NodeType: node.NodeType,
462+
Path: node.Path,
463+
NodeValue: node.NodeValue,
464+
LeafKey: leafKey,
465+
Account: &account,
466+
}
467+
// if this node's path did not show up in diffPathsAtB
468+
// that means the node at this path was deleted (or moved) in B
469+
if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; !ok {
470+
var diff types2.StateNode
471+
// if this node's leaf key also did not show up in diffAccountsAtB
472+
// that means the node was deleted
473+
// in that case, emit an empty "removed" diff state node
474+
// include empty "removed" diff storage nodes for all the storage slots
475+
if _, ok := diffAccountsAtB[common.Bytes2Hex(leafKey)]; !ok {
476+
diff = types2.StateNode{
477+
NodeType: types2.Removed,
478+
Path: node.Path,
479+
LeafKey: leafKey,
480+
NodeValue: []byte{},
461481
}
462482

463-
if err := output(diff); err != nil {
464-
return nil, err
483+
var storageDiffs []types2.StorageNode
484+
err := sdb.buildRemovedAccountStorageNodes(account.Root, intermediateStorageNodes, StorageNodeAppender(&storageDiffs))
485+
if err != nil {
486+
return nil, fmt.Errorf("failed building storage diffs for removed node %x\r\nerror: %v", node.Path, err)
465487
}
488+
diff.StorageNodes = storageDiffs
489+
} else {
490+
// emit an empty "removed" diff with empty leaf key if the account was moved
491+
diff = types2.StateNode{
492+
NodeType: types2.Removed,
493+
Path: node.Path,
494+
NodeValue: []byte{},
495+
}
496+
}
497+
498+
if err := output(diff); err != nil {
499+
return nil, err
466500
}
467501
}
468502
case types2.Extension, types2.Branch:
@@ -830,13 +864,29 @@ func (sdb *StateDiffBuilder) deletedOrUpdatedStorage(a, b trie.NodeIterator, dif
830864
return it.Error()
831865
}
832866

867+
// isValidPrefixPath is used to check if a node at currentPath is a parent | ancestor to one of the addresses the builder is configured to watch
868+
func isValidPrefixPath(watchedAddressesLeafPaths [][]byte, currentPath []byte) bool {
869+
for _, watchedAddressPath := range watchedAddressesLeafPaths {
870+
if bytes.HasPrefix(watchedAddressPath, currentPath) {
871+
return true
872+
}
873+
}
874+
875+
return false
876+
}
877+
833878
// isWatchedAddress is used to check if a state account corresponds to one of the addresses the builder is configured to watch
834-
func isWatchedAddress(watchedAddressesLeafKeys map[common.Hash]struct{}, stateLeafKey []byte) bool {
879+
func isWatchedAddress(watchedAddressesLeafPaths [][]byte, valueNodePath []byte) bool {
835880
// If we aren't watching any specific addresses, we are watching everything
836-
if len(watchedAddressesLeafKeys) == 0 {
881+
if len(watchedAddressesLeafPaths) == 0 {
837882
return true
838883
}
839884

840-
_, ok := watchedAddressesLeafKeys[common.BytesToHash(stateLeafKey)]
841-
return ok
885+
for _, watchedAddressPath := range watchedAddressesLeafPaths {
886+
if bytes.Equal(watchedAddressPath, valueNodePath) {
887+
return true
888+
}
889+
}
890+
891+
return false
842892
}

0 commit comments

Comments
 (0)