github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/iavl/proof_range.go (about)

     1  package iavl
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/gnolang/gno/tm2/pkg/crypto/tmhash"
    10  	"github.com/gnolang/gno/tm2/pkg/errors"
    11  )
    12  
    13  type RangeProof struct {
    14  	// You don't need the right path because
    15  	// it can be derived from what we have.
    16  	LeftPath   PathToLeaf      `json:"left_path"`
    17  	InnerNodes []PathToLeaf    `json:"inner_nodes"`
    18  	Leaves     []proofLeafNode `json:"leaves"`
    19  
    20  	// memoize
    21  	rootVerified bool
    22  	rootHash     []byte // valid iff rootVerified is true
    23  	treeEnd      bool   // valid iff rootVerified is true
    24  }
    25  
    26  // Keys returns all the keys in the RangeProof.  NOTE: The keys here may
    27  // include more keys than provided by tree.GetRangeWithProof or
    28  // MutableTree.GetVersionedRangeWithProof.  The keys returned there are only
    29  // in the provided [startKey,endKey){limit} range.  The keys returned here may
    30  // include extra keys, such as:
    31  // - the key before startKey if startKey is provided and doesn't exist;
    32  // - the key after a queried key with tree.GetWithProof, when the key is absent.
    33  func (proof *RangeProof) Keys() (keys [][]byte) {
    34  	if proof == nil {
    35  		return nil
    36  	}
    37  	for _, leaf := range proof.Leaves {
    38  		keys = append(keys, leaf.Key)
    39  	}
    40  	return keys
    41  }
    42  
    43  // String returns a string representation of the proof.
    44  func (proof *RangeProof) String() string {
    45  	if proof == nil {
    46  		return "<nil-RangeProof>"
    47  	}
    48  	return proof.StringIndented("")
    49  }
    50  
    51  func (proof *RangeProof) StringIndented(indent string) string {
    52  	istrs := make([]string, 0, len(proof.InnerNodes))
    53  	for _, ptl := range proof.InnerNodes {
    54  		istrs = append(istrs, ptl.stringIndented(indent+"    "))
    55  	}
    56  	lstrs := make([]string, 0, len(proof.Leaves))
    57  	for _, leaf := range proof.Leaves {
    58  		lstrs = append(lstrs, leaf.stringIndented(indent+"    "))
    59  	}
    60  	return fmt.Sprintf(`RangeProof{
    61  %s  LeftPath: %v
    62  %s  InnerNodes:
    63  %s    %v
    64  %s  Leaves:
    65  %s    %v
    66  %s  (rootVerified): %v
    67  %s  (rootHash): %X
    68  %s  (treeEnd): %v
    69  %s}`,
    70  		indent, proof.LeftPath.stringIndented(indent+"  "),
    71  		indent,
    72  		indent, strings.Join(istrs, "\n"+indent+"    "),
    73  		indent,
    74  		indent, strings.Join(lstrs, "\n"+indent+"    "),
    75  		indent, proof.rootVerified,
    76  		indent, proof.rootHash,
    77  		indent, proof.treeEnd,
    78  		indent)
    79  }
    80  
    81  // The index of the first leaf (of the whole tree).
    82  // Returns -1 if the proof is nil.
    83  func (proof *RangeProof) LeftIndex() int64 {
    84  	if proof == nil {
    85  		return -1
    86  	}
    87  	return proof.LeftPath.Index()
    88  }
    89  
    90  // Also see LeftIndex().
    91  // Verify that a key has some value.
    92  // Does not assume that the proof itself is valid, call Verify() first.
    93  func (proof *RangeProof) VerifyItem(key, value []byte) error {
    94  	if proof == nil {
    95  		return errors.Wrap(ErrInvalidProof, "proof is nil")
    96  	}
    97  	leaves := proof.Leaves
    98  	if !proof.rootVerified {
    99  		return errors.New("must call Verify(root) first")
   100  	}
   101  	i := sort.Search(len(leaves), func(i int) bool {
   102  		return bytes.Compare(key, leaves[i].Key) <= 0
   103  	})
   104  	if i >= len(leaves) || !bytes.Equal(leaves[i].Key, key) {
   105  		return errors.Wrap(ErrInvalidProof, "leaf key not found in proof")
   106  	}
   107  	valueHash := tmhash.Sum(value)
   108  	if !bytes.Equal(leaves[i].ValueHash, valueHash) {
   109  		return errors.Wrap(ErrInvalidProof, "leaf value hash not same")
   110  	}
   111  	return nil
   112  }
   113  
   114  // Verify that proof is valid absence proof for key.
   115  // Does not assume that the proof itself is valid.
   116  // For that, use Verify(root).
   117  func (proof *RangeProof) VerifyAbsence(key []byte) error {
   118  	if proof == nil {
   119  		return errors.Wrap(ErrInvalidProof, "proof is nil")
   120  	}
   121  	if !proof.rootVerified {
   122  		return errors.New("must call Verify(root) first")
   123  	}
   124  	cmp := bytes.Compare(key, proof.Leaves[0].Key)
   125  	if cmp < 0 {
   126  		if proof.LeftPath.isLeftmost() {
   127  			return nil
   128  		} else {
   129  			return errors.New("absence not proved by left path")
   130  		}
   131  	} else if cmp == 0 {
   132  		return errors.New("absence disproved via first item #0")
   133  	}
   134  	if len(proof.LeftPath) == 0 {
   135  		return nil // proof ok
   136  	}
   137  	if proof.LeftPath.isRightmost() {
   138  		return nil
   139  	}
   140  
   141  	// See if any of the leaves are greater than key.
   142  	for i := 1; i < len(proof.Leaves); i++ {
   143  		leaf := proof.Leaves[i]
   144  		cmp := bytes.Compare(key, leaf.Key)
   145  		if cmp < 0 {
   146  			return nil // proof ok
   147  		} else if cmp == 0 {
   148  			return errors.New(fmt.Sprintf("absence disproved via item #%v", i))
   149  		} else {
   150  			// if i == len(proof.Leaves)-1 {
   151  			// If last item, check whether
   152  			// it's the last item in the tree.
   153  
   154  			// }
   155  			continue
   156  		}
   157  	}
   158  
   159  	// It's still a valid proof if our last leaf is the rightmost child.
   160  	if proof.treeEnd {
   161  		return nil // OK!
   162  	}
   163  
   164  	// It's not a valid absence proof.
   165  	if len(proof.Leaves) < 2 {
   166  		return errors.New("absence not proved by right leaf (need another leaf?)")
   167  	} else {
   168  		return errors.New("absence not proved by right leaf")
   169  	}
   170  }
   171  
   172  // Verify that proof is valid.
   173  func (proof *RangeProof) Verify(root []byte) error {
   174  	if proof == nil {
   175  		return errors.Wrap(ErrInvalidProof, "proof is nil")
   176  	}
   177  	err := proof.verify(root)
   178  	return err
   179  }
   180  
   181  func (proof *RangeProof) verify(root []byte) (err error) {
   182  	rootHash := proof.rootHash
   183  	if rootHash == nil {
   184  		derivedHash, err := proof.computeRootHash()
   185  		if err != nil {
   186  			return err
   187  		}
   188  		rootHash = derivedHash
   189  	}
   190  	if !bytes.Equal(rootHash, root) {
   191  		return errors.Wrap(ErrInvalidRoot, "root hash doesn't match")
   192  	} else {
   193  		proof.rootVerified = true
   194  	}
   195  	return nil
   196  }
   197  
   198  // ComputeRootHash computes the root hash with leaves.
   199  // Returns nil if error or proof is nil.
   200  // Does not verify the root hash.
   201  func (proof *RangeProof) ComputeRootHash() []byte {
   202  	if proof == nil {
   203  		return nil
   204  	}
   205  	rootHash, _ := proof.computeRootHash()
   206  	return rootHash
   207  }
   208  
   209  func (proof *RangeProof) computeRootHash() (rootHash []byte, err error) {
   210  	rootHash, treeEnd, err := proof._computeRootHash()
   211  	if err == nil {
   212  		proof.rootHash = rootHash // memoize
   213  		proof.treeEnd = treeEnd   // memoize
   214  	}
   215  	return rootHash, err
   216  }
   217  
   218  func (proof *RangeProof) _computeRootHash() (rootHash []byte, treeEnd bool, err error) {
   219  	if len(proof.Leaves) == 0 {
   220  		return nil, false, errors.Wrap(ErrInvalidProof, "no leaves")
   221  	}
   222  	if len(proof.InnerNodes)+1 != len(proof.Leaves) {
   223  		return nil, false, errors.Wrap(ErrInvalidProof, "InnerNodes vs Leaves length mismatch, leaves should be 1 more.")
   224  	}
   225  
   226  	// Start from the left path and prove each leaf.
   227  
   228  	// shared across recursive calls
   229  	leaves := proof.Leaves
   230  	innersq := proof.InnerNodes
   231  	var COMPUTEHASH func(path PathToLeaf, rightmost bool) (hash []byte, treeEnd bool, done bool, err error)
   232  
   233  	// rightmost: is the root a rightmost child of the tree?
   234  	// treeEnd: true iff the last leaf is the last item of the tree.
   235  	// Returns the (possibly intermediate, possibly root) hash.
   236  	COMPUTEHASH = func(path PathToLeaf, rightmost bool) (hash []byte, treeEnd bool, done bool, err error) {
   237  		// Pop next leaf.
   238  		nleaf, rleaves := leaves[0], leaves[1:]
   239  		leaves = rleaves
   240  
   241  		// Compute hash.
   242  		hash = (pathWithLeaf{
   243  			Path: path,
   244  			Leaf: nleaf,
   245  		}).computeRootHash()
   246  
   247  		// If we don't have any leaves left, we're done.
   248  		if len(leaves) == 0 {
   249  			rightmost = rightmost && path.isRightmost()
   250  			return hash, rightmost, true, nil
   251  		}
   252  
   253  		// Prove along path (until we run out of leaves).
   254  		for len(path) > 0 {
   255  			// Drop the leaf-most (last-most) inner nodes from path
   256  			// until we encounter one with a left hash.
   257  			// We assume that the left side is already verified.
   258  			// rpath: rest of path
   259  			// lpath: last path item
   260  			rpath, lpath := path[:len(path)-1], path[len(path)-1]
   261  			path = rpath
   262  			if len(lpath.Right) == 0 {
   263  				continue
   264  			}
   265  
   266  			// Pop next inners, a PathToLeaf (e.g. []proofInnerNode).
   267  			inners, rinnersq := innersq[0], innersq[1:]
   268  			innersq = rinnersq
   269  
   270  			// Recursively verify inners against remaining leaves.
   271  			derivedRoot, treeEnd, done, err := COMPUTEHASH(inners, rightmost && rpath.isRightmost())
   272  			if err != nil {
   273  				return nil, treeEnd, false, errors.Wrap(err, "recursive COMPUTEHASH call")
   274  			}
   275  			if !bytes.Equal(derivedRoot, lpath.Right) {
   276  				return nil, treeEnd, false, errors.Wrap(ErrInvalidRoot, "intermediate root hash %X doesn't match, got %X", lpath.Right, derivedRoot)
   277  			}
   278  			if done {
   279  				return hash, treeEnd, true, nil
   280  			}
   281  		}
   282  
   283  		// We're not done yet (leaves left over). No error, not done either.
   284  		// Technically if rightmost, we know there's an error "left over leaves
   285  		// -- malformed proof", but we return that at the top level, below.
   286  		return hash, false, false, nil
   287  	}
   288  
   289  	// Verify!
   290  	path := proof.LeftPath
   291  	rootHash, treeEnd, done, err := COMPUTEHASH(path, true)
   292  	if err != nil {
   293  		return nil, treeEnd, errors.Wrap(err, "root COMPUTEHASH call")
   294  	} else if !done {
   295  		return nil, treeEnd, errors.Wrap(ErrInvalidProof, "left over leaves -- malformed proof")
   296  	}
   297  
   298  	// Ok!
   299  	return rootHash, treeEnd, nil
   300  }
   301  
   302  // -----------
   303  
   304  // keyStart is inclusive and keyEnd is exclusive.
   305  // If keyStart or keyEnd don't exist, the leaf before keyStart
   306  // or after keyEnd will also be included, but not be included in values.
   307  // If keyEnd-1 exists, no later leaves will be included.
   308  // If keyStart >= keyEnd and both not nil, panics.
   309  // Limit is never exceeded.
   310  func (t *ImmutableTree) getRangeProof(keyStart, keyEnd []byte, limit int) (proof *RangeProof, keys, values [][]byte, err error) {
   311  	if keyStart != nil && keyEnd != nil && bytes.Compare(keyStart, keyEnd) >= 0 {
   312  		panic("if keyStart and keyEnd are present, need keyStart < keyEnd.")
   313  	}
   314  	if limit < 0 {
   315  		panic("limit must be greater or equal to 0 -- 0 means no limit")
   316  	}
   317  	if t.root == nil {
   318  		return nil, nil, nil, nil
   319  	}
   320  	t.root.hashWithCount() // Ensure that all hashes are calculated.
   321  
   322  	// Get the first key/value pair proof, which provides us with the left key.
   323  	path, left, err := t.root.PathToLeaf(t, keyStart)
   324  	if err != nil {
   325  		// Key doesn't exist, but instead we got the prev leaf (or the
   326  		// first or last leaf), which provides proof of absence).
   327  		err = nil
   328  	}
   329  	startOK := keyStart == nil || bytes.Compare(keyStart, left.key) <= 0
   330  	endOK := keyEnd == nil || bytes.Compare(left.key, keyEnd) < 0
   331  	// If left.key is in range, add it to key/values.
   332  	if startOK && endOK {
   333  		keys = append(keys, left.key) // == keyStart
   334  		values = append(values, left.value)
   335  	}
   336  	// Either way, add to proof leaves.
   337  	leaves := []proofLeafNode{
   338  		{
   339  			Key:       left.key,
   340  			ValueHash: tmhash.Sum(left.value),
   341  			Version:   left.version,
   342  		},
   343  	}
   344  
   345  	// 1: Special case if limit is 1.
   346  	// 2: Special case if keyEnd is left.key+1.
   347  	_stop := false
   348  	if limit == 1 {
   349  		_stop = true // case 1
   350  	} else if keyEnd != nil && bytes.Compare(cpIncr(left.key), keyEnd) >= 0 {
   351  		_stop = true // case 2
   352  	}
   353  	if _stop {
   354  		return &RangeProof{
   355  			LeftPath: path,
   356  			Leaves:   leaves,
   357  		}, keys, values, nil
   358  	}
   359  
   360  	// Get the key after left.key to iterate from.
   361  	afterLeft := cpIncr(left.key)
   362  
   363  	// Traverse starting from afterLeft, until keyEnd or the next leaf
   364  	// after keyEnd.
   365  	innersq := []PathToLeaf(nil)
   366  	inners := PathToLeaf(nil)
   367  	leafCount := 1 // from left above.
   368  	pathCount := 0
   369  	// var keys, values [][]byte defined as function outs.
   370  
   371  	t.root.traverseInRange(t, afterLeft, nil, true, false, 0,
   372  		func(node *Node, depth uint8) (stop bool) {
   373  			// Track when we diverge from path, or when we've exhausted path,
   374  			// since the first innersq shouldn't include it.
   375  			if pathCount != -1 {
   376  				if len(path) <= pathCount {
   377  					// We're done with path counting.
   378  					pathCount = -1
   379  				} else {
   380  					pn := path[pathCount]
   381  					if pn.Height != node.height ||
   382  						pn.Left != nil && !bytes.Equal(pn.Left, node.leftHash) ||
   383  						pn.Right != nil && !bytes.Equal(pn.Right, node.rightHash) {
   384  						// We've diverged, so start appending to inners.
   385  						pathCount = -1
   386  					} else {
   387  						pathCount++
   388  					}
   389  				}
   390  			}
   391  
   392  			if node.height == 0 {
   393  				// Leaf node.
   394  				// Append inners to innersq.
   395  				innersq = append(innersq, inners)
   396  				inners = PathToLeaf(nil)
   397  				// Append leaf to leaves.
   398  				leaves = append(leaves, proofLeafNode{
   399  					Key:       node.key,
   400  					ValueHash: tmhash.Sum(node.value),
   401  					Version:   node.version,
   402  				})
   403  				leafCount++
   404  				// Maybe terminate because we found enough leaves.
   405  				if limit > 0 && limit <= leafCount {
   406  					return true
   407  				}
   408  				// Terminate if we've found keyEnd or after.
   409  				if keyEnd != nil && bytes.Compare(node.key, keyEnd) >= 0 {
   410  					return true
   411  				}
   412  				// Value is in range, append to keys and values.
   413  				keys = append(keys, node.key)
   414  				values = append(values, node.value)
   415  				// Terminate if we've found keyEnd-1 or after.
   416  				// We don't want to fetch any leaves for it.
   417  				if keyEnd != nil && bytes.Compare(cpIncr(node.key), keyEnd) >= 0 {
   418  					return true
   419  				}
   420  			} else {
   421  				// Inner node.
   422  				if pathCount >= 0 {
   423  					// Skip redundant path items.
   424  				} else {
   425  					inners = append(inners, proofInnerNode{
   426  						Height:  node.height,
   427  						Size:    node.size,
   428  						Version: node.version,
   429  						Left:    nil, // left is nil for range proof inners
   430  						Right:   node.rightHash,
   431  					})
   432  				}
   433  			}
   434  			return false
   435  		},
   436  	)
   437  
   438  	return &RangeProof{
   439  		LeftPath:   path,
   440  		InnerNodes: innersq,
   441  		Leaves:     leaves,
   442  	}, keys, values, nil
   443  }
   444  
   445  // ----------------------------------------
   446  
   447  // GetWithProof gets the value under the key if it exists, or returns nil.
   448  // A proof of existence or absence is returned alongside the value.
   449  func (t *ImmutableTree) GetWithProof(key []byte) (value []byte, proof *RangeProof, err error) {
   450  	proof, _, values, err := t.getRangeProof(key, cpIncr(key), 2)
   451  	if err != nil {
   452  		return nil, nil, errors.Wrap(err, "constructing range proof")
   453  	}
   454  	if len(values) > 0 && bytes.Equal(proof.Leaves[0].Key, key) {
   455  		return values[0], proof, nil
   456  	}
   457  	return nil, proof, nil
   458  }
   459  
   460  // GetRangeWithProof gets key/value pairs within the specified range and limit.
   461  func (t *ImmutableTree) GetRangeWithProof(startKey []byte, endKey []byte, limit int) (keys, values [][]byte, proof *RangeProof, err error) {
   462  	proof, keys, values, err = t.getRangeProof(startKey, endKey, limit)
   463  	return
   464  }
   465  
   466  // GetVersionedWithProof gets the value under the key at the specified version
   467  // if it exists, or returns nil.
   468  func (tree *MutableTree) GetVersionedWithProof(key []byte, version int64) ([]byte, *RangeProof, error) {
   469  	if tree.VersionExists(version) {
   470  		t, err := tree.GetImmutable(version)
   471  		if err != nil {
   472  			return nil, nil, err
   473  		}
   474  
   475  		return t.GetWithProof(key)
   476  	}
   477  	return nil, nil, errors.Wrap(ErrVersionDoesNotExist, "")
   478  }
   479  
   480  // GetVersionedRangeWithProof gets key/value pairs within the specified range
   481  // and limit.
   482  func (tree *MutableTree) GetVersionedRangeWithProof(startKey, endKey []byte, limit int, version int64) (
   483  	keys, values [][]byte, proof *RangeProof, err error,
   484  ) {
   485  	if tree.VersionExists(version) {
   486  		t, err := tree.GetImmutable(version)
   487  		if err != nil {
   488  			return nil, nil, nil, err
   489  		}
   490  		return t.GetRangeWithProof(startKey, endKey, limit)
   491  	}
   492  	return nil, nil, nil, errors.Wrap(ErrVersionDoesNotExist, "")
   493  }