github.com/core-coin/go-core/v2@v2.1.9/light/odr_util.go (about)

     1  // Copyright 2016 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core 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/core-coin/go-core/v2/common"
    26  	"github.com/core-coin/go-core/v2/core"
    27  	"github.com/core-coin/go-core/v2/core/rawdb"
    28  	"github.com/core-coin/go-core/v2/core/types"
    29  	"github.com/core-coin/go-core/v2/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(raisty) 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  }