#19634 RPC API: get receipts by block number

tests:
 - eth_getTransactionByBlockHashAndIndex
 - eth_getBlockReceipts
This commit is contained in:
Ramil Amerzyanov 2019-06-17 02:17:34 +03:00
parent f9c0e093ed
commit 1684583401
3 changed files with 171 additions and 7 deletions

View file

@ -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"

View file

@ -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 := &eth.Config{Genesis: genesis}
config := &eth.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())
}
}

View file

@ -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