github.com/theQRL/go-zond@v0.1.1/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  	"context"
    21  	"errors"
    22  	"math/big"
    23  
    24  	"github.com/theQRL/go-zond/common"
    25  	"github.com/theQRL/go-zond/consensus/misc/eip4844"
    26  	"github.com/theQRL/go-zond/core/rawdb"
    27  	"github.com/theQRL/go-zond/core/txpool"
    28  	"github.com/theQRL/go-zond/core/types"
    29  	"github.com/theQRL/go-zond/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.DecodeBytes(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. Receipts will be filled in with context data.
   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  		var blobGasPrice *big.Int
   179  		excessBlobGas := block.ExcessBlobGas()
   180  		if excessBlobGas != nil {
   181  			blobGasPrice = eip4844.CalcBlobFee(*excessBlobGas)
   182  		}
   183  
   184  		if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, block.Transactions()); err != nil {
   185  			return nil, err
   186  		}
   187  		rawdb.WriteReceipts(odr.Database(), hash, number, receipts)
   188  	}
   189  	return receipts, nil
   190  }
   191  
   192  // GetBlockLogs retrieves the logs generated by the transactions included in a
   193  // block given by its hash. Logs will be filled in with context data.
   194  func GetBlockLogs(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) ([][]*types.Log, error) {
   195  	receipts, err := GetBlockReceipts(ctx, odr, hash, number)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   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
   207  // the given bit index and section indexes.
   208  func GetBloomBits(ctx context.Context, odr OdrBackend, bit uint, sections []uint64) ([][]byte, error) {
   209  	var (
   210  		reqIndex    []int
   211  		reqSections []uint64
   212  		db          = odr.Database()
   213  		result      = make([][]byte, len(sections))
   214  	)
   215  	blooms, _, sectionHead := odr.BloomTrieIndexer().Sections()
   216  	for i, section := range sections {
   217  		sectionHead := rawdb.ReadCanonicalHash(db, (section+1)*odr.IndexerConfig().BloomSize-1)
   218  		// If we don't have the canonical hash stored for this section head number,
   219  		// we'll still look for an entry with a zero sectionHead (we store it with
   220  		// zero section head too if we don't know it at the time of the retrieval)
   221  		if bloomBits, _ := rawdb.ReadBloomBits(db, bit, section, sectionHead); len(bloomBits) != 0 {
   222  			result[i] = bloomBits
   223  			continue
   224  		}
   225  		// TODO(rjl493456442) Convert sectionIndex to BloomTrie relative index
   226  		if section >= blooms {
   227  			return nil, errNoTrustedBloomTrie
   228  		}
   229  		reqSections = append(reqSections, section)
   230  		reqIndex = append(reqIndex, i)
   231  	}
   232  	// Find all bloombits in database, nothing to query via odr, return.
   233  	if reqSections == nil {
   234  		return result, nil
   235  	}
   236  	// Send odr request to retrieve missing bloombits.
   237  	r := &BloomRequest{
   238  		BloomTrieRoot:    GetBloomTrieRoot(db, blooms-1, sectionHead),
   239  		BloomTrieNum:     blooms - 1,
   240  		BitIdx:           bit,
   241  		SectionIndexList: reqSections,
   242  		Config:           odr.IndexerConfig(),
   243  	}
   244  	if err := odr.Retrieve(ctx, r); err != nil {
   245  		return nil, err
   246  	}
   247  	for i, idx := range reqIndex {
   248  		result[idx] = r.BloomBits[i]
   249  	}
   250  	return result, nil
   251  }
   252  
   253  // GetTransaction retrieves a canonical transaction by hash and also returns
   254  // its position in the chain. There is no guarantee in the LES protocol that
   255  // the mined transaction will be retrieved back for sure because of different
   256  // reasons(the transaction is unindexed, the malicious server doesn't reply it
   257  // deliberately, etc). Therefore, unretrieved transactions will receive a certain
   258  // number of retries, thus giving a weak guarantee.
   259  func GetTransaction(ctx context.Context, odr OdrBackend, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
   260  	r := &TxStatusRequest{Hashes: []common.Hash{txHash}}
   261  	if err := odr.RetrieveTxStatus(ctx, r); err != nil || r.Status[0].Status != txpool.TxStatusIncluded {
   262  		return nil, common.Hash{}, 0, 0, err
   263  	}
   264  	pos := r.Status[0].Lookup
   265  	// first ensure that we have the header, otherwise block body retrieval will fail
   266  	// also verify if this is a canonical block by getting the header by number and checking its hash
   267  	if header, err := GetHeaderByNumber(ctx, odr, pos.BlockIndex); err != nil || header.Hash() != pos.BlockHash {
   268  		return nil, common.Hash{}, 0, 0, err
   269  	}
   270  	body, err := GetBody(ctx, odr, pos.BlockHash, pos.BlockIndex)
   271  	if err != nil || uint64(len(body.Transactions)) <= pos.Index || body.Transactions[pos.Index].Hash() != txHash {
   272  		return nil, common.Hash{}, 0, 0, err
   273  	}
   274  	return body.Transactions[pos.Index], pos.BlockHash, pos.BlockIndex, pos.Index, nil
   275  }