github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/store/rootmulti/rootmulti_store.go (about) 1 package rootmulti 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 "log" 8 "path/filepath" 9 "sort" 10 "strings" 11 "sync" 12 "time" 13 14 cfg "github.com/fibonacci-chain/fbc/libs/tendermint/config" 15 16 sdkmaps "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/internal/maps" 17 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/mem" 18 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/mpt" 19 "github.com/fibonacci-chain/fbc/libs/system/trace" 20 "github.com/fibonacci-chain/fbc/libs/system/trace/persist" 21 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto/merkle" 22 23 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/cachemulti" 24 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/dbadapter" 25 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/flatkv" 26 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/iavl" 27 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/tracekv" 28 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/transient" 29 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/types" 30 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 31 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 32 iavltree "github.com/fibonacci-chain/fbc/libs/iavl" 33 "github.com/fibonacci-chain/fbc/libs/iavl/config" 34 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 35 jsoniter "github.com/json-iterator/go" 36 37 //"github.com/fibonacci-chain/fbc/libs/tendermint/crypto/merkle" 38 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto/tmhash" 39 tmlog "github.com/fibonacci-chain/fbc/libs/tendermint/libs/log" 40 tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types" 41 dbm "github.com/fibonacci-chain/fbc/libs/tm-db" 42 "github.com/pkg/errors" 43 "github.com/spf13/viper" 44 ) 45 46 var itjs = jsoniter.ConfigCompatibleWithStandardLibrary 47 48 const ( 49 latestVersionKey = "s/latest" 50 pruneHeightsKey = "s/pruneheights" 51 versionsKey = "s/versions" 52 commitInfoKeyFmt = "s/%d" // s/<version> 53 maxPruneHeightsLength = 100 54 ) 55 56 // Store is composed of many CommitStores. Name contrasts with 57 // cacheMultiStore which is for cache-wrapping other MultiStores. It implements 58 // the CommitMultiStore interface. 59 type Store struct { 60 db dbm.DB 61 flatKVDB dbm.DB 62 lastCommitInfo commitInfo 63 pruningOpts types.PruningOptions 64 storesParams map[types.StoreKey]storeParams 65 stores map[types.StoreKey]types.CommitKVStore 66 keysByName map[string]types.StoreKey 67 lazyLoading bool 68 pruneHeights []int64 69 versions []int64 70 71 traceWriter io.Writer 72 traceContext types.TraceContext 73 74 interBlockCache types.MultiStorePersistentCache 75 76 logger tmlog.Logger 77 78 upgradeVersion int64 79 80 commitFilters []types.StoreFilter 81 pruneFilters []types.StoreFilter 82 versionFilters []types.VersionFilter 83 } 84 85 var ( 86 _ types.CommitMultiStore = (*Store)(nil) 87 _ types.Queryable = (*Store)(nil) 88 ) 89 90 // NewStore returns a reference to a new Store object with the provided DB. The 91 // store will be created with a PruneNothing pruning strategy by default. After 92 // a store is created, KVStores must be mounted and finally LoadLatestVersion or 93 // LoadVersion must be called. 94 func NewStore(db dbm.DB) *Store { 95 var flatKVDB dbm.DB 96 if viper.GetBool(flatkv.FlagEnable) { 97 flatKVDB = newFlatKVDB() 98 } 99 ret := &Store{ 100 db: db, 101 flatKVDB: flatKVDB, 102 pruningOpts: types.PruneNothing, 103 storesParams: make(map[types.StoreKey]storeParams), 104 stores: make(map[types.StoreKey]types.CommitKVStore), 105 keysByName: make(map[string]types.StoreKey), 106 pruneHeights: make([]int64, 0), 107 versions: make([]int64, 0), 108 upgradeVersion: -1, 109 } 110 111 return ret 112 } 113 114 func newFlatKVDB() dbm.DB { 115 rootDir := viper.GetString("home") 116 dataDir := filepath.Join(rootDir, "data") 117 var err error 118 flatKVDB, err := sdk.NewDB("flat", dataDir) 119 if err != nil { 120 panic(err) 121 } 122 return flatKVDB 123 } 124 125 // SetPruning sets the pruning strategy on the root store and all the sub-stores. 126 // Note, calling SetPruning on the root store prior to LoadVersion or 127 // LoadLatestVersion performs a no-op as the stores aren't mounted yet. 128 func (rs *Store) SetPruning(pruningOpts types.PruningOptions) { 129 rs.pruningOpts = pruningOpts 130 } 131 132 // SetLazyLoading sets if the iavl store should be loaded lazily or not 133 func (rs *Store) SetLazyLoading(lazyLoading bool) { 134 rs.lazyLoading = lazyLoading 135 } 136 137 // Implements Store. 138 func (rs *Store) GetStoreType() types.StoreType { 139 return types.StoreTypeMulti 140 } 141 142 func (rs *Store) GetStores() map[types.StoreKey]types.CommitKVStore { 143 return rs.stores 144 } 145 146 func (rs *Store) GetVersions() []int64 { 147 return rs.versions 148 } 149 150 func (rs *Store) GetPruningHeights() []int64 { 151 return rs.pruneHeights 152 } 153 154 // Implements CommitMultiStore. 155 func (rs *Store) MountStoreWithDB(key types.StoreKey, typ types.StoreType, db dbm.DB) { 156 if key == nil { 157 panic("MountIAVLStore() key cannot be nil") 158 } 159 if _, ok := rs.storesParams[key]; ok { 160 panic(fmt.Sprintf("Store duplicate store key %v", key)) 161 } 162 if _, ok := rs.keysByName[key.Name()]; ok { 163 panic(fmt.Sprintf("Store duplicate store key name %v", key)) 164 } 165 rs.storesParams[key] = storeParams{ 166 key: key, 167 typ: typ, 168 db: db, 169 } 170 rs.keysByName[key.Name()] = key 171 } 172 173 // GetCommitStore returns a mounted CommitStore for a given StoreKey. If the 174 // store is wrapped in an inter-block cache, it will be unwrapped before returning. 175 func (rs *Store) GetCommitStore(key types.StoreKey) types.CommitStore { 176 return rs.GetCommitKVStore(key) 177 } 178 179 // GetCommitKVStore returns a mounted CommitKVStore for a given StoreKey. If the 180 // store is wrapped in an inter-block cache, it will be unwrapped before returning. 181 func (rs *Store) GetCommitKVStore(key types.StoreKey) types.CommitKVStore { 182 // If the Store has an inter-block cache, first attempt to lookup and unwrap 183 // the underlying CommitKVStore by StoreKey. If it does not exist, fallback to 184 // the main mapping of CommitKVStores. 185 if rs.interBlockCache != nil { 186 if store := rs.interBlockCache.Unwrap(key); store != nil { 187 return store 188 } 189 } 190 191 return rs.stores[key] 192 } 193 194 // LoadLatestVersionAndUpgrade implements CommitMultiStore 195 func (rs *Store) LoadLatestVersionAndUpgrade(upgrades *types.StoreUpgrades) error { 196 ver := getLatestVersion(rs.db) 197 return rs.loadVersion(ver, upgrades) 198 } 199 200 // LoadVersionAndUpgrade allows us to rename substores while loading an older version 201 func (rs *Store) LoadVersionAndUpgrade(ver int64, upgrades *types.StoreUpgrades) error { 202 return rs.loadVersion(ver, upgrades) 203 } 204 205 // LoadLatestVersion implements CommitMultiStore. 206 func (rs *Store) LoadLatestVersion() error { 207 ver := getLatestVersion(rs.db) 208 return rs.loadVersion(ver, nil) 209 } 210 211 func (rs *Store) GetLatestVersion() int64 { 212 return getLatestVersion(rs.db) 213 } 214 215 // LoadVersion implements CommitMultiStore. 216 func (rs *Store) LoadVersion(ver int64) error { 217 return rs.loadVersion(ver, nil) 218 } 219 220 func (rs *Store) GetCommitVersion() (int64, error) { 221 var firstSp storeParams 222 var firstKey types.StoreKey 223 isFindIavlStoreParam := false 224 var versions []int64 225 var err error 226 //find a versions list in one iavl store 227 for firstKey, firstSp = range rs.storesParams { 228 if firstSp.typ == types.StoreTypeIAVL { 229 versions, err = rs.getCommitVersionFromParams(firstSp) 230 if err != nil { 231 return 0, err 232 } 233 // ignore not enabled store 234 if len(versions) == 0 { 235 continue 236 } 237 isFindIavlStoreParam = true 238 break 239 } 240 } 241 242 if !isFindIavlStoreParam { 243 version := GetLatestStoredMptHeight() 244 versions = []int64{int64(version)} 245 } 246 247 //sort the versions list 248 sort.Slice(versions, func(i, j int) bool { return versions[i] > versions[j] }) 249 rs.logger.Info("GetCommitVersion", "iavl:", firstKey.Name(), "versions :", versions) 250 //find version in rootmultistore 251 for _, version := range versions { 252 hasVersion, err := rs.hasVersion(version) 253 if err != nil { 254 return 0, err 255 } 256 if hasVersion { 257 rs.logger.Info("GetCommitVersion", "version :", version) 258 return version, nil 259 } 260 } 261 262 return 0, fmt.Errorf("not found any proper version") 263 } 264 265 // hasVersion means every storesParam in store has this version. 266 func (rs *Store) hasVersion(targetVersion int64) (bool, error) { 267 latestVersion := rs.GetLatestVersion() 268 for key, storeParams := range rs.storesParams { 269 if storeParams.typ == types.StoreTypeIAVL { 270 sName := storeParams.key.Name() 271 if evmAccStoreFilter(sName, latestVersion, true) { 272 continue 273 } 274 275 // filter block modules {} 276 if filter(storeParams.key.Name(), targetVersion, rs.stores[key], rs.commitFilters) { 277 continue 278 } 279 280 ok, err := findVersionInSubStores(rs, storeParams, targetVersion) 281 if err != nil { 282 return false, err 283 } 284 if !ok { 285 rs.logger.Info(fmt.Sprintf("iavl-%s does not have version: %d", key.Name(), targetVersion)) 286 return false, nil 287 } 288 289 } else if storeParams.typ == types.StoreTypeMPT { 290 if !tmtypes.HigherThanMars(targetVersion) { 291 continue 292 } 293 if ok := rs.stores[key].(*mpt.MptStore).HasVersion(targetVersion); !ok { 294 rs.logger.Info(fmt.Sprintf("mpt-%s does not have version: %d", key.Name(), targetVersion)) 295 return false, nil 296 } 297 } 298 } 299 return true, nil 300 } 301 302 // loadSubStoreVersion loads specific version for sub kvstore by given key and storeParams. 303 func (rs *Store) loadSubStoreVersion(ver int64, key types.StoreKey, storeParams storeParams, upgrades *types.StoreUpgrades, infos map[string]storeInfo) (types.CommitKVStore, error) { 304 305 commitID := rs.getCommitID(infos, key.Name()) 306 // Load it 307 store, err := rs.loadCommitStoreFromParams(key, commitID, storeParams) 308 if err != nil { 309 return nil, fmt.Errorf("failed to load %s Store: %v", key.Name(), err) 310 } 311 // If it has been added, set the initial version 312 if upgrades.IsAdded(key.Name()) { 313 storeParams.initialVersion = uint64(ver) + 1 314 } 315 316 // If it was deleted, remove all data 317 if upgrades.IsDeleted(key.Name()) { 318 if err := deleteKVStore(store.(types.KVStore)); err != nil { 319 return nil, fmt.Errorf("failed to delete store %s: %v", key.Name(), err) 320 } 321 } else if oldName := upgrades.RenamedFrom(key.Name()); oldName != "" { 322 // handle renames specially 323 // make an unregistered key to satify loadCommitStore params 324 oldKey := types.NewKVStoreKey(oldName) 325 oldParams := storeParams 326 oldParams.key = oldKey 327 328 // load from the old name 329 oldStore, err := rs.loadCommitStoreFromParams(oldKey, rs.getCommitID(infos, oldName), oldParams) 330 if err != nil { 331 return nil, fmt.Errorf("failed to load old Store '%s': %v", oldName, err) 332 } 333 // move all data 334 if err := moveKVStoreData(oldStore.(types.KVStore), store.(types.KVStore)); err != nil { 335 return nil, fmt.Errorf("failed to move store %s -> %s: %v", oldName, key.Name(), err) 336 } 337 } 338 return store, nil 339 } 340 341 // loadSubStoreVersionsAsync uses go-routines to load version async for each sub kvstore and returns kvstore maps 342 func (rs *Store) loadSubStoreVersionsAsync(ver int64, upgrades *types.StoreUpgrades, infos map[string]storeInfo) (map[types.StoreKey]types.CommitKVStore, map[int64][]byte, error) { 343 lock := &sync.Mutex{} 344 wg := &sync.WaitGroup{} 345 var newStores = make(map[types.StoreKey]types.CommitKVStore) 346 roots := make(map[int64][]byte) 347 errs := []error{} 348 for key, sp := range rs.storesParams { 349 if evmAccStoreFilter(key.Name(), ver) { 350 continue 351 } 352 wg.Add(1) 353 go func(_key types.StoreKey, _sp storeParams) { 354 store, err := rs.loadSubStoreVersion(ver, _key, _sp, upgrades, infos) 355 lock.Lock() 356 if err != nil { 357 errs = append(errs, err) 358 } else { 359 newStores[_key] = store 360 } 361 if _sp.typ == types.StoreTypeIAVL { 362 if len(roots) == 0 { 363 iStore := store.(*iavl.Store) 364 roots = iStore.GetHeights() 365 } 366 } 367 lock.Unlock() 368 wg.Done() 369 }(key, sp) 370 } 371 wg.Wait() 372 if len(errs) != 0 { 373 var errStr strings.Builder 374 for _, err := range errs { 375 errStr.WriteString(fmt.Sprintf("%s\n", err.Error())) 376 } 377 return nil, nil, fmt.Errorf("failed to load version async, err:%s", errStr.String()) 378 } 379 return newStores, roots, nil 380 } 381 382 func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { 383 var err error 384 infos := make(map[string]storeInfo) 385 var cInfo commitInfo 386 cInfo.Version = tmtypes.GetStartBlockHeight() 387 388 // load old data if we are not version 0 389 if ver != 0 { 390 var err error 391 cInfo, err = getCommitInfo(rs.db, ver) 392 if err != nil { 393 return err 394 } 395 396 // convert StoreInfos slice to map 397 for _, storeInfo := range cInfo.StoreInfos { 398 infos[storeInfo.Name] = storeInfo 399 } 400 401 rs.commitInfoFilter(infos, ver, MptStore) 402 403 //if upgrade version ne 404 callback := func(name string, version int64) { 405 ibcInfo := infos[name] 406 if ibcInfo.Core.CommitID.Version == 0 { 407 ibcInfo.Core.CommitID.Version = version //tmtypes.GetVenus1Height() 408 infos[name] = ibcInfo 409 for key, param := range rs.storesParams { 410 if key.Name() == name { 411 param.upgradeVersion = uint64(version) 412 rs.storesParams[key] = param 413 } 414 } 415 } 416 } 417 filterVersion(ver, rs.versionFilters, callback) 418 } 419 420 // load each Store (note this doesn't panic on unmounted keys now) 421 422 var newStores map[types.StoreKey]types.CommitKVStore 423 var roots map[int64][]byte 424 loadVersionAsync := viper.GetBool(types.FlagLoadVersionAsync) 425 if loadVersionAsync { 426 newStores, roots, err = rs.loadSubStoreVersionsAsync(ver, upgrades, infos) 427 if err != nil { 428 return err 429 } 430 } else { 431 newStores = make(map[types.StoreKey]types.CommitKVStore) 432 roots = make(map[int64][]byte) 433 for key, sp := range rs.storesParams { 434 if evmAccStoreFilter(key.Name(), ver) { 435 continue 436 } 437 438 store, err := rs.loadSubStoreVersion(ver, key, sp, upgrades, infos) 439 if err != nil { 440 return err 441 } 442 if sp.typ == types.StoreTypeIAVL { 443 if len(roots) == 0 { 444 iStore := store.(*iavl.Store) 445 roots = iStore.GetHeights() 446 } 447 } 448 newStores[key] = store 449 } 450 } 451 // 452 rs.lastCommitInfo = cInfo 453 rs.stores = newStores 454 455 err = rs.checkAndResetPruningHeights(roots) 456 if err != nil { 457 return err 458 } 459 vs, err := getVersions(rs.db) 460 if err != nil { 461 return err 462 } 463 if len(vs) > 0 { 464 rs.versions = vs 465 } 466 if rs.logger != nil { 467 rs.logger.Info("loadVersion info", "pruned heights length", len(rs.pruneHeights), "versions", len(rs.versions)) 468 } 469 if len(rs.pruneHeights) > maxPruneHeightsLength { 470 return fmt.Errorf("Pruned heights length <%d> exceeds <%d>, "+ 471 "need to prune them with command "+ 472 "<fbchaind data prune-compact all --home your_fbchaind_home_directory> before running fbchaind", 473 len(rs.pruneHeights), maxPruneHeightsLength) 474 } 475 return nil 476 } 477 478 func (rs *Store) checkAndResetPruningHeights(roots map[int64][]byte) error { 479 480 ph, err := getPruningHeights(rs.db, false) 481 if err != nil { 482 return err 483 } 484 485 if len(ph) == 0 { 486 return nil 487 } 488 489 needReset := false 490 var newPh []int64 491 for _, h := range ph { 492 if _, ok := roots[h]; ok { 493 newPh = append(newPh, h) 494 } else { 495 needReset = true 496 } 497 } 498 rs.pruneHeights = newPh 499 500 if needReset { 501 if rs.logger != nil { 502 msg := fmt.Sprintf("Detected pruned heights length <%d>, reset to <%d>", 503 len(ph), len(rs.pruneHeights)) 504 rs.logger.Info(msg) 505 } 506 batch := rs.db.NewBatch() 507 setPruningHeights(batch, newPh) 508 batch.Write() 509 batch.Close() 510 } 511 512 return nil 513 } 514 func (rs *Store) getCommitID(infos map[string]storeInfo, name string) types.CommitID { 515 info, ok := infos[name] 516 if !ok { 517 return types.CommitID{Version: tmtypes.GetStartBlockHeight()} 518 } 519 return info.Core.CommitID 520 } 521 522 func deleteKVStore(kv types.KVStore) error { 523 // Note that we cannot write while iterating, so load all keys here, delete below 524 var keys [][]byte 525 itr := kv.Iterator(nil, nil) 526 for itr.Valid() { 527 keys = append(keys, itr.Key()) 528 itr.Next() 529 } 530 itr.Close() 531 532 for _, k := range keys { 533 kv.Delete(k) 534 } 535 return nil 536 } 537 538 // we simulate move by a copy and delete 539 func moveKVStoreData(oldDB types.KVStore, newDB types.KVStore) error { 540 // we read from one and write to another 541 itr := oldDB.Iterator(nil, nil) 542 for itr.Valid() { 543 newDB.Set(itr.Key(), itr.Value()) 544 itr.Next() 545 } 546 itr.Close() 547 548 // then delete the old store 549 return deleteKVStore(oldDB) 550 } 551 552 // SetInterBlockCache sets the Store's internal inter-block (persistent) cache. 553 // When this is defined, all CommitKVStores will be wrapped with their respective 554 // inter-block cache. 555 func (rs *Store) SetInterBlockCache(c types.MultiStorePersistentCache) { 556 rs.interBlockCache = c 557 } 558 559 // SetTracer sets the tracer for the MultiStore that the underlying 560 // stores will utilize to trace operations. A MultiStore is returned. 561 func (rs *Store) SetTracer(w io.Writer) types.MultiStore { 562 rs.traceWriter = w 563 return rs 564 } 565 566 // SetTracingContext updates the tracing context for the MultiStore by merging 567 // the given context with the existing context by key. Any existing keys will 568 // be overwritten. It is implied that the caller should update the context when 569 // necessary between tracing operations. It returns a modified MultiStore. 570 func (rs *Store) SetTracingContext(tc types.TraceContext) types.MultiStore { 571 if rs.traceContext != nil { 572 for k, v := range tc { 573 rs.traceContext[k] = v 574 } 575 } else { 576 rs.traceContext = tc 577 } 578 579 return rs 580 } 581 582 // TracingEnabled returns if tracing is enabled for the MultiStore. 583 func (rs *Store) TracingEnabled() bool { 584 return rs.traceWriter != nil 585 } 586 587 //---------------------------------------- 588 // +CommitStore 589 590 // Implements Committer/CommitStore. 591 func (rs *Store) LastCommitID() types.CommitID { 592 return rs.lastCommitInfo.CommitID() 593 } 594 595 func (rs *Store) LastCommitVersion() int64 { 596 return rs.lastCommitInfo.Version 597 } 598 599 func (rs *Store) CommitterCommit(*iavltree.TreeDelta) (_ types.CommitID, _ *iavltree.TreeDelta) { 600 return 601 } 602 603 // Implements Committer/CommitStore. 604 func (rs *Store) CommitterCommitMap(inputDeltaMap iavltree.TreeDeltaMap) (types.CommitID, iavltree.TreeDeltaMap) { 605 iavltree.IavlCommitAsyncNoBatch = cfg.DynamicConfig.GetIavlAcNoBatch() 606 607 previousHeight := rs.lastCommitInfo.Version 608 version := previousHeight + 1 609 610 tsCommitStores := time.Now() 611 var outputDeltaMap iavltree.TreeDeltaMap 612 rs.lastCommitInfo, outputDeltaMap = commitStores(version, rs.stores, inputDeltaMap, rs.commitFilters) 613 614 if !iavltree.EnableAsyncCommit { 615 // Determine if pruneHeight height needs to be added to the list of heights to 616 // be pruned, where pruneHeight = (commitHeight - 1) - KeepRecent. 617 if int64(rs.pruningOpts.KeepRecent) < previousHeight { 618 pruneHeight := previousHeight - int64(rs.pruningOpts.KeepRecent) 619 // We consider this height to be pruned iff: 620 // 621 // - KeepEvery is zero as that means that all heights should be pruned. 622 // - KeepEvery % (height - KeepRecent) != 0 as that means the height is not 623 // a 'snapshot' height. 624 if rs.pruningOpts.KeepEvery == 0 || pruneHeight%int64(rs.pruningOpts.KeepEvery) != 0 { 625 rs.pruneHeights = append(rs.pruneHeights, pruneHeight) 626 for k, v := range rs.versions { 627 if v == pruneHeight { 628 rs.versions = append(rs.versions[:k], rs.versions[k+1:]...) 629 break 630 } 631 } 632 } 633 } 634 635 if uint64(len(rs.versions)) > rs.pruningOpts.MaxRetainNum { 636 rs.pruneHeights = append(rs.pruneHeights, rs.versions[:uint64(len(rs.versions))-rs.pruningOpts.MaxRetainNum]...) 637 rs.versions = rs.versions[uint64(len(rs.versions))-rs.pruningOpts.MaxRetainNum:] 638 } 639 640 // batch prune if the current height is a pruning interval height 641 if rs.pruningOpts.Interval > 0 && version%int64(rs.pruningOpts.Interval) == 0 { 642 rs.pruneStores() 643 } 644 645 rs.versions = append(rs.versions, version) 646 } 647 persist.GetStatistics().Accumulate(trace.CommitStores, tsCommitStores) 648 649 tsFlushMeta := time.Now() 650 flushMetadata(rs.db, version, rs.lastCommitInfo, rs.pruneHeights, rs.versions) 651 persist.GetStatistics().Accumulate(trace.FlushMeta, tsFlushMeta) 652 653 return types.CommitID{ 654 Version: version, 655 Hash: rs.lastCommitInfo.Hash(), 656 }, outputDeltaMap 657 } 658 659 // pruneStores will batch delete a list of heights from each mounted sub-store. 660 // Afterwards, pruneHeights is reset. 661 func (rs *Store) pruneStores() { 662 pruneCnt := len(rs.pruneHeights) 663 if pruneCnt == 0 { 664 return 665 } 666 667 if rs.logger != nil { 668 rs.logger.Info("pruning start", "pruning-count", pruneCnt, "curr-height", rs.lastCommitInfo.Version+1) 669 rs.logger.Debug("pruning", "pruning-heights", rs.pruneHeights) 670 } 671 defer func() { 672 if rs.logger != nil { 673 rs.logger.Info("pruning end") 674 } 675 }() 676 stores := rs.getFilterStores(rs.lastCommitInfo.Version) 677 //stores = rs.stores 678 for key, store := range stores { 679 if store.GetStoreType() == types.StoreTypeIAVL { 680 sName := key.Name() 681 682 if evmAccStoreFilter(sName, rs.lastCommitInfo.Version) { 683 continue 684 } 685 686 // If the store is wrapped with an inter-block cache, we must first unwrap 687 // it to get the underlying IAVL store. 688 store = rs.GetCommitKVStore(key) 689 if err := store.(*iavl.Store).DeleteVersions(rs.pruneHeights...); err != nil { 690 if errCause := errors.Cause(err); errCause != nil && errCause != iavltree.ErrVersionDoesNotExist { 691 panic(err) 692 } 693 } 694 } 695 } 696 697 rs.pruneHeights = make([]int64, 0) 698 } 699 700 func (rs *Store) FlushPruneHeights(pruneHeights []int64, versions []int64) { 701 flushMetadata(rs.db, rs.lastCommitInfo.Version, rs.lastCommitInfo, pruneHeights, versions) 702 } 703 704 // Implements CacheWrapper/Store/CommitStore. 705 func (rs *Store) CacheWrap() types.CacheWrap { 706 return rs.CacheMultiStore().(types.CacheWrap) 707 } 708 709 // CacheWrapWithTrace implements the CacheWrapper interface. 710 func (rs *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { 711 return rs.CacheWrap() 712 } 713 714 //---------------------------------------- 715 // +MultiStore 716 717 // CacheMultiStore cache-wraps the multi-store and returns a CacheMultiStore. 718 // It implements the MultiStore interface. 719 func (rs *Store) CacheMultiStore() types.CacheMultiStore { 720 stores := make(map[types.StoreKey]types.CacheWrapper) 721 for k, v := range rs.stores { 722 stores[k] = v 723 } 724 725 return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.traceContext) 726 } 727 728 // CacheMultiStoreWithVersion is analogous to CacheMultiStore except that it 729 // attempts to load stores at a given version (height). An error is returned if 730 // any store cannot be loaded. This should only be used for querying and 731 // iterating at past heights. 732 func (rs *Store) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStore, error) { 733 cachedStores := make(map[types.StoreKey]types.CacheWrapper) 734 for key, store := range rs.stores { 735 switch store.GetStoreType() { 736 case types.StoreTypeIAVL: 737 // If the store is wrapped with an inter-block cache, we must first unwrap 738 // it to get the underlying IAVL store. 739 store = rs.GetCommitKVStore(key) 740 741 if evmAccStoreFilter(key.Name(), version) { 742 cachedStores[key] = store.(*iavl.Store).GetEmptyImmutable() 743 continue 744 } 745 // filter block modules {} 746 if filter(key.Name(), version, nil, rs.commitFilters) { 747 cachedStores[key] = store.(*iavl.Store).GetEmptyImmutable() 748 continue 749 } 750 // Attempt to lazy-load an already saved IAVL store version. If the 751 // version does not exist or is pruned, an error should be returned. 752 iavlStore, err := store.(*iavl.Store).GetImmutable(version) 753 if err != nil { 754 return nil, err 755 } 756 757 cachedStores[key] = iavlStore 758 759 case types.StoreTypeMPT: 760 store := rs.GetCommitKVStore(key) 761 762 mptStore, err := store.(*mpt.MptStore).GetImmutable(version) 763 if err != nil { 764 return nil, err 765 } 766 767 cachedStores[key] = mptStore 768 769 default: 770 cachedStores[key] = store 771 } 772 } 773 774 return cachemulti.NewStore(rs.db, cachedStores, rs.keysByName, rs.traceWriter, rs.traceContext), nil 775 } 776 777 // GetStore returns a mounted Store for a given StoreKey. If the StoreKey does 778 // not exist, it will panic. If the Store is wrapped in an inter-block cache, it 779 // will be unwrapped prior to being returned. 780 // 781 // TODO: This isn't used directly upstream. Consider returning the Store as-is 782 // instead of unwrapping. 783 func (rs *Store) GetStore(key types.StoreKey) types.Store { 784 store := rs.GetCommitKVStore(key) 785 if store == nil { 786 panic(fmt.Sprintf("store does not exist for key: %s", key.Name())) 787 } 788 789 return store 790 } 791 792 // GetKVStore returns a mounted KVStore for a given StoreKey. If tracing is 793 // enabled on the KVStore, a wrapped TraceKVStore will be returned with the root 794 // store's tracer, otherwise, the original KVStore will be returned. 795 // 796 // NOTE: The returned KVStore may be wrapped in an inter-block cache if it is 797 // set on the root store. 798 func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { 799 s := rs.stores[key] 800 if s == nil { 801 panic(fmt.Sprintf("store does not exist for key: %s", key.Name())) 802 } 803 store := s.(types.KVStore) 804 805 if rs.TracingEnabled() { 806 store = tracekv.NewStore(store, rs.traceWriter, rs.traceContext) 807 } 808 809 return store 810 } 811 812 // getStoreByName performs a lookup of a StoreKey given a store name typically 813 // provided in a path. The StoreKey is then used to perform a lookup and return 814 // a Store. If the Store is wrapped in an inter-block cache, it will be unwrapped 815 // prior to being returned. If the StoreKey does not exist, nil is returned. 816 func (rs *Store) getStoreByName(name string) types.Store { 817 key := rs.keysByName[name] 818 if key == nil { 819 return nil 820 } 821 822 return rs.GetCommitKVStore(key) 823 } 824 825 //---------------------- Query ------------------ 826 827 // Query calls substore.Query with the same `req` where `req.Path` is 828 // modified to remove the substore prefix. 829 // Ie. `req.Path` here is `/<substore>/<path>`, and trimmed to `/<path>` for the substore. 830 // TODO: add proof for `multistore -> substore`. 831 func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { 832 path := req.Path 833 storeName, subpath, err := parsePath(path) 834 if err != nil { 835 return sdkerrors.QueryResult(err) 836 } 837 838 store := rs.getStoreByName(storeName) 839 if store == nil { 840 return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no such store: %s", storeName)) 841 } 842 843 queryable, ok := store.(types.Queryable) 844 if !ok { 845 return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "store %s (type %T) doesn't support queries", storeName, store)) 846 } 847 848 // trim the path and make the query 849 req.Path = subpath 850 res := queryable.Query(req) 851 852 if !req.Prove || !RequireProof(subpath) { 853 return res 854 } 855 856 if res.Proof == nil || len(res.Proof.Ops) == 0 { 857 return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("proof is unexpectedly empty; ensure height has not been pruned. Query log: %s", res.Log))) 858 } 859 860 // If the request's height is the latest height we've committed, then utilize 861 // the store's lastCommitInfo as this commit info may not be flushed to disk. 862 // Otherwise, we query for the commit info from disk. 863 var commitInfo commitInfo 864 865 if res.Height == rs.lastCommitInfo.Version { 866 commitInfo = rs.lastCommitInfo 867 } else { 868 commitInfo, err = getCommitInfo(rs.db, res.Height) 869 if err != nil { 870 return sdkerrors.QueryResult(err) 871 } 872 } 873 874 if tmtypes.HigherThanVenus1(req.Height) { 875 queryIbcProof(&res, &commitInfo, storeName) 876 } else { 877 // Restore origin path and append proof op. 878 res.Proof.Ops = append(res.Proof.Ops, NewMultiStoreProofOp( 879 []byte(storeName), 880 NewMultiStoreProof(commitInfo.StoreInfos), 881 ).ProofOp()) 882 } 883 884 // TODO: handle in another TM v0.26 update PR 885 // res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) 886 return res 887 } 888 889 // parsePath expects a format like /<storeName>[/<subpath>] 890 // Must start with /, subpath may be empty 891 // Returns error if it doesn't start with / 892 func parsePath(path string) (storeName string, subpath string, err error) { 893 if !strings.HasPrefix(path, "/") { 894 return storeName, subpath, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid path: %s", path) 895 } 896 897 paths := strings.SplitN(path[1:], "/", 2) 898 storeName = paths[0] 899 900 if len(paths) == 2 { 901 subpath = "/" + paths[1] 902 } 903 904 return storeName, subpath, nil 905 } 906 907 func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID, params storeParams) (types.CommitKVStore, error) { 908 var db dbm.DB 909 910 if params.db != nil { 911 db = dbm.NewPrefixDB(params.db, []byte("s/_/")) 912 } else { 913 prefix := "s/k:" + params.key.Name() + "/" 914 db = dbm.NewPrefixDB(rs.db, []byte(prefix)) 915 } 916 917 switch params.typ { 918 case types.StoreTypeMulti: 919 panic("recursive MultiStores not yet supported") 920 921 case types.StoreTypeIAVL: 922 var store types.CommitKVStore 923 var err error 924 prefix := "s/k:" + params.key.Name() + "/" 925 var prefixDB dbm.DB 926 if rs.flatKVDB != nil { 927 prefixDB = dbm.NewPrefixDB(rs.flatKVDB, []byte(prefix)) 928 } 929 if params.initialVersion == 0 && params.upgradeVersion != 0 { 930 store, err = iavl.LoadStoreWithInitialVersion(db, prefixDB, id, rs.lazyLoading, uint64(tmtypes.GetStartBlockHeight()), params.upgradeVersion) 931 } else if params.initialVersion == 0 { 932 store, err = iavl.LoadStore(db, prefixDB, id, rs.lazyLoading, tmtypes.GetStartBlockHeight()) 933 } else { 934 store, err = iavl.LoadStoreWithInitialVersion(db, prefixDB, id, rs.lazyLoading, params.initialVersion, params.upgradeVersion) 935 } 936 937 if err != nil { 938 return nil, err 939 } 940 941 if rs.interBlockCache != nil { 942 // Wrap and get a CommitKVStore with inter-block caching. Note, this should 943 // only wrap the primary CommitKVStore, not any store that is already 944 // cache-wrapped as that will create unexpected behavior. 945 store = rs.interBlockCache.GetStoreCache(key, store) 946 } 947 948 return store, err 949 950 case types.StoreTypeDB: 951 return commitDBStoreAdapter{Store: dbadapter.Store{DB: db}}, nil 952 953 case types.StoreTypeTransient: 954 _, ok := key.(*types.TransientStoreKey) 955 if !ok { 956 return nil, fmt.Errorf("invalid StoreKey for StoreTypeTransient: %s", key.String()) 957 } 958 959 return transient.NewStore(), nil 960 case types.StoreTypeMemory: 961 if _, ok := key.(*types.MemoryStoreKey); !ok { 962 return nil, fmt.Errorf("unexpected key type for a MemoryStoreKey; got: %s", key.String()) 963 } 964 965 return mem.NewStore(), nil 966 967 case types.StoreTypeMPT: 968 return mpt.NewMptStore(rs.logger, id) 969 970 default: 971 panic(fmt.Sprintf("unrecognized store type %v", params.typ)) 972 } 973 } 974 func (rs *Store) GetDBReadTime() int { 975 count := 0 976 for _, store := range rs.stores { 977 count += store.GetDBReadTime() 978 } 979 return count 980 } 981 func findVersionInSubStores(rs *Store, params storeParams, version int64) (bool, error) { 982 var db dbm.DB 983 984 if params.db != nil { 985 db = dbm.NewPrefixDB(params.db, []byte("s/_/")) 986 } else { 987 prefix := "s/k:" + params.key.Name() + "/" 988 db = dbm.NewPrefixDB(rs.db, []byte(prefix)) 989 } 990 991 return iavl.HasVersion(db, version) 992 } 993 func (rs *Store) getCommitVersionFromParams(params storeParams) ([]int64, error) { 994 var db dbm.DB 995 996 if params.db != nil { 997 db = dbm.NewPrefixDB(params.db, []byte("s/_/")) 998 } else { 999 prefix := "s/k:" + params.key.Name() + "/" 1000 db = dbm.NewPrefixDB(rs.db, []byte(prefix)) 1001 } 1002 1003 return iavl.GetCommitVersions(db) 1004 } 1005 1006 func (rs *Store) GetDBWriteCount() int { 1007 count := 0 1008 for _, store := range rs.stores { 1009 count += store.GetDBWriteCount() 1010 } 1011 return count 1012 } 1013 1014 func (rs *Store) GetDBReadCount() int { 1015 count := 0 1016 for _, store := range rs.stores { 1017 count += store.GetDBReadCount() 1018 } 1019 return count 1020 } 1021 1022 func (rs *Store) GetNodeReadCount() int { 1023 count := 0 1024 for _, store := range rs.stores { 1025 count += store.GetNodeReadCount() 1026 } 1027 return count 1028 } 1029 1030 func (rs *Store) ResetCount() { 1031 for _, store := range rs.stores { 1032 store.ResetCount() 1033 } 1034 } 1035 1036 func (rs *Store) GetFlatKVReadTime() int { 1037 rt := 0 1038 for _, store := range rs.stores { 1039 rt += store.GetFlatKVReadTime() 1040 } 1041 return rt 1042 } 1043 1044 func (rs *Store) GetFlatKVWriteTime() int { 1045 wt := 0 1046 for _, store := range rs.stores { 1047 wt += store.GetFlatKVWriteTime() 1048 } 1049 return wt 1050 } 1051 1052 func (rs *Store) GetFlatKVReadCount() int { 1053 count := 0 1054 for _, store := range rs.stores { 1055 count += store.GetFlatKVReadCount() 1056 } 1057 return count 1058 } 1059 1060 func (rs *Store) GetFlatKVWriteCount() int { 1061 count := 0 1062 for _, store := range rs.stores { 1063 count += store.GetFlatKVWriteCount() 1064 } 1065 return count 1066 } 1067 1068 //---------------------------------------- 1069 // storeParams 1070 1071 type storeParams struct { 1072 key types.StoreKey 1073 db dbm.DB 1074 typ types.StoreType 1075 initialVersion uint64 1076 upgradeVersion uint64 1077 } 1078 1079 //---------------------------------------- 1080 // commitInfo 1081 1082 // NOTE: Keep commitInfo a simple immutable struct. 1083 type commitInfo struct { 1084 1085 // Version 1086 Version int64 1087 1088 // Store info for 1089 StoreInfos []storeInfo 1090 } 1091 1092 // Hash returns the simple merkle root hash of the stores sorted by name. 1093 func (ci commitInfo) Hash() []byte { 1094 if tmtypes.HigherThanVenus1(ci.Version) { 1095 return ci.ibcHash() 1096 } 1097 return ci.originHash() 1098 } 1099 1100 func (ci commitInfo) originHash() []byte { 1101 // TODO: cache to ci.hash []byte 1102 m := make(map[string][]byte, len(ci.StoreInfos)) 1103 for _, storeInfo := range ci.StoreInfos { 1104 m[storeInfo.Name] = storeInfo.Hash() 1105 } 1106 return merkle.SimpleHashFromMap(m) 1107 } 1108 1109 // Hash returns the simple merkle root hash of the stores sorted by name. 1110 func (ci commitInfo) ibcHash() []byte { 1111 m := ci.toMap() 1112 rootHash, _, _ := sdkmaps.ProofsFromMap(m) 1113 return rootHash 1114 } 1115 1116 func (ci commitInfo) CommitID() types.CommitID { 1117 return types.CommitID{ 1118 Version: ci.Version, 1119 Hash: ci.Hash(), 1120 } 1121 } 1122 1123 //---------------------------------------- 1124 // storeInfo 1125 1126 // storeInfo contains the name and core reference for an 1127 // underlying store. It is the leaf of the Stores top 1128 // level simple merkle tree. 1129 type storeInfo struct { 1130 Name string 1131 Core storeCore 1132 } 1133 1134 type storeCore struct { 1135 // StoreType StoreType 1136 CommitID types.CommitID 1137 // ... maybe add more state 1138 } 1139 1140 // Implements merkle.Hasher. 1141 func (si storeInfo) Hash() []byte { 1142 // Doesn't write Name, since merkle.SimpleHashFromMap() will 1143 // include them via the keys. 1144 bz := si.Core.CommitID.Hash 1145 hasher := tmhash.New() 1146 1147 _, err := hasher.Write(bz) 1148 if err != nil { 1149 // TODO: Handle with #870 1150 panic(err) 1151 } 1152 1153 return hasher.Sum(nil) 1154 } 1155 1156 //---------------------------------------- 1157 // Misc. 1158 1159 func getLatestVersion(db dbm.DB) int64 { 1160 var latest int64 1161 latestBytes, err := db.Get([]byte(latestVersionKey)) 1162 if err != nil { 1163 panic(err) 1164 } else if latestBytes == nil { 1165 return 0 1166 } 1167 1168 err = cdc.UnmarshalBinaryLengthPrefixed(latestBytes, &latest) 1169 if err != nil { 1170 panic(err) 1171 } 1172 1173 return latest 1174 } 1175 1176 type StoreSorts []StoreSort 1177 1178 func (s StoreSorts) Len() int { 1179 return len(s) 1180 } 1181 1182 func (s StoreSorts) Less(i, j int) bool { 1183 return s[i].key.Name() < s[j].key.Name() 1184 } 1185 1186 func (s StoreSorts) Swap(i, j int) { 1187 s[i], s[j] = s[j], s[i] 1188 } 1189 1190 type StoreSort struct { 1191 key types.StoreKey 1192 v types.CommitKVStore 1193 } 1194 1195 // Commits each store and returns a new commitInfo. 1196 func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore, 1197 inputDeltaMap iavltree.TreeDeltaMap, filters []types.StoreFilter) (commitInfo, iavltree.TreeDeltaMap) { 1198 var storeInfos []storeInfo 1199 outputDeltaMap := iavltree.TreeDeltaMap{} 1200 1201 // updata commit gap height 1202 if iavltree.EnableAsyncCommit { 1203 iavltree.UpdateCommitGapHeight(config.DynamicConfig.GetCommitGapHeight()) 1204 } 1205 for key, store := range storeMap { 1206 sName := key.Name() 1207 if evmAccStoreFilter(sName, version) { 1208 continue 1209 } 1210 1211 if !mpt.TrieWriteAhead { 1212 if newMptStoreFilter(sName, version) { 1213 continue 1214 } 1215 } 1216 1217 if filter(key.Name(), version, store, filters) { 1218 continue 1219 } 1220 1221 commitID, outputDelta := store.CommitterCommit(inputDeltaMap[key.Name()]) // CommitterCommit 1222 1223 if store.GetStoreType() == types.StoreTypeTransient { 1224 continue 1225 } 1226 1227 // old version, mpt(acc) store, never allowed to participate the process of calculate root hash, or it will lead to SMB! 1228 if newMptStoreFilter(sName, version) { 1229 continue 1230 } 1231 1232 // evm and acc store should not participate in AppHash calculation process after Mars Height 1233 if evmAccStoreFilter(sName, version, true) { 1234 continue 1235 } 1236 1237 si := storeInfo{} 1238 si.Name = key.Name() 1239 si.Core.CommitID = commitID 1240 storeInfos = append(storeInfos, si) 1241 outputDeltaMap[key.Name()] = outputDelta 1242 } 1243 return commitInfo{ 1244 Version: version, 1245 StoreInfos: storeInfos, 1246 }, outputDeltaMap 1247 } 1248 1249 func filter(name string, h int64, st types.CommitKVStore, filters []types.StoreFilter) bool { 1250 for _, filter := range filters { 1251 if filter(name, h, st) { 1252 return true 1253 } 1254 } 1255 return false 1256 } 1257 1258 func filterVersion(h int64, filters []types.VersionFilter, cb types.VersionCallback) { 1259 for _, filter := range filters { 1260 if c := filter(h); c != nil { 1261 c(cb) 1262 } 1263 } 1264 } 1265 1266 // Gets commitInfo from disk. 1267 func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) { 1268 cInfoKey := fmt.Sprintf(commitInfoKeyFmt, ver) 1269 1270 cInfoBytes, err := db.Get([]byte(cInfoKey)) 1271 if err != nil { 1272 return commitInfo{}, fmt.Errorf("failed to get commit info: %v", err) 1273 } else if cInfoBytes == nil { 1274 return commitInfo{}, fmt.Errorf("failed to get commit info: no data") 1275 } 1276 1277 var cInfo commitInfo 1278 1279 err = cdc.UnmarshalBinaryLengthPrefixed(cInfoBytes, &cInfo) 1280 if err != nil { 1281 return commitInfo{}, fmt.Errorf("failed to get Store: %v", err) 1282 } 1283 1284 return cInfo, nil 1285 } 1286 1287 func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) { 1288 cInfoBytes := cdc.MustMarshalBinaryLengthPrefixed(cInfo) 1289 cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version) 1290 batch.Set([]byte(cInfoKey), cInfoBytes) 1291 } 1292 1293 func setLatestVersion(batch dbm.Batch, version int64) { 1294 latestBytes := cdc.MustMarshalBinaryLengthPrefixed(version) 1295 batch.Set([]byte(latestVersionKey), latestBytes) 1296 } 1297 1298 func setPruningHeights(batch dbm.Batch, pruneHeights []int64) { 1299 bz := cdc.MustMarshalBinaryBare(pruneHeights) 1300 batch.Set([]byte(pruneHeightsKey), bz) 1301 } 1302 1303 func SetPruningHeights(db dbm.DB, pruneHeights []int64) { 1304 batch := db.NewBatch() 1305 setPruningHeights(batch, pruneHeights) 1306 batch.Write() 1307 batch.Close() 1308 } 1309 1310 func GetPruningHeights(db dbm.DB) ([]int64, error) { 1311 return getPruningHeights(db, true) 1312 } 1313 1314 func getPruningHeights(db dbm.DB, reportZeroLengthErr bool) ([]int64, error) { 1315 bz, err := db.Get([]byte(pruneHeightsKey)) 1316 if err != nil { 1317 return nil, fmt.Errorf("failed to get pruned heights: %w", err) 1318 } 1319 if len(bz) == 0 { 1320 if reportZeroLengthErr { 1321 return nil, errors.New("no pruned heights found") 1322 } else { 1323 return nil, nil 1324 } 1325 } 1326 1327 var prunedHeights []int64 1328 if err := cdc.UnmarshalBinaryBare(bz, &prunedHeights); err != nil { 1329 return nil, fmt.Errorf("failed to unmarshal pruned heights: %w", err) 1330 } 1331 1332 return prunedHeights, nil 1333 } 1334 1335 func flushMetadata(db dbm.DB, version int64, cInfo commitInfo, pruneHeights []int64, versions []int64) { 1336 batch := db.NewBatch() 1337 defer batch.Close() 1338 1339 setCommitInfo(batch, version, cInfo) 1340 setLatestVersion(batch, version) 1341 setPruningHeights(batch, pruneHeights) 1342 setVersions(batch, versions) 1343 1344 if err := batch.Write(); err != nil { 1345 panic(fmt.Errorf("error on batch write %w", err)) 1346 } 1347 } 1348 1349 func setVersions(batch dbm.Batch, versions []int64) { 1350 bz := cdc.MustMarshalBinaryBare(versions) 1351 batch.Set([]byte(versionsKey), bz) 1352 } 1353 1354 func getVersions(db dbm.DB) ([]int64, error) { 1355 bz, err := db.Get([]byte(versionsKey)) 1356 if err != nil { 1357 return nil, fmt.Errorf("failed to get versions: %w", err) 1358 } 1359 1360 if len(bz) == 0 { 1361 return nil, nil 1362 } 1363 1364 var versions []int64 1365 if err := cdc.UnmarshalBinaryBare(bz, &versions); err != nil { 1366 return nil, fmt.Errorf("failed to unmarshal pruned heights: %w", err) 1367 } 1368 1369 return versions, nil 1370 } 1371 1372 // Snapshot implements snapshottypes.Snapshotter. The snapshot output for a given format must be 1373 // identical across nodes such that chunks from different sources fit together. If the output for a 1374 // given format changes (at the byte level), the snapshot format must be bumped - see 1375 // TestMultistoreSnapshot_Checksum test. 1376 func (rs *Store) Export(to *Store, initVersion int64) error { 1377 curVersion := rs.lastCommitInfo.Version 1378 // Collect stores to snapshot (only IAVL stores are supported) 1379 type namedStore struct { 1380 fromStore *iavl.Store 1381 toStore *iavl.Store 1382 name string 1383 } 1384 stores := []namedStore{} 1385 for key := range rs.stores { 1386 switch store := rs.GetCommitKVStore(key).(type) { 1387 case *iavl.Store: 1388 var toKVStore types.CommitKVStore 1389 for toKey, toValue := range to.stores { 1390 if key.Name() == toKey.Name() { 1391 toKVStore = toValue 1392 } 1393 } 1394 toStore, _ := toKVStore.(*iavl.Store) 1395 stores = append(stores, namedStore{name: key.Name(), fromStore: store, toStore: toStore}) 1396 case *transient.Store: 1397 // Non-persisted stores shouldn't be snapshotted 1398 continue 1399 default: 1400 return fmt.Errorf( 1401 "don't know how to snapshot store %q of type %T", key.Name(), store) 1402 } 1403 } 1404 sort.Slice(stores, func(i, j int) bool { 1405 return strings.Compare(stores[i].name, stores[j].name) == 1 1406 }) 1407 1408 // Export each IAVL store. Stores are serialized as a stream of SnapshotItem Protobuf 1409 // messages. The first item contains a SnapshotStore with store metadata (i.e. name), 1410 // and the following messages contain a SnapshotNode (i.e. an ExportNode). Store changes 1411 // are demarcated by new SnapshotStore items. 1412 for _, store := range stores { 1413 log.Println("--------- export ", store.name, " start ---------") 1414 exporter, err := store.fromStore.Export(curVersion) 1415 if err != nil { 1416 panic(err) 1417 } 1418 defer exporter.Close() 1419 1420 importer, err := store.toStore.Import(initVersion) 1421 if err != nil { 1422 panic(err) 1423 } 1424 defer importer.Close() 1425 1426 var totalCnt uint64 1427 var totalSize uint64 1428 for { 1429 node, err := exporter.Next() 1430 if err == iavltree.ExportDone { 1431 break 1432 } 1433 1434 err = importer.Add(node) 1435 if err != nil { 1436 panic(err) 1437 } 1438 nodeSize := len(node.Key) + len(node.Value) 1439 totalCnt++ 1440 totalSize += uint64(nodeSize) 1441 if totalCnt%10000 == 0 { 1442 log.Println("--------- total node count ", totalCnt, " ---------") 1443 log.Println("--------- total node size ", totalSize, " ---------") 1444 } 1445 } 1446 1447 exporter.Close() 1448 err = importer.Commit() 1449 if err != nil { 1450 panic(err) 1451 } 1452 importer.Close() 1453 log.Println("--------- export ", store.name, " end ---------") 1454 } 1455 1456 flushMetadata(to.db, initVersion, rs.buildCommitInfo(initVersion), []int64{}, []int64{}) 1457 1458 return nil 1459 } 1460 1461 func (rs *Store) buildCommitInfo(version int64) commitInfo { 1462 storeInfos := []storeInfo{} 1463 for key, store := range rs.stores { 1464 if store.GetStoreType() == types.StoreTypeTransient { 1465 continue 1466 } 1467 storeInfos = append(storeInfos, storeInfo{ 1468 Name: key.Name(), 1469 Core: storeCore{ 1470 store.LastCommitID(), 1471 }, 1472 }) 1473 } 1474 return commitInfo{ 1475 Version: version, 1476 StoreInfos: storeInfos, 1477 } 1478 } 1479 1480 func (src Store) Copy() *Store { 1481 dst := &Store{ 1482 db: src.db, 1483 pruningOpts: src.pruningOpts, 1484 storesParams: make(map[types.StoreKey]storeParams, len(src.storesParams)), 1485 stores: make(map[types.StoreKey]types.CommitKVStore, len(src.stores)), 1486 keysByName: make(map[string]types.StoreKey, len(src.keysByName)), 1487 lazyLoading: src.lazyLoading, 1488 pruneHeights: make([]int64, 0), 1489 versions: make([]int64, 0), 1490 1491 traceWriter: src.traceWriter, 1492 traceContext: src.traceContext, 1493 interBlockCache: src.interBlockCache, 1494 upgradeVersion: src.upgradeVersion, 1495 } 1496 1497 dst.lastCommitInfo = commitInfo{ 1498 Version: src.lastCommitInfo.Version, 1499 StoreInfos: make([]storeInfo, 0), 1500 } 1501 1502 for _, info := range src.lastCommitInfo.StoreInfos { 1503 dst.lastCommitInfo.StoreInfos = append(dst.lastCommitInfo.StoreInfos, info) 1504 } 1505 1506 for key, value := range src.storesParams { 1507 dst.storesParams[key] = value 1508 } 1509 1510 for key, value := range src.stores { 1511 dst.stores[key] = value 1512 } 1513 1514 for key, value := range src.keysByName { 1515 dst.keysByName[key] = value 1516 } 1517 1518 for _, value := range src.pruneHeights { 1519 dst.pruneHeights = append(dst.pruneHeights, value) 1520 } 1521 1522 for _, value := range src.versions { 1523 dst.versions = append(dst.versions, value) 1524 } 1525 1526 return dst 1527 } 1528 func (rs *Store) CurrentVersion() int64 { 1529 var currVer int64 = -1 1530 for key, store := range rs.stores { 1531 var version int64 1532 switch store.GetStoreType() { 1533 case types.StoreTypeIAVL: 1534 sName := key.Name() 1535 if evmAccStoreFilter(sName, rs.GetLatestVersion()) { 1536 continue 1537 } 1538 if filter(key.Name(), rs.lastCommitInfo.Version, nil, rs.commitFilters) { 1539 continue 1540 } 1541 s := store.(*iavl.Store) 1542 version = s.CurrentVersion() 1543 case types.StoreTypeMPT: 1544 s := store.(*mpt.MptStore) 1545 version = s.CurrentVersion() 1546 case types.StoreTypeTransient: 1547 default: 1548 continue 1549 } 1550 if currVer == -1 { 1551 currVer = version 1552 continue 1553 } 1554 if version < currVer { 1555 currVer = version 1556 } 1557 } 1558 return currVer 1559 } 1560 func (rs *Store) StopStore() { 1561 latestVersion := rs.CurrentVersion() 1562 for key, store := range rs.stores { 1563 switch store.GetStoreType() { 1564 case types.StoreTypeIAVL: 1565 sName := key.Name() 1566 if evmAccStoreFilter(sName, rs.GetLatestVersion()) { 1567 continue 1568 } 1569 if filter(key.Name(), rs.lastCommitInfo.Version, nil, rs.commitFilters) { 1570 continue 1571 } 1572 s := store.(*iavl.Store) 1573 s.StopStoreWithVersion(latestVersion) 1574 case types.StoreTypeDB: 1575 panic("unexpected db store") 1576 case types.StoreTypeMulti: 1577 panic("unexpected multi store") 1578 case types.StoreTypeMPT: 1579 s := store.(*mpt.MptStore) 1580 s.StopWithVersion(latestVersion) 1581 case types.StoreTypeTransient: 1582 default: 1583 } 1584 } 1585 1586 } 1587 1588 func (rs *Store) SetLogger(log tmlog.Logger) { 1589 rs.logger = log.With("module", "root-multi") 1590 } 1591 1592 // GetLatestStoredMptHeight get latest mpt storage height 1593 func GetLatestStoredMptHeight() uint64 { 1594 db := mpt.InstanceOfMptStore() 1595 rst, err := db.TrieDB().DiskDB().Get(mpt.KeyPrefixAccLatestStoredHeight) 1596 if err != nil || len(rst) == 0 { 1597 return 0 1598 } 1599 return binary.BigEndian.Uint64(rst) 1600 } 1601 1602 func (rs *Store) SetUpgradeVersion(version int64) { 1603 rs.upgradeVersion = version 1604 }