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  }