github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/store/mpt/store.go (about) 1 package mpt 2 3 import ( 4 "fmt" 5 "io" 6 "sync" 7 8 "github.com/VictoriaMetrics/fastcache" 9 ethcmn "github.com/ethereum/go-ethereum/common" 10 "github.com/ethereum/go-ethereum/common/prque" 11 "github.com/ethereum/go-ethereum/core/rawdb" 12 ethstate "github.com/ethereum/go-ethereum/core/state" 13 ethtypes "github.com/ethereum/go-ethereum/core/types" 14 "github.com/ethereum/go-ethereum/crypto" 15 "github.com/ethereum/go-ethereum/ethdb" 16 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 17 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/cachekv" 18 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/tracekv" 19 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/types" 20 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 21 "github.com/fibonacci-chain/fbc/libs/iavl" 22 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 23 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto/merkle" 24 tmlog "github.com/fibonacci-chain/fbc/libs/tendermint/libs/log" 25 tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types" 26 ) 27 28 const ( 29 FlagTrieAccStoreCache = "trie.account-store-cache" 30 ) 31 32 var ( 33 TrieAccStoreCache uint = 32 // MB 34 ) 35 36 var cdc = codec.New() 37 38 var ( 39 _ types.KVStore = (*MptStore)(nil) 40 _ types.CommitStore = (*MptStore)(nil) 41 _ types.CommitKVStore = (*MptStore)(nil) 42 _ types.Queryable = (*MptStore)(nil) 43 ) 44 45 // MptStore Implements types.KVStore and CommitKVStore. 46 // Its main purpose is to own the same interface as iavl store in libs/cosmos-sdk/store/iavl/iavl_store.go 47 type MptStore struct { 48 trie ethstate.Trie 49 db ethstate.Database 50 triegc *prque.Prque 51 logger tmlog.Logger 52 kvCache *fastcache.Cache 53 54 prefetcher *TriePrefetcher 55 originalRoot ethcmn.Hash 56 exitSignal chan struct{} 57 58 version int64 59 startVersion int64 60 cmLock sync.Mutex 61 } 62 63 func (ms *MptStore) CommitterCommitMap(deltaMap iavl.TreeDeltaMap) (_ types.CommitID, _ iavl.TreeDeltaMap) { 64 return 65 } 66 67 func (ms *MptStore) GetFlatKVReadTime() int { 68 return 0 69 } 70 71 func (ms *MptStore) GetFlatKVWriteTime() int { 72 return 0 73 } 74 75 func (ms *MptStore) GetFlatKVReadCount() int { 76 return 0 77 } 78 79 func (ms *MptStore) GetFlatKVWriteCount() int { 80 return 0 81 } 82 83 func NewMptStore(logger tmlog.Logger, id types.CommitID) (*MptStore, error) { 84 db := InstanceOfMptStore() 85 return generateMptStore(logger, id, db) 86 } 87 88 func generateMptStore(logger tmlog.Logger, id types.CommitID, db ethstate.Database) (*MptStore, error) { 89 triegc := prque.New(nil) 90 mptStore := &MptStore{ 91 db: db, 92 triegc: triegc, 93 logger: logger, 94 kvCache: fastcache.New(int(TrieAccStoreCache) * 1024 * 1024), 95 exitSignal: make(chan struct{}), 96 } 97 err := mptStore.openTrie(id) 98 99 return mptStore, err 100 } 101 102 func mockMptStore(logger tmlog.Logger, id types.CommitID) (*MptStore, error) { 103 db := ethstate.NewDatabase(rawdb.NewMemoryDatabase()) 104 return generateMptStore(logger, id, db) 105 } 106 107 func (ms *MptStore) openTrie(id types.CommitID) error { 108 latestStoredHeight := ms.GetLatestStoredBlockHeight() 109 openHeight := uint64(id.Version) 110 if latestStoredHeight > 0 && openHeight > latestStoredHeight { 111 return fmt.Errorf("fail to open mpt trie, the target version is: %d, the latest stored version is: %d, "+ 112 "please repair", openHeight, latestStoredHeight) 113 } 114 115 openedRootHash := ms.GetMptRootHash(openHeight) 116 tr, err := ms.db.OpenTrie(openedRootHash) 117 if err != nil { 118 panic("Fail to open root mpt: " + err.Error()) 119 } 120 121 ms.trie = tr 122 ms.version = id.Version 123 ms.startVersion = id.Version 124 ms.originalRoot = openedRootHash 125 126 if ms.logger != nil { 127 ms.logger.Info("open acc mpt trie", "version", openHeight, "trieHash", openedRootHash) 128 } 129 130 ms.StartPrefetcher("mptStore") 131 ms.prefetchData() 132 133 return nil 134 } 135 136 func (ms *MptStore) GetImmutable(height int64) (*MptStore, error) { 137 rootHash := ms.GetMptRootHash(uint64(height)) 138 tr, err := ms.db.OpenTrie(rootHash) 139 if err != nil { 140 return nil, fmt.Errorf("Fail to open root mpt: " + err.Error()) 141 } 142 mptStore := &MptStore{ 143 db: ms.db, 144 trie: tr, 145 originalRoot: rootHash, 146 exitSignal: make(chan struct{}), 147 version: height, 148 startVersion: height, 149 } 150 151 return mptStore, nil 152 } 153 154 /* 155 * implement KVStore 156 */ 157 func (ms *MptStore) GetStoreType() types.StoreType { 158 return StoreTypeMPT 159 } 160 161 func (ms *MptStore) CacheWrap() types.CacheWrap { 162 //TODO implement me 163 return cachekv.NewStore(ms) 164 } 165 166 func (ms *MptStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { 167 //TODO implement me 168 return cachekv.NewStore(tracekv.NewStore(ms, w, tc)) 169 } 170 171 func (ms *MptStore) Get(key []byte) []byte { 172 if ms.kvCache != nil { 173 if enc := ms.kvCache.Get(nil, key); len(enc) > 0 { 174 return enc 175 } 176 } 177 178 value, err := ms.trie.TryGet(key) 179 if err != nil { 180 return nil 181 } 182 if ms.kvCache != nil && value != nil { 183 ms.kvCache.Set(key, value) 184 } 185 186 return value 187 } 188 189 func (ms *MptStore) Has(key []byte) bool { 190 if ms.kvCache != nil { 191 if ms.kvCache.Has(key) { 192 return true 193 } 194 } 195 196 return ms.Get(key) != nil 197 } 198 199 func (ms *MptStore) Set(key, value []byte) { 200 types.AssertValidValue(value) 201 202 if ms.prefetcher != nil { 203 ms.prefetcher.Used(ms.originalRoot, [][]byte{key}) 204 } 205 if ms.kvCache != nil { 206 ms.kvCache.Set(key, value) 207 } 208 err := ms.trie.TryUpdate(key, value) 209 if err != nil { 210 return 211 } 212 return 213 } 214 215 func (ms *MptStore) Delete(key []byte) { 216 if ms.prefetcher != nil { 217 ms.prefetcher.Used(ms.originalRoot, [][]byte{key}) 218 } 219 220 if ms.kvCache != nil { 221 ms.kvCache.Del(key) 222 } 223 err := ms.trie.TryDelete(key) 224 if err != nil { 225 return 226 } 227 } 228 229 func (ms *MptStore) Iterator(start, end []byte) types.Iterator { 230 return newMptIterator(ms.trie, start, end) 231 } 232 233 func (ms *MptStore) ReverseIterator(start, end []byte) types.Iterator { 234 return newMptIterator(ms.trie, start, end) 235 } 236 237 /* 238 * implement CommitStore, CommitKVStore 239 */ 240 func (ms *MptStore) CommitterCommit(delta *iavl.TreeDelta) (types.CommitID, *iavl.TreeDelta) { 241 ms.version++ 242 243 // stop pre round prefetch 244 ms.StopPrefetcher() 245 246 root, err := ms.trie.Commit(nil) 247 if err != nil { 248 panic("fail to commit trie data: " + err.Error()) 249 } 250 ms.SetMptRootHash(uint64(ms.version), root) 251 ms.originalRoot = root 252 253 // TODO: use a thread to push data to database 254 // push data to database 255 ms.PushData2Database(ms.version) 256 257 // start next found prefetch 258 ms.StartPrefetcher("mptStore") 259 260 return types.CommitID{ 261 Version: ms.version, 262 Hash: root.Bytes(), 263 }, nil 264 } 265 266 func (ms *MptStore) LastCommitID() types.CommitID { 267 return types.CommitID{ 268 Version: ms.version, 269 Hash: ms.trie.Hash().Bytes(), 270 } 271 } 272 273 func (ms *MptStore) LastCommitVersion() int64 { 274 return ms.version 275 } 276 277 func (ms *MptStore) SetPruning(options types.PruningOptions) { 278 panic("cannot set pruning options on an initialized MPT store") 279 } 280 281 func (ms *MptStore) GetDBWriteCount() int { 282 return 0 283 } 284 285 func (ms *MptStore) GetDBReadCount() int { 286 return 0 287 } 288 289 func (ms *MptStore) GetNodeReadCount() int { 290 return 0 291 } 292 293 func (ms *MptStore) ResetCount() { 294 return 295 } 296 297 func (ms *MptStore) GetDBReadTime() int { 298 return 0 299 } 300 301 // PushData2Database writes all associated state in cache to the database 302 func (ms *MptStore) PushData2Database(curHeight int64) { 303 ms.cmLock.Lock() 304 defer ms.cmLock.Unlock() 305 306 curMptRoot := ms.GetMptRootHash(uint64(curHeight)) 307 if TrieDirtyDisabled { 308 // If we're running an archive node, always flush 309 ms.fullNodePersist(curMptRoot, curHeight) 310 } else { 311 ms.otherNodePersist(curMptRoot, curHeight) 312 } 313 } 314 315 // fullNodePersist persist data without pruning 316 func (ms *MptStore) fullNodePersist(curMptRoot ethcmn.Hash, curHeight int64) { 317 if curMptRoot == (ethcmn.Hash{}) || curMptRoot == ethtypes.EmptyRootHash { 318 curMptRoot = ethcmn.Hash{} 319 } else { 320 if err := ms.db.TrieDB().Commit(curMptRoot, false, nil); err != nil { 321 panic("fail to commit mpt data: " + err.Error()) 322 } 323 } 324 ms.SetLatestStoredBlockHeight(uint64(curHeight)) 325 if ms.logger != nil { 326 ms.logger.Info("sync push acc data to db", "block", curHeight, "trieHash", curMptRoot) 327 } 328 } 329 330 // otherNodePersist persist data with pruning 331 func (ms *MptStore) otherNodePersist(curMptRoot ethcmn.Hash, curHeight int64) { 332 triedb := ms.db.TrieDB() 333 334 // Full but not archive node, do proper garbage collection 335 triedb.Reference(curMptRoot, ethcmn.Hash{}) // metadata reference to keep trie alive 336 ms.triegc.Push(curMptRoot, -int64(curHeight)) 337 338 if curHeight > TriesInMemory { 339 // If we exceeded our memory allowance, flush matured singleton nodes to disk 340 var ( 341 nodes, imgs = triedb.Size() 342 nodesLimit = ethcmn.StorageSize(TrieNodesLimit) * 1024 * 1024 343 imgsLimit = ethcmn.StorageSize(TrieImgsLimit) * 1024 * 1024 344 ) 345 346 if nodes > nodesLimit || imgs > imgsLimit { 347 triedb.Cap(nodesLimit - ethdb.IdealBatchSize) 348 } 349 // Find the next state trie we need to commit 350 chosen := curHeight - TriesInMemory 351 352 // we start at startVersion, but the chosen height may be startVersion - triesInMemory 353 if chosen <= ms.startVersion { 354 return 355 } 356 357 // If we exceeded out time allowance, flush an entire trie to disk 358 if chosen%TrieCommitGap == 0 { 359 // If the header is missing (canonical chain behind), we're reorging a low 360 // diff sidechain. Suspend committing until this operation is completed. 361 chRoot := ms.GetMptRootHash(uint64(chosen)) 362 if chRoot == (ethcmn.Hash{}) || chRoot == ethtypes.EmptyRootHash { 363 chRoot = ethcmn.Hash{} 364 } else { 365 // Flush an entire trie and restart the counters, it's not a thread safe process, 366 // cannot use a go thread to run, or it will lead 'fatal error: concurrent map read and map write' error 367 if err := triedb.Commit(chRoot, true, nil); err != nil { 368 panic("fail to commit mpt data: " + err.Error()) 369 } 370 } 371 ms.SetLatestStoredBlockHeight(uint64(chosen)) 372 if ms.logger != nil { 373 ms.logger.Info("async push acc data to db", "block", chosen, "trieHash", chRoot) 374 } 375 } 376 377 // Garbage collect anything below our required write retention 378 for !ms.triegc.Empty() { 379 root, number := ms.triegc.Pop() 380 if int64(-number) > chosen { 381 ms.triegc.Push(root, number) 382 break 383 } 384 triedb.Dereference(root.(ethcmn.Hash)) 385 } 386 } 387 } 388 func (ms *MptStore) CurrentVersion() int64 { 389 return ms.version 390 } 391 392 func (ms *MptStore) OnStop() error { 393 return ms.StopWithVersion(ms.version) 394 } 395 396 // Stop stops the blockchain service. If any imports are currently in progress 397 // it will abort them using the procInterrupt. 398 func (ms *MptStore) StopWithVersion(targetVersion int64) error { 399 curVersion := uint64(targetVersion) 400 ms.exitSignal <- struct{}{} 401 ms.StopPrefetcher() 402 403 ms.cmLock.Lock() 404 defer ms.cmLock.Unlock() 405 406 if !tmtypes.HigherThanMars(ms.version) && !TrieWriteAhead { 407 return nil 408 } 409 410 // Ensure the state of a recent block is also stored to disk before exiting. 411 if !TrieDirtyDisabled { 412 triedb := ms.db.TrieDB() 413 oecStartHeight := uint64(tmtypes.GetStartBlockHeight()) // start height of oec 414 415 latestStoreVersion := ms.GetLatestStoredBlockHeight() 416 417 for version := latestStoreVersion; version <= curVersion; version++ { 418 if version <= oecStartHeight || version <= uint64(ms.startVersion) { 419 continue 420 } 421 422 recentMptRoot := ms.GetMptRootHash(version) 423 if recentMptRoot == (ethcmn.Hash{}) || recentMptRoot == ethtypes.EmptyRootHash { 424 recentMptRoot = ethcmn.Hash{} 425 } else { 426 if err := triedb.Commit(recentMptRoot, true, nil); err != nil { 427 if ms.logger != nil { 428 ms.logger.Error("Failed to commit recent state trie", "err", err) 429 } 430 break 431 } 432 } 433 ms.SetLatestStoredBlockHeight(version) 434 if ms.logger != nil { 435 ms.logger.Info("Writing acc cached state to disk", "block", version, "trieHash", recentMptRoot) 436 } 437 } 438 439 for !ms.triegc.Empty() { 440 ms.db.TrieDB().Dereference(ms.triegc.PopItem().(ethcmn.Hash)) 441 } 442 } 443 444 return nil 445 } 446 447 /* 448 * implement Queryable 449 */ 450 func (ms *MptStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { 451 if len(req.Data) == 0 { 452 return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrTxDecode, "query cannot be zero length")) 453 } 454 455 height := ms.getlatestHeight(uint64(req.Height)) 456 res.Height = int64(height) 457 458 // store the height we chose in the response, with 0 being changed to the 459 // latest height 460 trie, err := ms.getTrieByHeight(height) 461 if err != nil { 462 return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrInvalidVersion, "open trie failed: %s", err.Error())) 463 } 464 465 switch req.Path { 466 case "/key": // get by key 467 key := req.Data // data holds the key bytes 468 469 res.Key = key 470 if req.Prove { 471 value, proof, err := getVersionedWithProof(trie, key) 472 if err != nil { 473 res.Log = err.Error() 474 break 475 } 476 if proof == nil { 477 // Proof == nil implies that the store is empty. 478 if value != nil { 479 panic("unexpected value for an empty proof") 480 } 481 } 482 if value != nil { 483 // value was found 484 res.Value = value 485 res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{newProofOpMptValue(key, proof)}} 486 } else { 487 // value wasn't found 488 res.Value = nil 489 res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{newProofOpMptAbsence(key, proof)}} 490 } 491 } else { 492 res.Value, err = trie.TryGet(key) 493 if err != nil { 494 return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrKeyNotFound, "failed to query in trie: %s", err.Error())) 495 } 496 } 497 498 case "/subspace": 499 return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "not supported query subspace path: %v in mptStore", req.Path)) 500 501 default: 502 return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unexpected query path: %v", req.Path)) 503 } 504 505 return res 506 } 507 508 // Handle latest the latest height - 1 (committed), if height is 0 509 func (ms *MptStore) getlatestHeight(height uint64) uint64 { 510 if height == 0 { 511 height = uint64(ms.version) 512 } 513 return height 514 } 515 516 func (ms *MptStore) getTrieByHeight(height uint64) (ethstate.Trie, error) { 517 latestRootHash := ms.GetMptRootHash(height) 518 if latestRootHash == NilHash { 519 return nil, fmt.Errorf("header %d not found", height) 520 } 521 return ms.db.OpenTrie(latestRootHash) 522 } 523 524 // getVersionedWithProof returns the Merkle proof for given storage slot. 525 func getVersionedWithProof(trie ethstate.Trie, key []byte) ([]byte, [][]byte, error) { 526 value, err := trie.TryGet(key) 527 if err != nil { 528 return nil, nil, err 529 } 530 531 var proof ProofList 532 err = trie.Prove(crypto.Keccak256(key), 0, &proof) 533 return value, proof, err 534 } 535 536 func (ms *MptStore) StartPrefetcher(namespace string) { 537 if !tmtypes.HigherThanMars(ms.version) { 538 return 539 } 540 541 if ms.prefetcher != nil { 542 ms.prefetcher.Close() 543 ms.prefetcher = nil 544 } 545 546 ms.prefetcher = NewTriePrefetcher(ms.db, ms.originalRoot, namespace) 547 } 548 549 // StopPrefetcher terminates a running prefetcher and reports any leftover stats 550 // from the gathered metrics. 551 func (ms *MptStore) StopPrefetcher() { 552 if ms.prefetcher != nil { 553 ms.prefetcher.Close() 554 ms.prefetcher = nil 555 } 556 } 557 558 func (ms *MptStore) prefetchData() { 559 go func() { 560 for { 561 select { 562 case <-ms.exitSignal: 563 return 564 case <-GAccTryUpdateTrieChannel: 565 if ms.prefetcher != nil { 566 if trie := ms.prefetcher.Trie(ms.originalRoot); trie != nil { 567 ms.trie = trie 568 } 569 } 570 GAccTrieUpdatedChannel <- struct{}{} 571 case addr := <-GAccToPrefetchChannel: 572 if ms.prefetcher != nil { 573 ms.prefetcher.Prefetch(ms.originalRoot, addr) 574 } 575 } 576 } 577 }() 578 } 579 580 func (ms *MptStore) SetUpgradeVersion(i int64) {}