From 5e1143f02963eff61b177eca3e8d76d2b9c85a45 Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Mon, 18 Mar 2024 21:50:44 +0800 Subject: [PATCH 1/4] feat (internal/ethapi): implement eth_getBlockReceipts * ref: #2187 --- ethclient/ethclient.go | 9 +++++++++ internal/ethapi/api.go | 27 +++++++++++++++++++++++++++ internal/web3ext/web3ext.go | 5 +++++ 3 files changed, 41 insertions(+) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 68b5945cab..f5df668617 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -331,6 +331,15 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (* return r, err } +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 diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 94281a5d7d..f3f7174972 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -824,6 +824,33 @@ 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] = generateReceiptResponse(receipt, signer, txs[i], block.Hash(), block.NumberU64(), uint64(i)) + } + + 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 diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 551a728f80..ebdf3f3b0c 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -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', From d77ad8e6f617d33f16ab521210f2ed8d703ce044 Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Tue, 26 Mar 2024 20:45:13 +0800 Subject: [PATCH 2/4] inline-docs (eth-client): add description for BlockReceipts --- ethclient/ethclient.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index f5df668617..8f8b7ae0e6 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -331,6 +331,7 @@ 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) From 33d4baae1f472b89e0a8f31a48168ef4704da97b Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Fri, 29 Mar 2024 14:19:44 +0800 Subject: [PATCH 3/4] 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 --- internal/ethapi/api.go | 59 ++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f3f7174972..a2f9506d1e 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -845,7 +845,10 @@ func (s *PublicBlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHas signer := types.MakeSigner(s.b.ChainConfig(), block.Number()) result := make([]map[string]interface{}, len(receipts)) for i, receipt := range receipts { - result[i] = generateReceiptResponse(receipt, signer, txs[i], block.Hash(), block.NumberU64(), uint64(i)) + 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 @@ -1688,25 +1691,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 { - header, err := s.b.HeaderByHash(ctx, blockHash) - if err != nil { - return nil, err - } - gasPriceMinimum, err := s.b.GasPriceMinimumForHeader(ctx, tx.FeeCurrency(), 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 } @@ -1736,7 +1723,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 } @@ -2097,8 +2087,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), @@ -2134,7 +2124,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 { + header, err := backend.HeaderByHash(ctx, blockHash) + if err != nil { + return nil, err + } + gasPriceMinimum, err := backend.GasPriceMinimumForHeader(ctx, tx.FeeCurrency(), 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 } From 9eeeb3d91a1b306ef0ae86ebd2a756d6f0bcdd49 Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Tue, 2 Apr 2024 22:36:35 +0800 Subject: [PATCH 4/4] reconcile: receipt changes from master 272982b (#2278) --- internal/ethapi/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c8cc98e775..08edaa9c94 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2133,12 +2133,12 @@ func generateReceiptResponse(ctx context.Context, backend Backend, receipt *type 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 { + 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.FeeCurrency(), header) + gasPriceMinimum, err := backend.GasPriceMinimumForHeader(ctx, tx.DenominatedFeeCurrency(), header) if err == nil { fields["effectiveGasPrice"] = hexutil.Uint64(new(big.Int).Add(gasPriceMinimum, tx.EffectiveGasTipValue(gasPriceMinimum)).Uint64()) }