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