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