github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/ledger/partial/ptrie/partialTrie.go (about) 1 package ptrie 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/flow-go/ledger" 7 "github.com/onflow/flow-go/ledger/common/bitutils" 8 "github.com/onflow/flow-go/ledger/common/hash" 9 ) 10 11 // PSMT (Partial Sparse Merkle Tree) holds a subset of an sparse merkle tree at specific 12 // state (no historic views). Instead of keeping any unneeded branch, it only keeps 13 // the hash of subtree. This implementation is fully stored in memory and doesn't use 14 // a database. 15 // 16 // DEFINITIONS and CONVENTIONS: 17 // - HEIGHT of a node v in a tree is the number of edges on the longest downward path 18 // between v and a tree leaf. The height of a tree is the heights of its root. 19 // The height of a Trie is always the height of the fully-expanded tree. 20 type PSMT struct { 21 root *node // Root 22 pathLookUp map[ledger.Path]*node 23 } 24 25 // RootHash returns the rootNode hash value of the SMT 26 func (p *PSMT) RootHash() ledger.RootHash { 27 return ledger.RootHash(p.root.Hash()) 28 } 29 30 // GetSinglePayload returns payload of a given path 31 func (p *PSMT) GetSinglePayload(path ledger.Path) (*ledger.Payload, error) { 32 node, found := p.pathLookUp[path] 33 if !found { 34 return nil, &ErrMissingPath{Paths: []ledger.Path{path}} 35 } 36 return node.payload, nil 37 } 38 39 // Get returns an slice of payloads (same order), an slice of failed paths and errors (if any) 40 // TODO return list of indecies instead of paths 41 func (p *PSMT) Get(paths []ledger.Path) ([]*ledger.Payload, error) { 42 var failedPaths []ledger.Path 43 payloads := make([]*ledger.Payload, len(paths)) 44 for i, path := range paths { 45 // lookup the path for the payload 46 node, found := p.pathLookUp[path] 47 if !found { 48 failedPaths = append(failedPaths, path) 49 continue 50 } 51 payloads[i] = node.payload 52 } 53 if len(failedPaths) > 0 { 54 return nil, &ErrMissingPath{Paths: failedPaths} 55 } 56 return payloads, nil 57 } 58 59 // Update updates registers and returns rootValue after updates 60 // in case of error, it returns a list of paths for which update failed 61 func (p *PSMT) Update(paths []ledger.Path, payloads []*ledger.Payload) (ledger.RootHash, error) { 62 var failedPaths []ledger.Path 63 for i, path := range paths { 64 payload := payloads[i] 65 // lookup the path and update the value 66 node, found := p.pathLookUp[path] 67 if !found { 68 failedPaths = append(failedPaths, path) 69 continue 70 } 71 node.hashValue = ledger.ComputeCompactValue(hash.Hash(path), payload.Value(), node.height) 72 } 73 if len(failedPaths) > 0 { 74 return ledger.RootHash(hash.DummyHash), &ErrMissingPath{Paths: failedPaths} 75 } 76 // after updating all the nodes, compute the value recursively only once 77 return ledger.RootHash(p.root.forceComputeHash()), nil 78 } 79 80 // NewPSMT builds a Partial Sparse Merkle Tree (PSMT) given a chunkdatapack registertouches 81 // TODO just accept batch proof as input 82 func NewPSMT( 83 rootValue ledger.RootHash, 84 batchProof *ledger.TrieBatchProof, 85 ) (*PSMT, error) { 86 height := ledger.NodeMaxHeight 87 psmt := PSMT{newNode(ledger.GetDefaultHashForHeight(height), height), make(map[ledger.Path]*node)} 88 89 // iterating over proofs for building the tree 90 for i, pr := range batchProof.Proofs { 91 if pr == nil { 92 return nil, fmt.Errorf("proof at index %d is nil", i) 93 } 94 path := pr.Path 95 payload := pr.Payload 96 97 // we process the path, bit by bit, until we reach the end of the proof (due to compactness) 98 prValueIndex := 0 // we keep track of our progress through proofs by prValueIndex 99 currentNode := psmt.root // start from the rootNode and walk down the tree 100 for j := 0; j < int(pr.Steps); j++ { 101 // if a flag (bit j in flags) is false, the value is a default value 102 // otherwise the value is stored in the proofs 103 defaultHash := ledger.GetDefaultHashForHeight(currentNode.height - 1) 104 v := defaultHash 105 flag := bitutils.ReadBit(pr.Flags, j) 106 if flag == 1 { 107 // use the proof at index prValueIndex 108 v = pr.Interims[prValueIndex] 109 prValueIndex++ 110 } 111 bit := bitutils.ReadBit(path[:], j) 112 // look at the bit number j (left to right) for branching 113 if bit == 1 { // right branching 114 if currentNode.lChild == nil { // check left child 115 currentNode.lChild = newNode(v, currentNode.height-1) 116 } 117 if currentNode.rChild == nil { // create the right child if not exist 118 // Caution: we are temporarily initializing the node with default hash, which will later get updated to the 119 // proper value (if this is an interim node, its hash will be set when computing the root hash of the PTrie 120 // in the end; if this is a leaf, we'll set the hash at the end of processing the proof) 121 currentNode.rChild = newNode(defaultHash, currentNode.height-1) 122 } 123 currentNode = currentNode.rChild 124 } else { // left branching 125 if currentNode.rChild == nil { // check right child 126 currentNode.rChild = newNode(v, currentNode.height-1) 127 } 128 if currentNode.lChild == nil { // create the left child if not exist 129 // Caution: we are temporarily initializing the node with default hash, which will later get updated to the 130 // proper value (if this is an interim node, its hash will be set when computing the root hash of the PTrie 131 // in the end; if this is a leaf, we'll set the hash at the end of processing the proof) 132 currentNode.lChild = newNode(defaultHash, currentNode.height-1) 133 } 134 currentNode = currentNode.lChild 135 } 136 } 137 138 currentNode.payload = payload 139 // update node's hash value only for inclusion proofs (for others we assume default value) 140 if pr.Inclusion { 141 currentNode.hashValue = ledger.ComputeCompactValue(hash.Hash(path), payload.Value(), currentNode.height) 142 } 143 // keep a reference to this node by path (for update purpose) 144 psmt.pathLookUp[path] = currentNode 145 } 146 147 // check if the rootHash matches the root node's hash value of the partial trie 148 if ledger.RootHash(psmt.root.forceComputeHash()) != rootValue { 149 return nil, fmt.Errorf("rootNode hash doesn't match the proofs expected [%x], got [%x]", psmt.root.Hash(), rootValue) 150 } 151 return &psmt, nil 152 }