Skip to content

Commit 4855d93

Browse files
committed
Index Removed storage diffs on contract destruction
1 parent 2aaf6bc commit 4855d93

File tree

4 files changed

+135
-52
lines changed

4 files changed

+135
-52
lines changed

statediff/builder.go

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ func (sdb *builder) buildStateDiffWithIntermediateStateNodes(args types2.StateRo
202202
// a map of their leafkey to all the accounts that were touched and exist at A
203203
diffAccountsAtA, err := sdb.deletedOrUpdatedState(
204204
oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}),
205-
diffPathsAtB, params.watchedAddressesLeafKeys, output)
205+
diffPathsAtB, params.watchedAddressesLeafKeys,
206+
params.IntermediateStorageNodes, output)
206207
if err != nil {
207208
return fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err)
208209
}
@@ -256,7 +257,8 @@ func (sdb *builder) buildStateDiffWithoutIntermediateStateNodes(args types2.Stat
256257
// a map of their leafkey to all the accounts that were touched and exist at A
257258
diffAccountsAtA, err := sdb.deletedOrUpdatedState(
258259
oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}),
259-
diffPathsAtB, params.watchedAddressesLeafKeys, output)
260+
diffPathsAtB, params.watchedAddressesLeafKeys,
261+
params.IntermediateStorageNodes, output)
260262
if err != nil {
261263
return fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err)
262264
}
@@ -386,7 +388,7 @@ func (sdb *builder) createdAndUpdatedStateWithIntermediateNodes(a, b trie.NodeIt
386388

387389
// deletedOrUpdatedState returns a slice of all the pathes that are emptied at B
388390
// and a mapping of their leafkeys to all the accounts that exist in a different state at A than B
389-
func (sdb *builder) deletedOrUpdatedState(a, b trie.NodeIterator, diffPathsAtB map[string]bool, watchedAddressesLeafKeys map[common.Hash]struct{}, output types2.StateNodeSink) (types2.AccountMap, error) {
391+
func (sdb *builder) deletedOrUpdatedState(a, b trie.NodeIterator, diffPathsAtB map[string]bool, watchedAddressesLeafKeys map[common.Hash]struct{}, intermediateStorageNodes bool, output types2.StateNodeSink) (types2.AccountMap, error) {
390392
diffAccountAtA := make(types2.AccountMap)
391393
it, _ := trie.NewDifferenceIterator(b, a)
392394
for it.Next(true) {
@@ -420,13 +422,23 @@ func (sdb *builder) deletedOrUpdatedState(a, b trie.NodeIterator, diffPathsAtB m
420422
// if this node's path did not show up in diffPathsAtB
421423
// that means the node at this path was deleted (or moved) in B
422424
// emit an empty "removed" diff to signify as such
425+
// emit emtpy "removed" diff for all storage nodes
423426
if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; !ok {
424-
if err := output(types2.StateNode{
425-
Path: node.Path,
426-
NodeValue: []byte{},
427+
diff := types2.StateNode{
427428
NodeType: types2.Removed,
429+
Path: node.Path,
428430
LeafKey: leafKey,
429-
}); err != nil {
431+
NodeValue: []byte{},
432+
}
433+
434+
var storageDiffs []types2.StorageNode
435+
err := sdb.buildRemovedAccountStorageNodes(account.Root, intermediateStorageNodes, storageNodeAppender(&storageDiffs))
436+
if err != nil {
437+
return nil, fmt.Errorf("failed building storage diffs for removed node %x\r\nerror: %v", node.Path, err)
438+
}
439+
diff.StorageNodes = storageDiffs
440+
441+
if err := output(diff); err != nil {
430442
return nil, err
431443
}
432444
}
@@ -548,7 +560,6 @@ func (sdb *builder) buildStorageNodesEventual(sr common.Hash, intermediateNodes
548560
}
549561

550562
// buildStorageNodesFromTrie returns all the storage diff node objects in the provided node interator
551-
// if any storage keys are provided it will only return those leaf nodes
552563
// including intermediate nodes can be turned on or off
553564
func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, intermediateNodes bool, output types2.StorageNodeSink) error {
554565
for it.Next(true) {
@@ -591,6 +602,68 @@ func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, intermediate
591602
return it.Error()
592603
}
593604

605+
// buildRemovedAccountStorageNodes builds the "removed" diffs for all the storage nodes for a destroyed account
606+
func (sdb *builder) buildRemovedAccountStorageNodes(sr common.Hash, intermediateNodes bool, output types2.StorageNodeSink) error {
607+
if bytes.Equal(sr.Bytes(), emptyContractRoot.Bytes()) {
608+
return nil
609+
}
610+
log.Debug("Storage Root For Removed Diffs", "root", sr.Hex())
611+
sTrie, err := sdb.stateCache.OpenTrie(sr)
612+
if err != nil {
613+
log.Info("error in build removed account storage diffs", "error", err)
614+
return err
615+
}
616+
it := sTrie.NodeIterator(make([]byte, 0))
617+
err = sdb.buildRemovedStorageNodesFromTrie(it, intermediateNodes, output)
618+
if err != nil {
619+
return err
620+
}
621+
return nil
622+
}
623+
624+
// buildRemovedStorageNodesFromTrie returns diffs for all the storage nodes in the provided node interator
625+
// including intermediate nodes can be turned on or off
626+
func (sdb *builder) buildRemovedStorageNodesFromTrie(it trie.NodeIterator, intermediateNodes bool, output types2.StorageNodeSink) error {
627+
for it.Next(true) {
628+
// skip value nodes
629+
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
630+
continue
631+
}
632+
node, nodeElements, err := trie_helpers.ResolveNode(it, sdb.stateCache.TrieDB())
633+
if err != nil {
634+
return err
635+
}
636+
switch node.NodeType {
637+
case types2.Leaf:
638+
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
639+
valueNodePath := append(node.Path, partialPath...)
640+
encodedPath := trie.HexToCompact(valueNodePath)
641+
leafKey := encodedPath[1:]
642+
if err := output(types2.StorageNode{
643+
NodeType: types2.Removed,
644+
Path: node.Path,
645+
NodeValue: []byte{},
646+
LeafKey: leafKey,
647+
}); err != nil {
648+
return err
649+
}
650+
case types2.Extension, types2.Branch:
651+
if intermediateNodes {
652+
if err := output(types2.StorageNode{
653+
NodeType: types2.Removed,
654+
Path: node.Path,
655+
NodeValue: []byte{},
656+
}); err != nil {
657+
return err
658+
}
659+
}
660+
default:
661+
return fmt.Errorf("unexpected node type %s", node.NodeType)
662+
}
663+
}
664+
return it.Error()
665+
}
666+
594667
// buildStorageNodesIncremental builds the storage diff node objects for all nodes that exist in a different state at B than A
595668
func (sdb *builder) buildStorageNodesIncremental(oldSR common.Hash, newSR common.Hash, intermediateNodes bool, output types2.StorageNodeSink) error {
596669
if bytes.Equal(newSR.Bytes(), oldSR.Bytes()) {

statediff/indexer/database/dump/indexer.go

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -382,36 +382,38 @@ func (sdi *StateDiffIndexer) PushStateNode(batch interfaces.Batch, stateNode sdt
382382
return fmt.Errorf("sql batch is expected to be of type %T, got %T", &BatchTx{}, batch)
383383
}
384384
// publish the state node
385+
var stateModel models.StateNodeModel
385386
if stateNode.NodeType == sdtypes.Removed {
386387
// short circuit if it is a Removed node
387388
// this assumes the db has been initialized and a public.blocks entry for the Removed node is present
388-
stateModel := models.StateNodeModel{
389+
stateModel = models.StateNodeModel{
389390
HeaderID: headerID,
390391
Path: stateNode.Path,
391392
StateKey: common.BytesToHash(stateNode.LeafKey).String(),
392393
CID: shared.RemovedNodeStateCID,
393394
MhKey: shared.RemovedNodeMhKey,
394395
NodeType: stateNode.NodeType.Int(),
395396
}
396-
_, err := fmt.Fprintf(sdi.dump, "%+v\r\n", stateModel)
397-
return err
398-
}
399-
stateCIDStr, stateMhKey, err := tx.cacheRaw(ipld2.MEthStateTrie, multihash.KECCAK_256, stateNode.NodeValue)
400-
if err != nil {
401-
return fmt.Errorf("error generating and cacheing state node IPLD: %v", err)
402-
}
403-
stateModel := models.StateNodeModel{
404-
HeaderID: headerID,
405-
Path: stateNode.Path,
406-
StateKey: common.BytesToHash(stateNode.LeafKey).String(),
407-
CID: stateCIDStr,
408-
MhKey: stateMhKey,
409-
NodeType: stateNode.NodeType.Int(),
397+
} else {
398+
stateCIDStr, stateMhKey, err := tx.cacheRaw(ipld2.MEthStateTrie, multihash.KECCAK_256, stateNode.NodeValue)
399+
if err != nil {
400+
return fmt.Errorf("error generating and cacheing state node IPLD: %v", err)
401+
}
402+
stateModel = models.StateNodeModel{
403+
HeaderID: headerID,
404+
Path: stateNode.Path,
405+
StateKey: common.BytesToHash(stateNode.LeafKey).String(),
406+
CID: stateCIDStr,
407+
MhKey: stateMhKey,
408+
NodeType: stateNode.NodeType.Int(),
409+
}
410410
}
411+
411412
// index the state node, collect the stateID to reference by FK
412413
if _, err := fmt.Fprintf(sdi.dump, "%+v\r\n", stateModel); err != nil {
413414
return err
414415
}
416+
415417
// if we have a leaf, decode and index the account data
416418
if stateNode.NodeType == sdtypes.Leaf {
417419
var i []interface{}
@@ -437,6 +439,7 @@ func (sdi *StateDiffIndexer) PushStateNode(batch interfaces.Batch, stateNode sdt
437439
return err
438440
}
439441
}
442+
440443
// if there are any storage nodes associated with this node, publish and index them
441444
for _, storageNode := range stateNode.StorageNodes {
442445
if storageNode.NodeType == sdtypes.Removed {

statediff/indexer/database/file/indexer.go

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -392,34 +392,36 @@ func (sdi *StateDiffIndexer) processReceiptsAndTxs(args processArgs) error {
392392
// PushStateNode writes a state diff node object (including any child storage nodes) IPLD insert SQL stmt to a file
393393
func (sdi *StateDiffIndexer) PushStateNode(batch interfaces.Batch, stateNode sdtypes.StateNode, headerID string) error {
394394
// publish the state node
395+
var stateModel models.StateNodeModel
395396
if stateNode.NodeType == sdtypes.Removed {
396397
// short circuit if it is a Removed node
397398
// this assumes the db has been initialized and a public.blocks entry for the Removed node is present
398-
stateModel := models.StateNodeModel{
399+
stateModel = models.StateNodeModel{
399400
HeaderID: headerID,
400401
Path: stateNode.Path,
401402
StateKey: common.BytesToHash(stateNode.LeafKey).String(),
402403
CID: shared.RemovedNodeStateCID,
403404
MhKey: shared.RemovedNodeMhKey,
404405
NodeType: stateNode.NodeType.Int(),
405406
}
406-
sdi.fileWriter.upsertStateCID(stateModel)
407-
return nil
408-
}
409-
stateCIDStr, stateMhKey, err := sdi.fileWriter.upsertIPLDRaw(ipld2.MEthStateTrie, multihash.KECCAK_256, stateNode.NodeValue)
410-
if err != nil {
411-
return fmt.Errorf("error generating and cacheing state node IPLD: %v", err)
412-
}
413-
stateModel := models.StateNodeModel{
414-
HeaderID: headerID,
415-
Path: stateNode.Path,
416-
StateKey: common.BytesToHash(stateNode.LeafKey).String(),
417-
CID: stateCIDStr,
418-
MhKey: stateMhKey,
419-
NodeType: stateNode.NodeType.Int(),
407+
} else {
408+
stateCIDStr, stateMhKey, err := sdi.fileWriter.upsertIPLDRaw(ipld2.MEthStateTrie, multihash.KECCAK_256, stateNode.NodeValue)
409+
if err != nil {
410+
return fmt.Errorf("error generating and cacheing state node IPLD: %v", err)
411+
}
412+
stateModel = models.StateNodeModel{
413+
HeaderID: headerID,
414+
Path: stateNode.Path,
415+
StateKey: common.BytesToHash(stateNode.LeafKey).String(),
416+
CID: stateCIDStr,
417+
MhKey: stateMhKey,
418+
NodeType: stateNode.NodeType.Int(),
419+
}
420420
}
421+
421422
// index the state node
422423
sdi.fileWriter.upsertStateCID(stateModel)
424+
423425
// if we have a leaf, decode and index the account data
424426
if stateNode.NodeType == sdtypes.Leaf {
425427
var i []interface{}
@@ -443,6 +445,7 @@ func (sdi *StateDiffIndexer) PushStateNode(batch interfaces.Batch, stateNode sdt
443445
}
444446
sdi.fileWriter.upsertStateAccount(accountModel)
445447
}
448+
446449
// if there are any storage nodes associated with this node, publish and index them
447450
for _, storageNode := range stateNode.StorageNodes {
448451
if storageNode.NodeType == sdtypes.Removed {

statediff/indexer/database/sql/indexer.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -440,35 +440,38 @@ func (sdi *StateDiffIndexer) PushStateNode(batch interfaces.Batch, stateNode sdt
440440
return fmt.Errorf("sql batch is expected to be of type %T, got %T", &BatchTx{}, batch)
441441
}
442442
// publish the state node
443+
var stateModel models.StateNodeModel
443444
if stateNode.NodeType == sdtypes.Removed {
444445
// short circuit if it is a Removed node
445446
// this assumes the db has been initialized and a public.blocks entry for the Removed node is present
446-
stateModel := models.StateNodeModel{
447+
stateModel = models.StateNodeModel{
447448
HeaderID: headerID,
448449
Path: stateNode.Path,
449450
StateKey: common.BytesToHash(stateNode.LeafKey).String(),
450451
CID: shared.RemovedNodeStateCID,
451452
MhKey: shared.RemovedNodeMhKey,
452453
NodeType: stateNode.NodeType.Int(),
453454
}
454-
return sdi.dbWriter.upsertStateCID(tx.dbtx, stateModel)
455-
}
456-
stateCIDStr, stateMhKey, err := tx.cacheRaw(ipld2.MEthStateTrie, multihash.KECCAK_256, stateNode.NodeValue)
457-
if err != nil {
458-
return fmt.Errorf("error generating and cacheing state node IPLD: %v", err)
459-
}
460-
stateModel := models.StateNodeModel{
461-
HeaderID: headerID,
462-
Path: stateNode.Path,
463-
StateKey: common.BytesToHash(stateNode.LeafKey).String(),
464-
CID: stateCIDStr,
465-
MhKey: stateMhKey,
466-
NodeType: stateNode.NodeType.Int(),
455+
} else {
456+
stateCIDStr, stateMhKey, err := tx.cacheRaw(ipld2.MEthStateTrie, multihash.KECCAK_256, stateNode.NodeValue)
457+
if err != nil {
458+
return fmt.Errorf("error generating and cacheing state node IPLD: %v", err)
459+
}
460+
stateModel = models.StateNodeModel{
461+
HeaderID: headerID,
462+
Path: stateNode.Path,
463+
StateKey: common.BytesToHash(stateNode.LeafKey).String(),
464+
CID: stateCIDStr,
465+
MhKey: stateMhKey,
466+
NodeType: stateNode.NodeType.Int(),
467+
}
467468
}
469+
468470
// index the state node
469471
if err := sdi.dbWriter.upsertStateCID(tx.dbtx, stateModel); err != nil {
470472
return err
471473
}
474+
472475
// if we have a leaf, decode and index the account data
473476
if stateNode.NodeType == sdtypes.Leaf {
474477
var i []interface{}
@@ -494,6 +497,7 @@ func (sdi *StateDiffIndexer) PushStateNode(batch interfaces.Batch, stateNode sdt
494497
return err
495498
}
496499
}
500+
497501
// if there are any storage nodes associated with this node, publish and index them
498502
for _, storageNode := range stateNode.StorageNodes {
499503
if storageNode.NodeType == sdtypes.Removed {

0 commit comments

Comments
 (0)