Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new RPC method eth_getBlockReceipt to get a block's "system calls" receipt #1628

Merged
merged 7 commits into from
Aug 25, 2021
95 changes: 67 additions & 28 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1343,40 +1343,36 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
return nil, nil
}
receipt := receipts[index]
fields := generateReceiptResponse(receipt, tx, blockHash, blockNumber, index)
return fields, nil
}

var signer types.Signer = types.FrontierSigner{}
if tx.Protected() {
signer = types.NewEIP155Signer(tx.ChainId())
// GetBlockReceipt returns "system calls" receipt for the block with the given block hash.
func (s *PublicTransactionPoolAPI) GetBlockReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
block, err := s.b.BlockByHash(ctx, hash)
if block == nil || err != nil {
// If no header with that hash is found, err gives "header for hash not found".
// But we return nil with no error, to match the behavior of eth_getBlockByHash and eth_getTransactionReceipt in these cases.
return nil, nil
}
from, _ := types.Sender(signer, tx)

fields := map[string]interface{}{
"blockHash": blockHash,
"blockNumber": hexutil.Uint64(blockNumber),
"transactionHash": hash,
"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,
index := uint64(block.Transactions().Len())
blockNumber := block.NumberU64()
receipts, err := s.b.GetReceipts(ctx, block.Hash())
// GetReceipts() doesn't return an error if things go wrong, so we also check len(receipts)
if err != nil || len(receipts) < int(index) {
return nil, err
}

// Assign receipt status or post state.
if len(receipt.PostState) > 0 {
fields["root"] = hexutil.Bytes(receipt.PostState)
var receipt *types.Receipt
if len(receipts) == int(index) {
// The block didn't have any logs from system calls and no receipt was created.
// So we create an empty receipt to return, similarly to how system receipts are created.
receipt = types.NewReceipt(nil, false, 0)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
} 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
receipt = receipts[index]
}
fields := generateReceiptResponse(receipt, nil, hash, blockNumber, index)
return fields, nil
}

Expand Down Expand Up @@ -1868,3 +1864,46 @@ func checkTxFee(cm *currency.CurrencyManager, feeCurrency *common.Address, fee *
}
return nil
}

// generateReceiptResponse is a helper function which generates the response for GetTransactionReceipt() and GetBlockReceipt()
func generateReceiptResponse(receipt *types.Receipt, tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) map[string]interface{} {
fields := map[string]interface{}{
"blockHash": blockHash,
"blockNumber": hexutil.Uint64(blockNumber),
"transactionHash": common.Hash{},
"transactionIndex": hexutil.Uint64(index),
"from": common.Address{},
"to": nil,
"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
}
if tx == nil {
fields["transactionHash"] = blockHash
} else {
fields["transactionHash"] = tx.Hash()
var signer types.Signer = types.FrontierSigner{}
if tx.Protected() {
signer = types.NewEIP155Signer(tx.ChainId())
}
fields["from"], _ = types.Sender(signer, tx)
fields["to"] = tx.To()
}
return fields
}
6 changes: 6 additions & 0 deletions internal/web3ext/web3ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,12 @@ web3._extend({
call: 'eth_getBlockByHash',
params: 2
}),
new web3._extend.Method({
name: 'getBlockReceipt',
call: 'eth_getBlockReceipt',
params: 1,
outputFormatter: web3._extend.formatters.outputTransactionReceiptFormatter
}),
new web3._extend.Method({
name: 'getRawTransaction',
call: 'eth_getRawTransactionByHash',
Expand Down