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 }