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  }