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 }