github.com/vipernet-xyz/tm@v0.34.24/crypto/merkle/proof.go (about)

     1  package merkle
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/vipernet-xyz/tm/crypto/tmhash"
     9  	tmcrypto "github.com/vipernet-xyz/tm/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"`     // Total number of items.
    28  	Index    int64    `json:"index"`     // 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  	leafHash := leafHash(leaf)
    54  	if sp.Total < 0 {
    55  		return errors.New("proof total must be positive")
    56  	}
    57  	if sp.Index < 0 {
    58  		return errors.New("proof index cannot be negative")
    59  	}
    60  	if !bytes.Equal(sp.LeafHash, leafHash) {
    61  		return fmt.Errorf("invalid leaf hash: wanted %X got %X", leafHash, sp.LeafHash)
    62  	}
    63  	computedHash := sp.ComputeRootHash()
    64  	if !bytes.Equal(computedHash, rootHash) {
    65  		return fmt.Errorf("invalid root hash: wanted %X got %X", rootHash, computedHash)
    66  	}
    67  	return nil
    68  }
    69  
    70  // Compute the root hash given a leaf hash.  Does not verify the result.
    71  func (sp *Proof) ComputeRootHash() []byte {
    72  	return computeHashFromAunts(
    73  		sp.Index,
    74  		sp.Total,
    75  		sp.LeafHash,
    76  		sp.Aunts,
    77  	)
    78  }
    79  
    80  // String implements the stringer interface for Proof.
    81  // It is a wrapper around StringIndented.
    82  func (sp *Proof) String() string {
    83  	return sp.StringIndented("")
    84  }
    85  
    86  // StringIndented generates a canonical string representation of a Proof.
    87  func (sp *Proof) StringIndented(indent string) string {
    88  	return fmt.Sprintf(`Proof{
    89  %s  Aunts: %X
    90  %s}`,
    91  		indent, sp.Aunts,
    92  		indent)
    93  }
    94  
    95  // ValidateBasic performs basic validation.
    96  // NOTE: it expects the LeafHash and the elements of Aunts to be of size tmhash.Size,
    97  // and it expects at most MaxAunts elements in Aunts.
    98  func (sp *Proof) ValidateBasic() error {
    99  	if sp.Total < 0 {
   100  		return errors.New("negative Total")
   101  	}
   102  	if sp.Index < 0 {
   103  		return errors.New("negative Index")
   104  	}
   105  	if len(sp.LeafHash) != tmhash.Size {
   106  		return fmt.Errorf("expected LeafHash size to be %d, got %d", tmhash.Size, len(sp.LeafHash))
   107  	}
   108  	if len(sp.Aunts) > MaxAunts {
   109  		return fmt.Errorf("expected no more than %d aunts, got %d", MaxAunts, len(sp.Aunts))
   110  	}
   111  	for i, auntHash := range sp.Aunts {
   112  		if len(auntHash) != tmhash.Size {
   113  			return fmt.Errorf("expected Aunts#%d size to be %d, got %d", i, tmhash.Size, len(auntHash))
   114  		}
   115  	}
   116  	return nil
   117  }
   118  
   119  func (sp *Proof) ToProto() *tmcrypto.Proof {
   120  	if sp == nil {
   121  		return nil
   122  	}
   123  	pb := new(tmcrypto.Proof)
   124  
   125  	pb.Total = sp.Total
   126  	pb.Index = sp.Index
   127  	pb.LeafHash = sp.LeafHash
   128  	pb.Aunts = sp.Aunts
   129  
   130  	return pb
   131  }
   132  
   133  func ProofFromProto(pb *tmcrypto.Proof) (*Proof, error) {
   134  	if pb == nil {
   135  		return nil, errors.New("nil proof")
   136  	}
   137  
   138  	sp := new(Proof)
   139  
   140  	sp.Total = pb.Total
   141  	sp.Index = pb.Index
   142  	sp.LeafHash = pb.LeafHash
   143  	sp.Aunts = pb.Aunts
   144  
   145  	return sp, sp.ValidateBasic()
   146  }
   147  
   148  // Use the leafHash and innerHashes to get the root merkle hash.
   149  // If the length of the innerHashes slice isn't exactly correct, the result is nil.
   150  // Recursive impl.
   151  func computeHashFromAunts(index, total int64, leafHash []byte, innerHashes [][]byte) []byte {
   152  	if index >= total || index < 0 || total <= 0 {
   153  		return nil
   154  	}
   155  	switch total {
   156  	case 0:
   157  		panic("Cannot call computeHashFromAunts() with 0 total")
   158  	case 1:
   159  		if len(innerHashes) != 0 {
   160  			return nil
   161  		}
   162  		return leafHash
   163  	default:
   164  		if len(innerHashes) == 0 {
   165  			return nil
   166  		}
   167  		numLeft := getSplitPoint(total)
   168  		if index < numLeft {
   169  			leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1])
   170  			if leftHash == nil {
   171  				return nil
   172  			}
   173  			return innerHash(leftHash, innerHashes[len(innerHashes)-1])
   174  		}
   175  		rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1])
   176  		if rightHash == nil {
   177  			return nil
   178  		}
   179  		return innerHash(innerHashes[len(innerHashes)-1], rightHash)
   180  	}
   181  }
   182  
   183  // ProofNode is a helper structure to construct merkle proof.
   184  // The node and the tree is thrown away afterwards.
   185  // Exactly one of node.Left and node.Right is nil, unless node is the root, in which case both are nil.
   186  // node.Parent.Hash = hash(node.Hash, node.Right.Hash) or
   187  // hash(node.Left.Hash, node.Hash), depending on whether node is a left/right child.
   188  type ProofNode struct {
   189  	Hash   []byte
   190  	Parent *ProofNode
   191  	Left   *ProofNode // Left sibling  (only one of Left,Right is set)
   192  	Right  *ProofNode // Right sibling (only one of Left,Right is set)
   193  }
   194  
   195  // FlattenAunts will return the inner hashes for the item corresponding to the leaf,
   196  // starting from a leaf ProofNode.
   197  func (spn *ProofNode) FlattenAunts() [][]byte {
   198  	// Nonrecursive impl.
   199  	innerHashes := [][]byte{}
   200  	for spn != nil {
   201  		switch {
   202  		case spn.Left != nil:
   203  			innerHashes = append(innerHashes, spn.Left.Hash)
   204  		case spn.Right != nil:
   205  			innerHashes = append(innerHashes, spn.Right.Hash)
   206  		default:
   207  			break
   208  		}
   209  		spn = spn.Parent
   210  	}
   211  	return innerHashes
   212  }
   213  
   214  // trails[0].Hash is the leaf hash for items[0].
   215  // trails[i].Parent.Parent....Parent == root for all i.
   216  func trailsFromByteSlices(items [][]byte) (trails []*ProofNode, root *ProofNode) {
   217  	// Recursive impl.
   218  	switch len(items) {
   219  	case 0:
   220  		return []*ProofNode{}, &ProofNode{emptyHash(), nil, nil, nil}
   221  	case 1:
   222  		trail := &ProofNode{leafHash(items[0]), nil, nil, nil}
   223  		return []*ProofNode{trail}, trail
   224  	default:
   225  		k := getSplitPoint(int64(len(items)))
   226  		lefts, leftRoot := trailsFromByteSlices(items[:k])
   227  		rights, rightRoot := trailsFromByteSlices(items[k:])
   228  		rootHash := innerHash(leftRoot.Hash, rightRoot.Hash)
   229  		root := &ProofNode{rootHash, nil, nil, nil}
   230  		leftRoot.Parent = root
   231  		leftRoot.Right = rightRoot
   232  		rightRoot.Parent = root
   233  		rightRoot.Left = leftRoot
   234  		return append(lefts, rights...), root
   235  	}
   236  }