diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index a17696356c..40d66b6f72 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -78,6 +78,15 @@ func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo return ec.getBlock(ctx, "eth_getBlockByHash", hash, true) } +func (ec *Client) TransactionReceiptsInBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { + var rs []map[string]interface{} + err := ec.c.CallContext(ctx, &rs, "eth_getTransactionReceiptsByBlock", blockNrOrHash) + if err != nil { + return nil, err + } + return rs, err +} + // BlockByNumber returns a block from the current canonical chain. If number is nil, the // latest known block is returned. // diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 73b22852a8..7f708bf436 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -531,6 +531,82 @@ func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { return &PublicBlockChainAPI{b} } +// GetTransactionReceiptsByBlock returns the transaction receipts for the given block number or hash. +func (s *PublicBlockChainAPI) GetTransactionReceiptsByBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { + block, err := s.b.BlockByNumberOrHash(ctx, blockNrOrHash) + if err != nil { + return nil, err + } + + receipts, err := s.b.GetReceipts(ctx, block.Hash()) + if err != nil { + return nil, err + } + + txs := block.Transactions() + + var txHash common.Hash + + borReceipt := rawdb.ReadBorReceipt(s.b.ChainDb(), block.Hash(), block.NumberU64()) + if borReceipt != nil { + receipts = append(receipts, borReceipt) + txHash = types.GetDerivedBorTxHash(types.BorReceiptKey(block.Number().Uint64(), block.Hash())) + if txHash != (common.Hash{}) { + borTx, _, _, _, _ := s.b.GetBorBlockTransactionWithBlockHash(ctx, txHash, block.Hash()) + txs = append(txs, borTx) + } + } + + if len(txs) != len(receipts) { + return nil, fmt.Errorf("txs length doesn't equal to receipts' length", len(txs), len(receipts)) + } + + txReceipts := make([]map[string]interface{}, 0, len(txs)) + for idx, receipt := range receipts { + tx := txs[idx] + 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(block.NumberU64()), + "transactionHash": tx.Hash(), + "transactionIndex": hexutil.Uint64(idx), + "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 borReceipt != nil { + fields["transactionHash"] = txHash + } + // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation + if receipt.ContractAddress != (common.Address{}) { + fields["contractAddress"] = receipt.ContractAddress + } + + txReceipts = append(txReceipts, fields) + } + + return txReceipts, nil +} + // ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config. func (api *PublicBlockChainAPI) ChainId() (*hexutil.Big, error) { // if current block is at or past the EIP-155 replay-protection fork block, return chainID from config diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index f1fe15d48d..9b52740a59 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -5393,6 +5393,12 @@ var methods = function () { inputFormatter: [formatters.inputCallFormatter, formatters.inputDefaultBlockNumberFormatter] }); + var getTransactionReceiptsByBlock = new Method({ + name: 'getTransactionReceiptsByBlock', + call: 'eth_getTransactionReceiptsByBlock', + params: 1 + }); + var estimateGas = new Method({ name: 'estimateGas', call: 'eth_estimateGas', @@ -5454,7 +5460,8 @@ var methods = function () { compileLLL, compileSerpent, submitWork, - getWork + getWork, + getTransactionReceiptsByBlock ]; }; diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index a24de36b22..f610aa3192 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -494,6 +494,11 @@ const EthJs = ` web3._extend({ property: 'eth', methods: [ + new web3._extend.Method({ + name: 'getTransactionReceiptsByBlock', + call: 'eth_getTransactionReceiptsByBlock', + params: 1 + }), new web3._extend.Method({ name: 'chainId', call: 'eth_chainId',