Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(store/v2): updates from integration #18633

Merged
merged 8 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

178 changes: 178 additions & 0 deletions store/proof.go
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewer(s): This is a direct port from store v1 (with linting/cleanup).

Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package store

import (
"fmt"

"github.com/cometbft/cometbft/crypto/merkle"
cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto"
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
ics23 "github.com/cosmos/ics23/go"

errorsmod "cosmossdk.io/errors"
sdkmaps "cosmossdk.io/store/v2/internal/maps"
sdkproofs "cosmossdk.io/store/v2/internal/proofs"
)

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

var _ merkle.ProofOperator = CommitmentOp{}

// 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,
}
}

// CommitmentOpDecoder takes a ProofOp and attempts to decode it into a CommitmentOp
// ProofOperator. The proofOp.Data is just a marshaled CommitmentProof. The Key
// of the CommitmentOp is extracted from the unmarshalled proof.
func CommitmentOpDecoder(pop cmtcrypto.ProofOp) (merkle.ProofOperator, error) {
var spec *ics23.ProofSpec
switch pop.Type {
case ProofOpIAVLCommitment:
spec = ics23.IavlSpec

case ProofOpSimpleMerkleCommitment:
spec = ics23.TendermintSpec

case ProofOpSMTCommitment:
spec = ics23.SmtSpec

default:
return nil, errorsmod.Wrapf(ErrInvalidProof, "unexpected ProofOp.Type; got %s, want supported ics23 subtypes 'ProofOpSimpleMerkleCommitment', 'ProofOpIAVLCommitment', or 'ProofOpSMTCommitment'", pop.Type)
}

proof := &ics23.CommitmentProof{}
if err := proof.Unmarshal(pop.Data); err != nil {
return nil, err
}

return CommitmentOp{
Type: pop.Type,
Key: pop.Key,
Spec: spec,
Proof: proof,
}, nil
}

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)
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
}

// 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))
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
}

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])
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
}

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

return [][]byte{root}, nil
}

// ProofOp implements ProofOperator interface and converts a CommitmentOp into a
// ProofOp format that can later be decoded by CommitmentOpDecoder back into a
// CommitmentOp for proof verification.
func (op CommitmentOp) ProofOp() cmtcrypto.ProofOp {
bz, err := op.Proof.Marshal()
if err != nil {
panic(fmt.Errorf("failed to marshal CommitmentProof: %w", err))
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
}

return cmtcrypto.ProofOp{
Type: op.Type,
Key: op.Key,
Data: bz,
}
}

// ProofOpFromMap generates a single proof from a map and converts it to a ProofOp.
func ProofOpFromMap(cmap map[string][]byte, storeName string) (cmtcrypto.ProofOp, error) {
_, proofs, _ := sdkmaps.ProofsFromMap(cmap)

proof := proofs[storeName]
if proof == nil {
return cmtcrypto.ProofOp{}, fmt.Errorf("ProofOp for %s but not registered store name", storeName)
}

// convert merkle.SimpleProof to CommitmentProof
existProof, err := sdkproofs.ConvertExistenceProof(proof, []byte(storeName), cmap[storeName])
if err != nil {
return cmtcrypto.ProofOp{}, fmt.Errorf("could not convert simple proof to existence proof: %w", err)
}

commitmentProof := &ics23.CommitmentProof{
Proof: &ics23.CommitmentProof_Exist{
Exist: existProof,
},
}

return NewSimpleMerkleCommitmentOp([]byte(storeName), commitmentProof).ProofOp(), 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.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

//
// 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
Loading
Loading