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
|
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 {
|
func toBlockNumArg(number *big.Int) string {
|
||||||
if number == nil {
|
if number == nil {
|
||||||
return "latest"
|
return "latest"
|
||||||
|
|
|
||||||
|
|
@ -168,15 +168,24 @@ var (
|
||||||
testBalance = big.NewInt(2e10)
|
testBalance = big.NewInt(2e10)
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
func newTestBackend(t *testing.T, withTx bool) (*node.Node, []*types.Block) {
|
||||||
// Generate test chain.
|
var (
|
||||||
genesis, blocks := generateTestChain()
|
genesis *core.Genesis
|
||||||
|
blocks []*types.Block
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate test chain
|
||||||
|
if withTx {
|
||||||
|
genesis, blocks = generateTestChainWithTx(t)
|
||||||
|
} else {
|
||||||
|
genesis, blocks = generateTestChain()
|
||||||
|
}
|
||||||
|
|
||||||
// Start Ethereum service.
|
// Start Ethereum service.
|
||||||
var ethservice *eth.Ethereum
|
var ethservice *eth.Ethereum
|
||||||
n, err := node.New(&node.Config{})
|
n, err := node.New(&node.Config{})
|
||||||
n.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
n.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
config := ð.Config{Genesis: genesis}
|
config := ð.Config{Genesis: genesis, NoPrefetch: true}
|
||||||
config.Ethash.PowMode = ethash.ModeFake
|
config.Ethash.PowMode = ethash.ModeFake
|
||||||
ethservice, err = eth.New(ctx, config)
|
ethservice, err = eth.New(ctx, config)
|
||||||
return ethservice, err
|
return ethservice, err
|
||||||
|
|
@ -212,8 +221,47 @@ func generateTestChain() (*core.Genesis, []*types.Block) {
|
||||||
return genesis, blocks
|
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) {
|
func TestHeader(t *testing.T) {
|
||||||
backend, chain := newTestBackend(t)
|
backend, chain := newTestBackend(t, false)
|
||||||
client, _ := backend.Attach()
|
client, _ := backend.Attach()
|
||||||
defer backend.Stop()
|
defer backend.Stop()
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
@ -257,7 +305,7 @@ func TestHeader(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBalanceAt(t *testing.T) {
|
func TestBalanceAt(t *testing.T) {
|
||||||
backend, _ := newTestBackend(t)
|
backend, _ := newTestBackend(t, false)
|
||||||
client, _ := backend.Attach()
|
client, _ := backend.Attach()
|
||||||
defer backend.Stop()
|
defer backend.Stop()
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
@ -303,7 +351,7 @@ func TestBalanceAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTransactionInBlockInterrupted(t *testing.T) {
|
func TestTransactionInBlockInterrupted(t *testing.T) {
|
||||||
backend, _ := newTestBackend(t)
|
backend, _ := newTestBackend(t, false)
|
||||||
client, _ := backend.Attach()
|
client, _ := backend.Attach()
|
||||||
defer backend.Stop()
|
defer backend.Stop()
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
@ -319,3 +367,46 @@ func TestTransactionInBlockInterrupted(t *testing.T) {
|
||||||
t.Fatal("error should not be nil")
|
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
|
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.
|
// 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) {
|
func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||||
// Look up the wallet containing the requested signer
|
// Look up the wallet containing the requested signer
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue