github.com/consensys/gnark-crypto@v0.14.0/accumulator/merkletree/verify.go (about)

     1  // Original Copyright (c) 2015 Nebulous
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  // The above copyright notice and this permission notice shall be included in all
    10  // copies or substantial portions of the Software.
    11  //
    12  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    13  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    14  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    15  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    16  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    17  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    18  // SOFTWARE.
    19  
    20  package merkletree
    21  
    22  import (
    23  	"bytes"
    24  	"hash"
    25  )
    26  
    27  // VerifyProof takes a Merkle root, a proofSet, and a proofIndex and returns
    28  // true if the first element of the proof set is a leaf of data in the Merkle
    29  // root. False is returned if the proof set or Merkle root is nil, and if
    30  // 'numLeaves' equals 0.
    31  func VerifyProof(h hash.Hash, merkleRoot []byte, proofSet [][]byte, proofIndex uint64, numLeaves uint64) bool {
    32  	// Return false for nonsense input. A switch statement is used so that the
    33  	// cover tool will reveal if a case is not covered by the test suite. This
    34  	// would not be possible using a single if statement due to the limitations
    35  	// of the cover tool.
    36  	if merkleRoot == nil {
    37  		return false
    38  	}
    39  	if proofIndex >= numLeaves {
    40  		return false
    41  	}
    42  
    43  	// In a Merkle tree, every node except the root node has a sibling.
    44  	// Combining the two siblings in the correct order will create the parent
    45  	// node. Each of the remaining hashes in the proof set is a sibling to a
    46  	// node that can be built from all of the previous elements of the proof
    47  	// set. The next node is built by taking:
    48  	//
    49  	//		H(0x01 || sibling A || sibling B)
    50  	//
    51  	// The difficulty of the algorithm lies in determining whether the supplied
    52  	// hash is sibling A or sibling B. This information can be determined by
    53  	// using the proof index and the total number of leaves in the tree.
    54  	//
    55  	// A pair of two siblings forms a subtree. The subtree is complete if it
    56  	// has 1 << height total leaves. When the subtree is complete, the position
    57  	// of the proof index within the subtree can be determined by looking at
    58  	// the bounds of the subtree and determining if the proof index is in the
    59  	// first or second half of the subtree.
    60  	//
    61  	// When the subtree is not complete, either 1 or 0 of the remaining hashes
    62  	// will be sibling B. All remaining hashes after that will be sibling A.
    63  	// This is true because of the way that orphans are merged into the Merkle
    64  	// tree - an orphan at height n is elevated to height n + 1, and only
    65  	// hashed when it is no longer an orphan. Each subtree will therefore merge
    66  	// with at most 1 orphan to the right before becoming an orphan itself.
    67  	// Orphan nodes are always merged with larger subtrees to the left.
    68  	//
    69  	// One vulnerability with the proof verification is that the proofSet may
    70  	// not be long enough. Before looking at an element of proofSet, a check
    71  	// needs to be made that the element exists.
    72  
    73  	// The first element of the set is the original data. A sibling at height 1
    74  	// is created by getting the leafSum of the original data.
    75  	height := 0
    76  	if len(proofSet) <= height {
    77  		return false
    78  	}
    79  	sum := leafSum(h, proofSet[height])
    80  
    81  	height++
    82  
    83  	// While the current subtree (of height 'height') is complete, determine
    84  	// the position of the next sibling using the complete subtree algorithm.
    85  	// 'stableEnd' tells us the ending index of the last full subtree. It gets
    86  	// initialized to 'proofIndex' because the first full subtree was the
    87  	// subtree of height 1, created above (and had an ending index of
    88  	// 'proofIndex').
    89  	stableEnd := proofIndex
    90  	for {
    91  		// Determine if the subtree is complete. This is accomplished by
    92  		// rounding down the proofIndex to the nearest 1 << 'height', adding 1
    93  		// << 'height', and comparing the result to the number of leaves in the
    94  		// Merkle tree.
    95  		subTreeStartIndex := (proofIndex / (1 << uint(height))) * (1 << uint(height)) // round down to the nearest 1 << height
    96  		subTreeEndIndex := subTreeStartIndex + (1 << (uint(height))) - 1              // subtract 1 because the start index is inclusive
    97  		if subTreeEndIndex >= numLeaves {
    98  			// If the Merkle tree does not have a leaf at index
    99  			// 'subTreeEndIndex', then the subtree of the current height is not
   100  			// a complete subtree.
   101  			break
   102  		}
   103  		stableEnd = subTreeEndIndex
   104  
   105  		// Determine if the proofIndex is in the first or the second half of
   106  		// the subtree.
   107  		if len(proofSet) <= height {
   108  			return false
   109  		}
   110  		if proofIndex-subTreeStartIndex < 1<<uint(height-1) {
   111  			sum = nodeSum(h, sum, proofSet[height])
   112  		} else {
   113  			sum = nodeSum(h, proofSet[height], sum)
   114  		}
   115  		height++
   116  	}
   117  
   118  	// Determine if the next hash belongs to an orphan that was elevated. This
   119  	// is the case IFF 'stableEnd' (the last index of the largest full subtree)
   120  	// is equal to the number of leaves in the Merkle tree.
   121  	if stableEnd != numLeaves-1 {
   122  		if len(proofSet) <= height {
   123  			return false
   124  		}
   125  		sum = nodeSum(h, sum, proofSet[height])
   126  		height++
   127  	}
   128  
   129  	// All remaining elements in the proof set will belong to a left sibling.
   130  	for height < len(proofSet) {
   131  		sum = nodeSum(h, proofSet[height], sum)
   132  		height++
   133  	}
   134  
   135  	// Compare our calculated Merkle root to the desired Merkle root.
   136  	return bytes.Equal(sum, merkleRoot)
   137  }