Skip to content

Commit

Permalink
Merge branch 'master' into colin/stricter-iavl-checks
Browse files Browse the repository at this point in the history
  • Loading branch information
colin-axner authored Oct 23, 2024
2 parents 27e5e63 + c521320 commit 1841bd3
Show file tree
Hide file tree
Showing 14 changed files with 22 additions and 1,066 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ Anyone interested in adding support for Solidity could pick up where [#58](https
* [cometbft/crypto/merkle](https://github.com/cometbft/cometbft/tree/main/crypto/merkle)
* [penumbra-zone/jmt](https://github.com/penumbra-zone/jmt)

Supported merkle stores must be lexicographically ordered to maintain soundness.

### Unsupported

* [turbofish-org/merk](https://github.com/turbofish-org/merk)
Expand Down
3 changes: 3 additions & 0 deletions go/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

- deps: bump golang to v1.22 ([#363](https://github.com/cosmos/ics23/pull/363)).
- fix: guarantee that `spec.InnerSpec.MaxPrefixLength` < `spec.InnerSpec.MinPrefixLength` + `spec.InnerSpec.ChildSize` ([#369](https://github.com/cosmos/ics23/pull/369))
- refactor: support for `BatchProof` and `CompressedBatchProof` is being dropped.
* The API's `BatchVerifyMembership`, `BatchVerifyNonMembership`, and `CombineProofs` have been removed. ([#390](https://github.com/cosmos/ics23/pull/390))
* The API's `IsCompressed`, `Compress`, and `Decompress` have been removed. ([#392](https://github.com/cosmos/ics23/pull/392))

# v0.11.0

Expand Down
159 changes: 0 additions & 159 deletions go/compress.go

This file was deleted.

34 changes: 0 additions & 34 deletions go/fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,37 +172,3 @@ func FuzzVerifyMembership(f *testing.F) {
_ = VerifyMembership(spec, ref.RootHash, proof, ref.Key, ref.Value)
})
}

func FuzzCombineProofs(f *testing.F) {
// 1. Load in the CommitmentProofs
baseDirs := []string{"iavl", "tendermint", "smt"}
filenames := []string{
"exist_left.json",
"exist_right.json",
"exist_middle.json",
"nonexist_left.json",
"nonexist_right.json",
"nonexist_middle.json",
}

for _, baseDir := range baseDirs {
dir := filepath.Join("..", "testdata", baseDir)
for _, filename := range filenames {
proofs, _ := LoadFile(new(testing.T), dir, filename)
blob, err := json.Marshal(proofs)
if err != nil {
f.Fatal(err)
}
f.Add(blob)
}
}

// 2. Now let's run the fuzzer.
f.Fuzz(func(t *testing.T, proofsJSON []byte) {
var proofs []*CommitmentProof
if err := json.Unmarshal(proofsJSON, &proofs); err != nil {
return
}
_, _ = CombineProofs(proofs)
})
}
146 changes: 14 additions & 132 deletions go/ics23.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,23 @@ and determine neighbors
*/
package ics23

import (
"bytes"
"fmt"
)

// CommitmentRoot is a byte slice that represents the merkle root of a tree that can be used to validate proofs
type CommitmentRoot []byte

// VerifyMembership returns true iff
// proof is (contains) an ExistenceProof for the given key and value AND
// calculating the root for the ExistenceProof matches the provided CommitmentRoot
// proof is an ExistenceProof for the given key and value AND
// calculating the root for the ExistenceProof matches the provided CommitmentRoot.
func VerifyMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentProof, key []byte, value []byte) bool {
// decompress it before running code (no-op if not compressed)
proof = Decompress(proof)
ep := getExistProofForKey(proof, key)
if proof == nil {
return false
}

ep := proof.GetExist()
if ep == nil {
return false
}
err := ep.Verify(spec, root, key, value)
return err == nil

return ep.Verify(spec, root, key, value) == nil
}

// VerifyNonMembership returns true iff
Expand All @@ -51,129 +48,14 @@ func VerifyMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentPro
// left and right proofs are neighbors (or left/right most if one is nil)
// provided key is between the keys of the two proofs
func VerifyNonMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentProof, key []byte) bool {
// decompress it before running code (no-op if not compressed)
proof = Decompress(proof)
np := getNonExistProofForKey(spec, proof, key)
if np == nil {
return false
}
err := np.Verify(spec, root, key)
return err == nil
}

// BatchVerifyMembership will ensure all items are also proven by the CommitmentProof (which should be a BatchProof,
// unless there is one item, when a ExistenceProof may work)
func BatchVerifyMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentProof, items map[string][]byte) bool {
// decompress it before running code (no-op if not compressed) - once for batch
proof = Decompress(proof)
for k, v := range items {
valid := VerifyMembership(spec, root, proof, []byte(k), v)
if !valid {
return false
}
}
return true
}

// BatchVerifyNonMembership will ensure all items are also proven to not be in the Commitment by the CommitmentProof
// (which should be a BatchProof, unless there is one item, when a NonExistenceProof may work)
func BatchVerifyNonMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentProof, keys [][]byte) bool {
// decompress it before running code (no-op if not compressed) - once for batch
proof = Decompress(proof)
for _, k := range keys {
valid := VerifyNonMembership(spec, root, proof, k)
if !valid {
return false
}
}
return true
}

// CombineProofs takes a number of commitment proofs (simple or batch) and
// converts them into a batch and compresses them.
//
// This is designed for proof generation libraries to create efficient batches
func CombineProofs(proofs []*CommitmentProof) (*CommitmentProof, error) {
var entries []*BatchEntry

for _, proof := range proofs {
if ex := proof.GetExist(); ex != nil {
entry := &BatchEntry{
Proof: &BatchEntry_Exist{
Exist: ex,
},
}
entries = append(entries, entry)
} else if non := proof.GetNonexist(); non != nil {
entry := &BatchEntry{
Proof: &BatchEntry_Nonexist{
Nonexist: non,
},
}
entries = append(entries, entry)
} else if batch := proof.GetBatch(); batch != nil {
entries = append(entries, batch.Entries...)
} else if comp := proof.GetCompressed(); comp != nil {
decomp := Decompress(proof)
entries = append(entries, decomp.GetBatch().Entries...)
} else {
return nil, fmt.Errorf("proof neither exist or nonexist: %#v", proof.GetProof())
}
}

batch := &CommitmentProof{
Proof: &CommitmentProof_Batch{
Batch: &BatchProof{
Entries: entries,
},
},
}

return Compress(batch), nil
}

func getExistProofForKey(proof *CommitmentProof, key []byte) *ExistenceProof {
if proof == nil {
return nil
}

switch p := proof.Proof.(type) {
case *CommitmentProof_Exist:
ep := p.Exist
if bytes.Equal(ep.Key, key) {
return ep
}
case *CommitmentProof_Batch:
for _, sub := range p.Batch.Entries {
if ep := sub.GetExist(); ep != nil && bytes.Equal(ep.Key, key) {
return ep
}
}
return false
}
return nil
}

func getNonExistProofForKey(spec *ProofSpec, proof *CommitmentProof, key []byte) *NonExistenceProof {
switch p := proof.Proof.(type) {
case *CommitmentProof_Nonexist:
np := p.Nonexist
if isLeft(spec, np.Left, key) && isRight(spec, np.Right, key) {
return np
}
case *CommitmentProof_Batch:
for _, sub := range p.Batch.Entries {
if np := sub.GetNonexist(); np != nil && isLeft(spec, np.Left, key) && isRight(spec, np.Right, key) {
return np
}
}
np := proof.GetNonexist()
if np == nil {
return false
}
return nil
}

func isLeft(spec *ProofSpec, left *ExistenceProof, key []byte) bool {
return left == nil || bytes.Compare(keyForComparison(spec, left.Key), keyForComparison(spec, key)) < 0
}

func isRight(spec *ProofSpec, right *ExistenceProof, key []byte) bool {
return right == nil || bytes.Compare(keyForComparison(spec, right.Key), keyForComparison(spec, key)) > 0
return np.Verify(spec, root, key) == nil
}
Loading

0 comments on commit 1841bd3

Please sign in to comment.