cosmos-sdk/store/v2/smt/ics23_test.go

109 lines
3.4 KiB
Go
Raw Normal View History

feat: ADR-040: ICS-23 proofs for SMT store (#10015) ## Description Implements [ICS-23](https://github.com/cosmos/ibc/tree/master/spec/core/ics-023-vector-commitments) conformant proofs for the SMT-based KV store and defines the proof spec as part of [ADR-040](https://github.com/cosmos/cosmos-sdk/blob/eb7d939f86c6cd7b4218492364cdda3f649f06b5/docs/architecture/adr-040-storage-and-smt-state-commitments.md). Closes: https://github.com/vulcanize/cosmos-sdk/issues/8 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - n/a - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2022-02-22 04:22:06 -08:00
package smt_test
import (
"crypto/sha256"
"testing"
ics23 "github.com/confio/ics23/go"
"github.com/stretchr/testify/assert"
"github.com/cosmos/cosmos-sdk/db/memdb"
store "github.com/cosmos/cosmos-sdk/store/v2/smt"
)
func TestProofICS23(t *testing.T) {
txn := memdb.NewDB().ReadWriter()
s := store.NewStore(txn)
// pick keys whose hashes begin with different bits
key00 := []byte("foo") // 00101100 = sha256(foo)[0]
key01 := []byte("bill") // 01100010
key10 := []byte("baz") // 10111010
key11 := []byte("bar") // 11111100
path00 := sha256.Sum256(key00)
path01 := sha256.Sum256(key01)
path10 := sha256.Sum256(key10)
val1 := []byte("0")
val2 := []byte("1")
s.Set(key01, val1)
// Membership
proof, err := s.GetProofICS23(key01)
assert.NoError(t, err)
nonexist := proof.GetNonexist()
assert.Nil(t, nonexist)
exist := proof.GetExist()
assert.NotNil(t, exist)
assert.Equal(t, 0, len(exist.Path))
assert.NoError(t, exist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1))
// Non-membership
proof, err = s.GetProofICS23(key00) // When leaf is leftmost node
assert.NoError(t, err)
nonexist = proof.GetNonexist()
assert.NotNil(t, nonexist)
assert.Nil(t, nonexist.Left)
assert.Equal(t, path00[:], nonexist.Key)
assert.NotNil(t, nonexist.Right)
assert.Equal(t, 0, len(nonexist.Right.Path))
assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path00[:]))
proof, err = s.GetProofICS23(key10) // When rightmost
assert.NoError(t, err)
nonexist = proof.GetNonexist()
assert.NotNil(t, nonexist)
assert.NotNil(t, nonexist.Left)
assert.Equal(t, 0, len(nonexist.Left.Path))
assert.Nil(t, nonexist.Right)
assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path10[:]))
badNonexist := nonexist
s.Set(key11, val2)
proof, err = s.GetProofICS23(key10) // In between two keys
assert.NoError(t, err)
nonexist = proof.GetNonexist()
assert.NotNil(t, nonexist)
assert.Equal(t, path10[:], nonexist.Key)
assert.NotNil(t, nonexist.Left)
assert.Equal(t, 1, len(nonexist.Left.Path))
assert.NotNil(t, nonexist.Right)
assert.Equal(t, 1, len(nonexist.Right.Path))
assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path10[:]))
// Make sure proofs work with a loaded store
root := s.Root()
s = store.LoadStore(txn, root)
proof, err = s.GetProofICS23(key10)
assert.NoError(t, err)
nonexist = proof.GetNonexist()
assert.Equal(t, path10[:], nonexist.Key)
assert.NotNil(t, nonexist.Left)
assert.Equal(t, 1, len(nonexist.Left.Path))
assert.NotNil(t, nonexist.Right)
assert.Equal(t, 1, len(nonexist.Right.Path))
assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path10[:]))
// Invalid proofs should fail to verify
badExist := exist // expired proof
assert.Error(t, badExist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1))
badExist = nonexist.Left
badExist.Key = key01 // .Key must contain key path
assert.Error(t, badExist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1))
badExist = nonexist.Left
badExist.Path[0].Prefix = []byte{0} // wrong inner node prefix
assert.Error(t, badExist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1))
badExist = nonexist.Left
badExist.Path = []*ics23.InnerOp{} // empty path
assert.Error(t, badExist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1))
assert.Error(t, badNonexist.Verify(ics23.SmtSpec, s.Root(), path10[:]))
badNonexist = nonexist
badNonexist.Key = key10
assert.Error(t, badNonexist.Verify(ics23.SmtSpec, s.Root(), path10[:]))
}