Skip to content

Commit

Permalink
Add eth_getBlockReceipts (#1156)
Browse files Browse the repository at this point in the history
* Add rpc interface

* Impl the rpc

* Add tests

* Update the test

* Revert test config in dev process

* Revert lock.json changes

* Get BlockInfo in one place

* Move instance code outside

* Rename

* Update `substrate_hash` type

* Use reference

* Self review

* Use derive Default

* Better `transaction_receipt`

* Fix review
  • Loading branch information
boundless-forest authored Aug 23, 2023
1 parent c4ef2fe commit df8b163
Show file tree
Hide file tree
Showing 6 changed files with 367 additions and 261 deletions.
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<Option<Vec<Receipt>>>;

/// 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
107 changes: 37 additions & 70 deletions client/rpc/src/eth/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use fc_rpc_core::types::*;
use fp_rpc::EthereumRuntimeRPCApi;

use crate::{
eth::{rich_block_build, Eth, EthConfig},
eth::{rich_block_build, BlockInfo, Eth, EthConfig},
frontier_backend_client, internal_err,
};

Expand All @@ -43,34 +43,17 @@ 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>> {
let client = Arc::clone(&self.client);
let block_data_cache = Arc::clone(&self.block_data_cache);
let backend = Arc::clone(&self.backend);

let substrate_hash = match frontier_backend_client::load_hash::<B, C>(
client.as_ref(),
backend.as_ref(),
hash,
)
.await
.map_err(|err| internal_err(format!("{:?}", err)))?
{
Some(hash) => hash,
_ => return Ok(None),
};

let schema = fc_storage::onchain_storage_schema(client.as_ref(), substrate_hash);

let block = block_data_cache.current_block(schema, substrate_hash).await;
let statuses = block_data_cache
.current_transaction_statuses(schema, substrate_hash)
.await;

let base_fee = client.runtime_api().gas_price(substrate_hash).ok();
let BlockInfo {
block,
statuses,
substrate_hash,
base_fee,
..
} = self.block_info_by_eth_block_hash(hash).await?;

match (block, statuses) {
(Some(block), Some(statuses)) => {
Expand All @@ -79,7 +62,7 @@ where
statuses.into_iter().map(Option::Some).collect(),
Some(hash),
full,
base_fee,
Some(base_fee),
false,
);

Expand Down Expand Up @@ -203,26 +186,8 @@ where
}

pub async fn block_transaction_count_by_hash(&self, hash: H256) -> RpcResult<Option<U256>> {
let substrate_hash = match frontier_backend_client::load_hash::<B, C>(
self.client.as_ref(),
self.backend.as_ref(),
hash,
)
.await
.map_err(|err| internal_err(format!("{:?}", err)))?
{
Some(hash) => hash,
_ => return Ok(None),
};
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);

match block {
let blockinfo = self.block_info_by_eth_block_hash(hash).await?;
match blockinfo.block {
Some(block) => Ok(Some(U256::from(block.transactions.len()))),
None => Ok(None),
}
Expand All @@ -239,34 +204,36 @@ where
)));
}

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(None),
};
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);

match block {
let block_info = self.block_info_by_number(number).await?;
match block_info.block {
Some(block) => Ok(Some(U256::from(block.transactions.len()))),
None => Ok(None),
}
}

pub async fn block_transaction_receipts(
&self,
number: BlockNumber,
) -> RpcResult<Option<Vec<Receipt>>> {
let block_info = self.block_info_by_number(number).await?;
let Some(statuses) = block_info.clone().statuses else {
return Ok(None);
};

let mut receipts = Vec::new();
let transactions: Vec<(H256, usize)> = statuses
.iter()
.map(|tx| (tx.transaction_hash, tx.transaction_index as usize))
.collect();
for (hash, index) in transactions {
if let Some(receipt) = self.transaction_receipt(&block_info, hash, index).await? {
receipts.push(receipt);
}
}

Ok(Some(receipts))
}

pub fn block_uncles_count_by_hash(&self, _: H256) -> RpcResult<U256> {
Ok(U256::zero())
}
Expand Down
165 changes: 162 additions & 3 deletions client/rpc/src/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use fp_rpc::{
RuntimeStorageOverride, TransactionStatus,
};

use crate::{internal_err, public_key, signer::EthSigner};
use crate::{frontier_backend_client, internal_err, public_key, signer::EthSigner};

