mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
#19634 RPC API: get receipts by block number
tests: - eth_getTransactionByBlockHashAndIndex - eth_getBlockReceipts
This commit is contained in:
parent
f9c0e093ed
commit
1684583401
3 changed files with 171 additions and 7 deletions
|
|
@ -268,6 +268,14 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*
|
|||
return r, err
|
||||
}
|
||||
|
||||
// BlockReceipts returns the receipts of a transaction by block number.
|
||||
func (ec *Client) BlockReceipts(ctx context.Context, number *big.Int) (types.Receipts, error) {
|
||||
var result types.Receipts
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getBlockReceipts", toBlockNumArg(number))
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func toBlockNumArg(number *big.Int) string {
|
||||
if number == nil {
|
||||
return "latest"
|
||||
|
|
|
|||
|
|
@ -168,15 +168,24 @@ var (
|
|||
testBalance = big.NewInt(2e10)
|
||||
)
|
||||
|
||||
func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
||||
// Generate test chain.
|
||||
genesis, blocks := generateTestChain()
|
||||
func newTestBackend(t *testing.T, withTx bool) (*node.Node, []*types.Block) {
|
||||
var (
|
||||
genesis *core.Genesis
|
||||
blocks []*types.Block
|
||||
)
|
||||
|
||||
// Generate test chain
|
||||
if withTx {
|
||||
genesis, blocks = generateTestChainWithTx(t)
|
||||
} else {
|
||||
genesis, blocks = generateTestChain()
|
||||
}
|
||||
|
||||
// Start Ethereum service.
|
||||
var ethservice *eth.Ethereum
|
||||
n, err := node.New(&node.Config{})
|
||||
n.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
config := ð.Config{Genesis: genesis}
|
||||
config := ð.Config{Genesis: genesis, NoPrefetch: true}
|
||||
config.Ethash.PowMode = ethash.ModeFake
|
||||
ethservice, err = eth.New(ctx, config)
|
||||
return ethservice, err
|
||||
|
|
@ -212,8 +221,47 @@ func generateTestChain() (*core.Genesis, []*types.Block) {
|
|||
return genesis, blocks
|
||||
}
|
||||
|
||||
func generateTestChainWithTx(t *testing.T) (*core.Genesis, []*types.Block) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
config := params.AllEthashProtocolChanges
|
||||
signer := types.NewEIP155Signer(config.ChainID)
|
||||
genesis := &core.Genesis{
|
||||
Config: config,
|
||||
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
|
||||
ExtraData: []byte("test genesis"),
|
||||
Timestamp: 9000,
|
||||
}
|
||||
generate := func(i int, g *core.BlockGen) {
|
||||
g.OffsetTime(5)
|
||||
g.SetExtra([]byte("test"))
|
||||
|
||||
switch i {
|
||||
case 0:
|
||||
tx, err := types.SignTx(types.NewTransaction(g.TxNonce(testAddr), common.HexToAddress("0x1"), big.NewInt(1), 21000, big.NewInt(1), nil), signer, testKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
g.AddTx(tx)
|
||||
case 1:
|
||||
tx, err := types.SignTx(types.NewTransaction(g.TxNonce(testAddr), common.HexToAddress("0x2"), big.NewInt(2), 21000, big.NewInt(1), nil), signer, testKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
g.AddTx(tx)
|
||||
}
|
||||
}
|
||||
gblock := genesis.ToBlock(db)
|
||||
engine := ethash.NewFaker()
|
||||
blocks, _ := core.GenerateChain(config, gblock, engine, db, 2, generate)
|
||||
blocks = append([]*types.Block{gblock}, blocks...)
|
||||
|
||||
return genesis, blocks
|
||||
}
|
||||
|
||||
func TestHeader(t *testing.T) {
|
||||
backend, chain := newTestBackend(t)
|
||||
backend, chain := newTestBackend(t, false)
|
||||
client, _ := backend.Attach()
|
||||
defer backend.Stop()
|
||||
defer client.Close()
|
||||
|
|
@ -257,7 +305,7 @@ func TestHeader(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBalanceAt(t *testing.T) {
|
||||
backend, _ := newTestBackend(t)
|
||||
backend, _ := newTestBackend(t, false)
|
||||
client, _ := backend.Attach()
|
||||
defer backend.Stop()
|
||||
defer client.Close()
|
||||
|
|
@ -303,7 +351,7 @@ func TestBalanceAt(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTransactionInBlockInterrupted(t *testing.T) {
|
||||
backend, _ := newTestBackend(t)
|
||||
backend, _ := newTestBackend(t, false)
|
||||
client, _ := backend.Attach()
|
||||
defer backend.Stop()
|
||||
defer client.Close()
|
||||
|
|
@ -319,3 +367,46 @@ func TestTransactionInBlockInterrupted(t *testing.T) {
|
|||
t.Fatal("error should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransactionInBlock(t *testing.T) {
|
||||
backend, blocks := newTestBackend(t, true)
|
||||
client, _ := backend.Attach()
|
||||
defer backend.Stop()
|
||||
defer client.Close()
|
||||
|
||||
ec := NewClient(client)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
tx, err := ec.TransactionInBlock(ctx, blocks[1].Hash(), 0)
|
||||
if err != nil {
|
||||
t.Fatalf("TransactionInBlock error = %q", err)
|
||||
}
|
||||
if tx == nil {
|
||||
t.Fatal("transaction should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockReceipts(t *testing.T) {
|
||||
backend, blocks := newTestBackend(t, true)
|
||||
client, _ := backend.Attach()
|
||||
defer backend.Stop()
|
||||
defer client.Close()
|
||||
|
||||
ec := NewClient(client)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
receipts, err := ec.BlockReceipts(ctx, big.NewInt(1))
|
||||
if err != nil {
|
||||
t.Fatalf("BlockReceipts error = %q", err)
|
||||
}
|
||||
|
||||
if len(receipts) == 0 {
|
||||
t.Fatal("receipts should not be 0 len")
|
||||
}
|
||||
|
||||
if receipts[0].BlockHash != blocks[1].Hash() {
|
||||
t.Fatalf("BlockReceipts block hash mismatch, got %v, want %v", receipts[0].BlockHash, blocks[1].Hash())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1241,6 +1241,71 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
|
|||
return fields, nil
|
||||
}
|
||||
|
||||
// GetBlockReceipts returns the block receipts for the given block number.
|
||||
func (s *PublicTransactionPoolAPI) GetBlockReceipts(ctx context.Context, blockNr rpc.BlockNumber) ([]map[string]interface{}, error) {
|
||||
block, err := s.b.BlockByNumber(ctx, blockNr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if block == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
txs := block.Transactions()
|
||||
|
||||
receipts, err := s.b.GetReceipts(ctx, block.Hash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(receipts) != len(txs) {
|
||||
return nil, fmt.Errorf("receipt and transaction count mismatch")
|
||||
}
|
||||
|
||||
result := make([]map[string]interface{}, 0, len(receipts))
|
||||
|
||||
for index, receipt := range receipts {
|
||||
tx := txs[index]
|
||||
var signer types.Signer = types.FrontierSigner{}
|
||||
if tx.Protected() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
from, _ := types.Sender(signer, tx)
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"blockHash": block.Hash(),
|
||||
"blockNumber": hexutil.Uint64(blockNr),
|
||||
"transactionHash": receipt.TxHash,
|
||||
"transactionIndex": hexutil.Uint64(index),
|
||||
"from": from,
|
||||
"to": tx.To(),
|
||||
"gasUsed": hexutil.Uint64(receipt.GasUsed),
|
||||
"cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
|
||||
"contractAddress": nil,
|
||||
"logs": receipt.Logs,
|
||||
"logsBloom": receipt.Bloom,
|
||||
}
|
||||
|
||||
// Assign receipt status or post state.
|
||||
if len(receipt.PostState) > 0 {
|
||||
fields["root"] = hexutil.Bytes(receipt.PostState)
|
||||
} else {
|
||||
fields["status"] = hexutil.Uint(receipt.Status)
|
||||
}
|
||||
if receipt.Logs == nil {
|
||||
fields["logs"] = [][]*types.Log{}
|
||||
}
|
||||
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
||||
if receipt.ContractAddress != (common.Address{}) {
|
||||
fields["contractAddress"] = receipt.ContractAddress
|
||||
}
|
||||
|
||||
result = append(result, fields)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// sign is a helper function that signs a transaction with the private key of the given address.
|
||||
func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||
// Look up the wallet containing the requested signer
|
||||
|
|
|
|||
Loading…
Reference in a new issue