From 5cd5003a7a4efd4933e5f07ab6c68ed3ab894a37 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 20 Oct 2023 11:45:48 -0400 Subject: [PATCH] feat(store/v2): support queries via RootStore (#18169) --- store/root/store.go | 26 +++++++++++++++--- store/root/store_test.go | 13 +++++---- store/store.go | 57 +++++++++++++++++++++++++++++++++------- 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/store/root/store.go b/store/root/store.go index abd30191fd..fb3e5ed904 100644 --- a/store/root/store.go +++ b/store/root/store.go @@ -7,7 +7,6 @@ import ( "slices" "github.com/cockroachdb/errors" - ics23 "github.com/cosmos/ics23/go" "cosmossdk.io/log" "cosmossdk.io/store/v2" @@ -150,9 +149,28 @@ func (s *Store) GetLatestVersion() (uint64, error) { return lastCommitID.Version, nil } -// GetProof delegates the GetProof to the store's underlying SC backend. -func (s *Store) GetProof(_ string, version uint64, key []byte) (*ics23.CommitmentProof, error) { - return s.stateCommitment.GetProof(version, key) +func (s *Store) Query(storeKey string, version uint64, key []byte, prove bool) (store.QueryResult, error) { + val, err := s.stateStore.Get(storeKey, version, key) + if err != nil { + return store.QueryResult{}, err + } + + result := store.QueryResult{ + Key: key, + Value: val, + Version: version, + } + + if prove { + proof, err := s.stateCommitment.GetProof(version, key) + if err != nil { + return store.QueryResult{}, err + } + + result.Proof = proof + } + + return result, nil } // LoadVersion loads a specific version returning an error upon failure. diff --git a/store/root/store_test.go b/store/root/store_test.go index 67d0852fd8..5510efa4e4 100644 --- a/store/root/store_test.go +++ b/store/root/store_test.go @@ -69,10 +69,9 @@ func (s *RootStoreTestSuite) TestGetBranchedKVStore() { s.Require().Empty(bs.GetChangeset().Pairs) } -func (s *RootStoreTestSuite) TestGetProof() { - p, err := s.rootStore.GetProof("", 1, []byte("foo")) +func (s *RootStoreTestSuite) TestQuery() { + _, err := s.rootStore.Query("", 1, []byte("foo"), true) s.Require().Error(err) - s.Require().Nil(p) // write and commit a changeset bs := s.rootStore.GetBranchedKVStore("") @@ -88,11 +87,11 @@ func (s *RootStoreTestSuite) TestGetProof() { s.Require().Equal(workingHash, commitHash) // ensure the proof is non-nil for the corresponding version - p, err = s.rootStore.GetProof("", 1, []byte("foo")) + result, err := s.rootStore.Query("", 1, []byte("foo"), true) s.Require().NoError(err) - s.Require().NotNil(p) - s.Require().Equal([]byte("foo"), p.GetExist().Key) - s.Require().Equal([]byte("bar"), p.GetExist().Value) + s.Require().NotNil(result.Proof) + s.Require().Equal([]byte("foo"), result.Proof.GetExist().Key) + s.Require().Equal([]byte("bar"), result.Proof.GetExist().Value) } func (s *RootStoreTestSuite) TestBranch() { diff --git a/store/store.go b/store/store.go index 3f23fc6734..4024a81fd7 100644 --- a/store/store.go +++ b/store/store.go @@ -6,8 +6,6 @@ import ( ics23 "github.com/cosmos/ics23/go" ) -// TODO: Move relevant types to the 'core' package. - // StoreType defines a type of KVStore. type StoreType int @@ -21,32 +19,63 @@ const ( // RootStore defines an abstraction layer containing a State Storage (SS) engine // and one or more State Commitment (SC) engines. type RootStore interface { + // GetSCStore should return the SC backend for the given store key. A RootStore + // implementation may choose to ignore the store key in cases where only a single + // SC backend is used. GetSCStore(storeKey string) Tree + // MountSCStore should mount the given SC backend for the given store key. For + // implementations that utilize a single SC backend, this method may be optional + // or a no-op. MountSCStore(storeKey string, sc Tree) error + // GetKVStore returns the KVStore for the given store key. If an implementation + // chooses to have a single SS backend, the store key may be ignored. GetKVStore(storeKey string) KVStore + // GetBranchedKVStore returns the KVStore for the given store key. If an + // implementation chooses to have a single SS backend, the store key may be + // ignored. GetBranchedKVStore(storeKey string) BranchedKVStore - GetProof(storeKey string, version uint64, key []byte) (*ics23.CommitmentProof, error) + // Query performs a query on the RootStore for a given store key, version (height), + // and key tuple. Queries should be routed to the underlying SS engine. + Query(storeKey string, version uint64, key []byte, prove bool) (QueryResult, error) + // Branch should branch the entire RootStore, i.e. a copy of the original RootStore + // except with all internal KV store(s) branched. Branch() BranchedRootStore + // SetTracingContext sets the tracing context, i.e tracing metadata, on the + // RootStore. SetTracingContext(tc TraceContext) + // SetTracer sets the tracer on the RootStore, such that any calls to GetKVStore + // or GetBranchedKVStore, will have tracing enabled. SetTracer(w io.Writer) + // TracingEnabled returns true if tracing is enabled on the RootStore. TracingEnabled() bool LoadVersion(version uint64) error LoadLatestVersion() error + + // GetLatestVersion returns the latest version, i.e. height, committed. GetLatestVersion() (uint64, error) - WorkingHash() ([]byte, error) + // SetCommitHeader sets the commit header for the next commit. This call and + // implementation is optional. However, it must be supported in cases where + // queries based on block time need to be supported. SetCommitHeader(h CommitHeader) - Commit() ([]byte, error) - // TODO: - // - // - Queries - // - // Ref: https://github.com/cosmos/cosmos-sdk/issues/17314 + // WorkingHash returns the current WIP commitment hash. Depending on the underlying + // implementation, this may need to take the current changeset and write it to + // the SC backend(s). In such cases, Commit() would return this hash and flush + // writes to disk. This means that WorkingHash mutates the RootStore and must + // be called prior to Commit(). + WorkingHash() ([]byte, error) + // Commit should be responsible for taking the current changeset and flushing + // it to disk. Note, depending on the implementation, the changeset, at this + // point, may already be written to the SC backends. Commit() should ensure + // the changeset is committed to all SC and SC backends and flushed to disk. + // It must return a hash of the merkle-ized committed state. This hash should + // be the same as the hash returned by WorkingHash() prior to calling Commit(). + Commit() ([]byte, error) io.Closer } @@ -121,3 +150,11 @@ type BranchedKVStore interface { // BranchWithTrace recursively wraps with tracing enabled. BranchWithTrace(w io.Writer, tc TraceContext) BranchedKVStore } + +// QueryResult defines the response type to performing a query on a RootStore. +type QueryResult struct { + Key []byte + Value []byte + Version uint64 + Proof *ics23.CommitmentProof +}