Skip to content
Merged
7 changes: 7 additions & 0 deletions client/rpc-core/src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ pub trait EthApi {
number: BlockNumber,
) -> RpcResult<Option<U256>>;

/// Returns the receipts of a block by number or hash.
#[method(name = "eth_getBlockReceipts")]
async fn block_transaction_receipts(
&self,
number: BlockNumber,
) -> RpcResult<Vec<Option<Receipt>>>;
Comment thread
boundless-forest marked this conversation as resolved.
Outdated

/// Returns the number of uncles in a block with given hash.
#[method(name = "eth_getUncleCountByBlockHash")]
fn block_uncles_count_by_hash(&self, hash: H256) -> RpcResult<U256>;
Expand Down
41 changes: 40 additions & 1 deletion client/rpc/src/eth/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ where
C: ProvideRuntimeApi<B>,
C::Api: EthereumRuntimeRPCApi<B>,
C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
BE: Backend<B>,
BE: Backend<B> + 'static,
A: ChainApi<Block = B> + 'static,
{
pub async fn block_by_hash(&self, hash: H256, full: bool) -> RpcResult<Option<RichBlock>> {
Expand Down Expand Up @@ -267,6 +267,45 @@ where
}
}

pub async fn block_transaction_receipts(
&self,
number: BlockNumber,
) -> RpcResult<Vec<Option<Receipt>>> {
let id = match frontier_backend_client::native_block_id::<B, C>(
self.client.as_ref(),
self.backend.as_ref(),
Some(number),
)
.await?
{
Some(id) => id,
None => return Ok(vec![]),
};

let substrate_hash = self
.client
.expect_block_hash_from_id(&id)
.map_err(|_| internal_err(format!("Expect block number from id: {}", id)))?;
let schema = fc_storage::onchain_storage_schema(self.client.as_ref(), substrate_hash);
let block = self
.overrides
.schemas
.get(&schema)
.unwrap_or(&self.overrides.fallback)
.current_block(substrate_hash);

let transaction_hashes = match block {
Some(block) => block.transactions.iter().map(|tx| tx.hash()).collect(),
None => vec![],
};
let mut receipts = Vec::new();
for hash in transaction_hashes {
receipts.push(self.transaction_receipt(hash).await?);
}

Ok(receipts)
}

pub fn block_uncles_count_by_hash(&self, _: H256) -> RpcResult<U256> {
Ok(U256::zero())
}
Expand Down
7 changes: 7 additions & 0 deletions client/rpc/src/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ where
self.block_transaction_count_by_number(number).await
}

async fn block_transaction_receipts(
&self,
number: BlockNumber,
) -> RpcResult<Vec<Option<Receipt>>> {
self.block_transaction_receipts(number).await
}

fn block_uncles_count_by_hash(&self, hash: H256) -> RpcResult<U256> {
self.block_uncles_count_by_hash(hash)
}
Expand Down
70 changes: 69 additions & 1 deletion ts-tests/tests/test-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ describeWithFrontier("Frontier RPC (Pending Block)", (context) => {
nonce = nonce + 100;
await sendTransaction();

// do not seal, get pendign block
// do not seal, get pending block
let pending_transactions = [];
{
const pending = (await customRequest(context.web3, "eth_getBlockByNumber", ["pending", false])).result;
Expand All @@ -195,3 +195,71 @@ describeWithFrontier("Frontier RPC (Pending Block)", (context) => {
expect(pending_transactions).to.be.deep.eq(latest_block.transactions);
});
});

describeWithFrontier("Frontier RPC (BlockReceipts)", (context) => {
const TEST_ACCOUNT = "0x1111111111111111111111111111111111111111";
const N = 5;

it("should return empty if block without transaction", async function () {
await createAndFinalizeBlock(context.web3);
expect(await context.web3.eth.getBlockNumber()).to.equal(1);

let result = await customRequest(context.web3, "eth_getBlockReceipts", [
await context.web3.eth.getBlockNumber(),
]);
expect(result.result.length).to.be.eq(0);
});

it("should return multiple receipts", async function () {
var nonce = 0;
let sendTransaction = async () => {
const tx = await context.web3.eth.accounts.signTransaction(
{
from: GENESIS_ACCOUNT,
to: TEST_ACCOUNT,
value: "0x200", // Must be higher than ExistentialDeposit
gasPrice: "0x3B9ACA00",
gas: "0x100000",
nonce: nonce,
},
GENESIS_ACCOUNT_PRIVATE_KEY
);
nonce = nonce + 1;
return (await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction])).result;
};

// block 1 send 5 transactions
for (var _ of Array(N)) {
await sendTransaction();
}
await createAndFinalizeBlock(context.web3);
expect(await context.web3.eth.getBlockNumber()).to.equal(2);

let result = await customRequest(context.web3, "eth_getBlockReceipts", [2]);
expect(result.result.length).to.be.eq(N);
});

it("should support block number, tag and hash", async function () {
let block_number = await context.web3.eth.getBlockNumber();

// block number
expect((await customRequest(context.web3, "eth_getBlockReceipts", [block_number])).result.length).to.be.eq(N);
// block hash
let block = await context.web3.eth.getBlock(block_number);
expect(
(
await customRequest(context.web3, "eth_getBlockReceipts", [
{
blockHash: block.hash,
requireCanonical: true,
},
])
).result.length
).to.be.eq(N);
// block tags
expect((await customRequest(context.web3, "eth_getBlockReceipts", ["earliest"])).result.length).to.be.eq(0);
expect((await customRequest(context.web3, "eth_getBlockReceipts", ["pending"])).result.length).to.be.eq(0);
expect((await customRequest(context.web3, "eth_getBlockReceipts", ["finalized"])).result.length).to.be.eq(N);
expect((await customRequest(context.web3, "eth_getBlockReceipts", ["latest"])).result.length).to.be.eq(N);
});
});