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

feat: ADR-040: ICS-23 proofs for SMT store #10015

Merged
merged 6 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (gov) [\#11036](https://github.com/cosmos/cosmos-sdk/pull/11036) Add in-place migrations for 0.43->0.46. Add a `migrate v0.46` CLI command for v0.43->0.46 JSON genesis migration.
* [\#11006](https://github.com/cosmos/cosmos-sdk/pull/11006) Add `debug pubkey-raw` command to allow inspecting of pubkeys in legacy bech32 format
* (x/authz) [\#10714](https://github.com/cosmos/cosmos-sdk/pull/10714) Add support for pruning expired authorizations
* [\#10015](https://github.com/cosmos/cosmos-sdk/pull/10015) ADR-040: ICS-23 proofs for SMT store

### API Breaking Changes

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/btcsuite/btcd v0.22.0-beta
github.com/cockroachdb/apd/v2 v2.0.2
github.com/coinbase/rosetta-sdk-go v0.7.2
github.com/confio/ics23/go v0.6.6
github.com/confio/ics23/go v0.7.0-rc
github.com/cosmos/btcutil v1.0.4
github.com/cosmos/cosmos-proto v1.0.0-alpha7
github.com/cosmos/cosmos-sdk/api v0.1.0-alpha4
Expand All @@ -18,6 +18,7 @@ require (
github.com/cosmos/go-bip39 v1.0.0
github.com/cosmos/iavl v0.17.3
github.com/cosmos/ledger-cosmos-go v0.11.1
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/gogo/gateway v1.1.0
github.com/gogo/protobuf v1.3.3
github.com/golang/mock v1.6.0
Expand Down Expand Up @@ -72,7 +73,6 @@ require (
github.com/cosmos/ledger-go v0.9.2 // indirect
github.com/danieljoos/wincred v1.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,9 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coinbase/rosetta-sdk-go v0.7.2 h1:uCNrASIyt7rV9bA3gzPG3JDlxVP5v/zLgi01GWngncM=
github.com/coinbase/rosetta-sdk-go v0.7.2/go.mod h1:wk9dvjZFSZiWSNkFuj3dMleTA1adLFotg5y71PhqKB4=
github.com/confio/ics23/go v0.6.6 h1:pkOy18YxxJ/r0XFDCnrl4Bjv6h4LkBSpLS6F38mrKL8=
github.com/confio/ics23/go v0.6.6/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/confio/ics23/go v0.7.0-rc h1:cH2I3xkPE6oD4tP5pmZDAfYq8V7VeXCr98X1MpARTaI=
github.com/confio/ics23/go v0.7.0-rc/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
Expand Down
8 changes: 4 additions & 4 deletions store/internal/proofs/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var (
)

/*
CreateMembershipProof will produce a CommitmentProof that the given key (and queries value) exists in the iavl tree.
CreateMembershipProof will produce a CommitmentProof that the given key (and queries value) exists in the map.
If the key doesn't exist in the tree, this will return an error.
*/
func CreateMembershipProof(data map[string][]byte, key []byte) (*ics23.CommitmentProof, error) {
Expand All @@ -36,7 +36,7 @@ func CreateMembershipProof(data map[string][]byte, key []byte) (*ics23.Commitmen
}

/*
CreateNonMembershipProof will produce a CommitmentProof that the given key doesn't exist in the iavl tree.
CreateNonMembershipProof will produce a CommitmentProof that the given key doesn't exist in the map.
If the key exists in the tree, this will return an error.
*/
func CreateNonMembershipProof(data map[string][]byte, key []byte) (*ics23.CommitmentProof, error) {
Expand Down Expand Up @@ -94,8 +94,8 @@ func createExistenceProof(data map[string][]byte, key []byte) (*ics23.ExistenceP
return nil, fmt.Errorf("cannot make existence proof if key is not in map")
}

_, ics23, _ := sdkmaps.ProofsFromMap(data)
proof := ics23[string(key)]
_, proofs, _ := sdkmaps.ProofsFromMap(data)
Copy link
Collaborator

Choose a reason for hiding this comment

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

lat return value is an error - we must check it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's actually the list of keys in the map:

func ProofsFromMap(m map[string][]byte) ([]byte, map[string]*tmcrypto.Proof, []string) {

proof := proofs[string(key)]
if proof == nil {
return nil, fmt.Errorf("returned no proof for key")
}
Expand Down
8 changes: 0 additions & 8 deletions store/rootmulti/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"github.com/tendermint/tendermint/crypto/merkle"

storetypes "github.com/cosmos/cosmos-sdk/store/types"
"github.com/cosmos/cosmos-sdk/store/v2/smt"
)

// RequireProof returns whether proof is required for the subpath.
Expand All @@ -26,10 +25,3 @@ func DefaultProofRuntime() (prt *merkle.ProofRuntime) {
prt.RegisterOpDecoder(storetypes.ProofOpSimpleMerkleCommitment, storetypes.CommitmentOpDecoder)
return
}

// SMTProofRuntime returns a ProofRuntime for sparse merkle trees.
func SMTProofRuntime() (prt *merkle.ProofRuntime) {
prt = merkle.NewProofRuntime()
prt.RegisterOpDecoder(smt.ProofType, smt.ProofDecoder)
return prt
}
26 changes: 3 additions & 23 deletions store/types/commit_info.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package types

import (
fmt "fmt"

ics23 "github.com/confio/ics23/go"
tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto"

sdkmaps "github.com/cosmos/cosmos-sdk/store/internal/maps"
sdkproofs "github.com/cosmos/cosmos-sdk/store/internal/proofs"
)

// GetHash returns the GetHash from the CommitID.
Expand Down Expand Up @@ -42,27 +38,11 @@ func (ci CommitInfo) Hash() []byte {
}

func (ci CommitInfo) ProofOp(storeName string) tmcrypto.ProofOp {
cmap := ci.toMap()
_, proofs, _ := sdkmaps.ProofsFromMap(cmap)

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

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

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

return NewSimpleMerkleCommitmentOp([]byte(storeName), commitmentProof).ProofOp()
return ret
}

func (ci CommitInfo) CommitID() CommitID {
Expand Down
45 changes: 44 additions & 1 deletion store/types/proof.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package types

import (
"fmt"

ics23 "github.com/confio/ics23/go"
"github.com/tendermint/tendermint/crypto/merkle"
tmmerkle "github.com/tendermint/tendermint/proto/tendermint/crypto"

sdkmaps "github.com/cosmos/cosmos-sdk/store/internal/maps"
sdkproofs "github.com/cosmos/cosmos-sdk/store/internal/proofs"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

const (
ProofOpIAVLCommitment = "ics23:iavl"
ProofOpSimpleMerkleCommitment = "ics23:simple"
ProofOpSMTCommitment = "ics23:smt"
)

// CommitmentOp implements merkle.ProofOperator by wrapping an ics23 CommitmentProof
Expand Down Expand Up @@ -46,6 +51,15 @@ func NewSimpleMerkleCommitmentOp(key []byte, proof *ics23.CommitmentProof) Commi
}
}

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

// CommitmentOpDecoder takes a merkle.ProofOp and attempts to decode it into a CommitmentOp ProofOperator
// The proofOp.Data is just a marshalled CommitmentProof. The Key of the CommitmentOp is extracted
// from the unmarshalled proof.
Expand All @@ -56,8 +70,10 @@ func CommitmentOpDecoder(pop tmmerkle.ProofOp) (merkle.ProofOperator, error) {
spec = ics23.IavlSpec
case ProofOpSimpleMerkleCommitment:
spec = ics23.TendermintSpec
case ProofOpSMTCommitment:
spec = ics23.SmtSpec
default:
return nil, sdkerrors.Wrapf(ErrInvalidProof, "unexpected ProofOp.Type; got %s, want supported ics23 subtypes 'ProofOpIAVLCommitment' or 'ProofOpSimpleMerkleCommitment'", pop.Type)
return nil, sdkerrors.Wrapf(ErrInvalidProof, "unexpected ProofOp.Type; got %s, want supported ics23 subtypes 'ProofOpSimpleMerkleCommitment', 'ProofOpIAVLCommitment', or 'ProofOpSMTCommitment'", pop.Type)
}

proof := &ics23.CommitmentProof{}
Expand Down Expand Up @@ -129,3 +145,30 @@ func (op CommitmentOp) ProofOp() tmmerkle.ProofOp {
Data: bz,
}
}

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

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

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

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

ret = NewSimpleMerkleCommitmentOp([]byte(storeName), commitmentProof).ProofOp()
return
}
2 changes: 1 addition & 1 deletion store/v2/multi/cache_store.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package root
package multi

import (
"github.com/cosmos/cosmos-sdk/store/cachekv"
Expand Down
2 changes: 1 addition & 1 deletion store/v2/multi/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
// of a key's (non)existence within the substore SMT, and a proof of the substore's existence within the
// MultiStore (using the Merkle map proof spec (TendermintSpec)).

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

import (
"crypto/sha256"

"github.com/tendermint/tendermint/crypto/merkle"
tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto"

types "github.com/cosmos/cosmos-sdk/store/v2"
"github.com/cosmos/cosmos-sdk/store/v2/smt"
)

// DefaultProofRuntime returns a ProofRuntime supporting SMT and simple merkle proofs.
func DefaultProofRuntime() (prt *merkle.ProofRuntime) {
prt = merkle.NewProofRuntime()
prt.RegisterOpDecoder(types.ProofOpSMTCommitment, types.CommitmentOpDecoder)
prt.RegisterOpDecoder(types.ProofOpSimpleMerkleCommitment, types.CommitmentOpDecoder)
return prt
}

// Prove commitment of key within an smt store and return ProofOps
func proveKey(s *smt.Store, key []byte) (*tmcrypto.ProofOps, error) {
var ret tmcrypto.ProofOps
keyProof, err := s.GetProofICS23(key)
if err != nil {
return nil, err
}
hkey := sha256.Sum256(key)
ret.Ops = append(ret.Ops, types.NewSmtCommitmentOp(hkey[:], keyProof).ProofOp())
return &ret, nil
}

// GetProof returns ProofOps containing: a proof for the given key within this substore;
// and a proof of the substore's existence within the MultiStore.
func (s *viewSubstore) GetProof(key []byte) (*tmcrypto.ProofOps, error) {
ret, err := proveKey(s.stateCommitmentStore, key)
if err != nil {
return nil, err
}

// Prove commitment of substore within root store
storeHashes, err := s.root.getMerkleRoots()
if err != nil {
return nil, err
}
storeProof, err := types.ProofOpFromMap(storeHashes, s.name)
if err != nil {
return nil, err
}
ret.Ops = append(ret.Ops, storeProof)
return ret, nil
}
Loading