github.com/mprishchepo/go-ethereum@v1.9.7-0.20191031044858-21506be82b68/light/odr_util.go (about) 1 // Copyright 2016 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 light 18 19 import ( 20 "bytes" 21 "context" 22 23 "github.com/Fantom-foundation/go-ethereum/common" 24 "github.com/Fantom-foundation/go-ethereum/core" 25 "github.com/Fantom-foundation/go-ethereum/core/rawdb" 26 "github.com/Fantom-foundation/go-ethereum/core/types" 27 "github.com/Fantom-foundation/go-ethereum/crypto" 28 "github.com/Fantom-foundation/go-ethereum/rlp" 29 ) 30 31 var sha3Nil = crypto.Keccak256Hash(nil) 32 33 func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) { 34 db := odr.Database() 35 hash := rawdb.ReadCanonicalHash(db, number) 36 if (hash != common.Hash{}) { 37 // if there is a canonical hash, there is a header too 38 header := rawdb.ReadHeader(db, hash, number) 39 if header == nil { 40 panic("Canonical hash present but header not found") 41 } 42 return header, nil 43 } 44 45 var ( 46 chtCount, sectionHeadNum uint64 47 sectionHead common.Hash 48 ) 49 if odr.ChtIndexer() != nil { 50 chtCount, sectionHeadNum, sectionHead = odr.ChtIndexer().Sections() 51 canonicalHash := rawdb.ReadCanonicalHash(db, sectionHeadNum) 52 // if the CHT was injected as a trusted checkpoint, we have no canonical hash yet so we accept zero hash too 53 for chtCount > 0 && canonicalHash != sectionHead && canonicalHash != (common.Hash{}) { 54 chtCount-- 55 if chtCount > 0 { 56 sectionHeadNum = chtCount*odr.IndexerConfig().ChtSize - 1 57 sectionHead = odr.ChtIndexer().SectionHead(chtCount - 1) 58 canonicalHash = rawdb.ReadCanonicalHash(db, sectionHeadNum) 59 } 60 } 61 } 62 if number >= chtCount*odr.IndexerConfig().ChtSize { 63 return nil, errNoTrustedCht 64 } 65 r := &ChtRequest{ChtRoot: GetChtRoot(db, chtCount-1, sectionHead), ChtNum: chtCount - 1, BlockNum: number, Config: odr.IndexerConfig()} 66 if err := odr.Retrieve(ctx, r); err != nil { 67 return nil, err 68 } 69 return r.Header, nil 70 } 71 72 // GetUntrustedHeaderByNumber fetches specified block header without correctness checking. 73 // Note this function should only be used in light client checkpoint syncing. 74 func GetUntrustedHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64, peerId string) (*types.Header, error) { 75 r := &ChtRequest{BlockNum: number, ChtNum: number / odr.IndexerConfig().ChtSize, Untrusted: true, PeerId: peerId, Config: odr.IndexerConfig()} 76 if err := odr.Retrieve(ctx, r); err != nil { 77 return nil, err 78 } 79 return r.Header, nil 80 } 81 82 func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) { 83 hash := rawdb.ReadCanonicalHash(odr.Database(), number) 84 if (hash != common.Hash{}) { 85 return hash, nil 86 } 87 header, err := GetHeaderByNumber(ctx, odr, number) 88 if header != nil { 89 return header.Hash(), nil 90 } 91 return common.Hash{}, err 92 } 93 94 // GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. 95 func GetBodyRLP(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (rlp.RawValue, error) { 96 if data := rawdb.ReadBodyRLP(odr.Database(), hash, number); data != nil { 97 return data, nil 98 } 99 r := &BlockRequest{Hash: hash, Number: number} 100 if err := odr.Retrieve(ctx, r); err != nil { 101 return nil, err 102 } else { 103 return r.Rlp, nil 104 } 105 } 106 107 // GetBody retrieves the block body (transactons, uncles) corresponding to the 108 // hash. 109 func GetBody(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Body, error) { 110 data, err := GetBodyRLP(ctx, odr, hash, number) 111 if err != nil { 112 return nil, err 113 } 114 body := new(types.Body) 115 if err := rlp.Decode(bytes.NewReader(data), body); err != nil { 116 return nil, err 117 } 118 return body, nil 119 } 120 121 // GetBlock retrieves an entire block corresponding to the hash, assembling it 122 // back from the stored header and body. 123 func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Block, error) { 124 // Retrieve the block header and body contents 125 header := rawdb.ReadHeader(odr.Database(), hash, number) 126 if header == nil { 127 return nil, errNoHeader 128 } 129 body, err := GetBody(ctx, odr, hash, number) 130 if err != nil { 131 return nil, err 132 } 133 // Reassemble the block and return 134 return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles), nil 135 } 136 137 // GetBlockReceipts retrieves the receipts generated by the transactions included 138 // in a block given by its hash. 139 func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (types.Receipts, error) { 140 // Assume receipts are already stored locally and attempt to retrieve. 141 receipts := rawdb.ReadRawReceipts(odr.Database(), hash, number) 142 if receipts == nil { 143 r := &ReceiptsRequest{Hash: hash, Number: number} 144 if err := odr.Retrieve(ctx, r); err != nil { 145 return nil, err 146 } 147 receipts = r.Receipts 148 } 149 // If the receipts are incomplete, fill the derived fields 150 if len(receipts) > 0 && receipts[0].TxHash == (common.Hash{}) { 151 block, err := GetBlock(ctx, odr, hash, number) 152 if err != nil { 153 return nil, err 154 } 155 genesis := rawdb.ReadCanonicalHash(odr.Database(), 0) 156 config := rawdb.ReadChainConfig(odr.Database(), genesis) 157 158 if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Transactions()); err != nil { 159 return nil, err 160 } 161 rawdb.WriteReceipts(odr.Database(), hash, number, receipts) 162 } 163 return receipts, nil 164 } 165 166 // GetBlockLogs retrieves the logs generated by the transactions included in a 167 // block given by its hash. 168 func GetBlockLogs(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) ([][]*types.Log, error) { 169 // Retrieve the potentially incomplete receipts from disk or network 170 receipts, err := GetBlockReceipts(ctx, odr, hash, number) 171 if err != nil { 172 return nil, err 173 } 174 // Return the logs without deriving any computed fields on the receipts 175 logs := make([][]*types.Log, len(receipts)) 176 for i, receipt := range receipts { 177 logs[i] = receipt.Logs 178 } 179 return logs, nil 180 } 181 182 // GetUntrustedBlockLogs retrieves the logs generated by the transactions included in a 183 // block. The retrieved logs are regarded as untrusted and will not be stored in the 184 // database. This function should only be used in light client checkpoint syncing. 185 func GetUntrustedBlockLogs(ctx context.Context, odr OdrBackend, header *types.Header) ([][]*types.Log, error) { 186 // Retrieve the potentially incomplete receipts from disk or network 187 hash, number := header.Hash(), header.Number.Uint64() 188 receipts := rawdb.ReadRawReceipts(odr.Database(), hash, number) 189 if receipts == nil { 190 r := &ReceiptsRequest{Hash: hash, Number: number, Header: header, Untrusted: true} 191 if err := odr.Retrieve(ctx, r); err != nil { 192 return nil, err 193 } 194 receipts = r.Receipts 195 // Untrusted receipts won't be stored in the database. Therefore 196 // derived fields computation is unnecessary. 197 } 198 // Return the logs without deriving any computed fields on the receipts 199 logs := make([][]*types.Log, len(receipts)) 200 for i, receipt := range receipts { 201 logs[i] = receipt.Logs 202 } 203 return logs, nil 204 } 205 206 // GetBloomBits retrieves a batch of compressed bloomBits vectors belonging to the given bit index and section indexes 207 func GetBloomBits(ctx context.Context, odr OdrBackend, bitIdx uint, sectionIdxList []uint64) ([][]byte, error) { 208 var ( 209 db = odr.Database() 210 result = make([][]byte, len(sectionIdxList)) 211 reqList []uint64 212 reqIdx []int 213 ) 214 215 var ( 216 bloomTrieCount, sectionHeadNum uint64 217 sectionHead common.Hash 218 ) 219 if odr.BloomTrieIndexer() != nil { 220 bloomTrieCount, sectionHeadNum, sectionHead = odr.BloomTrieIndexer().Sections() 221 canonicalHash := rawdb.ReadCanonicalHash(db, sectionHeadNum) 222 // if the BloomTrie was injected as a trusted checkpoint, we have no canonical hash yet so we accept zero hash too 223 for bloomTrieCount > 0 && canonicalHash != sectionHead && canonicalHash != (common.Hash{}) { 224 bloomTrieCount-- 225 if bloomTrieCount > 0 { 226 sectionHeadNum = bloomTrieCount*odr.IndexerConfig().BloomTrieSize - 1 227 sectionHead = odr.BloomTrieIndexer().SectionHead(bloomTrieCount - 1) 228 canonicalHash = rawdb.ReadCanonicalHash(db, sectionHeadNum) 229 } 230 } 231 } 232 233 for i, sectionIdx := range sectionIdxList { 234 sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*odr.IndexerConfig().BloomSize-1) 235 // if we don't have the canonical hash stored for this section head number, we'll still look for 236 // an entry with a zero sectionHead (we store it with zero section head too if we don't know it 237 // at the time of the retrieval) 238 bloomBits, err := rawdb.ReadBloomBits(db, bitIdx, sectionIdx, sectionHead) 239 if err == nil { 240 result[i] = bloomBits 241 } else { 242 // TODO(rjl493456442) Convert sectionIndex to BloomTrie relative index 243 if sectionIdx >= bloomTrieCount { 244 return nil, errNoTrustedBloomTrie 245 } 246 reqList = append(reqList, sectionIdx) 247 reqIdx = append(reqIdx, i) 248 } 249 } 250 if reqList == nil { 251 return result, nil 252 } 253 254 r := &BloomRequest{BloomTrieRoot: GetBloomTrieRoot(db, bloomTrieCount-1, sectionHead), BloomTrieNum: bloomTrieCount - 1, 255 BitIdx: bitIdx, SectionIndexList: reqList, Config: odr.IndexerConfig()} 256 if err := odr.Retrieve(ctx, r); err != nil { 257 return nil, err 258 } else { 259 for i, idx := range reqIdx { 260 result[idx] = r.BloomBits[i] 261 } 262 return result, nil 263 } 264 } 265 266 // GetTransaction retrieves a canonical transaction by hash and also returns its position in the chain 267 func GetTransaction(ctx context.Context, odr OdrBackend, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { 268 r := &TxStatusRequest{Hashes: []common.Hash{txHash}} 269 if err := odr.Retrieve(ctx, r); err != nil || r.Status[0].Status != core.TxStatusIncluded { 270 return nil, common.Hash{}, 0, 0, err 271 } else { 272 pos := r.Status[0].Lookup 273 // first ensure that we have the header, otherwise block body retrieval will fail 274 // also verify if this is a canonical block by getting the header by number and checking its hash 275 if header, err := GetHeaderByNumber(ctx, odr, pos.BlockIndex); err != nil || header.Hash() != pos.BlockHash { 276 return nil, common.Hash{}, 0, 0, err 277 } 278 if body, err := GetBody(ctx, odr, pos.BlockHash, pos.BlockIndex); err != nil || uint64(len(body.Transactions)) <= pos.Index || body.Transactions[pos.Index].Hash() != txHash { 279 return nil, common.Hash{}, 0, 0, err 280 } else { 281 return body.Transactions[pos.Index], pos.BlockHash, pos.BlockIndex, pos.Index, nil 282 } 283 } 284 }