github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/iavl/proof_range.go (about) 1 package iavl 2 3 import ( 4 "bytes" 5 "fmt" 6 "sort" 7 "strings" 8 9 "github.com/gnolang/gno/tm2/pkg/crypto/tmhash" 10 "github.com/gnolang/gno/tm2/pkg/errors" 11 ) 12 13 type RangeProof struct { 14 // You don't need the right path because 15 // it can be derived from what we have. 16 LeftPath PathToLeaf `json:"left_path"` 17 InnerNodes []PathToLeaf `json:"inner_nodes"` 18 Leaves []proofLeafNode `json:"leaves"` 19 20 // memoize 21 rootVerified bool 22 rootHash []byte // valid iff rootVerified is true 23 treeEnd bool // valid iff rootVerified is true 24 } 25 26 // Keys returns all the keys in the RangeProof. NOTE: The keys here may 27 // include more keys than provided by tree.GetRangeWithProof or 28 // MutableTree.GetVersionedRangeWithProof. The keys returned there are only 29 // in the provided [startKey,endKey){limit} range. The keys returned here may 30 // include extra keys, such as: 31 // - the key before startKey if startKey is provided and doesn't exist; 32 // - the key after a queried key with tree.GetWithProof, when the key is absent. 33 func (proof *RangeProof) Keys() (keys [][]byte) { 34 if proof == nil { 35 return nil 36 } 37 for _, leaf := range proof.Leaves { 38 keys = append(keys, leaf.Key) 39 } 40 return keys 41 } 42 43 // String returns a string representation of the proof. 44 func (proof *RangeProof) String() string { 45 if proof == nil { 46 return "<nil-RangeProof>" 47 } 48 return proof.StringIndented("") 49 } 50 51 func (proof *RangeProof) StringIndented(indent string) string { 52 istrs := make([]string, 0, len(proof.InnerNodes)) 53 for _, ptl := range proof.InnerNodes { 54 istrs = append(istrs, ptl.stringIndented(indent+" ")) 55 } 56 lstrs := make([]string, 0, len(proof.Leaves)) 57 for _, leaf := range proof.Leaves { 58 lstrs = append(lstrs, leaf.stringIndented(indent+" ")) 59 } 60 return fmt.Sprintf(`RangeProof{ 61 %s LeftPath: %v 62 %s InnerNodes: 63 %s %v 64 %s Leaves: 65 %s %v 66 %s (rootVerified): %v 67 %s (rootHash): %X 68 %s (treeEnd): %v 69 %s}`, 70 indent, proof.LeftPath.stringIndented(indent+" "), 71 indent, 72 indent, strings.Join(istrs, "\n"+indent+" "), 73 indent, 74 indent, strings.Join(lstrs, "\n"+indent+" "), 75 indent, proof.rootVerified, 76 indent, proof.rootHash, 77 indent, proof.treeEnd, 78 indent) 79 } 80 81 // The index of the first leaf (of the whole tree). 82 // Returns -1 if the proof is nil. 83 func (proof *RangeProof) LeftIndex() int64 { 84 if proof == nil { 85 return -1 86 } 87 return proof.LeftPath.Index() 88 } 89 90 // Also see LeftIndex(). 91 // Verify that a key has some value. 92 // Does not assume that the proof itself is valid, call Verify() first. 93 func (proof *RangeProof) VerifyItem(key, value []byte) error { 94 if proof == nil { 95 return errors.Wrap(ErrInvalidProof, "proof is nil") 96 } 97 leaves := proof.Leaves 98 if !proof.rootVerified { 99 return errors.New("must call Verify(root) first") 100 } 101 i := sort.Search(len(leaves), func(i int) bool { 102 return bytes.Compare(key, leaves[i].Key) <= 0 103 }) 104 if i >= len(leaves) || !bytes.Equal(leaves[i].Key, key) { 105 return errors.Wrap(ErrInvalidProof, "leaf key not found in proof") 106 } 107 valueHash := tmhash.Sum(value) 108 if !bytes.Equal(leaves[i].ValueHash, valueHash) { 109 return errors.Wrap(ErrInvalidProof, "leaf value hash not same") 110 } 111 return nil 112 } 113 114 // Verify that proof is valid absence proof for key. 115 // Does not assume that the proof itself is valid. 116 // For that, use Verify(root). 117 func (proof *RangeProof) VerifyAbsence(key []byte) error { 118 if proof == nil { 119 return errors.Wrap(ErrInvalidProof, "proof is nil") 120 } 121 if !proof.rootVerified { 122 return errors.New("must call Verify(root) first") 123 } 124 cmp := bytes.Compare(key, proof.Leaves[0].Key) 125 if cmp < 0 { 126 if proof.LeftPath.isLeftmost() { 127 return nil 128 } else { 129 return errors.New("absence not proved by left path") 130 } 131 } else if cmp == 0 { 132 return errors.New("absence disproved via first item #0") 133 } 134 if len(proof.LeftPath) == 0 { 135 return nil // proof ok 136 } 137 if proof.LeftPath.isRightmost() { 138 return nil 139 } 140 141 // See if any of the leaves are greater than key. 142 for i := 1; i < len(proof.Leaves); i++ { 143 leaf := proof.Leaves[i] 144 cmp := bytes.Compare(key, leaf.Key) 145 if cmp < 0 { 146 return nil // proof ok 147 } else if cmp == 0 { 148 return errors.New(fmt.Sprintf("absence disproved via item #%v", i)) 149 } else { 150 // if i == len(proof.Leaves)-1 { 151 // If last item, check whether 152 // it's the last item in the tree. 153 154 // } 155 continue 156 } 157 } 158 159 // It's still a valid proof if our last leaf is the rightmost child. 160 if proof.treeEnd { 161 return nil // OK! 162 } 163 164 // It's not a valid absence proof. 165 if len(proof.Leaves) < 2 { 166 return errors.New("absence not proved by right leaf (need another leaf?)") 167 } else { 168 return errors.New("absence not proved by right leaf") 169 } 170 } 171 172 // Verify that proof is valid. 173 func (proof *RangeProof) Verify(root []byte) error { 174 if proof == nil { 175 return errors.Wrap(ErrInvalidProof, "proof is nil") 176 } 177 err := proof.verify(root) 178 return err 179 } 180 181 func (proof *RangeProof) verify(root []byte) (err error) { 182 rootHash := proof.rootHash 183 if rootHash == nil { 184 derivedHash, err := proof.computeRootHash() 185 if err != nil { 186 return err 187 } 188 rootHash = derivedHash 189 } 190 if !bytes.Equal(rootHash, root) { 191 return errors.Wrap(ErrInvalidRoot, "root hash doesn't match") 192 } else { 193 proof.rootVerified = true 194 } 195 return nil 196 } 197 198 // ComputeRootHash computes the root hash with leaves. 199 // Returns nil if error or proof is nil. 200 // Does not verify the root hash. 201 func (proof *RangeProof) ComputeRootHash() []byte { 202 if proof == nil { 203 return nil 204 } 205 rootHash, _ := proof.computeRootHash() 206 return rootHash 207 } 208 209 func (proof *RangeProof) computeRootHash() (rootHash []byte, err error) { 210 rootHash, treeEnd, err := proof._computeRootHash() 211 if err == nil { 212 proof.rootHash = rootHash // memoize 213 proof.treeEnd = treeEnd // memoize 214 } 215 return rootHash, err 216 } 217 218 func (proof *RangeProof) _computeRootHash() (rootHash []byte, treeEnd bool, err error) { 219 if len(proof.Leaves) == 0 { 220 return nil, false, errors.Wrap(ErrInvalidProof, "no leaves") 221 } 222 if len(proof.InnerNodes)+1 != len(proof.Leaves) { 223 return nil, false, errors.Wrap(ErrInvalidProof, "InnerNodes vs Leaves length mismatch, leaves should be 1 more.") 224 } 225 226 // Start from the left path and prove each leaf. 227 228 // shared across recursive calls 229 leaves := proof.Leaves 230 innersq := proof.InnerNodes 231 var COMPUTEHASH func(path PathToLeaf, rightmost bool) (hash []byte, treeEnd bool, done bool, err error) 232 233 // rightmost: is the root a rightmost child of the tree? 234 // treeEnd: true iff the last leaf is the last item of the tree. 235 // Returns the (possibly intermediate, possibly root) hash. 236 COMPUTEHASH = func(path PathToLeaf, rightmost bool) (hash []byte, treeEnd bool, done bool, err error) { 237 // Pop next leaf. 238 nleaf, rleaves := leaves[0], leaves[1:] 239 leaves = rleaves 240 241 // Compute hash. 242 hash = (pathWithLeaf{ 243 Path: path, 244 Leaf: nleaf, 245 }).computeRootHash() 246 247 // If we don't have any leaves left, we're done. 248 if len(leaves) == 0 { 249 rightmost = rightmost && path.isRightmost() 250 return hash, rightmost, true, nil 251 } 252 253 // Prove along path (until we run out of leaves). 254 for len(path) > 0 { 255 // Drop the leaf-most (last-most) inner nodes from path 256 // until we encounter one with a left hash. 257 // We assume that the left side is already verified. 258 // rpath: rest of path 259 // lpath: last path item 260 rpath, lpath := path[:len(path)-1], path[len(path)-1] 261 path = rpath 262 if len(lpath.Right) == 0 { 263 continue 264 } 265 266 // Pop next inners, a PathToLeaf (e.g. []proofInnerNode). 267 inners, rinnersq := innersq[0], innersq[1:] 268 innersq = rinnersq 269 270 // Recursively verify inners against remaining leaves. 271 derivedRoot, treeEnd, done, err := COMPUTEHASH(inners, rightmost && rpath.isRightmost()) 272 if err != nil { 273 return nil, treeEnd, false, errors.Wrap(err, "recursive COMPUTEHASH call") 274 } 275 if !bytes.Equal(derivedRoot, lpath.Right) { 276 return nil, treeEnd, false, errors.Wrap(ErrInvalidRoot, "intermediate root hash %X doesn't match, got %X", lpath.Right, derivedRoot) 277 } 278 if done { 279 return hash, treeEnd, true, nil 280 } 281 } 282 283 // We're not done yet (leaves left over). No error, not done either. 284 // Technically if rightmost, we know there's an error "left over leaves 285 // -- malformed proof", but we return that at the top level, below. 286 return hash, false, false, nil 287 } 288 289 // Verify! 290 path := proof.LeftPath 291 rootHash, treeEnd, done, err := COMPUTEHASH(path, true) 292 if err != nil { 293 return nil, treeEnd, errors.Wrap(err, "root COMPUTEHASH call") 294 } else if !done { 295 return nil, treeEnd, errors.Wrap(ErrInvalidProof, "left over leaves -- malformed proof") 296 } 297 298 // Ok! 299 return rootHash, treeEnd, nil 300 } 301 302 // ----------- 303 304 // keyStart is inclusive and keyEnd is exclusive. 305 // If keyStart or keyEnd don't exist, the leaf before keyStart 306 // or after keyEnd will also be included, but not be included in values. 307 // If keyEnd-1 exists, no later leaves will be included. 308 // If keyStart >= keyEnd and both not nil, panics. 309 // Limit is never exceeded. 310 func (t *ImmutableTree) getRangeProof(keyStart, keyEnd []byte, limit int) (proof *RangeProof, keys, values [][]byte, err error) { 311 if keyStart != nil && keyEnd != nil && bytes.Compare(keyStart, keyEnd) >= 0 { 312 panic("if keyStart and keyEnd are present, need keyStart < keyEnd.") 313 } 314 if limit < 0 { 315 panic("limit must be greater or equal to 0 -- 0 means no limit") 316 } 317 if t.root == nil { 318 return nil, nil, nil, nil 319 } 320 t.root.hashWithCount() // Ensure that all hashes are calculated. 321 322 // Get the first key/value pair proof, which provides us with the left key. 323 path, left, err := t.root.PathToLeaf(t, keyStart) 324 if err != nil { 325 // Key doesn't exist, but instead we got the prev leaf (or the 326 // first or last leaf), which provides proof of absence). 327 err = nil 328 } 329 startOK := keyStart == nil || bytes.Compare(keyStart, left.key) <= 0 330 endOK := keyEnd == nil || bytes.Compare(left.key, keyEnd) < 0 331 // If left.key is in range, add it to key/values. 332 if startOK && endOK { 333 keys = append(keys, left.key) // == keyStart 334 values = append(values, left.value) 335 } 336 // Either way, add to proof leaves. 337 leaves := []proofLeafNode{ 338 { 339 Key: left.key, 340 ValueHash: tmhash.Sum(left.value), 341 Version: left.version, 342 }, 343 } 344 345 // 1: Special case if limit is 1. 346 // 2: Special case if keyEnd is left.key+1. 347 _stop := false 348 if limit == 1 { 349 _stop = true // case 1 350 } else if keyEnd != nil && bytes.Compare(cpIncr(left.key), keyEnd) >= 0 { 351 _stop = true // case 2 352 } 353 if _stop { 354 return &RangeProof{ 355 LeftPath: path, 356 Leaves: leaves, 357 }, keys, values, nil 358 } 359 360 // Get the key after left.key to iterate from. 361 afterLeft := cpIncr(left.key) 362 363 // Traverse starting from afterLeft, until keyEnd or the next leaf 364 // after keyEnd. 365 innersq := []PathToLeaf(nil) 366 inners := PathToLeaf(nil) 367 leafCount := 1 // from left above. 368 pathCount := 0 369 // var keys, values [][]byte defined as function outs. 370 371 t.root.traverseInRange(t, afterLeft, nil, true, false, 0, 372 func(node *Node, depth uint8) (stop bool) { 373 // Track when we diverge from path, or when we've exhausted path, 374 // since the first innersq shouldn't include it. 375 if pathCount != -1 { 376 if len(path) <= pathCount { 377 // We're done with path counting. 378 pathCount = -1 379 } else { 380 pn := path[pathCount] 381 if pn.Height != node.height || 382 pn.Left != nil && !bytes.Equal(pn.Left, node.leftHash) || 383 pn.Right != nil && !bytes.Equal(pn.Right, node.rightHash) { 384 // We've diverged, so start appending to inners. 385 pathCount = -1 386 } else { 387 pathCount++ 388 } 389 } 390 } 391 392 if node.height == 0 { 393 // Leaf node. 394 // Append inners to innersq. 395 innersq = append(innersq, inners) 396 inners = PathToLeaf(nil) 397 // Append leaf to leaves. 398 leaves = append(leaves, proofLeafNode{ 399 Key: node.key, 400 ValueHash: tmhash.Sum(node.value), 401 Version: node.version, 402 }) 403 leafCount++ 404 // Maybe terminate because we found enough leaves. 405 if limit > 0 && limit <= leafCount { 406 return true 407 } 408 // Terminate if we've found keyEnd or after. 409 if keyEnd != nil && bytes.Compare(node.key, keyEnd) >= 0 { 410 return true 411 } 412 // Value is in range, append to keys and values. 413 keys = append(keys, node.key) 414 values = append(values, node.value) 415 // Terminate if we've found keyEnd-1 or after. 416 // We don't want to fetch any leaves for it. 417 if keyEnd != nil && bytes.Compare(cpIncr(node.key), keyEnd) >= 0 { 418 return true 419 } 420 } else { 421 // Inner node. 422 if pathCount >= 0 { 423 // Skip redundant path items. 424 } else { 425 inners = append(inners, proofInnerNode{ 426 Height: node.height, 427 Size: node.size, 428 Version: node.version, 429 Left: nil, // left is nil for range proof inners 430 Right: node.rightHash, 431 }) 432 } 433 } 434 return false 435 }, 436 ) 437 438 return &RangeProof{ 439 LeftPath: path, 440 InnerNodes: innersq, 441 Leaves: leaves, 442 }, keys, values, nil 443 } 444 445 // ---------------------------------------- 446 447 // GetWithProof gets the value under the key if it exists, or returns nil. 448 // A proof of existence or absence is returned alongside the value. 449 func (t *ImmutableTree) GetWithProof(key []byte) (value []byte, proof *RangeProof, err error) { 450 proof, _, values, err := t.getRangeProof(key, cpIncr(key), 2) 451 if err != nil { 452 return nil, nil, errors.Wrap(err, "constructing range proof") 453 } 454 if len(values) > 0 && bytes.Equal(proof.Leaves[0].Key, key) { 455 return values[0], proof, nil 456 } 457 return nil, proof, nil 458 } 459 460 // GetRangeWithProof gets key/value pairs within the specified range and limit. 461 func (t *ImmutableTree) GetRangeWithProof(startKey []byte, endKey []byte, limit int) (keys, values [][]byte, proof *RangeProof, err error) { 462 proof, keys, values, err = t.getRangeProof(startKey, endKey, limit) 463 return 464 } 465 466 // GetVersionedWithProof gets the value under the key at the specified version 467 // if it exists, or returns nil. 468 func (tree *MutableTree) GetVersionedWithProof(key []byte, version int64) ([]byte, *RangeProof, error) { 469 if tree.VersionExists(version) { 470 t, err := tree.GetImmutable(version) 471 if err != nil { 472 return nil, nil, err 473 } 474 475 return t.GetWithProof(key) 476 } 477 return nil, nil, errors.Wrap(ErrVersionDoesNotExist, "") 478 } 479 480 // GetVersionedRangeWithProof gets key/value pairs within the specified range 481 // and limit. 482 func (tree *MutableTree) GetVersionedRangeWithProof(startKey, endKey []byte, limit int, version int64) ( 483 keys, values [][]byte, proof *RangeProof, err error, 484 ) { 485 if tree.VersionExists(version) { 486 t, err := tree.GetImmutable(version) 487 if err != nil { 488 return nil, nil, nil, err 489 } 490 return t.GetRangeWithProof(startKey, endKey, limit) 491 } 492 return nil, nil, nil, errors.Wrap(ErrVersionDoesNotExist, "") 493 }