github.com/c2s/go-ethereum@v1.9.7/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/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/core"
    25  	"github.com/ethereum/go-ethereum/core/rawdb"
    26  	"github.com/ethereum/go-ethereum/core/types"
    27  	"github.com/ethereum/go-ethereum/crypto"
    28  	"github.com/ethereum/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  }