github.com/ethw3/go-ethereuma@v0.0.0-20221013053120-c14602a4c23c/trie/iterator.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package trie 18 19 import ( 20 "bytes" 21 "container/heap" 22 "errors" 23 24 "github.com/ethw3/go-ethereuma/common" 25 "github.com/ethw3/go-ethereuma/ethdb" 26 ) 27 28 // Iterator is a key-value trie iterator that traverses a Trie. 29 type Iterator struct { 30 nodeIt NodeIterator 31 32 Key []byte // Current data key on which the iterator is positioned on 33 Value []byte // Current data value on which the iterator is positioned on 34 Err error 35 } 36 37 // NewIterator creates a new key-value iterator from a node iterator. 38 // Note that the value returned by the iterator is raw. If the content is encoded 39 // (e.g. storage value is RLP-encoded), it's caller's duty to decode it. 40 func NewIterator(it NodeIterator) *Iterator { 41 return &Iterator{ 42 nodeIt: it, 43 } 44 } 45 46 // Next moves the iterator forward one key-value entry. 47 func (it *Iterator) Next() bool { 48 for it.nodeIt.Next(true) { 49 if it.nodeIt.Leaf() { 50 it.Key = it.nodeIt.LeafKey() 51 it.Value = it.nodeIt.LeafBlob() 52 return true 53 } 54 } 55 it.Key = nil 56 it.Value = nil 57 it.Err = it.nodeIt.Error() 58 return false 59 } 60 61 // Prove generates the Merkle proof for the leaf node the iterator is currently 62 // positioned on. 63 func (it *Iterator) Prove() [][]byte { 64 return it.nodeIt.LeafProof() 65 } 66 67 // NodeIterator is an iterator to traverse the trie pre-order. 68 type NodeIterator interface { 69 // Next moves the iterator to the next node. If the parameter is false, any child 70 // nodes will be skipped. 71 Next(bool) bool 72 73 // Error returns the error status of the iterator. 74 Error() error 75 76 // Hash returns the hash of the current node. 77 Hash() common.Hash 78 79 // Parent returns the hash of the parent of the current node. The hash may be the one 80 // grandparent if the immediate parent is an internal node with no hash. 81 Parent() common.Hash 82 83 // Path returns the hex-encoded path to the current node. 84 // Callers must not retain references to the return value after calling Next. 85 // For leaf nodes, the last element of the path is the 'terminator symbol' 0x10. 86 Path() []byte 87 88 // NodeBlob returns the rlp-encoded value of the current iterated node. 89 // If the node is an embedded node in its parent, nil is returned then. 90 NodeBlob() []byte 91 92 // Leaf returns true iff the current node is a leaf node. 93 Leaf() bool 94 95 // LeafKey returns the key of the leaf. The method panics if the iterator is not 96 // positioned at a leaf. Callers must not retain references to the value after 97 // calling Next. 98 LeafKey() []byte 99 100 // LeafBlob returns the content of the leaf. The method panics if the iterator 101 // is not positioned at a leaf. Callers must not retain references to the value 102 // after calling Next. 103 LeafBlob() []byte 104 105 // LeafProof returns the Merkle proof of the leaf. The method panics if the 106 // iterator is not positioned at a leaf. Callers must not retain references 107 // to the value after calling Next. 108 LeafProof() [][]byte 109 110 // AddResolver sets an intermediate database to use for looking up trie nodes 111 // before reaching into the real persistent layer. 112 // 113 // This is not required for normal operation, rather is an optimization for 114 // cases where trie nodes can be recovered from some external mechanism without 115 // reading from disk. In those cases, this resolver allows short circuiting 116 // accesses and returning them from memory. 117 // 118 // Before adding a similar mechanism to any other place in Geth, consider 119 // making trie.Database an interface and wrapping at that level. It's a huge 120 // refactor, but it could be worth it if another occurrence arises. 121 AddResolver(ethdb.KeyValueReader) 122 } 123 124 // nodeIteratorState represents the iteration state at one particular node of the 125 // trie, which can be resumed at a later invocation. 126 type nodeIteratorState struct { 127 hash common.Hash // Hash of the node being iterated (nil if not standalone) 128 node node // Trie node being iterated 129 parent common.Hash // Hash of the first full ancestor node (nil if current is the root) 130 index int // Child to be processed next 131 pathlen int // Length of the path to this node 132 } 133 134 type nodeIterator struct { 135 trie *Trie // Trie being iterated 136 stack []*nodeIteratorState // Hierarchy of trie nodes persisting the iteration state 137 path []byte // Path to the current node 138 err error // Failure set in case of an internal error in the iterator 139 140 resolver ethdb.KeyValueReader // Optional intermediate resolver above the disk layer 141 } 142 143 // errIteratorEnd is stored in nodeIterator.err when iteration is done. 144 var errIteratorEnd = errors.New("end of iteration") 145 146 // seekError is stored in nodeIterator.err if the initial seek has failed. 147 type seekError struct { 148 key []byte 149 err error 150 } 151 152 func (e seekError) Error() string { 153 return "seek error: " + e.err.Error() 154 } 155 156 func newNodeIterator(trie *Trie, start []byte) NodeIterator { 157 if trie.Hash() == emptyRoot { 158 return &nodeIterator{ 159 trie: trie, 160 err: errIteratorEnd, 161 } 162 } 163 it := &nodeIterator{trie: trie} 164 it.err = it.seek(start) 165 return it 166 } 167 168 func (it *nodeIterator) AddResolver(resolver ethdb.KeyValueReader) { 169 it.resolver = resolver 170 } 171 172 func (it *nodeIterator) Hash() common.Hash { 173 if len(it.stack) == 0 { 174 return common.Hash{} 175 } 176 return it.stack[len(it.stack)-1].hash 177 } 178 179 func (it *nodeIterator) Parent() common.Hash { 180 if len(it.stack) == 0 { 181 return common.Hash{} 182 } 183 return it.stack[len(it.stack)-1].parent 184 } 185 186 func (it *nodeIterator) Leaf() bool { 187 return hasTerm(it.path) 188 } 189 190 func (it *nodeIterator) LeafKey() []byte { 191 if len(it.stack) > 0 { 192 if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok { 193 return hexToKeybytes(it.path) 194 } 195 } 196 panic("not at leaf") 197 } 198 199 func (it *nodeIterator) LeafBlob() []byte { 200 if len(it.stack) > 0 { 201 if node, ok := it.stack[len(it.stack)-1].node.(valueNode); ok { 202 return node 203 } 204 } 205 panic("not at leaf") 206 } 207 208 func (it *nodeIterator) LeafProof() [][]byte { 209 if len(it.stack) > 0 { 210 if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok { 211 hasher := newHasher(false) 212 defer returnHasherToPool(hasher) 213 proofs := make([][]byte, 0, len(it.stack)) 214 215 for i, item := range it.stack[:len(it.stack)-1] { 216 // Gather nodes that end up as hash nodes (or the root) 217 node, hashed := hasher.proofHash(item.node) 218 if _, ok := hashed.(hashNode); ok || i == 0 { 219 proofs = append(proofs, nodeToBytes(node)) 220 } 221 } 222 return proofs 223 } 224 } 225 panic("not at leaf") 226 } 227 228 func (it *nodeIterator) Path() []byte { 229 return it.path 230 } 231 232 func (it *nodeIterator) NodeBlob() []byte { 233 if it.Hash() == (common.Hash{}) { 234 return nil // skip the non-standalone node 235 } 236 blob, err := it.resolveBlob(it.Hash().Bytes(), it.Path()) 237 if err != nil { 238 it.err = err 239 return nil 240 } 241 return blob 242 } 243 244 func (it *nodeIterator) Error() error { 245 if it.err == errIteratorEnd { 246 return nil 247 } 248 if seek, ok := it.err.(seekError); ok { 249 return seek.err 250 } 251 return it.err 252 } 253 254 // Next moves the iterator to the next node, returning whether there are any 255 // further nodes. In case of an internal error this method returns false and 256 // sets the Error field to the encountered failure. If `descend` is false, 257 // skips iterating over any subnodes of the current node. 258 func (it *nodeIterator) Next(descend bool) bool { 259 if it.err == errIteratorEnd { 260 return false 261 } 262 if seek, ok := it.err.(seekError); ok { 263 if it.err = it.seek(seek.key); it.err != nil { 264 return false 265 } 266 } 267 // Otherwise step forward with the iterator and report any errors. 268 state, parentIndex, path, err := it.peek(descend) 269 it.err = err 270 if it.err != nil { 271 return false 272 } 273 it.push(state, parentIndex, path) 274 return true 275 } 276 277 func (it *nodeIterator) seek(prefix []byte) error { 278 // The path we're looking for is the hex encoded key without terminator. 279 key := keybytesToHex(prefix) 280 key = key[:len(key)-1] 281 // Move forward until we're just before the closest match to key. 282 for { 283 state, parentIndex, path, err := it.peekSeek(key) 284 if err == errIteratorEnd { 285 return errIteratorEnd 286 } else if err != nil { 287 return seekError{prefix, err} 288 } else if bytes.Compare(path, key) >= 0 { 289 return nil 290 } 291 it.push(state, parentIndex, path) 292 } 293 } 294 295 // init initializes the iterator. 296 func (it *nodeIterator) init() (*nodeIteratorState, error) { 297 root := it.trie.Hash() 298 state := &nodeIteratorState{node: it.trie.root, index: -1} 299 if root != emptyRoot { 300 state.hash = root 301 } 302 return state, state.resolve(it, nil) 303 } 304 305 // peek creates the next state of the iterator. 306 func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, error) { 307 // Initialize the iterator if we've just started. 308 if len(it.stack) == 0 { 309 state, err := it.init() 310 return state, nil, nil, err 311 } 312 if !descend { 313 // If we're skipping children, pop the current node first 314 it.pop() 315 } 316 317 // Continue iteration to the next child 318 for len(it.stack) > 0 { 319 parent := it.stack[len(it.stack)-1] 320 ancestor := parent.hash 321 if (ancestor == common.Hash{}) { 322 ancestor = parent.parent 323 } 324 state, path, ok := it.nextChild(parent, ancestor) 325 if ok { 326 if err := state.resolve(it, path); err != nil { 327 return parent, &parent.index, path, err 328 } 329 return state, &parent.index, path, nil 330 } 331 // No more child nodes, move back up. 332 it.pop() 333 } 334 return nil, nil, nil, errIteratorEnd 335 } 336 337 // peekSeek is like peek, but it also tries to skip resolving hashes by skipping 338 // over the siblings that do not lead towards the desired seek position. 339 func (it *nodeIterator) peekSeek(seekKey []byte) (*nodeIteratorState, *int, []byte, error) { 340 // Initialize the iterator if we've just started. 341 if len(it.stack) == 0 { 342 state, err := it.init() 343 return state, nil, nil, err 344 } 345 if !bytes.HasPrefix(seekKey, it.path) { 346 // If we're skipping children, pop the current node first 347 it.pop() 348 } 349 350 // Continue iteration to the next child 351 for len(it.stack) > 0 { 352 parent := it.stack[len(it.stack)-1] 353 ancestor := parent.hash 354 if (ancestor == common.Hash{}) { 355 ancestor = parent.parent 356 } 357 state, path, ok := it.nextChildAt(parent, ancestor, seekKey) 358 if ok { 359 if err := state.resolve(it, path); err != nil { 360 return parent, &parent.index, path, err 361 } 362 return state, &parent.index, path, nil 363 } 364 // No more child nodes, move back up. 365 it.pop() 366 } 367 return nil, nil, nil, errIteratorEnd 368 } 369 370 func (it *nodeIterator) resolveHash(hash hashNode, path []byte) (node, error) { 371 if it.resolver != nil { 372 if blob, err := it.resolver.Get(hash); err == nil && len(blob) > 0 { 373 if resolved, err := decodeNode(hash, blob); err == nil { 374 return resolved, nil 375 } 376 } 377 } 378 return it.trie.resolveHash(hash, path) 379 } 380 381 func (it *nodeIterator) resolveBlob(hash hashNode, path []byte) ([]byte, error) { 382 if it.resolver != nil { 383 if blob, err := it.resolver.Get(hash); err == nil && len(blob) > 0 { 384 return blob, nil 385 } 386 } 387 return it.trie.resolveBlob(hash, path) 388 } 389 390 func (st *nodeIteratorState) resolve(it *nodeIterator, path []byte) error { 391 if hash, ok := st.node.(hashNode); ok { 392 resolved, err := it.resolveHash(hash, path) 393 if err != nil { 394 return err 395 } 396 st.node = resolved 397 st.hash = common.BytesToHash(hash) 398 } 399 return nil 400 } 401 402 func findChild(n *fullNode, index int, path []byte, ancestor common.Hash) (node, *nodeIteratorState, []byte, int) { 403 var ( 404 child node 405 state *nodeIteratorState 406 childPath []byte 407 ) 408 for ; index < len(n.Children); index++ { 409 if n.Children[index] != nil { 410 child = n.Children[index] 411 hash, _ := child.cache() 412 state = &nodeIteratorState{ 413 hash: common.BytesToHash(hash), 414 node: child, 415 parent: ancestor, 416 index: -1, 417 pathlen: len(path), 418 } 419 childPath = append(childPath, path...) 420 childPath = append(childPath, byte(index)) 421 return child, state, childPath, index 422 } 423 } 424 return nil, nil, nil, 0 425 } 426 427 func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Hash) (*nodeIteratorState, []byte, bool) { 428 switch node := parent.node.(type) { 429 case *fullNode: 430 // Full node, move to the first non-nil child. 431 if child, state, path, index := findChild(node, parent.index+1, it.path, ancestor); child != nil { 432 parent.index = index - 1 433 return state, path, true 434 } 435 case *shortNode: 436 // Short node, return the pointer singleton child 437 if parent.index < 0 { 438 hash, _ := node.Val.cache() 439 state := &nodeIteratorState{ 440 hash: common.BytesToHash(hash), 441 node: node.Val, 442 parent: ancestor, 443 index: -1, 444 pathlen: len(it.path), 445 } 446 path := append(it.path, node.Key...) 447 return state, path, true 448 } 449 } 450 return parent, it.path, false 451 } 452 453 // nextChildAt is similar to nextChild, except that it targets a child as close to the 454 // target key as possible, thus skipping siblings. 455 func (it *nodeIterator) nextChildAt(parent *nodeIteratorState, ancestor common.Hash, key []byte) (*nodeIteratorState, []byte, bool) { 456 switch n := parent.node.(type) { 457 case *fullNode: 458 // Full node, move to the first non-nil child before the desired key position 459 child, state, path, index := findChild(n, parent.index+1, it.path, ancestor) 460 if child == nil { 461 // No more children in this fullnode 462 return parent, it.path, false 463 } 464 // If the child we found is already past the seek position, just return it. 465 if bytes.Compare(path, key) >= 0 { 466 parent.index = index - 1 467 return state, path, true 468 } 469 // The child is before the seek position. Try advancing 470 for { 471 nextChild, nextState, nextPath, nextIndex := findChild(n, index+1, it.path, ancestor) 472 // If we run out of children, or skipped past the target, return the 473 // previous one 474 if nextChild == nil || bytes.Compare(nextPath, key) >= 0 { 475 parent.index = index - 1 476 return state, path, true 477 } 478 // We found a better child closer to the target 479 state, path, index = nextState, nextPath, nextIndex 480 } 481 case *shortNode: 482 // Short node, return the pointer singleton child 483 if parent.index < 0 { 484 hash, _ := n.Val.cache() 485 state := &nodeIteratorState{ 486 hash: common.BytesToHash(hash), 487 node: n.Val, 488 parent: ancestor, 489 index: -1, 490 pathlen: len(it.path), 491 } 492 path := append(it.path, n.Key...) 493 return state, path, true 494 } 495 } 496 return parent, it.path, false 497 } 498 499 func (it *nodeIterator) push(state *nodeIteratorState, parentIndex *int, path []byte) { 500 it.path = path 501 it.stack = append(it.stack, state) 502 if parentIndex != nil { 503 *parentIndex++ 504 } 505 } 506 507 func (it *nodeIterator) pop() { 508 last := it.stack[len(it.stack)-1] 509 it.path = it.path[:last.pathlen] 510 it.stack[len(it.stack)-1] = nil 511 it.stack = it.stack[:len(it.stack)-1] 512 } 513 514 func compareNodes(a, b NodeIterator) int { 515 if cmp := bytes.Compare(a.Path(), b.Path()); cmp != 0 { 516 return cmp 517 } 518 if a.Leaf() && !b.Leaf() { 519 return -1 520 } else if b.Leaf() && !a.Leaf() { 521 return 1 522 } 523 if cmp := bytes.Compare(a.Hash().Bytes(), b.Hash().Bytes()); cmp != 0 { 524 return cmp 525 } 526 if a.Leaf() && b.Leaf() { 527 return bytes.Compare(a.LeafBlob(), b.LeafBlob()) 528 } 529 return 0 530 } 531 532 type differenceIterator struct { 533 a, b NodeIterator // Nodes returned are those in b - a. 534 eof bool // Indicates a has run out of elements 535 count int // Number of nodes scanned on either trie 536 } 537 538 // NewDifferenceIterator constructs a NodeIterator that iterates over elements in b that 539 // are not in a. Returns the iterator, and a pointer to an integer recording the number 540 // of nodes seen. 541 func NewDifferenceIterator(a, b NodeIterator) (NodeIterator, *int) { 542 a.Next(true) 543 it := &differenceIterator{ 544 a: a, 545 b: b, 546 } 547 return it, &it.count 548 } 549 550 func (it *differenceIterator) Hash() common.Hash { 551 return it.b.Hash() 552 } 553 554 func (it *differenceIterator) Parent() common.Hash { 555 return it.b.Parent() 556 } 557 558 func (it *differenceIterator) Leaf() bool { 559 return it.b.Leaf() 560 } 561 562 func (it *differenceIterator) LeafKey() []byte { 563 return it.b.LeafKey() 564 } 565 566 func (it *differenceIterator) LeafBlob() []byte { 567 return it.b.LeafBlob() 568 } 569 570 func (it *differenceIterator) LeafProof() [][]byte { 571 return it.b.LeafProof() 572 } 573 574 func (it *differenceIterator) Path() []byte { 575 return it.b.Path() 576 } 577 578 func (it *differenceIterator) NodeBlob() []byte { 579 return it.b.NodeBlob() 580 } 581 582 func (it *differenceIterator) AddResolver(resolver ethdb.KeyValueReader) { 583 panic("not implemented") 584 } 585 586 func (it *differenceIterator) Next(bool) bool { 587 // Invariants: 588 // - We always advance at least one element in b. 589 // - At the start of this function, a's path is lexically greater than b's. 590 if !it.b.Next(true) { 591 return false 592 } 593 it.count++ 594 595 if it.eof { 596 // a has reached eof, so we just return all elements from b 597 return true 598 } 599 600 for { 601 switch compareNodes(it.a, it.b) { 602 case -1: 603 // b jumped past a; advance a 604 if !it.a.Next(true) { 605 it.eof = true 606 return true 607 } 608 it.count++ 609 case 1: 610 // b is before a 611 return true 612 case 0: 613 // a and b are identical; skip this whole subtree if the nodes have hashes 614 hasHash := it.a.Hash() == common.Hash{} 615 if !it.b.Next(hasHash) { 616 return false 617 } 618 it.count++ 619 if !it.a.Next(hasHash) { 620 it.eof = true 621 return true 622 } 623 it.count++ 624 } 625 } 626 } 627 628 func (it *differenceIterator) Error() error { 629 if err := it.a.Error(); err != nil { 630 return err 631 } 632 return it.b.Error() 633 } 634 635 type nodeIteratorHeap []NodeIterator 636 637 func (h nodeIteratorHeap) Len() int { return len(h) } 638 func (h nodeIteratorHeap) Less(i, j int) bool { return compareNodes(h[i], h[j]) < 0 } 639 func (h nodeIteratorHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 640 func (h *nodeIteratorHeap) Push(x interface{}) { *h = append(*h, x.(NodeIterator)) } 641 func (h *nodeIteratorHeap) Pop() interface{} { 642 n := len(*h) 643 x := (*h)[n-1] 644 *h = (*h)[0 : n-1] 645 return x 646 } 647 648 type unionIterator struct { 649 items *nodeIteratorHeap // Nodes returned are the union of the ones in these iterators 650 count int // Number of nodes scanned across all tries 651 } 652 653 // NewUnionIterator constructs a NodeIterator that iterates over elements in the union 654 // of the provided NodeIterators. Returns the iterator, and a pointer to an integer 655 // recording the number of nodes visited. 656 func NewUnionIterator(iters []NodeIterator) (NodeIterator, *int) { 657 h := make(nodeIteratorHeap, len(iters)) 658 copy(h, iters) 659 heap.Init(&h) 660 661 ui := &unionIterator{items: &h} 662 return ui, &ui.count 663 } 664 665 func (it *unionIterator) Hash() common.Hash { 666 return (*it.items)[0].Hash() 667 } 668 669 func (it *unionIterator) Parent() common.Hash { 670 return (*it.items)[0].Parent() 671 } 672 673 func (it *unionIterator) Leaf() bool { 674 return (*it.items)[0].Leaf() 675 } 676 677 func (it *unionIterator) LeafKey() []byte { 678 return (*it.items)[0].LeafKey() 679 } 680 681 func (it *unionIterator) LeafBlob() []byte { 682 return (*it.items)[0].LeafBlob() 683 } 684 685 func (it *unionIterator) LeafProof() [][]byte { 686 return (*it.items)[0].LeafProof() 687 } 688 689 func (it *unionIterator) Path() []byte { 690 return (*it.items)[0].Path() 691 } 692 693 func (it *unionIterator) NodeBlob() []byte { 694 return (*it.items)[0].NodeBlob() 695 } 696 697 func (it *unionIterator) AddResolver(resolver ethdb.KeyValueReader) { 698 panic("not implemented") 699 } 700 701 // Next returns the next node in the union of tries being iterated over. 702 // 703 // It does this by maintaining a heap of iterators, sorted by the iteration 704 // order of their next elements, with one entry for each source trie. Each 705 // time Next() is called, it takes the least element from the heap to return, 706 // advancing any other iterators that also point to that same element. These 707 // iterators are called with descend=false, since we know that any nodes under 708 // these nodes will also be duplicates, found in the currently selected iterator. 709 // Whenever an iterator is advanced, it is pushed back into the heap if it still 710 // has elements remaining. 711 // 712 // In the case that descend=false - eg, we're asked to ignore all subnodes of the 713 // current node - we also advance any iterators in the heap that have the current 714 // path as a prefix. 715 func (it *unionIterator) Next(descend bool) bool { 716 if len(*it.items) == 0 { 717 return false 718 } 719 720 // Get the next key from the union 721 least := heap.Pop(it.items).(NodeIterator) 722 723 // Skip over other nodes as long as they're identical, or, if we're not descending, as 724 // long as they have the same prefix as the current node. 725 for len(*it.items) > 0 && ((!descend && bytes.HasPrefix((*it.items)[0].Path(), least.Path())) || compareNodes(least, (*it.items)[0]) == 0) { 726 skipped := heap.Pop(it.items).(NodeIterator) 727 // Skip the whole subtree if the nodes have hashes; otherwise just skip this node 728 if skipped.Next(skipped.Hash() == common.Hash{}) { 729 it.count++ 730 // If there are more elements, push the iterator back on the heap 731 heap.Push(it.items, skipped) 732 } 733 } 734 if least.Next(descend) { 735 it.count++ 736 heap.Push(it.items, least) 737 } 738 return len(*it.items) > 0 739 } 740 741 func (it *unionIterator) Error() error { 742 for i := 0; i < len(*it.items); i++ { 743 if err := (*it.items)[i].Error(); err != nil { 744 return err 745 } 746 } 747 return nil 748 }