Skip to content

Commit

Permalink
refactor(store/v2): updates from integration (#18633)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderbez authored Dec 6, 2023
1 parent 52bbf81 commit 4b98d48
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 66 deletions.
12 changes: 4 additions & 8 deletions store/commit_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ import (
)

type (
// CommitHeader defines the interface for a block header that can be provided
// to a MultiStore upon Commit. This should be optional and used to facilitate
// time-based queries only.
CommitHeader interface {
GetTime() time.Time
GetHeight() uint64
}

// CommitInfo defines commit information used by the multi-store when committing
// a version/height.
CommitInfo struct {
Expand Down Expand Up @@ -80,3 +72,7 @@ func (m *CommitInfo) GetVersion() uint64 {
func (cid CommitID) String() string {
return fmt.Sprintf("CommitID{%v:%X}", cid.Hash, cid.Version)
}

func (cid CommitID) IsZero() bool {
return cid.Version == 0 && len(cid.Hash) == 0
}
2 changes: 1 addition & 1 deletion store/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
// SDK for store operations such as Get and Set calls. In addition, callers have
// the ability to explicitly charge gas for costly operations such as signature
// verification.
type Gas uint64
type Gas = uint64

// Gas consumption descriptors.
const (
Expand Down
7 changes: 4 additions & 3 deletions store/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module cosmossdk.io/store/v2
go 1.21

require (
cosmossdk.io/core v0.12.0
cosmossdk.io/errors v1.0.0
cosmossdk.io/log v1.2.1
cosmossdk.io/math v1.2.0
Expand Down Expand Up @@ -58,13 +59,13 @@ require (
github.com/rs/zerolog v1.31.0 // indirect
github.com/sasha-s/go-deadlock v0.3.1 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
57 changes: 44 additions & 13 deletions store/go.sum

Large diffs are not rendered by default.

98 changes: 98 additions & 0 deletions store/proof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package store

import (
ics23 "github.com/cosmos/ics23/go"

errorsmod "cosmossdk.io/errors"
)

// Proof operation types
const (
ProofOpIAVLCommitment = "ics23:iavl"
ProofOpSimpleMerkleCommitment = "ics23:simple"
ProofOpSMTCommitment = "ics23:smt"
)

// CommitmentOp implements merkle.ProofOperator by wrapping an ics23 CommitmentProof.
// It also contains a Key field to determine which key the proof is proving.
// NOTE: CommitmentProof currently can either be ExistenceProof or NonexistenceProof
//
// Type and Spec are classified by the kind of merkle proof it represents allowing
// the code to be reused by more types. Spec is never on the wire, but mapped
// from type in the code.
type CommitmentOp struct {
Type string
Key []byte
Spec *ics23.ProofSpec
Proof *ics23.CommitmentProof
}

func NewIAVLCommitmentOp(key []byte, proof *ics23.CommitmentProof) CommitmentOp {
return CommitmentOp{
Type: ProofOpIAVLCommitment,
Spec: ics23.IavlSpec,
Key: key,
Proof: proof,
}
}

func NewSimpleMerkleCommitmentOp(key []byte, proof *ics23.CommitmentProof) CommitmentOp {
return CommitmentOp{
Type: ProofOpSimpleMerkleCommitment,
Spec: ics23.TendermintSpec,
Key: key,
Proof: proof,
}
}

func NewSMTCommitmentOp(key []byte, proof *ics23.CommitmentProof) CommitmentOp {
return CommitmentOp{
Type: ProofOpSMTCommitment,
Spec: ics23.SmtSpec,
Key: key,
Proof: proof,
}
}

func (op CommitmentOp) GetKey() []byte {
return op.Key
}

// Run takes in a list of arguments and attempts to run the proof op against these
// arguments. Returns the root wrapped in [][]byte if the proof op succeeds with
// given args. If not, it will return an error.
//
// CommitmentOp will accept args of length 1 or length 0. If length 1 args is
// passed in, then CommitmentOp will attempt to prove the existence of the key
// with the value provided by args[0] using the embedded CommitmentProof and returns
// the CommitmentRoot of the proof. If length 0 args is passed in, then CommitmentOp
// will attempt to prove the absence of the key in the CommitmentOp and return the
// CommitmentRoot of the proof.
func (op CommitmentOp) Run(args [][]byte) ([][]byte, error) {
// calculate root from proof
root, err := op.Proof.Calculate()
if err != nil {
return nil, errorsmod.Wrapf(ErrInvalidProof, "could not calculate root for proof: %v", err)
}

// Only support an existence proof or nonexistence proof (batch proofs currently unsupported)
switch len(args) {
case 0:
// Args are nil, so we verify the absence of the key.
absent := ics23.VerifyNonMembership(op.Spec, root, op.Proof, op.Key)
if !absent {
return nil, errorsmod.Wrapf(ErrInvalidProof, "proof did not verify absence of key: %s", string(op.Key))
}

case 1:
// Args is length 1, verify existence of key with value args[0]
if !ics23.VerifyMembership(op.Spec, root, op.Proof, op.Key, args[0]) {
return nil, errorsmod.Wrapf(ErrInvalidProof, "proof did not verify existence of key %s with given value %x", op.Key, args[0])
}

default:
return nil, errorsmod.Wrapf(ErrInvalidProof, "args must be length 0 or 1, got: %d", len(args))
}

return [][]byte{root}, nil
}
9 changes: 9 additions & 0 deletions store/pruning/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,17 @@ func NewManager(

// SetStorageOptions sets the state storage options.
func (m *Manager) SetStorageOptions(opts Options) {
m.mtx.Lock()
defer m.mtx.Unlock()

m.storageOpts = opts
}

// SetCommitmentOptions sets the state commitment options.
func (m *Manager) SetCommitmentOptions(opts Options) {
m.mtx.Lock()
defer m.mtx.Unlock()

m.commitmentOpts = opts
}

Expand Down Expand Up @@ -113,6 +119,7 @@ func (m *Manager) Prune(height uint64) {
m.chStorage <- struct{}{}
}()
}

default:
m.logger.Debug("storage pruning is still running; skipping", "version", pruneHeight)
}
Expand All @@ -134,7 +141,9 @@ func (m *Manager) Prune(height uint64) {
m.chCommitment <- struct{}{}
}()
}

default:
m.logger.Debug("commitment pruning is still running; skipping", "version", pruneHeight)
}
}
}
Expand Down
45 changes: 24 additions & 21 deletions store/root/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/cockroachdb/errors"

coreheader "cosmossdk.io/core/header"
"cosmossdk.io/log"
"cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/kv/branch"
Expand Down Expand Up @@ -43,7 +44,7 @@ type Store struct {
rootKVStore store.BranchedKVStore

// commitHeader reflects the header used when committing state (note, this isn't required and only used for query purposes)
commitHeader store.CommitHeader
commitHeader *coreheader.Info

// lastCommitInfo reflects the last version/hash that has been committed
lastCommitInfo *store.CommitInfo
Expand All @@ -66,9 +67,9 @@ type Store struct {

func New(
logger log.Logger,
initVersion uint64,
ss store.VersionedDatabase,
sc store.Committer,
ssOpts, scOpts pruning.Options,
m metrics.StoreMetrics,
) (store.RootStore, error) {
rootKVStore, err := branch.New(defaultStoreKey, ss)
Expand All @@ -77,10 +78,13 @@ func New(
}

pruningManager := pruning.NewManager(logger, ss, sc)
pruningManager.SetStorageOptions(ssOpts)
pruningManager.SetCommitmentOptions(scOpts)
pruningManager.Start()

return &Store{
logger: logger.With("module", "root_store"),
initialVersion: initVersion,
initialVersion: 1,
stateStore: ss,
stateCommitment: sc,
rootKVStore: rootKVStore,
Expand All @@ -105,23 +109,22 @@ func (s *Store) Close() (err error) {
return err
}

// SetPruningOptions sets the pruning options on the SS and SC backends.
// NOTE: It will also start the pruning manager.
func (s *Store) SetPruningOptions(ssOpts, scOpts pruning.Options) {
s.pruningManager.SetStorageOptions(ssOpts)
s.pruningManager.SetCommitmentOptions(scOpts)

s.pruningManager.Start()
func (s *Store) SetMetrics(m metrics.Metrics) {
s.telemetry = m
}

// MountSCStore performs a no-op as a SC backend must be provided at initialization.
func (s *Store) MountSCStore(_ string, _ store.Committer) error {
return errors.New("cannot mount SC store; SC must be provided on initialization")
func (s *Store) SetInitialVersion(v uint64) error {
s.initialVersion = v

// TODO(bez): Call SetInitialVersion on s.stateCommitment.
//
// Ref: https://github.com/cosmos/cosmos-sdk/issues/18597

return nil
}

// GetSCStore returns the store's state commitment (SC) backend. Note, the store
// key is ignored as there exists only a single SC tree.
func (s *Store) GetSCStore(_ string) store.Committer {
// GetSCStore returns the store's state commitment (SC) backend.
func (s *Store) GetSCStore() store.Committer {
return s.stateCommitment
}

Expand Down Expand Up @@ -191,7 +194,7 @@ func (s *Store) Query(storeKey string, version uint64, key []byte, prove bool) (
return store.QueryResult{}, err
}

result.Proof = proof
result.Proof = store.NewIAVLCommitmentOp(key, proof)
}

return result, nil
Expand Down Expand Up @@ -274,7 +277,7 @@ func (s *Store) TracingEnabled() bool {
return s.traceWriter != nil
}

func (s *Store) SetCommitHeader(h store.CommitHeader) {
func (s *Store) SetCommitHeader(h *coreheader.Info) {
s.commitHeader = h
}

Expand Down Expand Up @@ -344,8 +347,8 @@ func (s *Store) Commit() ([]byte, error) {

version := s.lastCommitInfo.Version

if s.commitHeader != nil && s.commitHeader.GetHeight() != version {
s.logger.Debug("commit header and version mismatch", "header_height", s.commitHeader.GetHeight(), "version", version)
if s.commitHeader != nil && uint64(s.commitHeader.Height) != version {
s.logger.Debug("commit header and version mismatch", "header_height", s.commitHeader.Height, "version", version)
}

changeset := s.rootKVStore.GetChangeset()
Expand All @@ -361,7 +364,7 @@ func (s *Store) Commit() ([]byte, error) {
}

if s.commitHeader != nil {
s.lastCommitInfo.Timestamp = s.commitHeader.GetTime()
s.lastCommitInfo.Timestamp = s.commitHeader.Time
}

if err := s.rootKVStore.Reset(version); err != nil {
Expand Down
15 changes: 6 additions & 9 deletions store/root/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/commitment"
"cosmossdk.io/store/v2/commitment/iavl"
"cosmossdk.io/store/v2/pruning"
"cosmossdk.io/store/v2/storage/sqlite"
)

Expand All @@ -35,7 +36,7 @@ func (s *RootStoreTestSuite) SetupTest() {
sc, err := commitment.NewCommitStore(map[string]commitment.Tree{"default": tree}, noopLog)
s.Require().NoError(err)

rs, err := New(noopLog, 1, ss, sc, nil)
rs, err := New(noopLog, ss, sc, pruning.DefaultOptions(), pruning.DefaultOptions(), nil)
s.Require().NoError(err)

rs.SetTracer(io.Discard)
Expand All @@ -51,12 +52,8 @@ func (s *RootStoreTestSuite) TearDownTest() {
s.Require().NoError(err)
}

func (s *RootStoreTestSuite) TestMountSCStore() {
s.Require().Error(s.rootStore.MountSCStore("", nil))
}

func (s *RootStoreTestSuite) TestGetSCStore() {
s.Require().Equal(s.rootStore.GetSCStore(""), s.rootStore.(*Store).stateCommitment)
s.Require().Equal(s.rootStore.GetSCStore(), s.rootStore.(*Store).stateCommitment)
}

func (s *RootStoreTestSuite) TestGetKVStore() {
Expand Down Expand Up @@ -90,9 +87,9 @@ func (s *RootStoreTestSuite) TestQuery() {
// ensure the proof is non-nil for the corresponding version
result, err := s.rootStore.Query(defaultStoreKey, 1, []byte("foo"), true)
s.Require().NoError(err)
s.Require().NotNil(result.Proof)
s.Require().Equal([]byte("foo"), result.Proof.GetExist().Key)
s.Require().Equal([]byte("bar"), result.Proof.GetExist().Value)
s.Require().NotNil(result.Proof.Proof)
s.Require().Equal([]byte("foo"), result.Proof.Proof.GetExist().Key)
s.Require().Equal([]byte("bar"), result.Proof.Proof.GetExist().Value)
}

func (s *RootStoreTestSuite) TestBranch() {
Expand Down
Loading

0 comments on commit 4b98d48

Please sign in to comment.