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

     1  package iavl
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/gnolang/gno/tm2/pkg/amino"
     8  	"github.com/gnolang/gno/tm2/pkg/crypto/tmhash"
     9  	"github.com/gnolang/gno/tm2/pkg/errors"
    10  )
    11  
    12  var (
    13  	// ErrInvalidProof is returned by Verify when a proof cannot be validated.
    14  	ErrInvalidProof = fmt.Errorf("invalid proof")
    15  
    16  	// ErrInvalidInputs is returned when the inputs passed to the function are invalid.
    17  	ErrInvalidInputs = fmt.Errorf("invalid inputs")
    18  
    19  	// ErrInvalidRoot is returned when the root passed in does not match the proof's.
    20  	ErrInvalidRoot = fmt.Errorf("invalid root")
    21  )
    22  
    23  //----------------------------------------
    24  
    25  // Contract: Left and Right can never both be set. Will result in a empty `[]` roothash
    26  type proofInnerNode struct {
    27  	Height  int8   `json:"height"`
    28  	Size    int64  `json:"size"`
    29  	Version int64  `json:"version"`
    30  	Left    []byte `json:"left"`
    31  	Right   []byte `json:"right"`
    32  }
    33  
    34  func (pin proofInnerNode) String() string {
    35  	return pin.stringIndented("")
    36  }
    37  
    38  func (pin proofInnerNode) stringIndented(indent string) string {
    39  	return fmt.Sprintf(`proofInnerNode{
    40  %s  Height:  %v
    41  %s  Size:    %v
    42  %s  Version: %v
    43  %s  Left:    %X
    44  %s  Right:   %X
    45  %s}`,
    46  		indent, pin.Height,
    47  		indent, pin.Size,
    48  		indent, pin.Version,
    49  		indent, pin.Left,
    50  		indent, pin.Right,
    51  		indent)
    52  }
    53  
    54  func (pin proofInnerNode) Hash(childHash []byte) []byte {
    55  	hasher := tmhash.New()
    56  	buf := new(bytes.Buffer)
    57  
    58  	err := amino.EncodeVarint8(buf, pin.Height)
    59  	if err == nil {
    60  		err = amino.EncodeVarint(buf, pin.Size)
    61  	}
    62  	if err == nil {
    63  		err = amino.EncodeVarint(buf, pin.Version)
    64  	}
    65  
    66  	if len(pin.Left) > 0 && len(pin.Right) > 0 {
    67  		panic(fmt.Sprintf("both left and right child hashes are set"))
    68  	}
    69  
    70  	if len(pin.Left) == 0 {
    71  		if err == nil {
    72  			err = amino.EncodeByteSlice(buf, childHash)
    73  		}
    74  		if err == nil {
    75  			err = amino.EncodeByteSlice(buf, pin.Right)
    76  		}
    77  	} else {
    78  		if err == nil {
    79  			err = amino.EncodeByteSlice(buf, pin.Left)
    80  		}
    81  		if err == nil {
    82  			err = amino.EncodeByteSlice(buf, childHash)
    83  		}
    84  	}
    85  	if err != nil {
    86  		panic(fmt.Sprintf("Failed to hash proofInnerNode: %v", err))
    87  	}
    88  
    89  	hasher.Write(buf.Bytes())
    90  	return hasher.Sum(nil)
    91  }
    92  
    93  //----------------------------------------
    94  
    95  type proofLeafNode struct {
    96  	Key       []byte `json:"key"`
    97  	ValueHash []byte `json:"value"`
    98  	Version   int64  `json:"version"`
    99  }
   100  
   101  func (pln proofLeafNode) String() string {
   102  	return pln.stringIndented("")
   103  }
   104  
   105  func (pln proofLeafNode) stringIndented(indent string) string {
   106  	return fmt.Sprintf(`proofLeafNode{
   107  %s  Key:       %v
   108  %s  ValueHash: %X
   109  %s  Version:   %v
   110  %s}`,
   111  		indent, pln.Key,
   112  		indent, pln.ValueHash,
   113  		indent, pln.Version,
   114  		indent)
   115  }
   116  
   117  func (pln proofLeafNode) Hash() []byte {
   118  	hasher := tmhash.New()
   119  	buf := new(bytes.Buffer)
   120  
   121  	err := amino.EncodeVarint8(buf, 0)
   122  	if err == nil {
   123  		err = amino.EncodeVarint(buf, 1)
   124  	}
   125  	if err == nil {
   126  		err = amino.EncodeVarint(buf, pln.Version)
   127  	}
   128  	if err == nil {
   129  		err = amino.EncodeByteSlice(buf, pln.Key)
   130  	}
   131  	if err == nil {
   132  		err = amino.EncodeByteSlice(buf, pln.ValueHash)
   133  	}
   134  	if err != nil {
   135  		panic(fmt.Sprintf("Failed to hash proofLeafNode: %v", err))
   136  	}
   137  	hasher.Write(buf.Bytes())
   138  
   139  	return hasher.Sum(nil)
   140  }
   141  
   142  //----------------------------------------
   143  
   144  // If the key does not exist, returns the path to the next leaf left of key (w/
   145  // path), except when key is less than the least item, in which case it returns
   146  // a path to the least item.
   147  func (node *Node) PathToLeaf(t *ImmutableTree, key []byte) (PathToLeaf, *Node, error) {
   148  	path := new(PathToLeaf)
   149  	val, err := node.pathToLeaf(t, key, path)
   150  	return *path, val, err
   151  }
   152  
   153  // pathToLeaf is a helper which recursively constructs the PathToLeaf.
   154  // As an optimization the already constructed path is passed in as an argument
   155  // and is shared among recursive calls.
   156  func (node *Node) pathToLeaf(t *ImmutableTree, key []byte, path *PathToLeaf) (*Node, error) {
   157  	if node.height == 0 {
   158  		if bytes.Equal(node.key, key) {
   159  			return node, nil
   160  		}
   161  		return node, errors.New("key does not exist")
   162  	}
   163  
   164  	if bytes.Compare(key, node.key) < 0 {
   165  		// left side
   166  		pin := proofInnerNode{
   167  			Height:  node.height,
   168  			Size:    node.size,
   169  			Version: node.version,
   170  			Left:    nil,
   171  			Right:   node.getRightNode(t).hash,
   172  		}
   173  		*path = append(*path, pin)
   174  		n, err := node.getLeftNode(t).pathToLeaf(t, key, path)
   175  		return n, err
   176  	}
   177  	// right side
   178  	pin := proofInnerNode{
   179  		Height:  node.height,
   180  		Size:    node.size,
   181  		Version: node.version,
   182  		Left:    node.getLeftNode(t).hash,
   183  		Right:   nil,
   184  	}
   185  	*path = append(*path, pin)
   186  	n, err := node.getRightNode(t).pathToLeaf(t, key, path)
   187  	return n, err
   188  }