Skip to content

Commit

Permalink
feat (internal/ethapi): implement eth_getBlockReceipts (#2284)
Browse files Browse the repository at this point in the history
* feat (internal/ethapi): implement eth_getBlockReceipts

* ref: #2187

* inline-docs (eth-client): add description for BlockReceipts

* refactor (ethapi/api.go): generateReceiptResponse helper

* generateReceiptResponse now receives a context and a backend interface so as to also compute the effectiveGasPrice field in transaction receipts

* reconcile: receipt changes from master 272982b (#2278)
  • Loading branch information
kamikazechaser authored Apr 3, 2024
1 parent 272982b commit 6351612
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 24 deletions.
10 changes: 10 additions & 0 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,16 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*
return r, 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 rpcProgress struct {
StartingBlock hexutil.Uint64
CurrentBlock hexutil.Uint64
Expand Down
84 changes: 60 additions & 24 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,36 @@ 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], err = generateReceiptResponse(ctx, s.b, receipt, signer, txs[i], block.Hash(), block.NumberU64(), uint64(i))
if err != nil {
return nil, err
}
}

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
Expand Down Expand Up @@ -1666,25 +1696,9 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
bigblock := new(big.Int).SetUint64(blockNumber)
signer := types.MakeSigner(s.b.ChainConfig(), bigblock)

fields := generateReceiptResponse(receipt, signer, tx, blockHash, blockNumber, index)
// Assign the effective gas price paid
if !s.b.ChainConfig().IsLondon(bigblock) {
fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64())
} else {
// var gasPrice *big.Int = new(big.Int)
if tx.Type() == types.DynamicFeeTxType || tx.Type() == types.CeloDynamicFeeTxType || tx.Type() == types.CeloDynamicFeeTxV2Type || tx.Type() == types.CeloDenominatedTxType {
header, err := s.b.HeaderByHash(ctx, blockHash)
if err != nil {
return nil, err
}
gasPriceMinimum, err := s.b.GasPriceMinimumForHeader(ctx, tx.DenominatedFeeCurrency(), header)
if err == nil {
fields["effectiveGasPrice"] = hexutil.Uint64(new(big.Int).Add(gasPriceMinimum, tx.EffectiveGasTipValue(gasPriceMinimum)).Uint64())
}
// if err != nil, it's due to a state prune. In this case no effectiveGasPrice will be returned.
} else {
fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64())
}
fields, err := generateReceiptResponse(ctx, s.b, receipt, signer, tx, blockHash, blockNumber, index)
if err != nil {
return nil, err
}
return fields, nil
}
Expand Down Expand Up @@ -1714,7 +1728,10 @@ func (s *PublicTransactionPoolAPI) GetBlockReceipt(ctx context.Context, hash com
} else {
receipt = receipts[index]
}
fields := generateReceiptResponse(receipt, nil, nil, hash, blockNumber, index)
fields, err := generateReceiptResponse(ctx, s.b, receipt, nil, nil, hash, blockNumber, index)
if err != nil {
return nil, err
}
return fields, nil
}

Expand Down Expand Up @@ -2074,8 +2091,8 @@ func toHexSlice(b [][]byte) []string {
return r
}

// generateReceiptResponse is a helper function which generates the response for GetTransactionReceipt() and GetBlockReceipt()
func generateReceiptResponse(receipt *types.Receipt, signer types.Signer, tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) map[string]interface{} {
// generateReceiptResponse is a helper function which generates the response for GetTransactionReceipt(), GetBlockReceipt() and GetBlockReceipts()
func generateReceiptResponse(ctx context.Context, backend Backend, receipt *types.Receipt, signer types.Signer, tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) (map[string]interface{}, error) {
fields := map[string]interface{}{
"blockHash": blockHash,
"blockNumber": hexutil.Uint64(blockNumber),
Expand Down Expand Up @@ -2111,7 +2128,26 @@ func generateReceiptResponse(receipt *types.Receipt, signer types.Signer, tx *ty
// Derive the sender.
fields["from"], _ = types.Sender(signer, tx)
fields["to"] = tx.To()

// Assign the effective gas price paid
if backend.ChainConfig().IsLondon(new(big.Int).SetUint64(blockNumber)) {
fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64())
} else {
// var gasPrice *big.Int = new(big.Int)
if tx.Type() == types.DynamicFeeTxType || tx.Type() == types.CeloDynamicFeeTxType || tx.Type() == types.CeloDynamicFeeTxV2Type || tx.Type() == types.CeloDenominatedTxType {
header, err := backend.HeaderByHash(ctx, blockHash)
if err != nil {
return nil, err
}
gasPriceMinimum, err := backend.GasPriceMinimumForHeader(ctx, tx.DenominatedFeeCurrency(), header)
if err == nil {
fields["effectiveGasPrice"] = hexutil.Uint64(new(big.Int).Add(gasPriceMinimum, tx.EffectiveGasTipValue(gasPriceMinimum)).Uint64())
}
// if err != nil, it's due to a state prune. In this case no effectiveGasPrice will be returned.
} else {
fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64())
}
}
}
return fields

return fields, nil
}
5 changes: 5 additions & 0 deletions internal/web3ext/web3ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,11 @@ web3._extend({
params: 1,
outputFormatter: web3._extend.formatters.outputTransactionReceiptFormatter
}),
new web3._extend.Method({
name: 'getBlockReceipts',
call: 'eth_getBlockReceipts',
params: 1,
}),
new web3._extend.Method({
name: 'getRawTransaction',
call: 'eth_getRawTransactionByHash',
Expand Down

0 comments on commit 6351612

Please sign in to comment.