Search Apps Documentation Source Content File Folder Download Copy Actions Download

proof.gno

19.48 Kb · 654 lines
  1package ics23
  2
  3import (
  4	"bytes"
  5	"errors"
  6
  7	"gno.land/p/nt/ufmt/v0"
  8)
  9
 10type CommitmentProof interface {
 11	GetExist() *ExistenceProof
 12	GetNonexist() *NonExistenceProof
 13}
 14
 15type CommitmentProof_Exist struct {
 16	Exist *ExistenceProof
 17}
 18
 19var _ CommitmentProof = CommitmentProof_Exist{}
 20
 21func (c CommitmentProof_Exist) GetExist() *ExistenceProof       { return c.Exist }
 22func (c CommitmentProof_Exist) GetNonexist() *NonExistenceProof { return nil }
 23
 24// ExistenceProof takes a key and a value and a set of steps to perform on it.
 25// The result of peforming all these steps will provide a "root hash", which can
 26// be compared to the value in a header.
 27//
 28// Since it is computationally infeasible to produce a hash collission for any of the used
 29// cryptographic hash functions, if someone can provide a series of operations to transform
 30// a given key and value into a root hash that matches some trusted root, these key and values
 31// must be in the referenced merkle tree.
 32//
 33// The only possible issue is maliablity in LeafOp, such as providing extra prefix data,
 34// which should be controlled by a spec. Eg. with lengthOp as NONE,
 35// prefix = FOO, key = BAR, value = CHOICE
 36// and
 37// prefix = F, key = OOBAR, value = CHOICE
 38// would produce the same value.
 39//
 40// With LengthOp this is tricker but not impossible. Which is why the "leafPrefixEqual" field
 41// in the ProofSpec is valuable to prevent this mutability. And why all trees should
 42// length-prefix the data before hashing it.
 43type ExistenceProof struct {
 44	Key   []byte
 45	Value []byte
 46	Leaf  *LeafOp
 47	Path  []*InnerOp
 48}
 49
 50// LeafOp represents the raw key-value data we wish to prove, and
 51// must be flexible to represent the internal transformation from
 52// the original key-value pairs into the basis hash, for many existing
 53// merkle trees.
 54//
 55// key and value are passed in. So that the signature of this operation is:
 56// leafOp(key, value) -> output
 57//
 58// To process this, first prehash the keys and values if needed (ANY means no hash in this case):
 59// hkey = prehashKey(key)
 60// hvalue = prehashValue(value)
 61//
 62// Then combine the bytes, and hash it
 63// output = hash(prefix || length(hkey) || hkey || length(hvalue) || hvalue)
 64type LeafOp struct {
 65	Hash         HashOp
 66	PrehashKey   HashOp
 67	PrehashValue HashOp
 68	Length       LengthOp
 69	// prefix is a fixed bytes that may optionally be included at the beginning to differentiate
 70	// a leaf node from an inner node.
 71	Prefix []byte
 72}
 73
 74func (m *LeafOp) GetHash() HashOp {
 75	if m != nil {
 76		return m.Hash
 77	}
 78	return HashOp_NO_HASH
 79}
 80
 81func (m *LeafOp) GetPrefix() []byte {
 82	return m.Prefix
 83}
 84
 85// InnerOp represents a merkle-proof step that is not a leaf.
 86// It represents concatenating two children and hashing them to provide the next result.
 87//
 88// The result of the previous step is passed in, so the signature of this op is:
 89// innerOp(child) -> output
 90//
 91// The result of applying InnerOp should be:
 92// output = op.hash(op.prefix || child || op.suffix)
 93//
 94// where the || operator is concatenation of binary data,
 95// and child is the result of hashing all the tree below this step.
 96//
 97// Any special data, like prepending child with the length, or prepending the entire operation with
 98// some value to differentiate from leaf nodes, should be included in prefix and suffix.
 99// If either of prefix or suffix is empty, we just treat it as an empty string
