github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/evmstore/store.go (about) 1 package evmstore 2 3 import ( 4 "errors" 5 6 "github.com/syndtr/goleveldb/leveldb/opt" 7 "github.com/unicornultrafoundation/go-helios/hash" 8 "github.com/unicornultrafoundation/go-helios/native/idx" 9 "github.com/unicornultrafoundation/go-helios/u2udb" 10 "github.com/unicornultrafoundation/go-helios/u2udb/nokeyiserr" 11 "github.com/unicornultrafoundation/go-helios/u2udb/table" 12 "github.com/unicornultrafoundation/go-helios/utils/wlru" 13 "github.com/unicornultrafoundation/go-u2u/common" 14 "github.com/unicornultrafoundation/go-u2u/common/prque" 15 "github.com/unicornultrafoundation/go-u2u/core/rawdb" 16 "github.com/unicornultrafoundation/go-u2u/core/state" 17 "github.com/unicornultrafoundation/go-u2u/core/state/snapshot" 18 "github.com/unicornultrafoundation/go-u2u/core/types" 19 "github.com/unicornultrafoundation/go-u2u/ethdb" 20 "github.com/unicornultrafoundation/go-u2u/trie" 21 22 "github.com/unicornultrafoundation/go-u2u/logger" 23 "github.com/unicornultrafoundation/go-u2u/native/iblockproc" 24 "github.com/unicornultrafoundation/go-u2u/topicsdb" 25 "github.com/unicornultrafoundation/go-u2u/utils/adapters/udb2ethdb" 26 "github.com/unicornultrafoundation/go-u2u/utils/rlpstore" 27 ) 28 29 const nominalSize uint = 1 30 31 // Store is a node persistent storage working over physical key-value database. 32 type Store struct { 33 cfg StoreConfig 34 35 table struct { 36 Evm u2udb.Store `table:"M"` 37 // API-only tables 38 Receipts u2udb.Store `table:"r"` 39 TxPositions u2udb.Store `table:"x"` 40 Txs u2udb.Store `table:"X"` 41 } 42 43 EvmDb ethdb.Database 44 EvmState state.Database 45 EvmLogs topicsdb.Index 46 Snaps *snapshot.Tree 47 48 cache struct { 49 TxPositions *wlru.Cache `cache:"-"` // store by pointer 50 Receipts *wlru.Cache `cache:"-"` // store by value 51 EvmBlocks *wlru.Cache `cache:"-"` // store by pointer 52 } 53 54 rlp rlpstore.Helper 55 56 triegc *prque.Prque // Priority queue mapping block numbers to tries to gc 57 58 logger.Instance 59 } 60 61 const ( 62 TriesInMemory = 16 63 ) 64 65 // NewStore creates store over key-value db. 66 func NewStore(dbs u2udb.DBProducer, cfg StoreConfig) *Store { 67 s := &Store{ 68 cfg: cfg, 69 Instance: logger.New("evm-store"), 70 rlp: rlpstore.Helper{logger.New("rlp")}, 71 triegc: prque.New(nil), 72 } 73 74 err := table.OpenTables(&s.table, dbs, "evm") 75 if err != nil { 76 s.Log.Crit("Failed to open tables", "err", err) 77 } 78 79 s.initEVMDB() 80 s.EvmLogs = topicsdb.NewWithThreadPool(dbs) 81 s.initCache() 82 83 return s 84 } 85 86 // Close closes underlying database. 87 func (s *Store) Close() { 88 setnil := func() interface{} { 89 return nil 90 } 91 92 _ = table.CloseTables(&s.table) 93 table.MigrateTables(&s.table, nil) 94 table.MigrateCaches(&s.cache, setnil) 95 s.EvmLogs.Close() 96 } 97 98 func (s *Store) initCache() { 99 s.cache.Receipts = s.makeCache(s.cfg.Cache.ReceiptsSize, s.cfg.Cache.ReceiptsBlocks) 100 s.cache.TxPositions = s.makeCache(nominalSize*uint(s.cfg.Cache.TxPositions), s.cfg.Cache.TxPositions) 101 s.cache.EvmBlocks = s.makeCache(s.cfg.Cache.EvmBlocksSize, s.cfg.Cache.EvmBlocksNum) 102 } 103 104 func (s *Store) initEVMDB() { 105 s.EvmDb = rawdb.NewDatabase( 106 udb2ethdb.Wrap( 107 nokeyiserr.Wrap( 108 s.table.Evm))) 109 s.EvmState = state.NewDatabaseWithConfig(s.EvmDb, &trie.Config{ 110 Cache: s.cfg.Cache.EvmDatabase / opt.MiB, 111 Journal: s.cfg.Cache.TrieCleanJournal, 112 Preimages: s.cfg.EnablePreimageRecording, 113 GreedyGC: s.cfg.Cache.GreedyGC, 114 }) 115 } 116 117 func (s *Store) ResetWithEVMDB(evmStore u2udb.Store) *Store { 118 cp := *s 119 cp.table.Evm = evmStore 120 cp.initEVMDB() 121 cp.Snaps = nil 122 return &cp 123 } 124 125 func (s *Store) EVMDB() u2udb.Store { 126 return s.table.Evm 127 } 128 129 func (s *Store) GenerateEvmSnapshot(root common.Hash, rebuild, async bool) (err error) { 130 if s.Snaps != nil { 131 return errors.New("EVM snapshot is already opened") 132 } 133 s.Snaps, err = snapshot.New( 134 s.EvmDb, 135 s.EvmState.TrieDB(), 136 s.cfg.Cache.EvmSnap/opt.MiB, 137 root, 138 async, 139 rebuild, 140 false) 141 return 142 } 143 144 func (s *Store) RebuildEvmSnapshot(root common.Hash) { 145 if s.Snaps == nil { 146 return 147 } 148 s.Snaps.Rebuild(root) 149 } 150 151 // CleanCommit clean old state trie and commit changes. 152 func (s *Store) CleanCommit(block iblockproc.BlockState) error { 153 // Don't need to reference the current state root 154 // due to it already be referenced on `Commit()` function 155 triedb := s.EvmState.TrieDB() 156 stateRoot := common.Hash(block.FinalizedStateRoot) 157 if current := uint64(block.LastBlock.Idx); current > TriesInMemory { 158 // Find the next state trie we need to commit 159 chosen := current - TriesInMemory 160 // Garbage collect all below the chosen block 161 for !s.triegc.Empty() { 162 root, number := s.triegc.Pop() 163 if uint64(-number) > chosen { 164 s.triegc.Push(root, number) 165 break 166 } 167 triedb.Dereference(root.(common.Hash)) 168 } 169 } 170 // commit the state trie after clean up 171 err := triedb.Commit(stateRoot, false, nil) 172 if err != nil { 173 s.Log.Error("Failed to flush trie DB into main DB", "err", err) 174 } 175 return err 176 } 177 178 func (s *Store) PauseEvmSnapshot() { 179 s.Snaps.Disable() 180 } 181 182 func (s *Store) IsEvmSnapshotPaused() bool { 183 return rawdb.ReadSnapshotDisabled(s.table.Evm) 184 } 185 186 // Commit changes. 187 func (s *Store) Commit(block idx.Block, root hash.Hash, flush bool) error { 188 triedb := s.EvmState.TrieDB() 189 stateRoot := common.Hash(root) 190 // If we're applying genesis or running an archive node, always flush 191 if flush || s.cfg.Cache.TrieDirtyDisabled { 192 err := triedb.Commit(stateRoot, false, nil) 193 if err != nil { 194 s.Log.Error("Failed to flush trie DB into main DB", "err", err) 195 } 196 return err 197 } else { 198 // Full but not archive node, do proper garbage collection 199 triedb.Reference(stateRoot, common.Hash{}) // metadata reference to keep trie alive 200 s.triegc.Push(stateRoot, -int64(block)) 201 202 if current := uint64(block); current > TriesInMemory { 203 // If we exceeded our memory allowance, flush matured singleton nodes to disk 204 s.Cap() 205 206 // Find the next state trie we need to commit 207 chosen := current - TriesInMemory 208 209 // Garbage collect all below the chosen block 210 for !s.triegc.Empty() { 211 root, number := s.triegc.Pop() 212 if uint64(-number) > chosen { 213 s.triegc.Push(root, number) 214 break 215 } 216 triedb.Dereference(root.(common.Hash)) 217 } 218 } 219 return nil 220 } 221 } 222 223 func (s *Store) Flush(block iblockproc.BlockState) { 224 // Ensure that the entirety of the state snapshot is journalled to disk. 225 var snapBase common.Hash 226 if s.Snaps != nil { 227 var err error 228 if snapBase, err = s.Snaps.Journal(common.Hash(block.FinalizedStateRoot)); err != nil { 229 s.Log.Error("Failed to journal state snapshot", "err", err) 230 } 231 } 232 // Ensure the state of a recent block is also stored to disk before exiting. 233 if !s.cfg.Cache.TrieDirtyDisabled { 234 triedb := s.EvmState.TrieDB() 235 236 if number := uint64(block.LastBlock.Idx); number > 0 { 237 s.Log.Info("Writing cached state to disk", "block", number, "root", block.FinalizedStateRoot) 238 if err := triedb.Commit(common.Hash(block.FinalizedStateRoot), true, nil); err != nil { 239 s.Log.Error("Failed to commit recent state trie", "err", err) 240 } 241 } 242 if snapBase != (common.Hash{}) { 243 s.Log.Info("Writing snapshot state to disk", "root", snapBase) 244 if err := triedb.Commit(snapBase, true, nil); err != nil { 245 s.Log.Error("Failed to commit recent state trie", "err", err) 246 } 247 } 248 } 249 // Ensure all live cached entries be saved into disk, so that we can skip 250 // cache warmup when node restarts. 251 if s.cfg.Cache.TrieCleanJournal != "" { 252 triedb := s.EvmState.TrieDB() 253 triedb.SaveCache(s.cfg.Cache.TrieCleanJournal) 254 } 255 } 256 257 // Cap flush matured singleton nodes to disk 258 func (s *Store) Cap() { 259 triedb := s.EvmState.TrieDB() 260 var ( 261 nodes, imgs = triedb.Size() 262 limit = common.StorageSize(s.cfg.Cache.TrieDirtyLimit) 263 ) 264 // If we exceeded our memory allowance, flush matured singleton nodes to disk 265 if nodes > limit+ethdb.IdealBatchSize || imgs > 4*1024*1024 { 266 triedb.Cap(limit) 267 } 268 } 269 270 // StateDB returns state database. 271 func (s *Store) StateDB(from hash.Hash) (*state.StateDB, error) { 272 return state.NewWithSnapLayers(common.Hash(from), s.EvmState, s.Snaps, 0) 273 } 274 275 // HasStateDB returns if state database exists 276 func (s *Store) HasStateDB(from hash.Hash) bool { 277 _, err := s.StateDB(from) 278 return err == nil 279 } 280 281 // IndexLogs indexes EVM logs 282 func (s *Store) IndexLogs(recs ...*types.Log) { 283 err := s.EvmLogs.Push(recs...) 284 if err != nil { 285 s.Log.Crit("DB logs index error", "err", err) 286 } 287 } 288 289 func (s *Store) Snapshots() *snapshot.Tree { 290 return s.Snaps 291 } 292 293 /* 294 * Utils: 295 */ 296 297 func (s *Store) makeCache(weight uint, size int) *wlru.Cache { 298 cache, err := wlru.New(weight, size) 299 if err != nil { 300 s.Log.Crit("Failed to create LRU cache", "err", err) 301 return nil 302 } 303 return cache 304 }