github.com/Finschia/finschia-sdk@v0.49.1/store/rootmulti/store.go (about) 1 package rootmulti 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 "math" 8 "sort" 9 "strings" 10 "sync" 11 12 "github.com/Finschia/ostracon/libs/log" 13 iavltree "github.com/cosmos/iavl" 14 protoio "github.com/gogo/protobuf/io" 15 gogotypes "github.com/gogo/protobuf/types" 16 "github.com/pkg/errors" 17 abci "github.com/tendermint/tendermint/abci/types" 18 "github.com/tendermint/tendermint/proto/tendermint/crypto" 19 dbm "github.com/tendermint/tm-db" 20 21 snapshottypes "github.com/Finschia/finschia-sdk/snapshots/types" 22 "github.com/Finschia/finschia-sdk/store/cachemulti" 23 "github.com/Finschia/finschia-sdk/store/dbadapter" 24 "github.com/Finschia/finschia-sdk/store/iavl" 25 "github.com/Finschia/finschia-sdk/store/listenkv" 26 "github.com/Finschia/finschia-sdk/store/mem" 27 "github.com/Finschia/finschia-sdk/store/tracekv" 28 "github.com/Finschia/finschia-sdk/store/transient" 29 "github.com/Finschia/finschia-sdk/store/types" 30 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 31 ) 32 33 const ( 34 latestVersionKey = "s/latest" 35 pruneHeightsKey = "s/pruneheights" 36 commitInfoKeyFmt = "s/%d" // s/<version> 37 38 proofsPath = "proofs" 39 ) 40 41 const iavlDisablefastNodeDefault = true 42 43 // keysFromStoreKeyMap returns a slice of keys for the provided map lexically sorted by StoreKey.Name() 44 func keysFromStoreKeyMap[V any](m map[types.StoreKey]V) []types.StoreKey { 45 keys := make([]types.StoreKey, 0, len(m)) 46 for key := range m { 47 keys = append(keys, key) 48 } 49 sort.Slice(keys, func(i, j int) bool { 50 ki, kj := keys[i], keys[j] 51 return ki.Name() < kj.Name() 52 }) 53 return keys 54 } 55 56 // Store is composed of many CommitStores. Name contrasts with 57 // cacheMultiStore which is used for branching other MultiStores. It implements 58 // the CommitMultiStore interface. 59 type Store struct { 60 db dbm.DB 61 logger log.Logger 62 lastCommitInfo *types.CommitInfo 63 pruningOpts types.PruningOptions 64 iavlCacheSize int 65 iavlDisableFastNode bool 66 storesParams map[types.StoreKey]storeParams 67 stores map[types.StoreKey]types.CommitKVStore 68 keysByName map[string]types.StoreKey 69 lazyLoading bool 70 pruneHeights []int64 71 initialVersion int64 72 73 traceWriter io.Writer 74 traceContext types.TraceContext 75 traceContextMutex sync.Mutex 76 77 interBlockCache types.MultiStorePersistentCache 78 79 listeners map[types.StoreKey][]types.WriteListener 80 } 81 82 var ( 83 _ types.CommitMultiStore = (*Store)(nil) 84 _ types.Queryable = (*Store)(nil) 85 ) 86 87 // NewStore returns a reference to a new Store object with the provided DB. The 88 // store will be created with a PruneNothing pruning strategy by default. After 89 // a store is created, KVStores must be mounted and finally LoadLatestVersion or 90 // LoadVersion must be called. 91 func NewStore(db dbm.DB, logger log.Logger) *Store { 92 return &Store{ 93 db: db, 94 logger: logger, 95 pruningOpts: types.PruneNothing, 96 iavlCacheSize: iavl.DefaultIAVLCacheSize, 97 iavlDisableFastNode: iavlDisablefastNodeDefault, 98 storesParams: make(map[types.StoreKey]storeParams), 99 stores: make(map[types.StoreKey]types.CommitKVStore), 100 keysByName: make(map[string]types.StoreKey), 101 pruneHeights: make([]int64, 0), 102 listeners: make(map[types.StoreKey][]types.WriteListener), 103 } 104 } 105 106 // GetPruning fetches the pruning strategy from the root store. 107 func (rs *Store) GetPruning() types.PruningOptions { 108 return rs.pruningOpts 109 } 110 111 // SetPruning sets the pruning strategy on the root store and all the sub-stores. 112 // Note, calling SetPruning on the root store prior to LoadVersion or 113 // LoadLatestVersion performs a no-op as the stores aren't mounted yet. 114 func (rs *Store) SetPruning(pruningOpts types.PruningOptions) { 115 rs.pruningOpts = pruningOpts 116 } 117 118 func (rs *Store) SetIAVLCacheSize(cacheSize int) { 119 rs.iavlCacheSize = cacheSize 120 } 121 122 func (rs *Store) SetIAVLDisableFastNode(disableFastNode bool) { 123 rs.iavlDisableFastNode = disableFastNode 124 } 125 126 // SetLazyLoading sets if the iavl store should be loaded lazily or not 127 func (rs *Store) SetLazyLoading(lazyLoading bool) { 128 rs.lazyLoading = lazyLoading 129 } 130 131 // GetStoreType implements Store. 132 func (rs *Store) GetStoreType() types.StoreType { 133 return types.StoreTypeMulti 134 } 135 136 // MountStoreWithDB implements CommitMultiStore. 137 func (rs *Store) MountStoreWithDB(key types.StoreKey, typ types.StoreType, db dbm.DB) { 138 if key == nil { 139 panic("MountIAVLStore() key cannot be nil") 140 } 141 if _, ok := rs.storesParams[key]; ok { 142 panic(fmt.Sprintf("store duplicate store key %v", key)) 143 } 144 if _, ok := rs.keysByName[key.Name()]; ok { 145 panic(fmt.Sprintf("store duplicate store key name %v", key)) 146 } 147 rs.storesParams[key] = storeParams{ 148 key: key, 149 typ: typ, 150 db: db, 151 } 152 rs.keysByName[key.Name()] = key 153 } 154 155 // GetCommitStore returns a mounted CommitStore for a given StoreKey. If the 156 // store is wrapped in an inter-block cache, it will be unwrapped before returning. 157 func (rs *Store) GetCommitStore(key types.StoreKey) types.CommitStore { 158 return rs.GetCommitKVStore(key) 159 } 160 161 // GetCommitKVStore returns a mounted CommitKVStore for a given StoreKey. If the 162 // store is wrapped in an inter-block cache, it will be unwrapped before returning. 163 func (rs *Store) GetCommitKVStore(key types.StoreKey) types.CommitKVStore { 164 // If the Store has an inter-block cache, first attempt to lookup and unwrap 165 // the underlying CommitKVStore by StoreKey. If it does not exist, fallback to 166 // the main mapping of CommitKVStores. 167 if rs.interBlockCache != nil { 168 if store := rs.interBlockCache.Unwrap(key); store != nil { 169 return store 170 } 171 } 172 173 return rs.stores[key] 174 } 175 176 // GetStores returns mounted stores 177 func (rs *Store) GetStores() map[types.StoreKey]types.CommitKVStore { 178 return rs.stores 179 } 180 181 // LoadLatestVersionAndUpgrade implements CommitMultiStore 182 func (rs *Store) LoadLatestVersionAndUpgrade(upgrades *types.StoreUpgrades) error { 183 ver := GetLatestVersion(rs.db) 184 return rs.loadVersion(ver, upgrades) 185 } 186 187 // LoadVersionAndUpgrade allows us to rename substores while loading an older version 188 func (rs *Store) LoadVersionAndUpgrade(ver int64, upgrades *types.StoreUpgrades) error { 189 return rs.loadVersion(ver, upgrades) 190 } 191 192 // LoadLatestVersion implements CommitMultiStore. 193 func (rs *Store) LoadLatestVersion() error { 194 ver := GetLatestVersion(rs.db) 195 return rs.loadVersion(ver, nil) 196 } 197 198 // LoadVersion implements CommitMultiStore. 199 func (rs *Store) LoadVersion(ver int64) error { 200 return rs.loadVersion(ver, nil) 201 } 202 203 func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { 204 infos := make(map[string]types.StoreInfo) 205 206 cInfo := &types.CommitInfo{} 207 208 // load old data if we are not version 0 209 if ver != 0 { 210 var err error 211 cInfo, err = getCommitInfo(rs.db, ver) 212 if err != nil { 213 return err 214 } 215 216 // convert StoreInfos slice to map 217 for _, storeInfo := range cInfo.StoreInfos { 218 infos[storeInfo.Name] = storeInfo 219 } 220 } 221 222 // load each Store (note this doesn't panic on unmounted keys now) 223 newStores := make(map[types.StoreKey]types.CommitKVStore) 224 225 storesKeys := make([]types.StoreKey, 0, len(rs.storesParams)) 226 227 for key := range rs.storesParams { 228 storesKeys = append(storesKeys, key) 229 } 230 if upgrades != nil { 231 // deterministic iteration order for upgrades 232 // (as the underlying store may change and 233 // upgrades make store changes where the execution order may matter) 234 sort.Slice(storesKeys, func(i, j int) bool { 235 return storesKeys[i].Name() < storesKeys[j].Name() 236 }) 237 } 238 239 for _, key := range storesKeys { 240 storeParams := rs.storesParams[key] 241 commitID := rs.getCommitID(infos, key.Name()) 242 243 // If it has been added, set the initial version 244 if upgrades.IsAdded(key.Name()) { 245 storeParams.initialVersion = uint64(ver) + 1 246 } 247 248 store, err := rs.loadCommitStoreFromParams(key, commitID, storeParams) 249 if err != nil { 250 return errors.Wrap(err, "failed to load store") 251 } 252 253 newStores[key] = store 254 255 // If it was deleted, remove all data 256 if upgrades.IsDeleted(key.Name()) { 257 deleteKVStore(store.(types.KVStore)) 258 } else if oldName := upgrades.RenamedFrom(key.Name()); oldName != "" { 259 // handle renames specially 260 // make an unregistered key to satify loadCommitStore params 261 oldKey := types.NewKVStoreKey(oldName) 262 oldParams := storeParams 263 oldParams.key = oldKey 264 265 // load from the old name 266 oldStore, err := rs.loadCommitStoreFromParams(oldKey, rs.getCommitID(infos, oldName), oldParams) 267 if err != nil { 268 return errors.Wrapf(err, "failed to load old store %s", oldName) 269 } 270 271 // move all data 272 moveKVStoreData(oldStore.(types.KVStore), store.(types.KVStore)) 273 } 274 } 275 276 rs.lastCommitInfo = cInfo 277 rs.stores = newStores 278 279 // load any pruned heights we missed from disk to be pruned on the next run 280 ph, err := getPruningHeights(rs.db) 281 if err == nil && len(ph) > 0 { 282 rs.pruneHeights = ph 283 } 284 285 return nil 286 } 287 288 func (rs *Store) getCommitID(infos map[string]types.StoreInfo, name string) types.CommitID { 289 info, ok := infos[name] 290 if !ok { 291 return types.CommitID{} 292 } 293 294 return info.CommitId 295 } 296 297 func deleteKVStore(kv types.KVStore) { 298 // Note that we cannot write while iterating, so load all keys here, delete below 299 var keys [][]byte 300 itr := kv.Iterator(nil, nil) 301 for itr.Valid() { 302 keys = append(keys, itr.Key()) 303 itr.Next() 304 } 305 itr.Close() 306 307 for _, k := range keys { 308 kv.Delete(k) 309 } 310 } 311 312 // we simulate move by a copy and delete 313 func moveKVStoreData(oldDB, newDB types.KVStore) { 314 // we read from one and write to another 315 itr := oldDB.Iterator(nil, nil) 316 for itr.Valid() { 317 newDB.Set(itr.Key(), itr.Value()) 318 itr.Next() 319 } 320 itr.Close() 321 322 // then delete the old store 323 deleteKVStore(oldDB) 324 } 325 326 // SetInterBlockCache sets the Store's internal inter-block (persistent) cache. 327 // When this is defined, all CommitKVStores will be wrapped with their respective 328 // inter-block cache. 329 func (rs *Store) SetInterBlockCache(c types.MultiStorePersistentCache) { 330 rs.interBlockCache = c 331 } 332 333 // SetTracer sets the tracer for the MultiStore that the underlying 334 // stores will utilize to trace operations. A MultiStore is returned. 335 func (rs *Store) SetTracer(w io.Writer) types.MultiStore { 336 rs.traceWriter = w 337 return rs 338 } 339 340 // SetTracingContext updates the tracing context for the MultiStore by merging 341 // the given context with the existing context by key. Any existing keys will 342 // be overwritten. It is implied that the caller should update the context when 343 // necessary between tracing operations. It returns a modified MultiStore. 344 func (rs *Store) SetTracingContext(tc types.TraceContext) types.MultiStore { 345 rs.traceContextMutex.Lock() 346 defer rs.traceContextMutex.Unlock() 347 if rs.traceContext != nil { 348 for k, v := range tc { 349 rs.traceContext[k] = v 350 } 351 } else { 352 rs.traceContext = tc 353 } 354 355 return rs 356 } 357 358 func (rs *Store) getTracingContext() types.TraceContext { 359 rs.traceContextMutex.Lock() 360 defer rs.traceContextMutex.Unlock() 361 362 if rs.traceContext == nil { 363 return nil 364 } 365 366 ctx := types.TraceContext{} 367 for k, v := range rs.traceContext { 368 ctx[k] = v 369 } 370 371 return ctx 372 } 373 374 // TracingEnabled returns if tracing is enabled for the MultiStore. 375 func (rs *Store) TracingEnabled() bool { 376 return rs.traceWriter != nil 377 } 378 379 // AddListeners adds listeners for a specific KVStore 380 func (rs *Store) AddListeners(key types.StoreKey, listeners []types.WriteListener) { 381 if ls, ok := rs.listeners[key]; ok { 382 rs.listeners[key] = append(ls, listeners...) 383 } else { 384 rs.listeners[key] = listeners 385 } 386 } 387 388 // ListeningEnabled returns if listening is enabled for a specific KVStore 389 func (rs *Store) ListeningEnabled(key types.StoreKey) bool { 390 if ls, ok := rs.listeners[key]; ok { 391 return len(ls) != 0 392 } 393 return false 394 } 395 396 // LastCommitID implements Committer/CommitStore. 397 func (rs *Store) LastCommitID() types.CommitID { 398 if rs.lastCommitInfo == nil { 399 return types.CommitID{ 400 Version: GetLatestVersion(rs.db), 401 } 402 } 403 404 return rs.lastCommitInfo.CommitID() 405 } 406 407 // Commit implements Committer/CommitStore. 408 func (rs *Store) Commit() types.CommitID { 409 var previousHeight, version int64 410 if rs.lastCommitInfo.GetVersion() == 0 && rs.initialVersion > 1 { 411 // This case means that no commit has been made in the store, we 412 // start from initialVersion. 413 version = rs.initialVersion 414 } else { 415 // This case can means two things: 416 // - either there was already a previous commit in the store, in which 417 // case we increment the version from there, 418 // - or there was no previous commit, and initial version was not set, 419 // in which case we start at version 1. 420 previousHeight = rs.lastCommitInfo.GetVersion() 421 version = previousHeight + 1 422 } 423 424 rs.lastCommitInfo = commitStores(version, rs.stores) 425 426 // Determine if pruneHeight height needs to be added to the list of heights to 427 // be pruned, where pruneHeight = (commitHeight - 1) - KeepRecent. 428 if rs.pruningOpts.Interval > 0 && int64(rs.pruningOpts.KeepRecent) < previousHeight { 429 pruneHeight := previousHeight - int64(rs.pruningOpts.KeepRecent) 430 // We consider this height to be pruned iff: 431 // 432 // - KeepEvery is zero as that means that all heights should be pruned. 433 // - KeepEvery % (height - KeepRecent) != 0 as that means the height is not 434 // a 'snapshot' height. 435 if rs.pruningOpts.KeepEvery == 0 || pruneHeight%int64(rs.pruningOpts.KeepEvery) != 0 { 436 rs.pruneHeights = append(rs.pruneHeights, pruneHeight) 437 } 438 } 439 440 // batch prune if the current height is a pruning interval height 441 if rs.pruningOpts.Interval > 0 && version%int64(rs.pruningOpts.Interval) == 0 { 442 rs.PruneStores(true, nil) 443 } 444 445 flushMetadata(rs.db, version, rs.lastCommitInfo, rs.pruneHeights) 446 447 return types.CommitID{ 448 Version: version, 449 Hash: rs.lastCommitInfo.Hash(), 450 } 451 } 452 453 // PruneStores will batch delete a list of heights from each mounted sub-store. 454 // If clearStorePruningHeihgts is true, store's pruneHeights is appended to the 455 // pruningHeights and reset after finishing pruning. 456 func (rs *Store) PruneStores(clearStorePruningHeihgts bool, pruningHeights []int64) { 457 if clearStorePruningHeihgts { 458 pruningHeights = append(pruningHeights, rs.pruneHeights...) 459 } 460 461 if len(rs.pruneHeights) == 0 { 462 return 463 } 464 465 for key, store := range rs.stores { 466 if store.GetStoreType() == types.StoreTypeIAVL { 467 // If the store is wrapped with an inter-block cache, we must first unwrap 468 // it to get the underlying IAVL store. 469 store = rs.GetCommitKVStore(key) 470 471 if err := store.(*iavl.Store).DeleteVersions(pruningHeights...); err != nil { 472 if errCause := errors.Cause(err); errCause != nil && !errors.Is(errCause, iavltree.ErrVersionDoesNotExist) { 473 panic(err) 474 } 475 } 476 } 477 } 478 479 if clearStorePruningHeihgts { 480 rs.pruneHeights = make([]int64, 0) 481 } 482 } 483 484 // CacheWrap implements CacheWrapper/Store/CommitStore. 485 func (rs *Store) CacheWrap() types.CacheWrap { 486 return rs.CacheMultiStore().(types.CacheWrap) 487 } 488 489 // CacheWrapWithTrace implements the CacheWrapper interface. 490 func (rs *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { 491 return rs.CacheWrap() 492 } 493 494 // CacheWrapWithListeners implements the CacheWrapper interface. 495 func (rs *Store) CacheWrapWithListeners(_ types.StoreKey, _ []types.WriteListener) types.CacheWrap { 496 return rs.CacheWrap() 497 } 498 499 // CacheMultiStore creates ephemeral branch of the multi-store and returns a CacheMultiStore. 500 // It implements the MultiStore interface. 501 func (rs *Store) CacheMultiStore() types.CacheMultiStore { 502 stores := make(map[types.StoreKey]types.CacheWrapper) 503 for k, v := range rs.stores { 504 stores[k] = v 505 } 506 return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.getTracingContext(), rs.listeners) 507 } 508 509 // CacheMultiStoreWithVersion is analogous to CacheMultiStore except that it 510 // attempts to load stores at a given version (height). An error is returned if 511 // any store cannot be loaded. This should only be used for querying and 512 // iterating at past heights. 513 func (rs *Store) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStore, error) { 514 cachedStores := make(map[types.StoreKey]types.CacheWrapper) 515 for key, store := range rs.stores { 516 switch store.GetStoreType() { 517 case types.StoreTypeIAVL: 518 // If the store is wrapped with an inter-block cache, we must first unwrap 519 // it to get the underlying IAVL store. 520 store = rs.GetCommitKVStore(key) 521 522 // Attempt to lazy-load an already saved IAVL store version. If the 523 // version does not exist or is pruned, an error should be returned. 524 iavlStore, err := store.(*iavl.Store).GetImmutable(version) 525 if err != nil { 526 return nil, err 527 } 528 529 cachedStores[key] = iavlStore 530 531 default: 532 cachedStores[key] = store 533 } 534 } 535 536 return cachemulti.NewStore(rs.db, cachedStores, rs.keysByName, rs.traceWriter, rs.getTracingContext(), rs.listeners), nil 537 } 538 539 // GetStore returns a mounted Store for a given StoreKey. If the StoreKey does 540 // not exist, it will panic. If the Store is wrapped in an inter-block cache, it 541 // will be unwrapped prior to being returned. 542 // 543 // TODO: This isn't used directly upstream. Consider returning the Store as-is 544 // instead of unwrapping. 545 func (rs *Store) GetStore(key types.StoreKey) types.Store { 546 store := rs.GetCommitKVStore(key) 547 if store == nil { 548 panic(fmt.Sprintf("store does not exist for key: %s", key.Name())) 549 } 550 551 return store 552 } 553 554 // GetKVStore returns a mounted KVStore for a given StoreKey. If tracing is 555 // enabled on the KVStore, a wrapped TraceKVStore will be returned with the root 556 // store's tracer, otherwise, the original KVStore will be returned. 557 // 558 // NOTE: The returned KVStore may be wrapped in an inter-block cache if it is 559 // set on the root store. 560 func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { 561 s := rs.stores[key] 562 if s == nil { 563 panic(fmt.Sprintf("store does not exist for key: %s", key.Name())) 564 } 565 store := s.(types.KVStore) 566 567 if rs.TracingEnabled() { 568 store = tracekv.NewStore(store, rs.traceWriter, rs.getTracingContext()) 569 } 570 if rs.ListeningEnabled(key) { 571 store = listenkv.NewStore(store, key, rs.listeners[key]) 572 } 573 574 return store 575 } 576 577 // GetStoreByName performs a lookup of a StoreKey given a store name typically 578 // provided in a path. The StoreKey is then used to perform a lookup and return 579 // a Store. If the Store is wrapped in an inter-block cache, it will be unwrapped 580 // prior to being returned. If the StoreKey does not exist, nil is returned. 581 func (rs *Store) GetStoreByName(name string) types.Store { 582 key := rs.keysByName[name] 583 if key == nil { 584 return nil 585 } 586 587 return rs.GetCommitKVStore(key) 588 } 589 590 // Query calls substore.Query with the same `req` where `req.Path` is 591 // modified to remove the substore prefix. 592 // Ie. `req.Path` here is `/<substore>/<path>`, and trimmed to `/<path>` for the substore. 593 // Special case: if `req.Path` is `/proofs`, the commit hash is included 594 // as response value. In addition, proofs of every store are appended to the response for 595 // the requested height 596 func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { 597 path := req.Path 598 firstPath, subpath, err := parsePath(path) 599 if err != nil { 600 return sdkerrors.QueryResult(err) 601 } 602 603 if firstPath == proofsPath { 604 return rs.doProofsQuery(req) 605 } 606 607 store := rs.GetStoreByName(firstPath) 608 if store == nil { 609 return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no such store: %s", firstPath)) 610 } 611 612 queryable, ok := store.(types.Queryable) 613 if !ok { 614 return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "store %s (type %T) doesn't support queries", firstPath, store)) 615 } 616 617 // trim the path and make the query 618 req.Path = subpath 619 res := queryable.Query(req) 620 621 if !req.Prove || !RequireProof(subpath) { 622 return res 623 } 624 625 if res.ProofOps == nil || len(res.ProofOps.Ops) == 0 { 626 return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "proof is unexpectedly empty; ensure height has not been pruned")) 627 } 628 629 // If the request's height is the latest height we've committed, then utilize 630 // the store's lastCommitInfo as this commit info may not be flushed to disk. 631 // Otherwise, we query for the commit info from disk. 632 var commitInfo *types.CommitInfo 633 634 if res.Height == rs.lastCommitInfo.Version { 635 commitInfo = rs.lastCommitInfo 636 } else { 637 commitInfo, err = getCommitInfo(rs.db, res.Height) 638 if err != nil { 639 return sdkerrors.QueryResult(err) 640 } 641 } 642 643 // Restore origin path and append proof op. 644 res.ProofOps.Ops = append(res.ProofOps.Ops, commitInfo.ProofOp(firstPath)) 645 646 return res 647 } 648 649 // SetInitialVersion sets the initial version of the IAVL tree. It is used when 650 // starting a new chain at an arbitrary height. 651 // NOTE: this never errors. Can we fix the function signature ? 652 func (rs *Store) SetInitialVersion(version int64) error { 653 rs.initialVersion = version 654 655 // Loop through all the stores, if it's an IAVL store, then set initial 656 // version on it. 657 for key, store := range rs.stores { 658 if store.GetStoreType() == types.StoreTypeIAVL { 659 // If the store is wrapped with an inter-block cache, we must first unwrap 660 // it to get the underlying IAVL store. 661 store = rs.GetCommitKVStore(key) 662 store.(*iavl.Store).SetInitialVersion(version) 663 } 664 } 665 666 return nil 667 } 668 669 // parsePath expects a format like /<storeName>[/<subpath>] 670 // Must start with /, subpath may be empty 671 // Returns error if it doesn't start with / 672 func parsePath(path string) (storeName, subpath string, err error) { 673 if !strings.HasPrefix(path, "/") { 674 return storeName, subpath, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid path: %s", path) 675 } 676 677 paths := strings.SplitN(path[1:], "/", 2) 678 storeName = paths[0] 679 680 if len(paths) == 2 { 681 subpath = "/" + paths[1] 682 } 683 684 return storeName, subpath, nil 685 } 686 687 //---------------------- Snapshotting ------------------ 688 689 // Snapshot implements snapshottypes.Snapshotter. The snapshot output for a given format must be 690 // identical across nodes such that chunks from different sources fit together. If the output for a 691 // given format changes (at the byte level), the snapshot format must be bumped - see 692 // TestMultistoreSnapshot_Checksum test. 693 func (rs *Store) Snapshot(height uint64, protoWriter protoio.Writer) error { 694 if height == 0 { 695 return sdkerrors.Wrap(sdkerrors.ErrLogic, "cannot snapshot height 0") 696 } 697 if height > uint64(rs.LastCommitID().Version) { 698 return sdkerrors.Wrapf(sdkerrors.ErrLogic, "cannot snapshot future height %v", height) 699 } 700 701 // Collect stores to snapshot (only IAVL stores are supported) 702 type namedStore struct { 703 *iavl.Store 704 name string 705 } 706 stores := make([]namedStore, 0) 707 keys := keysFromStoreKeyMap(rs.stores) 708 for _, key := range keys { 709 switch store := rs.GetCommitKVStore(key).(type) { 710 case *iavl.Store: 711 stores = append(stores, namedStore{name: key.Name(), Store: store}) 712 case *transient.Store, *mem.Store: 713 // Non-persisted stores shouldn't be snapshotted 714 continue 715 default: 716 return sdkerrors.Wrapf(sdkerrors.ErrLogic, 717 "don't know how to snapshot store %q of type %T", key.Name(), store) 718 } 719 } 720 sort.Slice(stores, func(i, j int) bool { 721 return strings.Compare(stores[i].name, stores[j].name) == -1 722 }) 723 724 // Export each IAVL store. Stores are serialized as a stream of SnapshotItem Protobuf 725 // messages. The first item contains a SnapshotStore with store metadata (i.e. name), 726 // and the following messages contain a SnapshotNode (i.e. an ExportNode). Store changes 727 // are demarcated by new SnapshotStore items. 728 for _, store := range stores { 729 exporter, err := store.Export(int64(height)) 730 if err != nil { 731 return err 732 } 733 defer exporter.Close() 734 err = protoWriter.WriteMsg(&snapshottypes.SnapshotItem{ 735 Item: &snapshottypes.SnapshotItem_Store{ 736 Store: &snapshottypes.SnapshotStoreItem{ 737 Name: store.name, 738 }, 739 }, 740 }) 741 if err != nil { 742 return err 743 } 744 745 for { 746 node, err := exporter.Next() 747 if errors.Is(err, iavltree.ExportDone) { 748 break 749 } else if err != nil { 750 return err 751 } 752 err = protoWriter.WriteMsg(&snapshottypes.SnapshotItem{ 753 Item: &snapshottypes.SnapshotItem_IAVL{ 754 IAVL: &snapshottypes.SnapshotIAVLItem{ 755 Key: node.Key, 756 Value: node.Value, 757 Height: int32(node.Height), 758 Version: node.Version, 759 }, 760 }, 761 }) 762 if err != nil { 763 return err 764 } 765 } 766 exporter.Close() 767 } 768 769 return nil 770 } 771 772 // Restore implements snapshottypes.Snapshotter. 773 // returns next snapshot item and error. 774 func (rs *Store) Restore( 775 height uint64, format uint32, protoReader protoio.Reader, 776 ) (snapshottypes.SnapshotItem, error) { 777 // Import nodes into stores. The first item is expected to be a SnapshotItem containing 778 // a SnapshotStoreItem, telling us which store to import into. The following items will contain 779 // SnapshotNodeItem (i.e. ExportNode) until we reach the next SnapshotStoreItem or EOF. 780 var importer *iavltree.Importer 781 var snapshotItem snapshottypes.SnapshotItem 782 loop: 783 for { 784 snapshotItem = snapshottypes.SnapshotItem{} 785 err := protoReader.ReadMsg(&snapshotItem) 786 if errors.Is(err, io.EOF) { 787 break 788 } else if err != nil { 789 return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "invalid protobuf message") 790 } 791 792 switch item := snapshotItem.Item.(type) { 793 case *snapshottypes.SnapshotItem_Store: 794 if importer != nil { 795 err = importer.Commit() 796 if err != nil { 797 return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "IAVL commit failed") 798 } 799 importer.Close() 800 } 801 store, ok := rs.GetStoreByName(item.Store.Name).(*iavl.Store) 802 if !ok || store == nil { 803 return snapshottypes.SnapshotItem{}, sdkerrors.Wrapf(sdkerrors.ErrLogic, "cannot import into non-IAVL store %q", item.Store.Name) 804 } 805 importer, err = store.Import(int64(height)) 806 if err != nil { 807 return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "import failed") 808 } 809 defer importer.Close() 810 811 case *snapshottypes.SnapshotItem_IAVL: 812 if importer == nil { 813 return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(sdkerrors.ErrLogic, "received IAVL node item before store item") 814 } 815 if item.IAVL.Height > math.MaxInt8 { 816 return snapshottypes.SnapshotItem{}, sdkerrors.Wrapf(sdkerrors.ErrLogic, "node height %v cannot exceed %v", 817 item.IAVL.Height, math.MaxInt8) 818 } 819 node := &iavltree.ExportNode{ 820 Key: item.IAVL.Key, 821 Value: item.IAVL.Value, 822 Height: int8(item.IAVL.Height), 823 Version: item.IAVL.Version, 824 } 825 // Protobuf does not differentiate between []byte{} as nil, but fortunately IAVL does 826 // not allow nil keys nor nil values for leaf nodes, so we can always set them to empty. 827 if node.Key == nil { 828 node.Key = []byte{} 829 } 830 if node.Height == 0 && node.Value == nil { 831 node.Value = []byte{} 832 } 833 err := importer.Add(node) 834 if err != nil { 835 return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "IAVL node import failed") 836 } 837 838 default: 839 break loop 840 } 841 } 842 843 if importer != nil { 844 err := importer.Commit() 845 if err != nil { 846 return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "IAVL commit failed") 847 } 848 importer.Close() 849 } 850 851 flushMetadata(rs.db, int64(height), rs.buildCommitInfo(int64(height)), []int64{}) 852 return snapshotItem, rs.LoadLatestVersion() 853 } 854 855 func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID, params storeParams) (types.CommitKVStore, error) { 856 var db dbm.DB 857 858 if params.db != nil { 859 db = dbm.NewPrefixDB(params.db, []byte("s/_/")) 860 } else { 861 prefix := "s/k:" + params.key.Name() + "/" 862 db = dbm.NewPrefixDB(rs.db, []byte(prefix)) 863 } 864 865 switch params.typ { 866 case types.StoreTypeMulti: 867 panic("recursive MultiStores not yet supported") 868 869 case types.StoreTypeIAVL: 870 var store types.CommitKVStore 871 var err error 872 873 if params.initialVersion == 0 { 874 store, err = iavl.LoadStore(db, rs.logger, key, id, rs.lazyLoading, rs.iavlCacheSize, rs.iavlDisableFastNode) 875 } else { 876 store, err = iavl.LoadStoreWithInitialVersion(db, rs.logger, key, id, rs.lazyLoading, params.initialVersion, rs.iavlCacheSize, rs.iavlDisableFastNode) 877 } 878 879 if err != nil { 880 return nil, err 881 } 882 883 if rs.interBlockCache != nil { 884 // Wrap and get a CommitKVStore with inter-block caching. Note, this should 885 // only wrap the primary CommitKVStore, not any store that is already 886 // branched as that will create unexpected behavior. 887 store = rs.interBlockCache.GetStoreCache(key, store) 888 } 889 890 return store, err 891 892 case types.StoreTypeDB: 893 return commitDBStoreAdapter{Store: dbadapter.Store{DB: db}}, nil 894 895 case types.StoreTypeTransient: 896 _, ok := key.(*types.TransientStoreKey) 897 if !ok { 898 return nil, fmt.Errorf("invalid StoreKey for StoreTypeTransient: %s", key.String()) 899 } 900 901 return transient.NewStore(), nil 902 903 case types.StoreTypeMemory: 904 if _, ok := key.(*types.MemoryStoreKey); !ok { 905 return nil, fmt.Errorf("unexpected key type for a MemoryStoreKey; got: %s", key.String()) 906 } 907 908 return mem.NewStore(), nil 909 910 default: 911 panic(fmt.Sprintf("unrecognized store type %v", params.typ)) 912 } 913 } 914 915 func (rs *Store) buildCommitInfo(version int64) *types.CommitInfo { 916 keys := keysFromStoreKeyMap(rs.stores) 917 storeInfos := []types.StoreInfo{} 918 for _, key := range keys { 919 store := rs.stores[key] 920 storeType := store.GetStoreType() 921 if storeType == types.StoreTypeTransient || storeType == types.StoreTypeMemory { 922 continue 923 } 924 storeInfos = append(storeInfos, types.StoreInfo{ 925 Name: key.Name(), 926 CommitId: store.LastCommitID(), 927 }) 928 } 929 return &types.CommitInfo{ 930 Version: version, 931 StoreInfos: storeInfos, 932 } 933 } 934 935 // RollbackToVersion delete the versions after `target` and update the latest version. 936 func (rs *Store) RollbackToVersion(target int64) error { 937 if target <= 0 { 938 return fmt.Errorf("invalid rollback height target: %d", target) 939 } 940 941 for key, store := range rs.stores { 942 if store.GetStoreType() == types.StoreTypeIAVL { 943 // If the store is wrapped with an inter-block cache, we must first unwrap 944 // it to get the underlying IAVL store. 945 store = rs.GetCommitKVStore(key) 946 _, err := store.(*iavl.Store).LoadVersionForOverwriting(target) 947 if err != nil { 948 return err 949 } 950 } 951 } 952 953 flushMetadata(rs.db, target, rs.buildCommitInfo(target), []int64{}) 954 955 return rs.LoadLatestVersion() 956 } 957 958 type storeParams struct { 959 key types.StoreKey 960 db dbm.DB 961 typ types.StoreType 962 initialVersion uint64 963 } 964 965 func GetLatestVersion(db dbm.DB) int64 { 966 bz, err := db.Get([]byte(latestVersionKey)) 967 if err != nil { 968 panic(err) 969 } else if bz == nil { 970 return 0 971 } 972 973 var latestVersion int64 974 975 if err := gogotypes.StdInt64Unmarshal(&latestVersion, bz); err != nil { 976 panic(err) 977 } 978 979 return latestVersion 980 } 981 982 // Commits each store and returns a new commitInfo. 983 func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore) *types.CommitInfo { 984 storeInfos := make([]types.StoreInfo, 0, len(storeMap)) 985 986 for key, store := range storeMap { 987 last := store.LastCommitID() 988 989 // If a commit event execution is interrupted, a new iavl store's version will be larger than the rootmulti's metadata, when the block is replayed, we should avoid committing that iavl store again. 990 var commitID types.CommitID 991 if last.Version >= version { 992 last.Version = version 993 commitID = last 994 } else { 995 commitID = store.Commit() 996 } 997 if store.GetStoreType() == types.StoreTypeTransient { 998 continue 999 } 1000 1001 si := types.StoreInfo{} 1002 si.Name = key.Name() 1003 si.CommitId = commitID 1004 storeInfos = append(storeInfos, si) 1005 } 1006 1007 return &types.CommitInfo{ 1008 Version: version, 1009 StoreInfos: storeInfos, 1010 } 1011 } 1012 1013 func (rs *Store) doProofsQuery(req abci.RequestQuery) abci.ResponseQuery { 1014 commitInfo, err := getCommitInfo(rs.db, req.Height) 1015 if err != nil { 1016 return sdkerrors.QueryResult(err) 1017 } 1018 res := abci.ResponseQuery{ 1019 Height: req.Height, 1020 Key: []byte(proofsPath), 1021 Value: commitInfo.CommitID().Hash, 1022 ProofOps: &crypto.ProofOps{Ops: make([]crypto.ProofOp, 0, len(commitInfo.StoreInfos))}, 1023 } 1024 1025 for _, storeInfo := range commitInfo.StoreInfos { 1026 res.ProofOps.Ops = append(res.ProofOps.Ops, commitInfo.ProofOp(storeInfo.Name)) 1027 } 1028 return res 1029 } 1030 1031 // Gets commitInfo from disk. 1032 func getCommitInfo(db dbm.DB, ver int64) (*types.CommitInfo, error) { 1033 cInfoKey := fmt.Sprintf(commitInfoKeyFmt, ver) 1034 1035 bz, err := db.Get([]byte(cInfoKey)) 1036 if err != nil { 1037 return nil, errors.Wrap(err, "failed to get commit info") 1038 } else if bz == nil { 1039 return nil, errors.New("no commit info found") 1040 } 1041 1042 cInfo := &types.CommitInfo{} 1043 if err = cInfo.Unmarshal(bz); err != nil { 1044 return nil, errors.Wrap(err, "failed unmarshal commit info") 1045 } 1046 1047 return cInfo, nil 1048 } 1049 1050 func setCommitInfo(batch dbm.Batch, version int64, cInfo *types.CommitInfo) { 1051 bz, err := cInfo.Marshal() 1052 if err != nil { 1053 panic(err) 1054 } 1055 1056 cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version) 1057 err = batch.Set([]byte(cInfoKey), bz) 1058 if err != nil { 1059 panic(err) 1060 } 1061 } 1062 1063 func setLatestVersion(batch dbm.Batch, version int64) { 1064 bz, err := gogotypes.StdInt64Marshal(version) 1065 if err != nil { 1066 panic(err) 1067 } 1068 1069 err = batch.Set([]byte(latestVersionKey), bz) 1070 if err != nil { 1071 panic(err) 1072 } 1073 } 1074 1075 func setPruningHeights(batch dbm.Batch, pruneHeights []int64) { 1076 bz := make([]byte, 0) 1077 for _, ph := range pruneHeights { 1078 buf := make([]byte, 8) 1079 binary.BigEndian.PutUint64(buf, uint64(ph)) 1080 bz = append(bz, buf...) 1081 } 1082 1083 err := batch.Set([]byte(pruneHeightsKey), bz) 1084 if err != nil { 1085 panic(err) 1086 } 1087 } 1088 1089 func getPruningHeights(db dbm.DB) ([]int64, error) { 1090 bz, err := db.Get([]byte(pruneHeightsKey)) 1091 if err != nil { 1092 return nil, fmt.Errorf("failed to get pruned heights: %w", err) 1093 } 1094 if len(bz) == 0 { 1095 return nil, errors.New("no pruned heights found") 1096 } 1097 1098 prunedHeights := make([]int64, len(bz)/8) 1099 i, offset := 0, 0 1100 for offset < len(bz) { 1101 prunedHeights[i] = int64(binary.BigEndian.Uint64(bz[offset : offset+8])) 1102 i++ 1103 offset += 8 1104 } 1105 1106 return prunedHeights, nil 1107 } 1108 1109 func flushMetadata(db dbm.DB, version int64, cInfo *types.CommitInfo, pruneHeights []int64) { 1110 batch := db.NewBatch() 1111 defer batch.Close() 1112 1113 setCommitInfo(batch, version, cInfo) 1114 setLatestVersion(batch, version) 1115 setPruningHeights(batch, pruneHeights) 1116 1117 if err := batch.Write(); err != nil { 1118 panic(fmt.Errorf("error on batch write %w", err)) 1119 } 1120 }