github.com/Finschia/finschia-sdk@v0.48.1/store/iavl/store.go (about)

     1  package iavl
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"time"
     8  
     9  	ics23 "github.com/confio/ics23/go"
    10  	"github.com/cosmos/iavl"
    11  	abci "github.com/tendermint/tendermint/abci/types"
    12  	tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto"
    13  	dbm "github.com/tendermint/tm-db"
    14  
    15  	"github.com/Finschia/ostracon/libs/log"
    16  
    17  	"github.com/Finschia/finschia-sdk/store/cachekv"
    18  	"github.com/Finschia/finschia-sdk/store/listenkv"
    19  	"github.com/Finschia/finschia-sdk/store/tracekv"
    20  	"github.com/Finschia/finschia-sdk/store/types"
    21  	"github.com/Finschia/finschia-sdk/telemetry"
    22  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    23  	"github.com/Finschia/finschia-sdk/types/kv"
    24  )
    25  
    26  const (
    27  	// DefaultIAVLCacheSize is default Iavl cache units size. 1 unit is 128 byte
    28  	// default 64MB
    29  	DefaultIAVLCacheSize = 1024 * 512
    30  )
    31  
    32  var (
    33  	_ types.KVStore                 = (*Store)(nil)
    34  	_ types.CommitStore             = (*Store)(nil)
    35  	_ types.CommitKVStore           = (*Store)(nil)
    36  	_ types.Queryable               = (*Store)(nil)
    37  	_ types.StoreWithInitialVersion = (*Store)(nil)
    38  )
    39  
    40  // Store Implements types.KVStore and CommitKVStore.
    41  type Store struct {
    42  	tree Tree
    43  }
    44  
    45  // LoadStore returns an IAVL Store as a CommitKVStore. Internally, it will load the
    46  // store's version (id) from the provided DB. An error is returned if the version
    47  // fails to load, or if called with a positive version on an empty tree.
    48  func LoadStore(db dbm.DB, logger log.Logger, key types.StoreKey, id types.CommitID, lazyLoading bool, cacheSize int, disableFastNode bool) (types.CommitKVStore, error) {
    49  	return LoadStoreWithInitialVersion(db, logger, key, id, lazyLoading, 0, cacheSize, disableFastNode)
    50  }
    51  
    52  // LoadStoreWithInitialVersion returns an IAVL Store as a CommitKVStore setting its initialVersion
    53  // to the one given. Internally, it will load the store's version (id) from the
    54  // provided DB. An error is returned if the version fails to load, or if called with a positive
    55  // version on an empty tree.
    56  func LoadStoreWithInitialVersion(db dbm.DB, logger log.Logger, key types.StoreKey, id types.CommitID, lazyLoading bool, initialVersion uint64, cacheSize int, disableFastNode bool) (types.CommitKVStore, error) {
    57  	tree, err := iavl.NewMutableTreeWithOpts(db, cacheSize, &iavl.Options{InitialVersion: initialVersion}, disableFastNode)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	isUpgradeable, err := tree.IsUpgradeable()
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	if isUpgradeable && logger != nil {
    68  		logger.Info(
    69  			"Upgrading IAVL storage for faster queries + execution on live state. This may take a while",
    70  			"store_key", key.String(),
    71  			"version", initialVersion,
    72  			"commit", fmt.Sprintf("%X", id),
    73  			"is_lazy", lazyLoading,
    74  		)
    75  	}
    76  
    77  	if lazyLoading {
    78  		_, err = tree.LazyLoadVersion(id.Version)
    79  	} else {
    80  		_, err = tree.LoadVersion(id.Version)
    81  	}
    82  
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	if logger != nil {
    88  		logger.Debug("Finished loading IAVL tree")
    89  	}
    90  
    91  	return &Store{
    92  		tree: tree,
    93  	}, nil
    94  }
    95  
    96  // UnsafeNewStore returns a reference to a new IAVL Store with a given mutable
    97  // IAVL tree reference. It should only be used for testing purposes.
    98  //
    99  // CONTRACT: The IAVL tree should be fully loaded.
   100  // CONTRACT: PruningOptions passed in as argument must be the same as pruning options
   101  // passed into iavl.MutableTree
   102  func UnsafeNewStore(tree *iavl.MutableTree) *Store {
   103  	return &Store{
   104  		tree: tree,
   105  	}
   106  }
   107  
   108  // GetImmutable returns a reference to a new store backed by an immutable IAVL
   109  // tree at a specific version (height) without any pruning options. This should
   110  // be used for querying and iteration only. If the version does not exist or has
   111  // been pruned, an empty immutable IAVL tree will be used.
   112  // Any mutable operations executed will result in a panic.
   113  func (st *Store) GetImmutable(version int64) (*Store, error) {
   114  	if !st.VersionExists(version) {
   115  		return &Store{tree: &immutableTree{&iavl.ImmutableTree{}}}, nil
   116  	}
   117  
   118  	iTree, err := st.tree.GetImmutable(version)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	return &Store{
   124  		tree: &immutableTree{iTree},
   125  	}, nil
   126  }
   127  
   128  // Commit commits the current store state and returns a CommitID with the new
   129  // version and hash.
   130  func (st *Store) Commit() types.CommitID {
   131  	defer telemetry.MeasureSince(time.Now(), "store", "iavl", "commit")
   132  
   133  	hash, version, err := st.tree.SaveVersion()
   134  	if err != nil {
   135  		panic(err)
   136  	}
   137  
   138  	return types.CommitID{
   139  		Version: version,
   140  		Hash:    hash,
   141  	}
   142  }
   143  
   144  // LastCommitID implements Committer.
   145  func (st *Store) LastCommitID() types.CommitID {
   146  	hash, err := st.tree.Hash()
   147  	if err != nil {
   148  		panic(err)
   149  	}
   150  
   151  	return types.CommitID{
   152  		Version: st.tree.Version(),
   153  		Hash:    hash,
   154  	}
   155  }
   156  
   157  // SetPruning panics as pruning options should be provided at initialization
   158  // since IAVl accepts pruning options directly.
   159  func (st *Store) SetPruning(_ types.PruningOptions) {
   160  	panic("cannot set pruning options on an initialized IAVL store")
   161  }
   162  
   163  // SetPruning panics as pruning options should be provided at initialization
   164  // since IAVl accepts pruning options directly.
   165  func (st *Store) GetPruning() types.PruningOptions {
   166  	panic("cannot get pruning options on an initialized IAVL store")
   167  }
   168  
   169  // VersionExists returns whether or not a given version is stored.
   170  func (st *Store) VersionExists(version int64) bool {
   171  	return st.tree.VersionExists(version)
   172  }
   173  
   174  // GetAllVersions returns all versions in the iavl tree
   175  func (st *Store) GetAllVersions() []int {
   176  	return st.tree.(*iavl.MutableTree).AvailableVersions()
   177  }
   178  
   179  // Implements Store.
   180  func (st *Store) GetStoreType() types.StoreType {
   181  	return types.StoreTypeIAVL
   182  }
   183  
   184  // Implements Store.
   185  func (st *Store) CacheWrap() types.CacheWrap {
   186  	return cachekv.NewStore(st)
   187  }
   188  
   189  // CacheWrapWithTrace implements the Store interface.
   190  func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap {
   191  	return cachekv.NewStore(tracekv.NewStore(st, w, tc))
   192  }
   193  
   194  // CacheWrapWithListeners implements the CacheWrapper interface.
   195  func (st *Store) CacheWrapWithListeners(storeKey types.StoreKey, listeners []types.WriteListener) types.CacheWrap {
   196  	return cachekv.NewStore(listenkv.NewStore(st, storeKey, listeners))
   197  }
   198  
   199  // Implements types.KVStore.
   200  func (st *Store) Set(key, value []byte) {
   201  	types.AssertValidKey(key)
   202  	types.AssertValidValue(value)
   203  	st.tree.Set(key, value)
   204  }
   205  
   206  // Implements types.KVStore.
   207  func (st *Store) Get(key []byte) []byte {
   208  	defer telemetry.MeasureSince(time.Now(), "store", "iavl", "get")
   209  	value, err := st.tree.Get(key)
   210  	if err != nil {
   211  		panic(err)
   212  	}
   213  	return value
   214  }
   215  
   216  // Implements types.KVStore.
   217  func (st *Store) Has(key []byte) (exists bool) {
   218  	defer telemetry.MeasureSince(time.Now(), "store", "iavl", "has")
   219  	has, err := st.tree.Has(key)
   220  	if err != nil {
   221  		panic(err)
   222  	}
   223  	return has
   224  }
   225  
   226  // Implements types.KVStore.
   227  func (st *Store) Delete(key []byte) {
   228  	defer telemetry.MeasureSince(time.Now(), "store", "iavl", "delete")
   229  	st.tree.Remove(key)
   230  }
   231  
   232  // DeleteVersions deletes a series of versions from the MutableTree. An error
   233  // is returned if any single version is invalid or the delete fails. All writes
   234  // happen in a single batch with a single commit.
   235  func (st *Store) DeleteVersions(versions ...int64) error {
   236  	return st.tree.DeleteVersions(versions...)
   237  }
   238  
   239  // LoadVersionForOverwriting attempts to load a tree at a previously committed
   240  // version, or the latest version below it. Any versions greater than targetVersion will be deleted.
   241  func (st *Store) LoadVersionForOverwriting(targetVersion int64) (int64, error) {
   242  	return st.tree.LoadVersionForOverwriting(targetVersion)
   243  }
   244  
   245  // Implements types.KVStore.
   246  func (st *Store) Iterator(start, end []byte) types.Iterator {
   247  	iterator, err := st.tree.Iterator(start, end, true)
   248  	if err != nil {
   249  		panic(err)
   250  	}
   251  	return iterator
   252  }
   253  
   254  // Implements types.KVStore.
   255  func (st *Store) ReverseIterator(start, end []byte) types.Iterator {
   256  	iterator, err := st.tree.Iterator(start, end, false)
   257  	if err != nil {
   258  		panic(err)
   259  	}
   260  	return iterator
   261  }
   262  
   263  // SetInitialVersion sets the initial version of the IAVL tree. It is used when
   264  // starting a new chain at an arbitrary height.
   265  func (st *Store) SetInitialVersion(version int64) {
   266  	st.tree.SetInitialVersion(uint64(version))
   267  }
   268  
   269  // Exports the IAVL store at the given version, returning an iavl.Exporter for the tree.
   270  func (st *Store) Export(version int64) (*iavl.Exporter, error) {
   271  	istore, err := st.GetImmutable(version)
   272  	if err != nil {
   273  		return nil, fmt.Errorf("iavl export failed for version %v: %w", version, err)
   274  	}
   275  	tree, ok := istore.tree.(*immutableTree)
   276  	if !ok || tree == nil {
   277  		return nil, fmt.Errorf("iavl export failed: unable to fetch tree for version %v", version)
   278  	}
   279  	return tree.Export(), nil
   280  }
   281  
   282  // Import imports an IAVL tree at the given version, returning an iavl.Importer for importing.
   283  func (st *Store) Import(version int64) (*iavl.Importer, error) {
   284  	tree, ok := st.tree.(*iavl.MutableTree)
   285  	if !ok {
   286  		return nil, errors.New("iavl import failed: unable to find mutable tree")
   287  	}
   288  	return tree.Import(version)
   289  }
   290  
   291  // Handle gatest the latest height, if height is 0
   292  func getHeight(tree Tree, req abci.RequestQuery) int64 {
   293  	height := req.Height
   294  	if height == 0 {
   295  		latest := tree.Version()
   296  		if tree.VersionExists(latest - 1) {
   297  			height = latest - 1
   298  		} else {
   299  			height = latest
   300  		}
   301  	}
   302  	return height
   303  }
   304  
   305  // Query implements ABCI interface, allows queries
   306  //
   307  // by default we will return from (latest height -1),
   308  // as we will have merkle proofs immediately (header height = data height + 1)
   309  // If latest-1 is not present, use latest (which must be present)
   310  // if you care to have the latest data to see a tx results, you must
   311  // explicitly set the height you want to see
   312  func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
   313  	defer telemetry.MeasureSince(time.Now(), "store", "iavl", "query")
   314  
   315  	if len(req.Data) == 0 {
   316  		return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrTxDecode, "query cannot be zero length"))
   317  	}
   318  
   319  	tree := st.tree
   320  
   321  	// store the height we chose in the response, with 0 being changed to the
   322  	// latest height
   323  	res.Height = getHeight(tree, req)
   324  
   325  	switch req.Path {
   326  	case "/key": // get by key
   327  		key := req.Data // data holds the key bytes
   328  
   329  		res.Key = key
   330  		if !st.VersionExists(res.Height) {
   331  			res.Log = iavl.ErrVersionDoesNotExist.Error()
   332  			break
   333  		}
   334  
   335  		value, err := tree.GetVersioned(key, res.Height)
   336  		if err != nil {
   337  			panic(err)
   338  		}
   339  		res.Value = value
   340  
   341  		if !req.Prove {
   342  			break
   343  		}
   344  
   345  		// Continue to prove existence/absence of value
   346  		// Must convert store.Tree to iavl.MutableTree with given version to use in CreateProof
   347  		iTree, err := tree.GetImmutable(res.Height)
   348  		if err != nil {
   349  			// sanity check: If value for given version was retrieved, immutable tree must also be retrievable
   350  			panic(fmt.Sprintf("version exists in store but could not retrieve corresponding versioned tree in store, %s", err.Error()))
   351  		}
   352  		mtree := &iavl.MutableTree{
   353  			ImmutableTree: iTree,
   354  		}
   355  
   356  		// get proof from tree and convert to merkle.Proof before adding to result
   357  		res.ProofOps = getProofFromTree(mtree, req.Data, res.Value != nil)
   358  
   359  	case "/subspace":
   360  		pairs := kv.Pairs{
   361  			Pairs: make([]kv.Pair, 0),
   362  		}
   363  
   364  		subspace := req.Data
   365  		res.Key = subspace
   366  
   367  		iterator := types.KVStorePrefixIterator(st, subspace)
   368  		for ; iterator.Valid(); iterator.Next() {
   369  			pairs.Pairs = append(pairs.Pairs, kv.Pair{Key: iterator.Key(), Value: iterator.Value()})
   370  		}
   371  		iterator.Close()
   372  
   373  		bz, err := pairs.Marshal()
   374  		if err != nil {
   375  			panic(fmt.Errorf("failed to marshal KV pairs: %w", err))
   376  		}
   377  
   378  		res.Value = bz
   379  
   380  	default:
   381  		return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unexpected query path: %v", req.Path))
   382  	}
   383  
   384  	return res
   385  }
   386  
   387  // Takes a MutableTree, a key, and a flag for creating existence or absence proof and returns the
   388  // appropriate merkle.Proof. Since this must be called after querying for the value, this function should never error
   389  // Thus, it will panic on error rather than returning it
   390  func getProofFromTree(tree *iavl.MutableTree, key []byte, exists bool) *tmcrypto.ProofOps {
   391  	var (
   392  		commitmentProof *ics23.CommitmentProof
   393  		err             error
   394  	)
   395  
   396  	if exists {
   397  		// value was found
   398  		commitmentProof, err = tree.GetMembershipProof(key)
   399  		if err != nil {
   400  			// sanity check: If value was found, membership proof must be creatable
   401  			panic(fmt.Sprintf("unexpected value for empty proof: %s", err.Error()))
   402  		}
   403  	} else {
   404  		// value wasn't found
   405  		commitmentProof, err = tree.GetNonMembershipProof(key)
   406  		if err != nil {
   407  			// sanity check: If value wasn't found, nonmembership proof must be creatable
   408  			panic(fmt.Sprintf("unexpected error for nonexistence proof: %s", err.Error()))
   409  		}
   410  	}
   411  
   412  	op := types.NewIavlCommitmentOp(key, commitmentProof)
   413  	return &tmcrypto.ProofOps{Ops: []tmcrypto.ProofOp{op.ProofOp()}}
   414  }
   415  
   416  //----------------------------------------
   417  
   418  // Implements types.Iterator.
   419  type iavlIterator struct {
   420  	dbm.Iterator
   421  }
   422  
   423  var _ types.Iterator = (*iavlIterator)(nil)