100type InnerOp struct {
101	Hash   HashOp
102	Prefix []byte
103	Suffix []byte
104}
105
106func (m *InnerOp) GetHash() HashOp {
107	if m != nil {
108		return m.Hash
109	}
110	return HashOp_NO_HASH
111}
112
113func (m *InnerOp) GetPrefix() []byte {
114	return m.Prefix
115}
116
117// Verify does all checks to ensure this proof proves this key, value -> root
118// and matches the spec.
119func (p *ExistenceProof) Verify(spec *ProofSpec, root, key, value []byte) error {
120	if err := p.CheckAgainstSpec(spec); err != nil {
121		return err
122	}
123
124	if !bytes.Equal(key, p.Key) {
125		return ufmt.Errorf("provided key doesn't match proof")
126	}
127	if !bytes.Equal(value, p.Value) {
128		return ufmt.Errorf("provided value doesn't match proof")
129	}
130
131	calc, err := p.calculate(spec)
132	if err != nil {
133		return ufmt.Errorf("error calculating root, %v", err)
134	}
135	if !bytes.Equal(root, calc) {
136		return ufmt.Errorf("calculated root doesn't match provided root")
137	}
138
139	return nil
140}
141
142// Calculate determines the root hash that matches the given proof.
143// You must validate the result is what you have in a header.
144// Returns error if the calculations cannot be performed.
145func (p *ExistenceProof) Calculate() ([]byte, error) {
146	return p.calculate(nil)
147}
148
149func (p *ExistenceProof) calculate(spec *ProofSpec) ([]byte, error) {
150	if p.Leaf == nil {
151		return nil, errors.New("existence Proof needs defined LeafOp")
152	}
153
154	// leaf step takes the key and value as input
155	res, err := p.Leaf.Apply(p.Key, p.Value)
156	if err != nil {
157		return nil, ufmt.Errorf("leaf, %v", err)
158	}
159
160	// the rest just take the output of the last step (reducing it)
161	for _, step := range p.Path {
162		res, err = step.Apply(res)
163		if err != nil {
164			return nil, ufmt.Errorf("inner, %v", err)
165		}
166		if spec != nil {
167			if len(res) > int(spec.InnerSpec.ChildSize) && int(spec.InnerSpec.ChildSize) >= 32 {
168				return nil, ufmt.Errorf("inner, %v", err)
169			}
170		}
171	}
172	return res, nil
173}
174
175type CommitmentProof_Nonexist struct {
176	Nonexist *NonExistenceProof
177}
178
179var _ CommitmentProof = CommitmentProof_Nonexist{}
180
181func (c CommitmentProof_Nonexist) GetExist() *ExistenceProof       { return nil }
182func (c CommitmentProof_Nonexist) GetNonexist() *NonExistenceProof { return c.Nonexist }
183
184// NonExistenceProof takes a proof of two neighbors, one left of the desired key,
185// one right of the desired key. If both proofs are valid AND they are neighbors,
186// then there is no valid proof for the given key.
187type NonExistenceProof struct {
188	Key   []byte
189	Left  *ExistenceProof
190	Right *ExistenceProof
191}
192
193// Calculate determines the root hash that matches the given nonexistence proof.
194// You must validate the result is what you have in a header.
195// Returns error if the calculations cannot be performed.
196func (p *NonExistenceProof) Calculate() ([]byte, error) {
197	// A Nonexist proof may have left or right proof nil
198	switch {
199	case p.Left != nil:
200		return p.Left.Calculate()
201	case p.Right != nil:
202		return p.Right.Calculate()
203	default:
204		return nil, errors.New("nonexistence proof has empty Left and Right proof")
205	}
206}
207
208// CheckAgainstSpec will verify the leaf and all path steps are in the format defined in spec
209func (p *ExistenceProof) CheckAgainstSpec(spec *ProofSpec) error {
210	leaf := p.Leaf
211	if leaf == nil {
212		return errors.New("existence Proof needs defined LeafOp")
213	}
214	if err := leaf.CheckAgainstSpec(spec); err != nil {
215		return ufmt.Errorf("leaf, %v", err)
216	}
217	if spec.MinDepth > 0 && len(p.Path) < int(spec.MinDepth) {
218		return ufmt.Errorf("innerOps depth too short: %d", len(p.Path))
219	}
220
221	maxDepth := spec.MaxDepth
222	if maxDepth == 0 {
223		maxDepth = 128
224	}
225
226	if len(p.Path) > int(maxDepth) {
227		return ufmt.Errorf("innerOps depth too long: %d", len(p.Path))
228	}
229
230	layerNum := 1
231
232	for _, inner := range p.Path {
233		if err := inner.CheckAgainstSpec(spec, layerNum); err != nil {
234			return ufmt.Errorf("inner, %v", err)
235		}
236		layerNum++
237	}
238	return nil
239}
240
241// If we should prehash the key before comparison, do so; otherwise, return the key. Prehashing
242// changes lexical comparison, so we do so before comparison if the spec sets
243// `PrehashKeyBeforeComparison`.
244func keyForComparison(spec *ProofSpec, key []byte) []byte {
245	if !spec.PrehashKeyBeforeComparison {
246		return key
247	}
248	hash, _ := doHashOrNoop(spec.LeafSpec.PrehashKey, key)
249	return hash
250}
251
252// Verify does all checks to ensure the proof has valid non-existence proofs,
253// and they ensure the given key is not in the CommitmentState
254func (p *NonExistenceProof) Verify(spec *ProofSpec, root, key []byte) error {
255	// ensure the existence proofs are valid
256	var leftKey, rightKey []byte
257	if p.Left != nil {
258		if err := p.Left.Verify(spec, root, p.Left.Key, p.Left.Value); err != nil {
259			return ufmt.Errorf("left proof, %v", err)
260		}
261		leftKey = p.Left.Key
262	}
263	if p.Right != nil {
264		if err := p.Right.Verify(spec, root, p.Right.Key, p.Right.Value); err != nil {
265			return ufmt.Errorf("right proof, %v", err)
266		}
267		rightKey = p.Right.Key
268	}
269
270	// If both proofs are missing, this is not a valid proof
271	if leftKey == nil && rightKey == nil {
272		return errors.New("both left and right proofs missing")
273	}
274
275	// Ensure in valid range
276	if rightKey != nil {
277		if bytes.Compare(keyForComparison(spec, key), keyForComparison(spec, rightKey)) >= 0 {
278			return errors.New("key is not left of right proof")
279		}
280	}
281
282	if leftKey != nil {
283		if bytes.Compare(keyForComparison(spec, key), keyForComparison(spec, leftKey)) <= 0 {
284			return errors.New("key is not right of left proof")
285		}
286	}
287
288	switch {
289	case leftKey == nil:
290		isLeftMost, err := IsLeftMost(spec.InnerSpec, p.Right.Path)
291		if err != nil {
292			return err
293		}
294
295		if !isLeftMost {
296			return errors.New("left proof missing, right proof must be left-most")
297		}
298	case rightKey == nil:
299		isRightMost, err := IsRightMost(spec.InnerSpec, p.Left.Path)
300		if err != nil {
301			return err
302		}
303
304		if !isRightMost {
305			return errors.New("right proof missing, left proof must be right-most")
306		}
307	default:
308		isLeftNeighbor, err := IsLeftNeighbor(spec.InnerSpec, p.Left.Path, p.Right.Path)
309		if err != nil {
310			return err
311		}
312
313		if !isLeftNeighbor {
314			return errors.New("right proof missing, left proof must be right-most")
315		}
316	}
317
318	return nil
319}
320
321// IsLeftMost returns true if this is the left-most path in the tree, excluding placeholder (empty child) nodes
322func IsLeftMost(spec *InnerSpec, path []*InnerOp) (bool, error) {
323	minPrefix, maxPrefix, suffix, err := getPadding(spec, 0)
324	if err != nil {
325		return false, err
326	}
327
328	// ensure every step has a prefix and suffix defined to be leftmost, unless it is a placeholder node
329	for _, step := range path {
330		if !hasPadding(step, minPrefix, maxPrefix, suffix) {
331			leftBranchesAreEmpty, err := leftBranchesAreEmpty(spec, step)
332			if err != nil {
333				return false, err
334			}
335
336			if !leftBranchesAreEmpty {
337				return false, nil
338			}
339		}
340	}
341	return true, nil
342}
343
344// IsRightMost returns true if this is the right-most path in the tree, excluding placeholder (empty child) nodes
345func IsRightMost(spec *InnerSpec, path []*InnerOp) (bool, error) {
346	last := len(spec.ChildOrder) - 1
347	minPrefix, maxPrefix, suffix, err := getPadding(spec, int32(last))
348	if err != nil {
349		return false, err
350	}
351
352	// ensure every step has a prefix and suffix defined to be rightmost, unless it is a placeholder node
353	for _, step := range path {
354		if !hasPadding(step, minPrefix, maxPrefix, suffix) {
355			rightBranchesAreEmpty, err := rightBranchesAreEmpty(spec, step)
356			if err != nil {
357				return false, err
358			}
359
360			if !rightBranchesAreEmpty {
361				return false, nil
362			}
363		}
364	}
365	return true, nil
366}
367
368// IsLeftNeighbor returns true if `right` is the next possible path right of `left`
369//
370//	Find the common suffix from the Left.Path and Right.Path and remove it. We have LPath and RPath now, which must be neighbors.
371//	Validate that LPath[len-1] is the left neighbor of RPath[len-1]
372//	For step in LPath[0..len-1], validate step is right-most node
373//	For step in RPath[0..len-1], validate step is left-most node
374func IsLeftNeighbor(spec *InnerSpec, left []*InnerOp, right []*InnerOp) (bool, error) {
375	if len(left) == 0 || len(right) == 0 {
376		return false, nil
377	}
378
379	// count common tail (from end, near root)
380	left, topleft := left[:len(left)-1], left[len(left)-1]
381	right, topright := right[:len(right)-1], right[len(right)-1]
382	for bytes.Equal(topleft.Prefix, topright.Prefix) && bytes.Equal(topleft.Suffix, topright.Suffix) {
383		if len(left) == 0 || len(right) == 0 {
384			return false, nil
385		}
386		left, topleft = left[:len(left)-1], left[len(left)-1]
387		right, topright = right[:len(right)-1], right[len(right)-1]
388	}
389
390	// now topleft and topright are the first divergent nodes
391	// make sure they are left and right of each other
392	ok, err := isLeftStep(spec, topleft, topright)
393	if err != nil {
394		return false, err
395	}
396	if !ok {
397		return false, nil
398	}
399
400	// left and right are remaining children below the split,
401	// ensure left child is the rightmost path, and visa versa
402	isRightMost, err := IsRightMost(spec, left)
403	if err != nil {
404		return false, err
405	}
406	if !isRightMost {
407		return false, nil
408	}
409
410	isLeftMost, err := IsLeftMost(spec, right)
411	if err != nil {
412		return false, err
413	}
414	if !isLeftMost {
415		return false, nil
416	}
417	return true, nil
418}
419
420// isLeftStep assumes left and right have common parents
421// checks if left is exactly one slot to the left of right
422func isLeftStep(spec *InnerSpec, left *InnerOp, right *InnerOp) (bool, error) {
423	leftidx, err := orderFromPadding(spec, left)
424	if err != nil {
425		return false, err
426	}
427	rightidx, err := orderFromPadding(spec, right)
428	if err != nil {
429		return false, err
430	}
431
432	// TODO: is it possible there are empty (nil) children???
433	return rightidx == leftidx+1, nil
434}
435
436// checks if an op has the expected padding
437func hasPadding(op *InnerOp, minPrefix, maxPrefix, suffix int) bool {
438	if len(op.Prefix) < minPrefix {
439		return false
440	}
441	if len(op.Prefix) > maxPrefix {
442		return false
443	}
444	return len(op.Suffix) == suffix
445}
446
447// getPadding determines prefix and suffix with the given spec and position in the tree
448func getPadding(spec *InnerSpec, branch int32) (minPrefix, maxPrefix, suffix int, err error) {
449	idx, err := getPosition(spec.ChildOrder, branch)
450	if err != nil {
451		return 0, 0, 0, err
452	}
453
454	// count how many children are in the prefix
455	prefix := idx * int(spec.ChildSize)
456	minPrefix = prefix + int(spec.MinPrefixLength)
457	maxPrefix = prefix + int(spec.MaxPrefixLength)
458
459	// count how many children are in the suffix
460	suffix = (len(spec.ChildOrder) - 1 - idx) * int(spec.ChildSize)
461	return minPrefix, maxPrefix, suffix, nil
462}
463
464// leftBranchesAreEmpty returns true if the padding bytes correspond to all empty siblings
465// on the left side of a branch, ie. it's a valid placeholder on a leftmost path
466func leftBranchesAreEmpty(spec *InnerSpec, op *InnerOp) (bool, error) {
467	idx, err := orderFromPadding(spec, op)
468	if err != nil {
469		return false, err
470	}
471	// count branches to left of this
472	leftBranches := int(idx)
473	if leftBranches == 0 {
474		return false, nil
475	}
476	// compare prefix with the expected number of empty branches
477	actualPrefix := len(op.Prefix) - leftBranches*int(spec.ChildSize)
478	if actualPrefix < 0 {
479		return false, nil
480	}
481	for i := 0; i < leftBranches; i++ {
482		idx, err := getPosition(spec.ChildOrder, int32(i))
483		if err != nil {
484			return false, err
485		}
486
487		from := actualPrefix + idx*int(spec.ChildSize)
488		if !bytes.Equal(spec.EmptyChild, op.Prefix[from:from+int(spec.ChildSize)]) {
489			return false, nil
490		}
491	}
492	return true, nil
493}
494
495// rightBranchesAreEmpty returns true if the padding bytes correspond to all empty siblings
496// on the right side of a branch, ie. it's a valid placeholder on a rightmost path
497func rightBranchesAreEmpty(spec *InnerSpec, op *InnerOp) (bool, error) {
498	idx, err := orderFromPadding(spec, op)
499	if err != nil {
500		return false, err
501	}
502	// count branches to right of this one
503	rightBranches := len(spec.ChildOrder) - 1 - int(idx)
504	if rightBranches == 0 {
505		return false, nil
506	}
507	// compare suffix with the expected number of empty branches
508	if len(op.Suffix) != rightBranches*int(spec.ChildSize) {
509		return false, nil // sanity check
510	}
511	for i := 0; i < rightBranches; i++ {
512		idx, err := getPosition(spec.ChildOrder, int32(i))
513		if err != nil {
514			return false, err
515		}
516
517		from := idx * int(spec.ChildSize)
518		if !bytes.Equal(spec.EmptyChild, op.Suffix[from:from+int(spec.ChildSize)]) {
519			return false, nil
520		}
521	}
522	return true, nil
523}
524
525// getPosition checks where the branch is in the order and returns
526// the index of this branch
527func getPosition(order []int32, branch int32) (int, error) {
528	if branch < 0 || int(branch) >= len(order) {
529		return -1, ufmt.Errorf("invalid branch: %d", branch)
530	}
531	for i, item := range order {
532		if branch == item {
533			return i, nil
534		}
535	}
536
537	return -1, ufmt.Errorf("branch %d not found in order %v", branch, order)
538}
539
540// This will look at the proof and determine which order it is...
541// So we can see if it is branch 0, 1, 2 etc... to determine neighbors
542func orderFromPadding(spec *InnerSpec, inner *InnerOp) (int32, error) {
543	maxbranch := int32(len(spec.ChildOrder))
544	for branch := int32(0); branch < maxbranch; branch++ {
545		minp, maxp, suffix, err := getPadding(spec, branch)
546		if err != nil {
547			return 0, err
548		}
549		if hasPadding(inner, minp, maxp, suffix) {
550			return branch, nil
551		}
552	}
553	return 0, errors.New("cannot find any valid spacing for this node")
554}
555
556// Equal returns true if the two ProofSpec instances are deeply equal across
557// every field. Use this when you need a strict content comparison (e.g. to
558// detect attempts at substituting a different spec for an existing client).
559// For the looser, "same shape" comparison historically used to route proof
560// verification through IavlSpec/TendermintSpec, use SpecEquals instead.
561func (p *ProofSpec) Equal(other *ProofSpec) bool {
562	if p == nil || other == nil {
563		return p == other
564	}
565	if p.MaxDepth != other.MaxDepth ||
566		p.MinDepth != other.MinDepth ||
567		p.PrehashKeyBeforeComparison != other.PrehashKeyBeforeComparison {
568		return false
569	}
570	if !p.LeafSpec.Equal(other.LeafSpec) {
571		return false
572	}
573	return p.InnerSpec.Equal(other.InnerSpec)
574}
575
576// Equal returns true if the two LeafOp instances are deeply equal.
577func (p *LeafOp) Equal(other *LeafOp) bool {
578	if p == nil || other == nil {
579		return p == other
580	}
581	return p.Hash == other.Hash &&
582		p.PrehashKey == other.PrehashKey &&
583		p.PrehashValue == other.PrehashValue &&
584		p.Length == other.Length &&
585		bytes.Equal(p.Prefix, other.Prefix)
586}
587
588// Equal returns true if the two InnerSpec instances are deeply equal.
589func (p *InnerSpec) Equal(other *InnerSpec) bool {
590	if p == nil || other == nil {
591		return p == other
592	}
593	if p.ChildSize != other.ChildSize ||
594		p.MinPrefixLength != other.MinPrefixLength ||
595		p.MaxPrefixLength != other.MaxPrefixLength ||
596		p.Hash != other.Hash {
597		return false
598	}
599	if len(p.ChildOrder) != len(other.ChildOrder) {
600		return false
601	}
602	for i := range p.ChildOrder {
603		if p.ChildOrder[i] != other.ChildOrder[i] {
604			return false
605		}
606	}
607	return bytes.Equal(p.EmptyChild, other.EmptyChild)
608}
609
610// SpecEquals returns true if two ProofSpec instances match on the subset of
611// fields that matter for routing proof verification through a known spec
612// (IavlSpec/TendermintSpec). It intentionally ignores MaxDepth/MinDepth,
613// LeafOp.Prefix, ChildOrder contents, and InnerSpec.EmptyChild — it
614// over-declares equality, which we consider fine for that use case.
615// Use Equal for strict field-by-field comparison (e.g. RecoverClient).
616func (p *ProofSpec) SpecEquals(spec *ProofSpec) bool {
617	// 1. Compare LeafSpecs values.
618	switch {
619	case (p.LeafSpec == nil) != (spec.LeafSpec == nil): // One of them is nil.
620		return false
621
622	case p.LeafSpec != nil && spec.LeafSpec != nil:
623		ok := p.LeafSpec.Hash == spec.LeafSpec.Hash &&
624			p.LeafSpec.PrehashKey == spec.LeafSpec.PrehashKey &&
625			p.LeafSpec.PrehashValue == spec.LeafSpec.PrehashValue &&
626			p.LeafSpec.Length == spec.LeafSpec.Length
627		if !ok {
628			return false
629		}
630
631	default: // Both are nil, hence LeafSpec values are equal.
632	}
633
634	// 2. Compare InnerSpec values.
635	switch {
636	case (p.InnerSpec == nil) != (spec.InnerSpec == nil): // One of them is not nil.
637		return false
638
639	case p.InnerSpec != nil && spec.InnerSpec != nil: // Both are non-nil
640		ok := p.InnerSpec.Hash == spec.InnerSpec.Hash &&
641			p.InnerSpec.MinPrefixLength == spec.InnerSpec.MinPrefixLength &&
642			p.InnerSpec.MaxPrefixLength == spec.InnerSpec.MaxPrefixLength &&
643			p.InnerSpec.ChildSize == spec.InnerSpec.ChildSize &&
644			len(p.InnerSpec.ChildOrder) == len(spec.InnerSpec.ChildOrder)
645		if !ok {
646			return false
647		}
648
649	default: // Both are nil, hence InnerSpec values are equal.
650	}
651
652	// By this point all the above conditions pass so they are equal.
653	return true
654}