github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/store.go (about) 1 package gossip 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "time" 7 8 "github.com/unicornultrafoundation/go-u2u/common" 9 "github.com/unicornultrafoundation/go-u2u/log" 10 11 "github.com/unicornultrafoundation/go-helios/common/bigendian" 12 "github.com/unicornultrafoundation/go-helios/u2udb" 13 "github.com/unicornultrafoundation/go-helios/u2udb/flushable" 14 "github.com/unicornultrafoundation/go-helios/u2udb/memorydb" 15 "github.com/unicornultrafoundation/go-helios/u2udb/table" 16 "github.com/unicornultrafoundation/go-helios/utils/wlru" 17 "github.com/unicornultrafoundation/go-u2u/gossip/evmstore" 18 txtracer "github.com/unicornultrafoundation/go-u2u/gossip/txtracer" 19 "github.com/unicornultrafoundation/go-u2u/logger" 20 "github.com/unicornultrafoundation/go-u2u/utils/adapters/snap2udb" 21 "github.com/unicornultrafoundation/go-u2u/utils/dbutil/switchable" 22 "github.com/unicornultrafoundation/go-u2u/utils/eventid" 23 "github.com/unicornultrafoundation/go-u2u/utils/randat" 24 "github.com/unicornultrafoundation/go-u2u/utils/rlpstore" 25 ) 26 27 // Store is a node persistent storage working over physical key-value database. 28 type Store struct { 29 dbs u2udb.FlushableDBProducer 30 cfg StoreConfig 31 32 snapshotedEVMDB *switchable.Snapshot 33 evm *evmstore.Store 34 txtracer *txtracer.Store 35 table struct { 36 Version u2udb.Store `table:"_"` 37 38 // Main DAG tables 39 BlockEpochState u2udb.Store `table:"D"` 40 BlockEpochStateHistory u2udb.Store `table:"h"` 41 Events u2udb.Store `table:"e"` 42 Blocks u2udb.Store `table:"b"` 43 EpochBlocks u2udb.Store `table:"P"` 44 Genesis u2udb.Store `table:"g"` 45 UpgradeHeights u2udb.Store `table:"U"` 46 47 // Transaction traces 48 TransactionTraces u2udb.Store `table:"t"` 49 50 // P2P-only 51 HighestLamport u2udb.Store `table:"l"` 52 53 // Network version 54 NetworkVersion u2udb.Store `table:"V"` 55 56 // API-only 57 BlockHashes u2udb.Store `table:"B"` 58 59 LlrState u2udb.Store `table:"S"` 60 LlrBlockResults u2udb.Store `table:"R"` 61 LlrEpochResults u2udb.Store `table:"Q"` 62 LlrBlockVotes u2udb.Store `table:"T"` 63 LlrBlockVotesIndex u2udb.Store `table:"J"` 64 LlrEpochVotes u2udb.Store `table:"E"` 65 LlrEpochVoteIndex u2udb.Store `table:"I"` 66 LlrLastBlockVotes u2udb.Store `table:"G"` 67 LlrLastEpochVote u2udb.Store `table:"F"` 68 } 69 70 prevFlushTime time.Time 71 72 epochStore atomic.Value 73 74 cache struct { 75 Events *wlru.Cache `cache:"-"` // store by pointer 76 EventIDs *eventid.Cache 77 EventsHeaders *wlru.Cache `cache:"-"` // store by pointer 78 Blocks *wlru.Cache `cache:"-"` // store by pointer 79 BlockHashes *wlru.Cache `cache:"-"` // store by value 80 BlockRecordHashes *wlru.Cache `cache:"-"` // store by value 81 EvmBlocks *wlru.Cache `cache:"-"` // store by pointer 82 BlockEpochStateHistory *wlru.Cache `cache:"-"` // store by pointer 83 BlockEpochState atomic.Value // store by value 84 HighestLamport atomic.Value // store by value 85 LastBVs atomic.Value // store by pointer 86 LastEV atomic.Value // store by pointer 87 LlrState atomic.Value // store by value 88 KvdbEvmSnap atomic.Value // store by pointer 89 UpgradeHeights atomic.Value // store by pointer 90 Genesis atomic.Value // store by value 91 LlrBlockVotesIndex *VotesCache // store by pointer 92 LlrEpochVoteIndex *VotesCache // store by pointer 93 } 94 95 mutex struct { 96 WriteLlrState sync.Mutex 97 } 98 99 rlp rlpstore.Helper 100 101 logger.Instance 102 } 103 104 // NewMemStore creates store over memory map. 105 func NewMemStore() *Store { 106 mems := memorydb.NewProducer("") 107 dbs := flushable.NewSyncedPool(mems, []byte{0}) 108 cfg := LiteStoreConfig() 109 110 return NewStore(dbs, cfg) 111 } 112 113 // NewStore creates store over key-value db. 114 func NewStore(dbs u2udb.FlushableDBProducer, cfg StoreConfig) *Store { 115 s := &Store{ 116 dbs: dbs, 117 cfg: cfg, 118 Instance: logger.New("gossip-store"), 119 prevFlushTime: time.Now(), 120 rlp: rlpstore.Helper{logger.New("rlp")}, 121 } 122 123 err := table.OpenTables(&s.table, dbs, "gossip") 124 if err != nil { 125 log.Crit("Failed to open DB", "name", "gossip", "err", err) 126 } 127 128 s.initCache() 129 s.evm = evmstore.NewStore(dbs, cfg.EVM) 130 131 if cfg.TraceTransactions { 132 s.txtracer = txtracer.NewStore(s.table.TransactionTraces) 133 } 134 135 if err := s.migrateData(); err != nil { 136 s.Log.Crit("Failed to migrate Gossip DB", "err", err) 137 } 138 139 return s 140 } 141 142 func (s *Store) initCache() { 143 s.cache.Events = s.makeCache(s.cfg.Cache.EventsSize, s.cfg.Cache.EventsNum) 144 s.cache.Blocks = s.makeCache(s.cfg.Cache.BlocksSize, s.cfg.Cache.BlocksNum) 145 146 blockHashesNum := s.cfg.Cache.BlocksNum 147 blockHashesCacheSize := nominalSize * uint(blockHashesNum) 148 s.cache.BlockHashes = s.makeCache(blockHashesCacheSize, blockHashesNum) 149 s.cache.BlockRecordHashes = s.makeCache(blockHashesCacheSize, blockHashesNum) 150 151 eventsHeadersNum := s.cfg.Cache.EventsNum 152 eventsHeadersCacheSize := nominalSize * uint(eventsHeadersNum) 153 s.cache.EventsHeaders = s.makeCache(eventsHeadersCacheSize, eventsHeadersNum) 154 155 s.cache.EventIDs = eventid.NewCache(s.cfg.Cache.EventsIDsNum) 156 157 blockEpochStatesNum := s.cfg.Cache.BlockEpochStateNum 158 blockEpochStatesSize := nominalSize * uint(blockEpochStatesNum) 159 s.cache.BlockEpochStateHistory = s.makeCache(blockEpochStatesSize, blockEpochStatesNum) 160 161 s.cache.LlrBlockVotesIndex = NewVotesCache(s.cfg.Cache.LlrBlockVotesIndexes, s.flushLlrBlockVoteWeight) 162 s.cache.LlrEpochVoteIndex = NewVotesCache(s.cfg.Cache.LlrEpochVotesIndexes, s.flushLlrEpochVoteWeight) 163 } 164 165 // Close closes underlying database. 166 func (s *Store) Close() { 167 setnil := func() interface{} { 168 return nil 169 } 170 171 if s.snapshotedEVMDB != nil { 172 s.snapshotedEVMDB.Release() 173 } 174 _ = table.CloseTables(&s.table) 175 table.MigrateTables(&s.table, nil) 176 table.MigrateCaches(&s.cache, setnil) 177 178 if s.txtracer != nil { 179 s.txtracer.Close() 180 } 181 _ = s.closeEpochStore() 182 s.evm.Close() 183 } 184 185 func (s *Store) IsCommitNeeded() bool { 186 // randomize flushing criteria for each epoch so that nodes would desynchronize flushes 187 ratio := 900 + randat.RandAt(uint64(s.GetEpoch()))%100 188 return s.isCommitNeeded(ratio, ratio) 189 } 190 191 func (s *Store) isCommitNeeded(sc, tc uint64) bool { 192 period := s.cfg.MaxNonFlushedPeriod * time.Duration(sc) / 1000 193 size := (uint64(s.cfg.MaxNonFlushedSize) / 2) * tc / 1000 194 return time.Since(s.prevFlushTime) > period || 195 uint64(s.dbs.NotFlushedSizeEst()) > size 196 } 197 198 // commitEVM commits EVM storage 199 func (s *Store) commitEVM(flush bool) { 200 bs := s.GetBlockState() 201 err := s.evm.Commit(bs.LastBlock.Idx, bs.FinalizedStateRoot, flush) 202 if err != nil { 203 s.Log.Crit("Failed to commit EVM storage", "err", err) 204 } 205 s.evm.Cap() 206 } 207 208 func (s *Store) cleanCommitEVM() { 209 err := s.evm.CleanCommit(s.GetBlockState()) 210 if err != nil { 211 s.Log.Crit("Failed to commit EVM storage", "err", err) 212 } 213 s.evm.Cap() 214 } 215 216 func (s *Store) GenerateSnapshotAt(root common.Hash, async bool) (err error) { 217 err = s.generateSnapshotAt(s.evm, root, true, async) 218 if err != nil { 219 s.Log.Error("EVM snapshot", "at", root, "err", err) 220 } else { 221 gen, _ := s.evm.Snaps.Generating() 222 s.Log.Info("EVM snapshot", "at", root, "generating", gen) 223 } 224 return err 225 } 226 227 func (s *Store) generateSnapshotAt(evmStore *evmstore.Store, root common.Hash, rebuild, async bool) (err error) { 228 return evmStore.GenerateEvmSnapshot(root, rebuild, async) 229 } 230 231 // Commit changes. 232 func (s *Store) Commit() error { 233 s.FlushBlockEpochState() 234 s.FlushHighestLamport() 235 s.FlushLastBVs() 236 s.FlushLastEV() 237 s.FlushLlrState() 238 s.cache.LlrBlockVotesIndex.FlushMutated(s.flushLlrBlockVoteWeight) 239 s.cache.LlrEpochVoteIndex.FlushMutated(s.flushLlrEpochVoteWeight) 240 es := s.getAnyEpochStore() 241 if es != nil { 242 es.FlushHeads() 243 es.FlushLastEvents() 244 } 245 return s.flushDBs() 246 } 247 248 func (s *Store) flushDBs() error { 249 s.prevFlushTime = time.Now() 250 flushID := bigendian.Uint64ToBytes(uint64(s.prevFlushTime.UnixNano())) 251 return s.dbs.Flush(flushID) 252 } 253 254 func (s *Store) EvmStore() *evmstore.Store { 255 return s.evm 256 } 257 258 func (s *Store) TxTraceStore() *txtracer.Store { 259 return s.txtracer 260 } 261 262 func (s *Store) CaptureEvmKvdbSnapshot() { 263 if s.evm.Snaps == nil { 264 return 265 } 266 gen, err := s.evm.Snaps.Generating() 267 if err != nil { 268 s.Log.Error("Failed to check EVM snapshot generation", "err", err) 269 return 270 } 271 if gen { 272 return 273 } 274 newEvmKvdbSnap, err := s.evm.EVMDB().GetSnapshot() 275 if err != nil { 276 s.Log.Error("Failed to initialize frozen KVDB", "err", err) 277 return 278 } 279 if s.snapshotedEVMDB == nil { 280 s.snapshotedEVMDB = switchable.Wrap(newEvmKvdbSnap) 281 } else { 282 old := s.snapshotedEVMDB.SwitchTo(newEvmKvdbSnap) 283 // release only after DB is atomically switched 284 if old != nil { 285 old.Release() 286 } 287 } 288 newStore := s.evm.ResetWithEVMDB(snap2udb.Wrap(s.snapshotedEVMDB)) 289 newStore.Snaps = nil 290 root := s.GetBlockState().FinalizedStateRoot 291 err = s.generateSnapshotAt(newStore, common.Hash(root), false, false) 292 if err != nil { 293 s.Log.Error("Failed to initialize EVM snapshot for frozen KVDB", "err", err) 294 return 295 } 296 s.cache.KvdbEvmSnap.Store(newStore) 297 } 298 299 func (s *Store) LastKvdbEvmSnapshot() *evmstore.Store { 300 if v := s.cache.KvdbEvmSnap.Load(); v != nil { 301 return v.(*evmstore.Store) 302 } 303 return s.evm 304 } 305 306 /* 307 * Utils: 308 */ 309 310 func (s *Store) makeCache(weight uint, size int) *wlru.Cache { 311 cache, err := wlru.New(weight, size) 312 if err != nil { 313 s.Log.Crit("Failed to create LRU cache", "err", err) 314 return nil 315 } 316 return cache 317 }