github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/merkle/simple_proof.go (about) 1 package merkle 2 3 import ( 4 "bytes" 5 "fmt" 6 7 "github.com/gnolang/gno/tm2/pkg/crypto/tmhash" 8 "github.com/gnolang/gno/tm2/pkg/errors" 9 ) 10 11 const ( 12 maxAunts = 100 13 ) 14 15 // SimpleProof represents a simple Merkle proof. 16 // NOTE: The convention for proofs is to include leaf hashes but to 17 // exclude the root hash. 18 // This convention is implemented across IAVL range proofs as well. 19 // Keep this consistent unless there's a very good reason to change 20 // everything. This also affects the generalized proof system as 21 // well. 22 type SimpleProof struct { 23 Total int `json:"total"` // Total number of items. 24 Index int `json:"index"` // Index of item to prove. 25 LeafHash []byte `json:"leaf_hash"` // Hash of item value. 26 Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child. 27 } 28 29 // SimpleProofsFromByteSlices computes inclusion proof for given items. 30 // proofs[0] is the proof for items[0]. 31 func SimpleProofsFromByteSlices(items [][]byte) (rootHash []byte, proofs []*SimpleProof) { 32 trails, rootSPN := trailsFromByteSlices(items) 33 rootHash = rootSPN.Hash 34 proofs = make([]*SimpleProof, len(items)) 35 for i, trail := range trails { 36 proofs[i] = &SimpleProof{ 37 Total: len(items), 38 Index: i, 39 LeafHash: trail.Hash, 40 Aunts: trail.FlattenAunts(), 41 } 42 } 43 return 44 } 45 46 // SimpleProofsFromMap generates proofs from a map. The keys/values of the map will be used as the keys/values 47 // in the underlying key-value pairs. 48 // The keys are sorted before the proofs are computed. 49 func SimpleProofsFromMap(m map[string][]byte) (rootHash []byte, proofs map[string]*SimpleProof, keys []string) { 50 sm := newSimpleMap() 51 for k, v := range m { 52 sm.Set(k, v) 53 } 54 sm.Sort() 55 kvs := sm.kvs 56 kvsBytes := make([][]byte, len(kvs)) 57 for i, kvp := range kvs { 58 kvsBytes[i] = KVPair(kvp).Bytes() 59 } 60 61 rootHash, proofList := SimpleProofsFromByteSlices(kvsBytes) 62 proofs = make(map[string]*SimpleProof) 63 keys = make([]string, len(proofList)) 64 for i, kvp := range kvs { 65 proofs[string(kvp.Key)] = proofList[i] 66 keys[i] = string(kvp.Key) 67 } 68 return 69 } 70 71 // Verify that the SimpleProof proves the root hash. 72 // Check sp.Index/sp.Total manually if needed 73 func (sp *SimpleProof) Verify(rootHash []byte, leaf []byte) error { 74 leafHash := leafHash(leaf) 75 if sp.Total < 0 { 76 return errors.New("Proof total must be positive") 77 } 78 if sp.Index < 0 { 79 return errors.New("Proof index cannot be negative") 80 } 81 if !bytes.Equal(sp.LeafHash, leafHash) { 82 return errors.New("invalid leaf hash: wanted %X got %X", leafHash, sp.LeafHash) 83 } 84 computedHash := sp.ComputeRootHash() 85 if !bytes.Equal(computedHash, rootHash) { 86 return errors.New("invalid root hash: wanted %X got %X", rootHash, computedHash) 87 } 88 return nil 89 } 90 91 // Compute the root hash given a leaf hash. Does not verify the result. 92 func (sp *SimpleProof) ComputeRootHash() []byte { 93 return computeHashFromAunts( 94 sp.Index, 95 sp.Total, 96 sp.LeafHash, 97 sp.Aunts, 98 ) 99 } 100 101 // String implements the stringer interface for SimpleProof. 102 // It is a wrapper around StringIndented. 103 func (sp *SimpleProof) String() string { 104 return sp.StringIndented("") 105 } 106 107 // StringIndented generates a canonical string representation of a SimpleProof. 108 func (sp *SimpleProof) StringIndented(indent string) string { 109 return fmt.Sprintf(`SimpleProof{ 110 %s Aunts: %X 111 %s}`, 112 indent, sp.Aunts, 113 indent) 114 } 115 116 // ValidateBasic performs basic validation. 117 // NOTE: - it expects LeafHash and Aunts of tmhash.Size size 118 // - it expects no more than 100 aunts 119 func (sp *SimpleProof) ValidateBasic() error { 120 if sp.Total < 0 { 121 return errors.New("negative Total") 122 } 123 if sp.Index < 0 { 124 return errors.New("negative Index") 125 } 126 if len(sp.LeafHash) != tmhash.Size { 127 return errors.New("expected LeafHash size to be %d, got %d", tmhash.Size, len(sp.LeafHash)) 128 } 129 if len(sp.Aunts) > maxAunts { 130 return errors.New("expected no more than %d aunts, got %d", maxAunts, len(sp.Aunts)) 131 } 132 for i, auntHash := range sp.Aunts { 133 if len(auntHash) != tmhash.Size { 134 return errors.New("expected Aunts#%d size to be %d, got %d", i, tmhash.Size, len(auntHash)) 135 } 136 } 137 return nil 138 } 139 140 // Use the leafHash and innerHashes to get the root merkle hash. 141 // If the length of the innerHashes slice isn't exactly correct, the result is nil. 142 // Recursive impl. 143 func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][]byte) []byte { 144 if index >= total || index < 0 || total <= 0 { 145 return nil 146 } 147 switch total { 148 case 0: 149 panic("Cannot call computeHashFromAunts() with 0 total") 150 case 1: 151 if len(innerHashes) != 0 { 152 return nil 153 } 154 return leafHash 155 default: 156 if len(innerHashes) == 0 { 157 return nil 158 } 159 numLeft := getSplitPoint(total) 160 if index < numLeft { 161 leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1]) 162 if leftHash == nil { 163 return nil 164 } 165 return innerHash(leftHash, innerHashes[len(innerHashes)-1]) 166 } 167 rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1]) 168 if rightHash == nil { 169 return nil 170 } 171 return innerHash(innerHashes[len(innerHashes)-1], rightHash) 172 } 173 } 174 175 // SimpleProofNode is a helper structure to construct merkle proof. 176 // The node and the tree is thrown away afterwards. 177 // Exactly one of node.Left and node.Right is nil, unless node is the root, in which case both are nil. 178 // node.Parent.Hash = hash(node.Hash, node.Right.Hash) or 179 // hash(node.Left.Hash, node.Hash), depending on whether node is a left/right child. 180 type SimpleProofNode struct { 181 Hash []byte 182 Parent *SimpleProofNode 183 Left *SimpleProofNode // Left sibling (only one of Left,Right is set) 184 Right *SimpleProofNode // Right sibling (only one of Left,Right is set) 185 } 186 187 // FlattenAunts will return the inner hashes for the item corresponding to the leaf, 188 // starting from a leaf SimpleProofNode. 189 func (spn *SimpleProofNode) FlattenAunts() [][]byte { 190 // Nonrecursive impl. 191 innerHashes := [][]byte{} 192 for spn != nil { 193 switch { 194 case spn.Left != nil: 195 innerHashes = append(innerHashes, spn.Left.Hash) 196 case spn.Right != nil: 197 innerHashes = append(innerHashes, spn.Right.Hash) 198 default: 199 break 200 } 201 spn = spn.Parent 202 } 203 return innerHashes 204 } 205 206 // trails[0].Hash is the leaf hash for items[0]. 207 // trails[i].Parent.Parent....Parent == root for all i. 208 func trailsFromByteSlices(items [][]byte) (trails []*SimpleProofNode, root *SimpleProofNode) { 209 // Recursive impl. 210 switch len(items) { 211 case 0: 212 return nil, nil 213 case 1: 214 trail := &SimpleProofNode{leafHash(items[0]), nil, nil, nil} 215 return []*SimpleProofNode{trail}, trail 216 default: 217 k := getSplitPoint(len(items)) 218 lefts, leftRoot := trailsFromByteSlices(items[:k]) 219 rights, rightRoot := trailsFromByteSlices(items[k:]) 220 rootHash := innerHash(leftRoot.Hash, rightRoot.Hash) 221 root := &SimpleProofNode{rootHash, nil, nil, nil} 222 leftRoot.Parent = root 223 leftRoot.Right = rightRoot 224 rightRoot.Parent = root 225 rightRoot.Left = leftRoot 226 return append(lefts, rights...), root 227 } 228 }