Skip to content

Commit b44946e

Browse files
committed
Add proof spec verification code
1 parent 9d8aad4 commit b44946e

File tree

4 files changed

+109
-79
lines changed

4 files changed

+109
-79
lines changed

go/ops.go

+16
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@ func WrapInner(inner *InnerOp) *ProofOp {
2727
}
2828
}
2929

30+
func asLeaf(op *ProofOp) (*LeafOp, error) {
31+
leaf, ok := op.Op.(*ProofOp_Leaf)
32+
if !ok {
33+
return nil, fmt.Errorf("op is not leaf")
34+
}
35+
return leaf.Leaf, nil
36+
}
37+
38+
func asInner(op *ProofOp) (*InnerOp, error) {
39+
inner, ok := op.Op.(*ProofOp_Inner)
40+
if !ok {
41+
return nil, fmt.Errorf("op is not inner")
42+
}
43+
return inner.Inner, nil
44+
}
45+
3046
func (op *ProofOp) Apply(args ...[]byte) ([]byte, error) {
3147
o := op.Op
3248
switch o.(type) {

go/proof.go

+60-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package proofs
22

3-
import fmt "fmt"
3+
import (
4+
"bytes"
5+
"fmt"
6+
)
47

58
// Calculate determines the root hash that matches the given proof.
69
// You must validate the result is what you have in a header.
@@ -10,19 +13,71 @@ func (p *ExistenceProof) Calculate() ([]byte, error) {
1013
return nil, fmt.Errorf("Existence Proof needs at least one step")
1114
}
1215

13-
first, rem := p.Steps[0], p.Steps[1:]
14-
// first step takes the key and value as input
15-
res, err := first.Apply(p.Key, p.Value)
16+
leaf, inners := p.Steps[0], p.Steps[1:]
17+
// leaf step takes the key and value as input
18+
res, err := leaf.Apply(p.Key, p.Value)
1619
if err != nil {
1720
return nil, err
1821
}
1922

2023
// the rest just take the output of the last step (reducing it)
21-
for _, step := range rem {
24+
for _, step := range inners {
2225
res, err = step.Apply(res)
2326
if err != nil {
2427
return nil, err
2528
}
2629
}
2730
return res, nil
2831
}
32+
33+
func (p *ExistenceProof) ValidSpec(spec *ProofSpec) error {
34+
if len(p.Steps) == 0 {
35+
return fmt.Errorf("Existence Proof needs at least one step")
36+
}
37+
38+
leafOp, inners := p.Steps[0], p.Steps[1:]
39+
leaf, err := asLeaf(leafOp)
40+
if err != nil {
41+
return err
42+
}
43+
err = p.checkLeaf(leaf, spec.LeafSpec)
44+
if err != nil {
45+
return err
46+
}
47+
for _, innerOp := range inners {
48+
inner, err := asInner(innerOp)
49+
if err != nil {
50+
return err
51+
}
52+
if err := p.checkInner(inner, spec.LeafSpec.Prefix); err != nil {
53+
return err
54+
}
55+
}
56+
return nil
57+
}
58+
59+
func (p *ExistenceProof) checkLeaf(leaf *LeafOp, spec *LeafOp) error {
60+
if leaf.Hash != spec.Hash {
61+
return fmt.Errorf("Unexpected HashOp: %d", leaf.Hash)
62+
}
63+
if leaf.PrehashKey != spec.PrehashKey {
64+
return fmt.Errorf("Unexpected PrehashKey: %d", leaf.PrehashKey)
65+
}
66+
if leaf.PrehashValue != spec.PrehashValue {
67+
return fmt.Errorf("Unexpected PrehashValue: %d", leaf.PrehashValue)
68+
}
69+
if leaf.Length != spec.Length {
70+
return fmt.Errorf("Unexpected LengthOp: %d", leaf.Length)
71+
}
72+
if !bytes.HasPrefix(leaf.Prefix, spec.Prefix) {
73+
return fmt.Errorf("Leaf Prefix doesn't start with %X", spec.Prefix)
74+
}
75+
return nil
76+
}
77+
78+
func (p *ExistenceProof) checkInner(inner *InnerOp, leafPrefix []byte) error {
79+
if bytes.HasPrefix(inner.Prefix, leafPrefix) {
80+
return fmt.Errorf("Inner Prefix starts with %X", leafPrefix)
81+
}
82+
return nil
83+
}

go/proofs.pb.go

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

proofs.proto

-3
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,4 @@ message ProofSpec {
142142
// any field in the ExistenceProof must be the same as in this spec.
143143
// except Prefix, which is just the first bytes of prefix (spec can be longer)
144144
LeafOp leaf_spec = 1;
145-
// the minimum length of a prefix in an InnerOp
146-
// note that the prefix *must not* start with the same bytes as leaf_spec.prefix
147-
int32 min_inner_prefix = 2;
148145
}

0 commit comments

Comments
 (0)