github.com/koko1123/flow-go-1@v0.29.6/ledger/complete/mtrie/flattener/encoding_v4.go (about) 1 package flattener 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 8 "github.com/koko1123/flow-go-1/ledger" 9 "github.com/koko1123/flow-go-1/ledger/common/hash" 10 "github.com/koko1123/flow-go-1/ledger/complete/mtrie/node" 11 "github.com/koko1123/flow-go-1/ledger/complete/mtrie/trie" 12 ) 13 14 // This file contains decoding functions for checkpoint v4 for backwards compatibility. 15 16 const ( 17 encNodeTypeSizeV4 = 1 18 encHeightSizeV4 = 2 19 encMaxDepthSizeV4 = 2 20 encRegCountSizeV4 = 8 21 encHashSizeV4 = hash.HashLen 22 encPathSizeV4 = ledger.PathLen 23 encNodeIndexSizeV4 = 8 24 encPayloadLengthSizeV4 = 4 25 ) 26 27 // ReadNodeFromCheckpointV4 reconstructs a node from data read from reader. 28 // Scratch buffer is used to avoid allocs. It should be used directly instead 29 // of using append. This function uses len(scratch) and ignores cap(scratch), 30 // so any extra capacity will not be utilized. 31 // If len(scratch) < 1024, then a new buffer will be allocated and used. 32 // Leaf node is encoded in the following format: 33 // - node type (1 byte) 34 // - height (2 bytes) 35 // - max depth (2 bytes) 36 // - reg count (8 bytes) 37 // - hash (32 bytes) 38 // - path (32 bytes) 39 // - payload (4 bytes + n bytes) 40 func ReadNodeFromCheckpointV4(reader io.Reader, scratch []byte, getNode getNodeFunc) (*node.Node, uint64, uint64, error) { 41 42 // minBufSize should be large enough for interim node and leaf node with small payload. 43 // minBufSize is a failsafe and is only used when len(scratch) is much smaller 44 // than expected. len(scratch) is 4096 by default, so minBufSize isn't likely to be used. 45 const minBufSize = 1024 46 47 if len(scratch) < minBufSize { 48 scratch = make([]byte, minBufSize) 49 } 50 51 // fixLengthSize is the size of shared data of leaf node and interim node 52 const fixLengthSize = encNodeTypeSizeV4 + 53 encHeightSizeV4 + 54 encMaxDepthSizeV4 + 55 encRegCountSizeV4 + 56 encHashSizeV4 57 58 _, err := io.ReadFull(reader, scratch[:fixLengthSize]) 59 if err != nil { 60 return nil, 0, 0, fmt.Errorf("failed to read fixed-length part of serialized node: %w", err) 61 } 62 63 pos := 0 64 65 // Decode node type (1 byte) 66 nType := scratch[pos] 67 pos += encNodeTypeSizeV4 68 69 if nType != byte(leafNodeType) && nType != byte(interimNodeType) { 70 return nil, 0, 0, fmt.Errorf("failed to decode node type %d", nType) 71 } 72 73 // Decode height (2 bytes) 74 height := binary.BigEndian.Uint16(scratch[pos:]) 75 pos += encHeightSizeV4 76 77 // Decode max depth (2 bytes) 78 maxDepth := binary.BigEndian.Uint16(scratch[pos:]) 79 pos += encMaxDepthSizeV4 80 81 // Decode reg count (8 bytes) 82 regCount := binary.BigEndian.Uint64(scratch[pos:]) 83 pos += encRegCountSizeV4 84 85 // Decode and create hash.Hash (32 bytes) 86 nodeHash, err := hash.ToHash(scratch[pos : pos+encHashSizeV4]) 87 if err != nil { 88 return nil, 0, 0, fmt.Errorf("failed to decode hash of serialized node: %w", err) 89 } 90 91 // maxDepth and regCount are removed from Node. 92 _ = maxDepth 93 _ = regCount 94 95 if nType == byte(leafNodeType) { 96 97 // Read path (32 bytes) 98 encPath := scratch[:encPathSizeV4] 99 _, err := io.ReadFull(reader, encPath) 100 if err != nil { 101 return nil, 0, 0, fmt.Errorf("failed to read path of serialized node: %w", err) 102 } 103 104 // Decode and create ledger.Path. 105 path, err := ledger.ToPath(encPath) 106 if err != nil { 107 return nil, 0, 0, fmt.Errorf("failed to decode path of serialized node: %w", err) 108 } 109 110 // Read encoded payload data and create ledger.Payload. 111 payload, err := readPayloadV0FromReader(reader, scratch) 112 if err != nil { 113 return nil, 0, 0, fmt.Errorf("failed to read and decode payload of serialized node: %w", err) 114 } 115 116 n := node.NewNode(int(height), nil, nil, path, payload, nodeHash) 117 118 // Leaf node has 1 register and register size is payload size. 119 return n, 1, uint64(payload.Size()), nil 120 } 121 122 // Read interim node 123 124 // Read left and right child index (16 bytes) 125 _, err = io.ReadFull(reader, scratch[:encNodeIndexSizeV4*2]) 126 if err != nil { 127 return nil, 0, 0, fmt.Errorf("failed to read child index of serialized node: %w", err) 128 } 129 130 pos = 0 131 132 // Decode left child index (8 bytes) 133 lchildIndex := binary.BigEndian.Uint64(scratch[pos:]) 134 pos += encNodeIndexSizeV4 135 136 // Decode right child index (8 bytes) 137 rchildIndex := binary.BigEndian.Uint64(scratch[pos:]) 138 139 // Get left child node by node index 140 lchild, lchildRegCount, lchildRegSize, err := getNode(lchildIndex) 141 if err != nil { 142 return nil, 0, 0, fmt.Errorf("failed to find left child node of serialized node: %w", err) 143 } 144 145 // Get right child node by node index 146 rchild, rchildRegCount, rchildRegSize, err := getNode(rchildIndex) 147 if err != nil { 148 return nil, 0, 0, fmt.Errorf("failed to find right child node of serialized node: %w", err) 149 } 150 151 n := node.NewNode(int(height), lchild, rchild, ledger.DummyPath, nil, nodeHash) 152 return n, lchildRegCount + rchildRegCount, lchildRegSize + rchildRegSize, nil 153 } 154 155 // ReadTrieFromCheckpointV4 reconstructs a trie from data read from reader. 156 func ReadTrieFromCheckpointV4(reader io.Reader, scratch []byte, getNode getNodeFunc) (*trie.MTrie, error) { 157 158 // encodedTrieSize is a failsafe and is only used when len(scratch) is much smaller 159 // than expected (4096 by default). 160 const encodedTrieSize = encNodeIndexSizeV4 + encHashSizeV4 161 162 if len(scratch) < encodedTrieSize { 163 scratch = make([]byte, encodedTrieSize) 164 } 165 166 // Read encoded trie (8 + 32 bytes) 167 _, err := io.ReadFull(reader, scratch[:encodedTrieSize]) 168 if err != nil { 169 return nil, fmt.Errorf("failed to read serialized trie: %w", err) 170 } 171 172 pos := 0 173 174 // Decode root node index 175 rootIndex := binary.BigEndian.Uint64(scratch) 176 pos += encNodeIndexSizeV4 177 178 // Decode root node hash 179 readRootHash, err := hash.ToHash(scratch[pos : pos+encHashSizeV4]) 180 if err != nil { 181 return nil, fmt.Errorf("failed to decode hash of serialized trie: %w", err) 182 } 183 184 rootNode, regCount, regSize, err := getNode(rootIndex) 185 if err != nil { 186 return nil, fmt.Errorf("failed to find root node of serialized trie: %w", err) 187 } 188 189 mtrie, err := trie.NewMTrie(rootNode, regCount, regSize) 190 if err != nil { 191 return nil, fmt.Errorf("failed to restore serialized trie: %w", err) 192 } 193 194 rootHash := mtrie.RootHash() 195 if !rootHash.Equals(ledger.RootHash(readRootHash)) { 196 return nil, fmt.Errorf("failed to restore serialized trie: roothash doesn't match") 197 } 198 199 return mtrie, nil 200 } 201 202 // readPayloadV0FromReader reads and decodes payload from reader. 203 // Returned payload is a copy. 204 func readPayloadV0FromReader(reader io.Reader, scratch []byte) (*ledger.Payload, error) { 205 206 if len(scratch) < encPayloadLengthSizeV4 { 207 scratch = make([]byte, encPayloadLengthSizeV4) 208 } 209 210 // Read payload size 211 _, err := io.ReadFull(reader, scratch[:encPayloadLengthSizeV4]) 212 if err != nil { 213 return nil, fmt.Errorf("cannot read payload length: %w", err) 214 } 215 216 // Decode payload size 217 size := binary.BigEndian.Uint32(scratch) 218 219 if len(scratch) < int(size) { 220 scratch = make([]byte, size) 221 } else { 222 scratch = scratch[:size] 223 } 224 225 _, err = io.ReadFull(reader, scratch) 226 if err != nil { 227 return nil, fmt.Errorf("cannot read payload: %w", err) 228 } 229 230 // Decode and copy payload 231 payload, err := ledger.DecodePayloadWithoutPrefix(scratch, false, 0) 232 if err != nil { 233 return nil, fmt.Errorf("failed to decode payload: %w", err) 234 } 235 236 return payload, nil 237 }