pub use self::{
cache::{EthBlockDataCacheTask, EthTask},
Expand Down Expand Up @@ -91,7 +91,16 @@ pub struct Eth<B: BlockT, C, P, CT, BE, A: ChainApi, EC: EthConfig<B, C>> {
_marker: PhantomData<(B, BE, EC)>,
}

impl<B: BlockT, C, P, CT, BE, A: ChainApi> Eth<B, C, P, CT, BE, A, ()> {
impl<B, C, P, CT, BE, A, EC> Eth<B, C, P, CT, BE, A, EC>
where
A: ChainApi,
B: BlockT,
C: ProvideRuntimeApi<B>,
C::Api: EthereumRuntimeRPCApi<B>,
C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
BE: Backend<B> + 'static,
EC: EthConfig<B, C>,
{
pub fn new(
client: Arc<C>,
pool: Arc<P>,
Expand Down Expand Up @@ -126,6 +135,117 @@ impl<B: BlockT, C, P, CT, BE, A: ChainApi> Eth<B, C, P, CT, BE, A, ()> {
_marker: PhantomData,
}
}

pub async fn block_info_by_number(&self, number: BlockNumber) -> RpcResult<BlockInfo<B::Hash>> {
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(BlockInfo::default()),
};

let substrate_hash = self
.client
.expect_block_hash_from_id(&id)
.map_err(|_| internal_err(format!("Expect block number from id: {}", id)))?;

self.block_info_by_substrate_hash(substrate_hash).await
}

pub async fn block_info_by_eth_block_hash(
&self,
eth_block_hash: H256,
) -> RpcResult<BlockInfo<B::Hash>> {
let substrate_hash = match frontier_backend_client::load_hash::<B, C>(
self.client.as_ref(),
self.backend.as_ref(),
eth_block_hash,
)
.await
.map_err(|err| internal_err(format!("{:?}", err)))?
{
Some(hash) => hash,
_ => return Ok(BlockInfo::default()),
};

self.block_info_by_substrate_hash(substrate_hash).await
}

pub async fn block_info_by_eth_transaction_hash(
&self,
ethereum_tx_hash: H256,
) -> RpcResult<(BlockInfo<B::Hash>, usize)> {
let (eth_block_hash, index) = match frontier_backend_client::load_transactions::<B, C>(
self.client.as_ref(),
self.backend.as_ref(),
ethereum_tx_hash,
true,
)
.await
.map_err(|err| internal_err(format!("{:?}", err)))?
{
Some((hash, index)) => (hash, index as usize),
None => return Ok((BlockInfo::default(), 0)),
};

let substrate_hash = match frontier_backend_client::load_hash::<B, C>(
self.client.as_ref(),
self.backend.as_ref(),
eth_block_hash,
)
.await
.map_err(|err| internal_err(format!("{:?}", err)))?
{
Some(hash) => hash,
_ => return Ok((BlockInfo::default(), 0)),
};

Ok((
self.block_info_by_substrate_hash(substrate_hash).await?,
index,
))
}

pub async fn block_info_by_substrate_hash(
&self,
substrate_hash: B::Hash,
) -> RpcResult<BlockInfo<B::Hash>> {
let schema = fc_storage::onchain_storage_schema(self.client.as_ref(), substrate_hash);
let handler = self
.overrides
.schemas
.get(&schema)
.unwrap_or(&self.overrides.fallback);

let block = self
.block_data_cache
.current_block(schema, substrate_hash)
.await;
let receipts = handler.current_receipts(substrate_hash);
let statuses = self
.block_data_cache
.current_transaction_statuses(schema, substrate_hash)
.await;
let is_eip1559 = handler.is_eip1559(substrate_hash);
let base_fee = self
.client
.runtime_api()
.gas_price(substrate_hash)
.unwrap_or_default();

Ok(BlockInfo::new(
block,
receipts,
statuses,
substrate_hash,
is_eip1559,
base_fee,
))
}
}

impl<B: BlockT, C, P, CT, BE, A: ChainApi, EC: EthConfig<B, C>> Eth<B, C, P, CT, BE, A, EC> {
Expand Down Expand Up @@ -236,6 +356,13 @@ where
self.block_transaction_count_by_number(number).await
}

async fn block_transaction_receipts(
&self,
number: BlockNumber,
) -> RpcResult<Option<Vec<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 Expand Up @@ -286,7 +413,8 @@ where
}

async fn transaction_receipt(&self, hash: H256) -> RpcResult<Option<Receipt>> {
self.transaction_receipt(hash).await
let (block_info, index) = self.block_info_by_eth_transaction_hash(hash).await?;
self.transaction_receipt(&block_info, hash, index).await
}

// ########################################################################
Expand Down Expand Up @@ -587,3 +715,34 @@ where
)))
}
}

/// The most commonly used block information in the rpc interfaces.
#[derive(Clone, Default)]
pub struct BlockInfo<H> {
block: Option<EthereumBlock>,
receipts: Option<Vec<ethereum::ReceiptV3>>,
statuses: Option<Vec<TransactionStatus>>,
substrate_hash: H,
is_eip1559: bool,
base_fee: U256,
}

impl<H> BlockInfo<H> {
pub fn new(
block: Option<EthereumBlock>,
receipts: Option<Vec<ethereum::ReceiptV3>>,
statuses: Option<Vec<TransactionStatus>>,
substrate_hash: H,
is_eip1559: bool,
base_fee: U256,
) -> Self {
Self {
block,
receipts,
statuses,
substrate_hash,
is_eip1559,
base_fee,
}
}
}
Loading

1 comment on commit df8b163

@zzz6519003
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orz

Please sign in to comment.