github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/crypto/merkle/proof.go (about)

     1  package merkle
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/ari-anchor/sei-tendermint/crypto"
     9  	tmcrypto "github.com/ari-anchor/sei-tendermint/proto/tendermint/crypto"
    10  )
    11  
    12  const (
    13  	// MaxAunts is the maximum number of aunts that can be included in a Proof.
    14  	// This corresponds to a tree of size 2^100, which should be sufficient for all conceivable purposes.
    15  	// This maximum helps prevent Denial-of-Service attacks by limitting the size of the proofs.
    16  	MaxAunts = 100
    17  )
    18  
    19  // Proof represents a Merkle proof.
    20  // NOTE: The convention for proofs is to include leaf hashes but to
    21  // exclude the root hash.
    22  // This convention is implemented across IAVL range proofs as well.
    23  // Keep this consistent unless there's a very good reason to change
    24  // everything.  This also affects the generalized proof system as
    25  // well.
    26  type Proof struct {
    27  	Total    int64    `json:"total,string"` // Total number of items.
    28  	Index    int64    `json:"index,string"` // Index of item to prove.
    29  	LeafHash []byte   `json:"leaf_hash"`    // Hash of item value.
    30  	Aunts    [][]byte `json:"aunts"`        // Hashes from leaf's sibling to a root's child.
    31  }
    32  
    33  // ProofsFromByteSlices computes inclusion proof for given items.
    34  // proofs[0] is the proof for items[0].
    35  func ProofsFromByteSlices(items [][]byte) (rootHash []byte, proofs []*Proof) {
    36  	trails, rootSPN := trailsFromByteSlices(items)
    37  	rootHash = rootSPN.Hash
    38  	proofs = make([]*Proof, len(items))
    39  	for i, trail := range trails {
    40  		proofs[i] = &Proof{
    41  			Total:    int64(len(items)),
    42  			Index:    int64(i),
    43  			LeafHash: trail.Hash,
    44  			Aunts:    trail.FlattenAunts(),
    45  		}
    46  	}
    47  	return
    48  }
    49  
    50  // Verify that the Proof proves the root hash.
    51  // Check sp.Index/sp.Total manually if needed
    52  func (sp *Proof) Verify(rootHash []byte, leaf []byte) error {
    53  	if sp.Total < 0 {
    54  		return errors.New("proof total must be positive")
    55  	}
    56  	if sp.Index < 0 {
    57  		return errors.New("proof index cannot be negative")
    58  	}
    59  	leafHash := leafHash(leaf)
    60  	if !bytes.Equal(sp.LeafHash, leafHash) {
    61  		return fmt.Errorf("invalid leaf hash: wanted %X got %X", leafHash, sp.LeafHash)
    62  	}
    63  	computedHash, err := sp.ComputeRootHash()
    64  	if err != nil {
    65  		return err
    66  	}
    67  	if !bytes.Equal(computedHash, rootHash) {
    68  		return fmt.Errorf("invalid root hash: wanted %X got %X", rootHash, computedHash)
    69  	}
    70  	return nil
    71  }
    72  
    73  // Compute the root hash given a leaf hash.  Does not verify the result.
    74  func (sp *Proof) ComputeRootHash() ([]byte, error) {
    75  	return computeHashFromAunts(
    76  		sp.Index,
    77  		sp.Total,
    78  		sp.LeafHash,
    79  		sp.Aunts,
    80  	)
    81  }
    82  
    83  // String implements the stringer interface for Proof.
    84  // It is a wrapper around StringIndented.
    85  func (sp *Proof) String() string {
    86  	return sp.StringIndented("")
    87  }
    88  
    89  // StringIndented generates a canonical string representation of a Proof.
    90  func (sp *Proof) StringIndented(indent string) string {
    91  	return fmt.Sprintf(`Proof{
    92  %s  Aunts: %X
    93  %s}`,
    94  		indent, sp.Aunts,
    95  		indent)
    96  }
    97  
    98  // ValidateBasic performs basic validation.
    99  // NOTE: it expects the LeafHash and the elements of Aunts to be of size tmhash.Size,
   100  // and it expects at most MaxAunts elements in Aunts.
   101  func (sp *Proof) ValidateBasic() error {
   102  	if sp.Total < 0 {
   103  		return errors.New("negative Total")
   104  	}
   105  	if sp.Index < 0 {
   106  		return errors.New("negative Index")
   107  	}
   108  	if len(sp.LeafHash) != crypto.HashSize {
   109  		return fmt.Errorf("expected LeafHash size to be %d, got %d", crypto.HashSize, len(sp.LeafHash))
   110  	}
   111  	if len(sp.Aunts) > MaxAunts {
   112  		return fmt.Errorf("expected no more than %d aunts, got %d", MaxAunts, len(sp.Aunts))
   113  	}
   114  	for i, auntHash := range sp.Aunts {
   115  		if len(auntHash) != crypto.HashSize {
   116  			return fmt.Errorf("expected Aunts#%d size to be %d, got %d", i, crypto.HashSize, len(auntHash))
   117  		}
   118  	}
   119  	return nil
   120  }
   121  
   122  func (sp *Proof) ToProto() *tmcrypto.Proof {
   123  	if sp == nil {
   124  		return nil
   125  	}
   126  	pb := new(tmcrypto.Proof)
   127  
   128  	pb.Total = sp.Total
   129  	pb.Index = sp.Index
   130  	pb.LeafHash = sp.LeafHash
   131  	pb.Aunts = sp.Aunts
   132  
   133  	return pb
   134  }
   135  
   136  func ProofFromProto(pb *tmcrypto.Proof) (*Proof, error) {
   137  	if pb == nil {
   138  		return nil, errors.New("nil proof")
   139  	}
   140  
   141  	sp := new(Proof)
   142  
   143  	sp.Total = pb.Total
   144  	sp.Index = pb.Index
   145  	sp.LeafHash = pb.LeafHash
   146  	sp.Aunts = pb.Aunts
   147  
   148  	return sp, sp.ValidateBasic()
   149  }
   150  
   151  // Use the leafHash and innerHashes to get the root merkle hash.
   152  // If the length of the innerHashes slice isn't exactly correct, the result is nil.
   153  // Recursive impl.
   154  func computeHashFromAunts(index, total int64, leafHash []byte, innerHashes [][]byte) ([]byte, error) {
   155  	if index >= total || index < 0 || total <= 0 {
   156  		return nil, fmt.Errorf("Calling computeHashFromAunts() with invalid index (%d) and total (%d)", index, total)
   157  	}
   158  	switch total {
   159  	case 0:
   160  		panic("Cannot call computeHashFromAunts() with 0 total")
   161  	case 1:
   162  		if len(innerHashes) != 0 {
   163  			return nil, errors.New("Calling computeHashFromAunts() with total 1 but non-empty inner hashes")
   164  		}
   165  		return leafHash, nil
   166  	default:
   167  		if len(innerHashes) == 0 {
   168  			return nil, errors.New("Calling computeHashFromAunts() with total > 1 but empty inner hashes")
   169  		}
   170  		numLeft := getSplitPoint(total)
   171  		if index < numLeft {
   172  			leftHash, err := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1])
   173  			if err != nil {
   174  				return nil, err
   175  			}
   176  			return innerHash(leftHash, innerHashes[len(innerHashes)-1]), nil
   177  		}
   178  		rightHash, err := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1])
   179  		if err != nil {
   180  			return nil, err
   181  		}
   182  		return innerHash(innerHashes[len(innerHashes)-1], rightHash), nil
   183  	}
   184  }
   185  
   186  // ProofNode is a helper structure to construct merkle proof.
   187  // The node and the tree is thrown away afterwards.
   188  // Exactly one of node.Left and node.Right is nil, unless node is the root, in which case both are nil.
   189  // node.Parent.Hash = hash(node.Hash, node.Right.Hash) or
   190  // hash(node.Left.Hash, node.Hash), depending on whether node is a left/right child.
   191  type ProofNode struct {
   192  	Hash   []byte
   193  	Parent *ProofNode
   194  	Left   *ProofNode // Left sibling  (only one of Left,Right is set)
   195  	Right  *ProofNode // Right sibling (only one of Left,Right is set)
   196  }
   197  
   198  // FlattenAunts will return the inner hashes for the item corresponding to the leaf,
   199  // starting from a leaf ProofNode.
   200  func (spn *ProofNode) FlattenAunts() [][]byte {
   201  	// Nonrecursive impl.
   202  	innerHashes := [][]byte{}
   203  	for spn != nil {
   204  		switch {
   205  		case spn.Left != nil:
   206  			innerHashes = append(innerHashes, spn.Left.Hash)
   207  		case spn.Right != nil:
   208  			innerHashes = append(innerHashes, spn.Right.Hash)
   209  		default:
   210  			// FIXME(fromberger): Per the documentation above, exactly one of
   211  			// these fields should be set. If that is true, this should probably
   212  			// be a panic since it violates the invariant. If not, when can it
   213  			// be OK to have no siblings? Does this occur at the leaves?
   214  		}
   215  		spn = spn.Parent
   216  	}
   217  	return innerHashes
   218  }
   219  
   220  // trails[0].Hash is the leaf hash for items[0].
   221  // trails[i].Parent.Parent....Parent == root for all i.
   222  func trailsFromByteSlices(items [][]byte) (trails []*ProofNode, root *ProofNode) {
   223  	// Recursive impl.
   224  	switch len(items) {
   225  	case 0:
   226  		return []*ProofNode{}, &ProofNode{emptyHash(), nil, nil, nil}
   227  	case 1:
   228  		trail := &ProofNode{leafHash(items[0]), nil, nil, nil}
   229  		return []*ProofNode{trail}, trail
   230  	default:
   231  		k := getSplitPoint(int64(len(items)))
   232  		lefts, leftRoot := trailsFromByteSlices(items[:k])
   233  		rights, rightRoot := trailsFromByteSlices(items[k:])
   234  		rootHash := innerHash(leftRoot.Hash, rightRoot.Hash)
   235  		root := &ProofNode{rootHash, nil, nil, nil}
   236  		leftRoot.Parent = root
   237  		leftRoot.Right = rightRoot
   238  		rightRoot.Parent = root
   239  		rightRoot.Left = leftRoot
   240  		return append(lefts, rights...), root
   241  	}
   242  }