Skip to content

Commit f1801a9

Browse files
authored
internal/ethapi: implement eth_getBlockReceipts (#27702)
1 parent 509cd42 commit f1801a9

File tree

4 files changed

+163
-19
lines changed

4 files changed

+163
-19
lines changed

ethclient/ethclient.go

+10
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ func (ec *Client) PeerCount(ctx context.Context) (uint64, error) {
108108
return uint64(result), err
109109
}
110110

111+
// BlockReceipts returns the receipts of a given block number or hash
112+
func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.Receipt, error) {
113+
var r []*types.Receipt
114+
err := ec.c.CallContext(ctx, &r, "eth_getBlockReceipts", blockNrOrHash)
115+
if err == nil && r == nil {
116+
return nil, ethereum.NotFound
117+
}
118+
return r, err
119+
}
120+
111121
type rpcBlock struct {
112122
Hash common.Hash `json:"hash"`
113123
Transactions []rpcTransaction `json:"transactions"`

internal/ethapi/api.go

+36-3
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,34 @@ func (s *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address
897897
return res[:], state.Error()
898898
}
899899

900+
// GetBlockReceipts returns the block receipts for the given block hash or number or tag.
901+
func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) {
902+
block, err := s.b.BlockByNumberOrHash(ctx, blockNrOrHash)
903+
if block == nil || err != nil {
904+
// When the block doesn't exist, the RPC method should return JSON null
905+
// as per specification.
906+
return nil, nil
907+
}
908+
receipts, err := s.b.GetReceipts(ctx, block.Hash())
909+
if err != nil {
910+
return nil, err
911+
}
912+
txs := block.Transactions()
913+
if len(txs) != len(receipts) {
914+
return nil, fmt.Errorf("receipts length mismatch: %d vs %d", len(txs), len(receipts))
915+
}
916+
917+
// Derive the sender.
918+
signer := types.MakeSigner(s.b.ChainConfig(), block.Number(), block.Time())
919+
920+
result := make([]map[string]interface{}, len(receipts))
921+
for i, receipt := range receipts {
922+
result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i)
923+
}
924+
925+
return result, nil
926+
}
927+
900928
// OverrideAccount indicates the overriding fields of account during the execution
901929
// of a message call.
902930
// Note, state and stateDiff can't be specified at the same time. If state is
@@ -1717,13 +1745,18 @@ func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.
17171745

17181746
// Derive the sender.
17191747
signer := types.MakeSigner(s.b.ChainConfig(), header.Number, header.Time)
1748+
return marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)), nil
1749+
}
1750+
1751+
// marshalReceipt marshals a transaction receipt into a JSON object.
1752+
func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber uint64, signer types.Signer, tx *types.Transaction, txIndex int) map[string]interface{} {
17201753
from, _ := types.Sender(signer, tx)
17211754

17221755
fields := map[string]interface{}{
17231756
"blockHash": blockHash,
17241757
"blockNumber": hexutil.Uint64(blockNumber),
1725-
"transactionHash": hash,
1726-
"transactionIndex": hexutil.Uint64(index),
1758+
"transactionHash": tx.Hash(),
1759+
"transactionIndex": hexutil.Uint64(txIndex),
17271760
"from": from,
17281761
"to": tx.To(),
17291762
"gasUsed": hexutil.Uint64(receipt.GasUsed),
@@ -1749,7 +1782,7 @@ func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.
17491782
if receipt.ContractAddress != (common.Address{}) {
17501783
fields["contractAddress"] = receipt.ContractAddress
17511784
}
1752-
return fields, nil
1785+
return fields
17531786
}
17541787

17551788
// sign is a helper function that signs a transaction with the private key of the given address.

internal/ethapi/api_test.go

+112-16
Original file line numberDiff line numberDiff line change
@@ -1766,9 +1766,7 @@ func TestRPCGetBlockOrHeader(t *testing.T) {
17661766
}
17671767
}
17681768

