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 eth_getBlockReceipts #1156

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<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 {
arturgontijo marked this conversation as resolved.
Show resolved Hide resolved
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
Loading