Skip to content

Commit cea74ba

Browse files
plaidfinchavahowellromac
authored
feat: Add prehash_compare_key to allow proving nonexistence in sparse trees (cosmos#136)
* add prehash_compare_key prehash_compare_key indicates whether to compare the keys lexicographically according to their _hashed_ values (implied by the hash function given by prehash_key). This is required for nonexistence proofs in proof specs that use prehashing. * use keyForComparison in getNonExistProofForKey * apply keyForComparison in typescript verifyNonExistence * update smt spec to use prehash_sha256 * rename prehash_compare_key -> prehash_key_before_comparison * fix: Make it compile in no_std by removing naming of Vec * doc: Add comment describing keyForComparison/key_for_comparison * doc: Elaborate more on why prehashing before comparison * use updated smt test vectors * Avoid cloning `leaf_spec` when prehashing keys for comparison co-authored-by: @romac Co-authored-by: Romain Ruetschi <[email protected]> * Fix missing `Eq` derive on prost build (reverting) * Revert "Fix missing `Eq` derive on prost build (reverting)" This reverts commit 69a38e8. * Fix fmt issue in CI, fix generated code to contain `Eq` impl * add testing for SMT proofs in js --------- Co-authored-by: Ava Howell <[email protected]> Co-authored-by: Ava Howell <[email protected]> Co-authored-by: Romain Ruetschi <[email protected]>
1 parent f4deb05 commit cea74ba

23 files changed

+516
-287
lines changed

go/ics23.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func VerifyMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentPro
5252
func VerifyNonMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentProof, key []byte) bool {
5353
// decompress it before running code (no-op if not compressed)
5454
proof = Decompress(proof)
55-
np := getNonExistProofForKey(proof, key)
55+
np := getNonExistProofForKey(spec, proof, key)
5656
if np == nil {
5757
return false
5858
}
@@ -148,27 +148,27 @@ func getExistProofForKey(proof *CommitmentProof, key []byte) *ExistenceProof {
148148
return nil
149149
}
150150

151-
func getNonExistProofForKey(proof *CommitmentProof, key []byte) *NonExistenceProof {
151+
func getNonExistProofForKey(spec *ProofSpec, proof *CommitmentProof, key []byte) *NonExistenceProof {
152152
switch p := proof.Proof.(type) {
153153
case *CommitmentProof_Nonexist:
154154
np := p.Nonexist
155-
if isLeft(np.Left, key) && isRight(np.Right, key) {
155+
if isLeft(spec, np.Left, key) && isRight(spec, np.Right, key) {
156156
return np
157157
}
158158
case *CommitmentProof_Batch:
159159
for _, sub := range p.Batch.Entries {
160-
if np := sub.GetNonexist(); np != nil && isLeft(np.Left, key) && isRight(np.Right, key) {
160+
if np := sub.GetNonexist(); np != nil && isLeft(spec, np.Left, key) && isRight(spec, np.Right, key) {
161161
return np
162162
}
163163
}
164164
}
165165
return nil
166166
}
167167

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

172-
func isRight(right *ExistenceProof, key []byte) bool {
173-
return right == nil || bytes.Compare(right.Key, key) > 0
172+
func isRight(spec *ProofSpec, right *ExistenceProof, key []byte) bool {
173+
return right == nil || bytes.Compare(keyForComparison(spec, right.Key), keyForComparison(spec, key)) > 0
174174
}

go/proof.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ var TendermintSpec = &ProofSpec{
4747
var SmtSpec = &ProofSpec{
4848
LeafSpec: &LeafOp{
4949
Hash: HashOp_SHA256,
50-
PrehashKey: HashOp_NO_HASH,
50+
PrehashKey: HashOp_SHA256,
5151
PrehashValue: HashOp_SHA256,
5252
Length: LengthOp_NO_PREFIX,
5353
Prefix: []byte{0},
@@ -60,7 +60,8 @@ var SmtSpec = &ProofSpec{
6060
EmptyChild: make([]byte, 32),
6161
Hash: HashOp_SHA256,
6262
},
63-
MaxDepth: 256,
63+
MaxDepth: 256,
64+
PrehashKeyBeforeComparison: true,
6465
}
6566

6667
func encodeVarintProto(l int) []byte {
@@ -204,6 +205,17 @@ func (p *ExistenceProof) CheckAgainstSpec(spec *ProofSpec) error {
204205
return nil
205206
}
206207

208+
// If we should prehash the key before comparison, do so; otherwise, return the key. Prehashing
209+
// changes lexical comparison, so we do so before comparison if the spec sets
210+
// `PrehashKeyBeforeComparison`.
211+
func keyForComparison(spec *ProofSpec, key []byte) []byte {
212+
if !spec.PrehashKeyBeforeComparison {
213+
return key
214+
}
215+
hash, _ := doHashOrNoop(spec.LeafSpec.PrehashKey, key)
216+
return hash
217+
}
218+
207219
// Verify does all checks to ensure the proof has valid non-existence proofs,
208220
// and they ensure the given key is not in the CommitmentState
209221
func (p *NonExistenceProof) Verify(spec *ProofSpec, root CommitmentRoot, key []byte) error {
@@ -229,13 +241,13 @@ func (p *NonExistenceProof) Verify(spec *ProofSpec, root CommitmentRoot, key []b
229241

230242
// Ensure in valid range
231243
if rightKey != nil {
232-
if bytes.Compare(key, rightKey) >= 0 {
244+
if bytes.Compare(keyForComparison(spec, key), keyForComparison(spec, rightKey)) >= 0 {
233245
return errors.New("key is not left of right proof")
234246
}
235247
}
236248

237249
if leftKey != nil {
238-
if bytes.Compare(key, leftKey) <= 0 {
250+
if bytes.Compare(keyForComparison(spec, key), keyForComparison(spec, leftKey)) <= 0 {
239251
return errors.New("key is not right of left proof")
240252
}
241253
}

go/proofs.pb.go

+109-64
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/src/generated/codecimpl.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,9 @@ export namespace ics23 {
703703

704704
/** ProofSpec minDepth */
705705
minDepth?: number | null;
706+
707+
/** ProofSpec prehashKeyBeforeComparison */
708+
prehashKeyBeforeComparison?: boolean | null;
706709
}
707710

708711
/**
@@ -736,6 +739,9 @@ export namespace ics23 {
736739
/** ProofSpec minDepth. */
737740
public minDepth: number;
738741

742+
/** ProofSpec prehashKeyBeforeComparison. */
743+
public prehashKeyBeforeComparison: boolean;
744+
739745
/**
740746
* Creates a new ProofSpec instance using the specified properties.
741747
* @param [properties] Properties to set

0 commit comments

Comments
 (0)