1769-
func TestRPCGetTransactionReceipt(t *testing.T) {
1770-
t.Parallel()
1771-
1769+
func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Hash) {
17721770
// Initialize test accounts
17731771
var (
17741772
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
@@ -1794,9 +1792,8 @@ func TestRPCGetTransactionReceipt(t *testing.T) {
17941792
contract: {Balance: big.NewInt(params.Ether), Code: common.FromHex("0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a9059cbb14610030575b600080fd5b61004a6004803603810190610045919061016a565b610060565b60405161005791906101c5565b60405180910390f35b60008273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516100bf91906101ef565b60405180910390a36001905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610101826100d6565b9050919050565b610111816100f6565b811461011c57600080fd5b50565b60008135905061012e81610108565b92915050565b6000819050919050565b61014781610134565b811461015257600080fd5b50565b6000813590506101648161013e565b92915050565b60008060408385031215610181576101806100d1565b5b600061018f8582860161011f565b92505060206101a085828601610155565b9150509250929050565b60008115159050919050565b6101bf816101aa565b82525050565b60006020820190506101da60008301846101b6565b92915050565b6101e981610134565b82525050565b600060208201905061020460008301846101e0565b9291505056fea2646970667358221220b469033f4b77b9565ee84e0a2f04d496b18160d26034d54f9487e57788fd36d564736f6c63430008120033")},
17951793
},
17961794
}
1797-
genBlocks = 5
1798-
signer = types.LatestSignerForChainID(params.TestChainConfig.ChainID)
1799-
txHashes = make([]common.Hash, genBlocks)
1795+
signer = types.LatestSignerForChainID(params.TestChainConfig.ChainID)
1796+
txHashes = make([]common.Hash, genBlocks)
18001797
)
18011798
backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
18021799
var (
@@ -1838,16 +1835,16 @@ func TestRPCGetTransactionReceipt(t *testing.T) {
18381835
txHashes[i] = tx.Hash()
18391836
}
18401837
})
1841-
api := NewTransactionAPI(backend, new(AddrLocker))
1842-
blockHashes := make([]common.Hash, genBlocks+1)
1843-
ctx := context.Background()
1844-
for i := 0; i <= genBlocks; i++ {
1845-
header, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(i))
1846-
if err != nil {
1847-
t.Errorf("failed to get block: %d err: %v", i, err)
1848-
}
1849-
blockHashes[i] = header.Hash()
1850-
}
1838+
return backend, txHashes
1839+
}
1840+
1841+
func TestRPCGetTransactionReceipt(t *testing.T) {
1842+
t.Parallel()
1843+
1844+
var (
1845+
backend, txHashes = setupReceiptBackend(t, 5)
1846+
api = NewTransactionAPI(backend, new(AddrLocker))
1847+
)
18511848

18521849
var testSuite = []struct {
18531850
txHash common.Hash
@@ -2000,3 +1997,102 @@ func TestRPCGetTransactionReceipt(t *testing.T) {
20001997
require.JSONEqf(t, want, have, "test %d: json not match, want: %s, have: %s", i, want, have)
20011998
}
20021999
}
2000+
2001+
func TestRPCGetBlockReceipts(t *testing.T) {
2002+
t.Parallel()
2003+
2004+
var (
2005+
genBlocks = 5
2006+
backend, _ = setupReceiptBackend(t, genBlocks)
2007+
api = NewBlockChainAPI(backend)
2008+
)
2009+
blockHashes := make([]common.Hash, genBlocks+1)
2010+
ctx := context.Background()
2011+
for i := 0; i <= genBlocks; i++ {
2012+
header, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(i))
2013+
if err != nil {
2014+
t.Errorf("failed to get block: %d err: %v", i, err)
2015+
}
2016+
blockHashes[i] = header.Hash()
2017+
}
2018+
2019+
var testSuite = []struct {
2020+
test rpc.BlockNumberOrHash
2021+
want string
2022+
}{
2023+
// 0. block without any txs(hash)
2024+
{
2025+
test: rpc.BlockNumberOrHashWithHash(blockHashes[0], false),
2026+
want: `[]`,
2027+
},
2028+
// 1. block without any txs(number)
2029+
{
2030+
test: rpc.BlockNumberOrHashWithNumber(0),
2031+
want: `[]`,
2032+
},
2033+
// 2. earliest tag
2034+
{
2035+
test: rpc.BlockNumberOrHashWithNumber(rpc.EarliestBlockNumber),
2036+
want: `[]`,
2037+
},
2038+
// 3. latest tag
2039+
{
2040+
test: rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber),
2041+
want: `[{"blockHash":"0x08e23d8e3711a21fbb8becd7de22fda8fb0a49fba14e1be763d00f99063627e1","blockNumber":"0x5","contractAddress":"0xfdaa97661a584d977b4d3abb5370766ff5b86a18","cumulativeGasUsed":"0xe01a","effectiveGasPrice":"0x1ecb3f75","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0xe01a","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x1","to":null,"transactionHash":"0x8f3c4e2663af0312d508ebd8587f0c88dccbbc8a9bcc322421ff4bc28c456a92","transactionIndex":"0x0","type":"0x1"}]`,
2042+
},
2043+
// 4. block with legacy transfer tx(hash)
2044+
{
2045+
test: rpc.BlockNumberOrHashWithHash(blockHashes[1], false),
2046+
want: `[{"blockHash":"0x1356e49a24d4504e450b303aa770f4ae13c29b9ffacaea1d7dd4043396229dd9","blockNumber":"0x1","contractAddress":null,"cumulativeGasUsed":"0x5208","effectiveGasPrice":"0x342770c0","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x1","to":"0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e","transactionHash":"0x644a31c354391520d00e95b9affbbb010fc79ac268144ab8e28207f4cf51097e","transactionIndex":"0x0","type":"0x0"}]`,
2047+
},
2048+
// 5. block with contract create tx(number)
2049+
{
2050+
test: rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(2)),
2051+
want: `[{"blockHash":"0x4fc27a4efa7fb8faa04b12b53ec8c8424ab4c21aab1323846365f000e8b4a594","blockNumber":"0x2","contractAddress":"0xae9bea628c4ce503dcfd7e305cab4e29e7476592","cumulativeGasUsed":"0xcf4e","effectiveGasPrice":"0x2db16291","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0xcf4e","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x1","to":null,"transactionHash":"0x340e58cda5086495010b571fe25067fecc9954dc4ee3cedece00691fa3f5904a","transactionIndex":"0x0","type":"0x0"}]`,
2052+
},
2053+
// 6. block with legacy contract call tx(hash)
2054+
{
2055+
test: rpc.BlockNumberOrHashWithHash(blockHashes[3], false),
2056+
want: `[{"blockHash":"0x73385c190219326907524b0020ef453ebc450eaa971ebce16f79e2d23e7e8d4d","blockNumber":"0x3","contractAddress":null,"cumulativeGasUsed":"0x5e28","effectiveGasPrice":"0x281c2534","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0x5e28","logs":[{"address":"0x0000000000000000000000000000000000031ec7","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000703c4b2bd70c169f5717101caee543299fc946c7","0x0000000000000000000000000000000000000000000000000000000000000003"],"data":"0x000000000000000000000000000000000000000000000000000000000000000d","blockNumber":"0x3","transactionHash":"0x9dbf43ec9afc8d711932618616471088f66ba4f25fd5c672d97473d02dae967f","transactionIndex":"0x0","blockHash":"0x73385c190219326907524b0020ef453ebc450eaa971ebce16f79e2d23e7e8d4d","logIndex":"0x0","removed":false}],"logsBloom":"0x00000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000800000000000000008000000000000000000000000000000000020000000080000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000400000000002000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000","status":"0x1","to":"0x0000000000000000000000000000000000031ec7","transactionHash":"0x9dbf43ec9afc8d711932618616471088f66ba4f25fd5c672d97473d02dae967f","transactionIndex":"0x0","type":"0x0"}]`,
2057+
},
2058+
// 7. block with dynamic fee tx(number)
2059+
{
2060+
test: rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(4)),
2061+
want: `[{"blockHash":"0x77c3f8919590e0e68db4ce74a3da3140ac3e96dd3d078a48db1da4c08b07503d","blockNumber":"0x4","contractAddress":null,"cumulativeGasUsed":"0x538d","effectiveGasPrice":"0x2325c3e8","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0x538d","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x0","to":"0x0000000000000000000000000000000000031ec7","transactionHash":"0x672e3e39adf23b5656989b7a36e54d54004b1866f53871113bc52e137edb9faf","transactionIndex":"0x0","type":"0x2"}]`,
2062+
},
2063+
// 8. block is empty
2064+
{
2065+
test: rpc.BlockNumberOrHashWithHash(common.Hash{}, false),
2066+
want: `null`,
2067+
},
2068+
// 9. block is not found
2069+
{
2070+
test: rpc.BlockNumberOrHashWithHash(common.HexToHash("deadbeef"), false),
2071+
want: `null`,
2072+
},
2073+
// 10. block is not found
2074+
{
2075+
test: rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(genBlocks + 1)),
2076+
want: `null`,
2077+
},
2078+
}
2079+
2080+
for i, tt := range testSuite {
2081+
var (
2082+
result interface{}
2083+
err error
2084+
)
2085+
result, err = api.GetBlockReceipts(context.Background(), tt.test)
2086+
if err != nil {
2087+
t.Errorf("test %d: want no error, have %v", i, err)
2088+
continue
2089+
}
2090+
data, err := json.Marshal(result)
2091+
if err != nil {
2092+
t.Errorf("test %d: json marshal error", i)
2093+
continue
2094+
}
2095+
want, have := tt.want, string(data)
2096+
require.JSONEqf(t, want, have, "test %d: json not match, want: %s, have: %s", i, want, have)
2097+
}
2098+
}

internal/web3ext/web3ext.go

+5
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,11 @@ web3._extend({
617617
params: 4,
618618
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputDefaultBlockNumberFormatter, null, null],
619619
}),
620+
new web3._extend.Method({
621+
name: 'getBlockReceipts',
622+
call: 'eth_getBlockReceipts',
623+
params: 1,
624+
}),
620625
],
621626
properties: [
622627
new web3._extend.Property({

0 commit comments

Comments
 (0)