github.com/MetalBlockchain/subnet-evm@v0.4.9/sync/statesync/trie_sync_tasks.go (about) 1 // (c) 2022, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package statesync 5 6 import ( 7 "fmt" 8 9 "github.com/MetalBlockchain/subnet-evm/core/rawdb" 10 "github.com/MetalBlockchain/subnet-evm/core/types" 11 "github.com/MetalBlockchain/subnet-evm/ethdb" 12 "github.com/MetalBlockchain/subnet-evm/sync/syncutils" 13 "github.com/MetalBlockchain/subnet-evm/trie" 14 "github.com/ethereum/go-ethereum/common" 15 "github.com/ethereum/go-ethereum/log" 16 "github.com/ethereum/go-ethereum/rlp" 17 ) 18 19 var ( 20 _ syncTask = &mainTrieTask{} 21 _ syncTask = &storageTrieTask{} 22 ) 23 24 type syncTask interface { 25 // IterateLeafs should return an iterator over 26 // trie leafs already persisted to disk for this 27 // trie. Used for restoring progress in case of an 28 // interrupted sync and for hashing segments. 29 IterateLeafs(seek common.Hash) ethdb.Iterator 30 31 // callbacks used to form a LeafSyncTask 32 OnStart() (bool, error) 33 OnLeafs(db ethdb.KeyValueWriter, keys, vals [][]byte) error 34 OnFinish() error 35 } 36 37 type mainTrieTask struct { 38 sync *stateSync 39 } 40 41 func NewMainTrieTask(sync *stateSync) syncTask { 42 return &mainTrieTask{ 43 sync: sync, 44 } 45 } 46 47 func (m *mainTrieTask) IterateLeafs(seek common.Hash) ethdb.Iterator { 48 snapshot := m.sync.snapshot 49 return &syncutils.AccountIterator{AccountIterator: snapshot.AccountIterator(seek)} 50 } 51 52 // OnStart always returns false since the main trie task cannot be skipped. 53 func (m *mainTrieTask) OnStart() (bool, error) { 54 return false, nil 55 } 56 57 func (m *mainTrieTask) OnFinish() error { 58 return m.sync.onMainTrieFinished() 59 } 60 61 func (m *mainTrieTask) OnLeafs(db ethdb.KeyValueWriter, keys, vals [][]byte) error { 62 codeHashes := make([]common.Hash, 0) 63 // loop over the keys, decode them as accounts, then check for any 64 // storage or code we need to sync as well. 65 for i, key := range keys { 66 var acc types.StateAccount 67 accountHash := common.BytesToHash(key) 68 if err := rlp.DecodeBytes(vals[i], &acc); err != nil { 69 return fmt.Errorf("could not decode main trie as account, key=%s, valueLen=%d, err=%w", accountHash, len(vals[i]), err) 70 } 71 72 // persist the account data 73 writeAccountSnapshot(db, accountHash, acc) 74 75 // check if this account has storage root that we need to fetch 76 if acc.Root != (common.Hash{}) && acc.Root != types.EmptyRootHash { 77 m.sync.trieQueue.RegisterStorageTrie(acc.Root, accountHash) 78 } 79 80 // check if this account has code and add it to codeHashes to fetch 81 // at the end of this loop. 82 codeHash := common.BytesToHash(acc.CodeHash) 83 if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash { 84 codeHashes = append(codeHashes, codeHash) 85 } 86 } 87 // Add collected code hashes to the code syncer. 88 return m.sync.codeSyncer.addCode(codeHashes) 89 } 90 91 type storageTrieTask struct { 92 sync *stateSync 93 root common.Hash 94 accounts []common.Hash 95 } 96 97 func NewStorageTrieTask(sync *stateSync, root common.Hash, accounts []common.Hash) syncTask { 98 return &storageTrieTask{ 99 sync: sync, 100 root: root, 101 accounts: accounts, 102 } 103 } 104 105 func (s *storageTrieTask) IterateLeafs(seek common.Hash) ethdb.Iterator { 106 snapshot := s.sync.snapshot 107 it, _ := snapshot.StorageIterator(s.accounts[0], seek) 108 return &syncutils.StorageIterator{StorageIterator: it} 109 } 110 111 func (s *storageTrieTask) OnStart() (bool, error) { 112 // check if this storage root is on disk 113 storageTrie, err := trie.New(common.Hash{}, s.root, s.sync.trieDB) 114 if err != nil { 115 return false, nil 116 } 117 118 // If the storage trie is already on disk, we only need to populate the storage snapshot for [accountHash] 119 // with the trie contents. There is no need to re-sync the trie, since it is already present. 120 for _, account := range s.accounts { 121 if err := writeAccountStorageSnapshotFromTrie(s.sync.db.NewBatch(), s.sync.batchSize, account, storageTrie); err != nil { 122 // If the storage trie cannot be iterated (due to an incomplete trie from pruning this storage trie in the past) 123 // then we re-sync it here. Therefore, this error is not fatal and we can safely continue here. 124 log.Info("could not populate storage snapshot from trie with existing root, syncing from peers instead", "account", account, "root", s.root, "err", err) 125 return false, nil 126 } 127 } 128 129 // Populating the snapshot from the existing storage trie succeeded, 130 // return true to skip this task. 131 return true, s.sync.onStorageTrieFinished(s.root) 132 } 133 134 func (s *storageTrieTask) OnFinish() error { 135 return s.sync.onStorageTrieFinished(s.root) 136 } 137 138 func (s *storageTrieTask) OnLeafs(db ethdb.KeyValueWriter, keys, vals [][]byte) error { 139 // persists the trie leafs to the snapshot for all accounts associated with this root 140 for _, account := range s.accounts { 141 for i, key := range keys { 142 rawdb.WriteStorageSnapshot(db, account, common.BytesToHash(key), vals[i]) 143 } 144 } 145 return nil 146 }