github.com/dim4egster/coreth@v0.10.2/plugin/evm/atomic_syncer.go (about)

     1  // (c) 2019-2022, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package evm
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/binary"
    10  	"fmt"
    11  
    12  	"github.com/dim4egster/qmallgo/database/versiondb"
    13  	"github.com/dim4egster/qmallgo/utils/wrappers"
    14  
    15  	"github.com/dim4egster/coreth/plugin/evm/message"
    16  	syncclient "github.com/dim4egster/coreth/sync/client"
    17  	"github.com/dim4egster/coreth/trie"
    18  	"github.com/ethereum/go-ethereum/common"
    19  )
    20  
    21  var (
    22  	_ Syncer                  = &atomicSyncer{}
    23  	_ syncclient.LeafSyncTask = &atomicSyncerLeafTask{}
    24  )
    25  
    26  // atomicSyncer is used to sync the atomic trie from the network. The CallbackLeafSyncer
    27  // is responsible for orchestrating the sync while atomicSyncer is responsible for maintaining
    28  // the state of progress and writing the actual atomic trie to the trieDB.
    29  type atomicSyncer struct {
    30  	db           *versiondb.Database
    31  	atomicTrie   AtomicTrie
    32  	trie         *trie.Trie // used to update the atomic trie
    33  	targetRoot   common.Hash
    34  	targetHeight uint64
    35  
    36  	// syncer is used to sync leaves from the network.
    37  	syncer *syncclient.CallbackLeafSyncer
    38  
    39  	// nextHeight is the height which key / values
    40  	// are being inserted into [atomicTrie] for
    41  	nextHeight uint64
    42  }
    43  
    44  // addZeros adds [common.HashLenth] zeros to [height] and returns the result as []byte
    45  func addZeroes(height uint64) []byte {
    46  	packer := wrappers.Packer{Bytes: make([]byte, atomicKeyLength)}
    47  	packer.PackLong(height)
    48  	packer.PackFixedBytes(bytes.Repeat([]byte{0x00}, common.HashLength))
    49  	return packer.Bytes
    50  }
    51  
    52  func newAtomicSyncer(client syncclient.LeafClient, atomicBackend *atomicBackend, targetRoot common.Hash, targetHeight uint64) (*atomicSyncer, error) {
    53  	atomicTrie := atomicBackend.AtomicTrie()
    54  	lastCommittedRoot, lastCommit := atomicTrie.LastCommitted()
    55  	trie, err := atomicTrie.OpenTrie(lastCommittedRoot)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	atomicSyncer := &atomicSyncer{
    61  		db:           atomicBackend.db,
    62  		atomicTrie:   atomicTrie,
    63  		trie:         trie,
    64  		targetRoot:   targetRoot,
    65  		targetHeight: targetHeight,
    66  		nextHeight:   lastCommit + 1,
    67  	}
    68  	tasks := make(chan syncclient.LeafSyncTask, 1)
    69  	tasks <- &atomicSyncerLeafTask{atomicSyncer: atomicSyncer}
    70  	close(tasks)
    71  	atomicSyncer.syncer = syncclient.NewCallbackLeafSyncer(client, tasks)
    72  	return atomicSyncer, nil
    73  }
    74  
    75  // Start begins syncing the target atomic root.
    76  func (s *atomicSyncer) Start(ctx context.Context) error {
    77  	s.syncer.Start(ctx, 1, s.onSyncFailure)
    78  	return nil
    79  }
    80  
    81  // onLeafs is the callback for the leaf syncer, which will insert the key-value pairs into the trie.
    82  func (s *atomicSyncer) onLeafs(keys [][]byte, values [][]byte) error {
    83  	_, lastCommittedHeight := s.atomicTrie.LastCommitted()
    84  	lastHeight := lastCommittedHeight // track heights so we calculate roots after each height
    85  	for i, key := range keys {
    86  		if len(key) != atomicKeyLength {
    87  			return fmt.Errorf("unexpected key len (%d) in atomic trie sync", len(key))
    88  		}
    89  		// key = height + blockchainID
    90  		height := binary.BigEndian.Uint64(key[:wrappers.LongLen])
    91  		if height > lastHeight {
    92  			// If this key belongs to a new height, we commit
    93  			// the trie at the previous height before adding this key.
    94  			root, nodes, err := s.trie.Commit(false)
    95  			if err != nil {
    96  				return err
    97  			}
    98  			if err := s.atomicTrie.InsertTrie(nodes, root); err != nil {
    99  				return err
   100  			}
   101  			// AcceptTrie commits the trieDB and returns [isCommit] as true
   102  			// if we have reached or crossed a commit interval.
   103  			isCommit, err := s.atomicTrie.AcceptTrie(lastHeight, root)
   104  			if err != nil {
   105  				return err
   106  			}
   107  			if isCommit {
   108  				// Flush pending changes to disk to preserve progress and
   109  				// free up memory if the trieDB was committed.
   110  				if err := s.db.Commit(); err != nil {
   111  					return err
   112  				}
   113  			}
   114  			lastHeight = height
   115  		}
   116  
   117  		if err := s.trie.TryUpdate(key, values[i]); err != nil {
   118  			return err
   119  		}
   120  	}
   121  	return nil
   122  }
   123  
   124  // onFinish is called when sync for this trie is complete.
   125  // commit the trie to disk and perform the final checks that we synced the target root correctly.
   126  func (s *atomicSyncer) onFinish() error {
   127  	// commit the trie on finish
   128  	root, nodes, err := s.trie.Commit(false)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	if err := s.atomicTrie.InsertTrie(nodes, root); err != nil {
   133  		return err
   134  	}
   135  	if _, err := s.atomicTrie.AcceptTrie(s.targetHeight, root); err != nil {
   136  		return err
   137  	}
   138  	if err := s.db.Commit(); err != nil {
   139  		return err
   140  	}
   141  
   142  	// the root of the trie should always match the targetRoot  since we already verified the proofs,
   143  	// here we check the root mainly for correctness of the atomicTrie's pointers and it should never fail.
   144  	if s.targetRoot != root {
   145  		return fmt.Errorf("synced root (%s) does not match expected (%s) for atomic trie ", root, s.targetRoot)
   146  	}
   147  	return nil
   148  }
   149  
   150  // onSyncFailure is a no-op since we flush progress to disk at the regular commit interval when syncing
   151  // the atomic trie.
   152  func (s *atomicSyncer) onSyncFailure(error) error {
   153  	return nil
   154  }
   155  
   156  // Done returns a channel which produces any error that occurred during syncing or nil on success.
   157  func (s *atomicSyncer) Done() <-chan error { return s.syncer.Done() }
   158  
   159  type atomicSyncerLeafTask struct {
   160  	atomicSyncer *atomicSyncer
   161  }
   162  
   163  func (a *atomicSyncerLeafTask) Start() []byte              { return addZeroes(a.atomicSyncer.nextHeight) }
   164  func (a *atomicSyncerLeafTask) End() []byte                { return nil }
   165  func (a *atomicSyncerLeafTask) NodeType() message.NodeType { return message.AtomicTrieNode }
   166  func (a *atomicSyncerLeafTask) OnFinish() error            { return a.atomicSyncer.onFinish() }
   167  func (a *atomicSyncerLeafTask) OnStart() (bool, error)     { return false, nil }
   168  func (a *atomicSyncerLeafTask) Root() common.Hash          { return a.atomicSyncer.targetRoot }
   169  func (a *atomicSyncerLeafTask) Account() common.Hash       { return common.Hash{} }
   170  func (a *atomicSyncerLeafTask) OnLeafs(keys, vals [][]byte) error {
   171  	return a.atomicSyncer.onLeafs(keys, vals)
   172  }