From d383160a40138eda7234697efddc7cb2219c49c2 Mon Sep 17 00:00:00 2001 From: Delweng Date: Tue, 15 Aug 2023 20:35:48 +0800 Subject: [PATCH] internal/ethapi: implement eth_getBlockReceipts (#27702) commit https://github.com/ethereum/go-ethereum/commit/f1801a9feda8f81532c92077d2c9a8b785fd699b. The response is the same as eth_getTransactionReceipt but allow user to get all receipts in a block instead of having to query each transaction as eth_getTransactionReceipt. --- ethclient/ethclient.go | 10 +++++++ internal/ethapi/api.go | 54 ++++++++++++++++++++++++++++++++----- internal/web3ext/web3ext.go | 5 ++++ 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 330e57b92e..4d953e6b6a 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -98,6 +98,16 @@ func (ec *Client) BlockNumber(ctx context.Context) (uint64, error) { return uint64(result), err } +// BlockReceipts returns the receipts of a given block number or hash +func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.Receipt, error) { + var r []*types.Receipt + err := ec.c.CallContext(ctx, &r, "eth_getBlockReceipts", blockNrOrHash) + if err == nil && r == nil { + return nil, ethereum.NotFound + } + return r, err +} + type rpcBlock struct { Hash common.Hash `json:"hash"` Transactions []rpcTransaction `json:"transactions"` diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 4e4fe97044..3f1f8b95d6 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -863,6 +863,34 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A return res[:], state.Error() } +// GetBlockReceipts returns the block receipts for the given block hash or number or tag. +func (s *PublicBlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { + block, err := s.b.BlockByNumberOrHash(ctx, blockNrOrHash) + if block == nil || err != nil { + // When the block doesn't exist, the RPC method should return JSON null + // as per specification. + return nil, nil + } + receipts, err := s.b.GetReceipts(ctx, block.Hash()) + if err != nil { + return nil, err + } + txs := block.Transactions() + if len(txs) != len(receipts) { + return nil, fmt.Errorf("receipts length mismatch: %d vs %d", len(txs), len(receipts)) + } + + // Derive the sender. + signer := types.MakeSigner(s.b.ChainConfig(), block.Number()) + + result := make([]map[string]interface{}, len(receipts)) + for i, receipt := range receipts { + result[i] = marshalReceipt(ctx, s.b, receipt, block.Hash(), block.NumberU64(), signer, txs[i], i) + } + + return result, nil +} + // OverrideAccount indicates the overriding fields of account during the execution // of a message call. // Note, state and stateDiff can't be specified at the same time. If state is @@ -1729,13 +1757,27 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha // Derive the sender. bigblock := new(big.Int).SetUint64(blockNumber) signer := types.MakeSigner(s.b.ChainConfig(), bigblock) + return marshalReceipt(ctx, s.b, receipt, blockHash, blockNumber, signer, tx, int(index)), nil +} + +// marshalReceipt marshals a transaction receipt into a JSON object. +func marshalReceipt( + ctx context.Context, + backend Backend, + receipt *types.Receipt, + blockHash common.Hash, + blockNumber uint64, + signer types.Signer, + tx *types.Transaction, + txIndex int, +) map[string]interface{} { from, _ := types.Sender(signer, tx) fields := map[string]interface{}{ "blockHash": blockHash, "blockNumber": hexutil.Uint64(blockNumber), - "transactionHash": hash, - "transactionIndex": hexutil.Uint64(index), + "transactionHash": tx.Hash(), + "transactionIndex": hexutil.Uint64(txIndex), "from": from, "to": tx.To(), "gasUsed": hexutil.Uint64(receipt.GasUsed), @@ -1751,12 +1793,12 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha } // Assign the effective gas price paid - if !s.b.ChainConfig().IsLondon(bigblock) { + if !backend.ChainConfig().IsLondon(new(big.Int).SetUint64(blockNumber)) { fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64()) } else { - header, err := s.b.HeaderByHash(ctx, blockHash) + header, err := backend.HeaderByHash(ctx, blockHash) if err != nil { - return nil, err + return nil } gasPrice := new(big.Int).Add(header.BaseFee, tx.EffectiveGasTipValue(header.BaseFee)) fields["effectiveGasPrice"] = hexutil.Uint64(gasPrice.Uint64()) @@ -1778,7 +1820,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha if receipt.ContractAddress != (common.Address{}) { fields["contractAddress"] = receipt.ContractAddress } - return fields, nil + return fields } // sign is a helper function that signs a transaction with the private key of the given address. diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index c9f47ca093..f0fb732e89 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -587,6 +587,11 @@ web3._extend({ params: 4, inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputDefaultBlockNumberFormatter, null, null], }), + new web3._extend.Method({ + name: 'getBlockReceipts', + call: 'eth_getBlockReceipts', + params: 1, + }), ], properties: [ new web3._extend.Property({