gitlab.com/flarenetwork/coreth@v0.1.1/core/state_manager.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2014 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package core 28 29 import ( 30 "fmt" 31 "math/rand" 32 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/ethdb" 35 "gitlab.com/flarenetwork/coreth/core/types" 36 ) 37 38 const ( 39 commitInterval = 4096 40 tipBufferSize = 16 41 ) 42 43 type TrieWriter interface { 44 InsertTrie(block *types.Block) error // Insert reference to trie [root] 45 AcceptTrie(block *types.Block) error // Mark [root] as part of an accepted block 46 RejectTrie(block *types.Block) error // Notify TrieWriter that the block containing [root] has been rejected 47 Shutdown() error 48 } 49 50 type TrieDB interface { 51 Reference(child common.Hash, parent common.Hash) 52 Dereference(root common.Hash) 53 Commit(root common.Hash, report bool, callback func(common.Hash)) error 54 Size() (common.StorageSize, common.StorageSize) 55 Cap(limit common.StorageSize) error 56 } 57 58 func NewTrieWriter(db TrieDB, config *CacheConfig) TrieWriter { 59 if config.Pruning { 60 return &cappedMemoryTrieWriter{ 61 TrieDB: db, 62 memoryCap: common.StorageSize(config.TrieDirtyLimit) * 1024 * 1024, 63 imageCap: 4 * 1024 * 1024, 64 commitInterval: commitInterval, 65 tipBuffer: make([]common.Hash, tipBufferSize), 66 randomizedInterval: uint64(rand.Int63n(commitInterval)) + commitInterval, 67 } 68 } else { 69 return &noPruningTrieWriter{ 70 TrieDB: db, 71 } 72 } 73 } 74 75 type noPruningTrieWriter struct { 76 TrieDB 77 } 78 79 func (np *noPruningTrieWriter) InsertTrie(block *types.Block) error { 80 return np.TrieDB.Commit(block.Root(), false, nil) 81 } 82 83 func (np *noPruningTrieWriter) AcceptTrie(block *types.Block) error { return nil } 84 85 func (np *noPruningTrieWriter) RejectTrie(block *types.Block) error { return nil } 86 87 func (np *noPruningTrieWriter) Shutdown() error { return nil } 88 89 type cappedMemoryTrieWriter struct { 90 TrieDB 91 memoryCap common.StorageSize 92 imageCap common.StorageSize 93 commitInterval, randomizedInterval uint64 94 95 lastPos int 96 tipBuffer []common.Hash 97 } 98 99 func (cm *cappedMemoryTrieWriter) InsertTrie(block *types.Block) error { 100 cm.TrieDB.Reference(block.Root(), common.Hash{}) 101 102 nodes, imgs := cm.TrieDB.Size() 103 if nodes > cm.memoryCap || imgs > cm.imageCap { 104 return cm.TrieDB.Cap(cm.memoryCap - ethdb.IdealBatchSize) 105 } 106 107 return nil 108 } 109 110 func (cm *cappedMemoryTrieWriter) AcceptTrie(block *types.Block) error { 111 root := block.Root() 112 113 // Attempt to dereference roots at least [tipBufferSize] old (so queries at tip 114 // can still be completed). 115 // 116 // Note: It is safe to dereference roots that have been committed to disk 117 // (they are no-ops). 118 nextPos := (cm.lastPos + 1) % tipBufferSize 119 if cm.tipBuffer[nextPos] != (common.Hash{}) { 120 cm.TrieDB.Dereference(cm.tipBuffer[nextPos]) 121 } 122 cm.tipBuffer[nextPos] = root 123 cm.lastPos = nextPos 124 125 // Commit this root if we haven't committed an accepted block root within 126 // the desired interval. 127 // Note: a randomized interval is added here to ensure that pruning nodes 128 // do not all only commit at the exact same heights. 129 if height := block.NumberU64(); height%cm.commitInterval == 0 || height%cm.randomizedInterval == 0 { 130 if err := cm.TrieDB.Commit(root, true, nil); err != nil { 131 return fmt.Errorf("failed to commit trie for block %s: %w", block.Hash().Hex(), err) 132 } 133 } 134 return nil 135 } 136 137 func (cm *cappedMemoryTrieWriter) RejectTrie(block *types.Block) error { 138 cm.TrieDB.Dereference(block.Root()) 139 return nil 140 } 141 142 func (cm *cappedMemoryTrieWriter) Shutdown() error { 143 // If [tipBuffer] entry is empty, no need to do any cleanup on 144 // shutdown. 145 if cm.tipBuffer[cm.lastPos] == (common.Hash{}) { 146 return nil 147 } 148 149 // Attempt to commit last item added to [dereferenceQueue] on shutdown to avoid 150 // re-processing the state on the next startup. 151 return cm.TrieDB.Commit(cm.tipBuffer[cm.lastPos], true, nil) 152 }