github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/rpc/get_block_body.go (about)

     1  // Copyright 2021 The TrueBlocks Authors. All rights reserved.
     2  // Use of this source code is governed by a license that can
     3  // be found in the LICENSE file.
     4  
     5  package rpc
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    11  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc/query"
    12  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    13  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk"
    14  	"github.com/ethereum/go-ethereum"
    15  )
    16  
    17  // GetBlockBodyByNumber fetches the block with transactions from the RPC.
    18  func (conn *Connection) GetBlockBodyByNumber(bn base.Blknum) (types.Block, error) {
    19  	var err error
    20  	if conn.StoreReadable() {
    21  		// walk.Cache_Transactions, walk.Cache_Blocks
    22  		lightBlock := &types.LightBlock{
    23  			BlockNumber: bn,
    24  		}
    25  		if err := conn.Store.Read(lightBlock, nil); err == nil {
    26  			// We need to fill in the actual transactions (from cache hopefully, but
    27  			// if not, then from the RPC)
    28  			transactions := make([]types.Transaction, 0, len(lightBlock.Transactions))
    29  			for index := range lightBlock.Transactions {
    30  				tx, thisErr := conn.GetTransactionByNumberAndId(lightBlock.BlockNumber, base.Txnum(index))
    31  				if thisErr != nil {
    32  					err = thisErr
    33  					break
    34  				}
    35  				transactions = append(transactions, *tx)
    36  			}
    37  
    38  			if err == nil && len(transactions) == len(lightBlock.Transactions) {
    39  				lightToBody := func(block *types.LightBlock) *types.Block {
    40  					var ret types.Block
    41  					ret.BaseFeePerGas = block.BaseFeePerGas
    42  					ret.BlockNumber = block.BlockNumber
    43  					ret.Difficulty = block.Difficulty
    44  					ret.GasLimit = block.GasLimit
    45  					ret.GasUsed = block.GasUsed
    46  					ret.Hash = block.Hash
    47  					ret.Miner = block.Miner
    48  					ret.ParentHash = block.ParentHash
    49  					ret.Timestamp = block.Timestamp
    50  					ret.Uncles = block.Uncles
    51  					ret.Withdrawals = block.Withdrawals
    52  					return &ret
    53  				}
    54  
    55  				ret := lightToBody(lightBlock)
    56  				ret.Transactions = transactions
    57  				// TODO: BOGUS - avoid copy
    58  				return *ret, err
    59  			}
    60  		}
    61  	}
    62  
    63  	// The block is not in the cache, or reading the cache failed. We
    64  	// need to fetch the block from the RPC.
    65  	block, err := conn.getBlockFromRpc(bn, notAHash)
    66  	if err != nil {
    67  		return types.Block{}, err
    68  	}
    69  	isFinal := base.IsFinal(conn.LatestBlockTimestamp, block.Timestamp)
    70  
    71  	_, receiptMap, _ := conn.GetReceiptsByNumber(bn, block.Timestamp)
    72  	for i := 0; i < len(block.Transactions); i++ {
    73  		receipt := receiptMap[block.Transactions[i].TransactionIndex]
    74  		if receipt == nil {
    75  			rec, err := conn.GetReceipt(bn, block.Transactions[i].TransactionIndex, block.Timestamp)
    76  			if err != nil {
    77  				return types.Block{}, err
    78  			}
    79  			receipt = &rec
    80  		}
    81  		block.Transactions[i].GasUsed = receipt.GasUsed
    82  		block.Transactions[i].IsError = receipt.IsError
    83  		block.Transactions[i].Receipt = receipt
    84  		block.Transactions[i].Timestamp = block.Timestamp
    85  		block.Transactions[i].HasToken = types.IsTokenFunction(block.Transactions[i].Input)
    86  		if isFinal && conn.StoreWritable() && conn.EnabledMap[walk.Cache_Transactions] {
    87  			_ = conn.Store.Write(&block.Transactions[i], nil)
    88  		}
    89  	}
    90  
    91  	if isFinal && conn.StoreWritable() && conn.EnabledMap[walk.Cache_Blocks] {
    92  		_ = conn.Store.Write(block, nil)
    93  	}
    94  
    95  	// TODO: BOGUS - avoid copy
    96  	return *block, nil
    97  }
    98  
    99  // getBlockFromRpc returns the block as received from the node
   100  func (conn *Connection) getBlockFromRpc(bn base.Blknum, hash base.Hash) (*types.Block, error) {
   101  	method := "eth_getBlockByNumber"
   102  	params := query.Params{fmt.Sprintf("0x%x", bn), true}
   103  	if hash != notAHash {
   104  		method = "eth_getBlockByHash"
   105  		params = query.Params{hash, true}
   106  	}
   107  
   108  	if block, err := query.Query[types.Block](conn.Chain, method, params); err != nil {
   109  		return &types.Block{}, err
   110  	} else {
   111  		block.BlockNumber = block.Number
   112  		if bn == 0 {
   113  			block.Timestamp = conn.GetBlockTimestamp(0)
   114  		} else if block.Timestamp == 0 {
   115  			return &types.Block{}, fmt.Errorf("block at %s returned an error: %w", fmt.Sprintf("%d", bn), ethereum.NotFound)
   116  		}
   117  		for i := 0; i < len(block.Withdrawals); i++ {
   118  			block.Withdrawals[i].BlockNumber = block.BlockNumber
   119  			block.Withdrawals[i].Timestamp = block.Timestamp
   120  		}
   121  		return block, nil
   122  	}
   123  }