feat(rpc): rpc handler for eth_getUncleByBlock{Hash/Number}AndIndex (#1595)

Co-authored-by: lambdaclass-user <github@lambdaclass.com>
This commit is contained in:
Francisco Krause Arnim
2023-03-06 11:43:44 -03:00
committed by GitHub
parent 92a0ebbf04
commit ce2b83e774
4 changed files with 62 additions and 24 deletions

View File

@ -73,19 +73,11 @@ where
EthApiClient::block_transaction_count_by_hash(client, hash).await.unwrap();
EthApiClient::block_uncles_count_by_hash(client, hash).await.unwrap();
EthApiClient::block_uncles_count_by_number(client, block_number).await.unwrap();
EthApiClient::uncle_by_block_hash_and_index(client, hash, index).await.unwrap();
EthApiClient::uncle_by_block_number_and_index(client, block_number, index).await.unwrap();
// Unimplemented
assert!(is_unimplemented(EthApiClient::syncing(client).await.err().unwrap()));
assert!(is_unimplemented(EthApiClient::author(client).await.err().unwrap()));
assert!(is_unimplemented(
EthApiClient::uncle_by_block_hash_and_index(client, hash, index).await.err().unwrap()
));
assert!(is_unimplemented(
EthApiClient::uncle_by_block_number_and_index(client, block_number, index)
.await
.err()
.unwrap()
));
assert!(is_unimplemented(EthApiClient::transaction_by_hash(client, hash).await.err().unwrap()));
assert!(is_unimplemented(
EthApiClient::transaction_by_block_hash_and_index(client, hash, index).await.err().unwrap()

View File

@ -8,7 +8,8 @@ use reth_rlp::Encodable;
use serde::{ser::Error, Deserialize, Serialize, Serializer};
use std::{collections::BTreeMap, ops::Deref};
/// Block Transactions depending on the boolean attribute of `eth_getBlockBy*`
/// Block Transactions depending on the boolean attribute of `eth_getBlockBy*`,
/// or if used by `eth_getUncle*`
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum BlockTransactions {
@ -16,8 +17,16 @@ pub enum BlockTransactions {
Hashes(Vec<H256>),
/// Full transactions
Full(Vec<Transaction>),
/// Special case for uncle response.
Uncle,
}
impl BlockTransactions {
/// Check if the enum variant is
/// used for an uncle response.
pub fn is_uncle(&self) -> bool {
matches!(self, Self::Uncle)
}
}
/// Determines how the `transactions` field of [Block] should be filled.
///
/// This essentially represents the `full:bool` argument in RPC calls that determine whether the
@ -55,11 +64,14 @@ pub struct Block {
/// Header of the block
#[serde(flatten)]
pub header: Header,
/// Total difficulty
pub total_difficulty: U256,
/// Total difficulty, this field is None only if representing
/// an Uncle block.
#[serde(skip_serializing_if = "Option::is_none")]
pub total_difficulty: Option<U256>,
/// Uncles' hashes
pub uncles: Vec<H256>,
/// Transactions
#[serde(skip_serializing_if = "BlockTransactions::is_uncle")]
pub transactions: BlockTransactions,
/// Integer the size of this block in bytes.
pub size: Option<U256>,
@ -67,6 +79,7 @@ pub struct Block {
#[serde(skip_serializing_if = "Option::is_none")]
pub base_fee_per_gas: Option<U256>,
/// Withdrawals
#[serde(skip_serializing_if = "Option::is_none")]
pub withdrawals: Option<Vec<Withdrawal>>,
}
@ -160,11 +173,29 @@ impl Block {
uncles,
transactions,
base_fee_per_gas: base_fee_per_gas.map(U256::from),
total_difficulty,
total_difficulty: Some(total_difficulty),
size: Some(U256::from(block_length)),
withdrawals: block.withdrawals,
}
}
/// Build an RPC block response representing
/// an Uncle from its header.
pub fn uncle_block_from_header(header: PrimitiveHeader) -> Self {
let hash = header.hash_slow();
let rpc_header = Header::from_primitive_with_hash(header.clone(), hash);
let uncle_block = PrimitiveBlock { header, ..Default::default() };
let size = Some(U256::from(uncle_block.length()));
Self {
uncles: vec![],
header: rpc_header,
transactions: BlockTransactions::Uncle,
base_fee_per_gas: None,
withdrawals: None,
size,
total_difficulty: None,
}
}
}
/// Block header representation.
@ -189,6 +220,7 @@ pub struct Header {
/// Transactions receipts root hash
pub receipts_root: H256,
/// Withdrawals root hash
#[serde(skip_serializing_if = "Option::is_none")]
pub withdrawals_root: Option<H256>,
/// Block number
pub number: Option<U256>,
@ -354,7 +386,7 @@ mod tests {
mix_hash: H256::from_low_u64_be(14),
nonce: Some(H64::from_low_u64_be(15)),
},
total_difficulty: U256::from(100000),
total_difficulty: Some(U256::from(100000)),
uncles: vec![H256::from_low_u64_be(17)],
transactions: BlockTransactions::Hashes(vec![H256::from_low_u64_be(18)]),
size: Some(U256::from(19)),
@ -364,7 +396,7 @@ mod tests {
let serialized = serde_json::to_string(&block).unwrap();
assert_eq!(
serialized,
r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000002","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000003","author":"0x0000000000000000000000000000000000000004","miner":"0x0000000000000000000000000000000000000004","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000006","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000007","withdrawalsRoot":"0x0000000000000000000000000000000000000000000000000000000000000008","number":"0x9","gasUsed":"0xa","gasLimit":"0xb","extraData":"0x010203","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0xc","difficulty":"0xd","mixHash":"0x000000000000000000000000000000000000000000000000000000000000000e","nonce":"0x000000000000000f","totalDifficulty":"0x186a0","uncles":["0x0000000000000000000000000000000000000000000000000000000000000011"],"transactions":["0x0000000000000000000000000000000000000000000000000000000000000012"],"size":"0x13","baseFeePerGas":"0x14","withdrawals":null}"#
r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000002","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000003","author":"0x0000000000000000000000000000000000000004","miner":"0x0000000000000000000000000000000000000004","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000006","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000007","withdrawalsRoot":"0x0000000000000000000000000000000000000000000000000000000000000008","number":"0x9","gasUsed":"0xa","gasLimit":"0xb","extraData":"0x010203","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0xc","difficulty":"0xd","mixHash":"0x000000000000000000000000000000000000000000000000000000000000000e","nonce":"0x000000000000000f","totalDifficulty":"0x186a0","uncles":["0x0000000000000000000000000000000000000000000000000000000000000011"],"transactions":["0x0000000000000000000000000000000000000000000000000000000000000012"],"size":"0x13","baseFeePerGas":"0x14"}"#
);
let deserialized: Block = serde_json::from_str(&serialized).unwrap();
assert_eq!(block, deserialized);

View File

@ -6,7 +6,7 @@ use crate::{
};
use reth_primitives::BlockId;
use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderFactory};
use reth_rpc_types::{Block, RichBlock};
use reth_rpc_types::{Block, Index, RichBlock};
impl<Client, Pool, Network> EthApi<Client, Pool, Network>
where
@ -23,6 +23,20 @@ where
Ok(self.client().ommers(block_id)?)
}
pub(crate) async fn ommer_by_block_and_index(
&self,
block_id: impl Into<BlockId>,
index: Index,
) -> EthResult<Option<RichBlock>> {
let block_id = block_id.into();
let index = usize::from(index);
let uncles = self.client().ommers(block_id)?.unwrap_or_default();
let uncle = uncles
.into_iter()
.nth(index)
.map(|header| Block::uncle_block_from_header(header).into());
Ok(uncle)
}
pub(crate) async fn block_transaction_count(
&self,
block_id: impl Into<BlockId>,

View File

@ -102,19 +102,19 @@ where
/// Handler for: `eth_getUncleByBlockHashAndIndex`
async fn uncle_by_block_hash_and_index(
&self,
_hash: H256,
_index: Index,
hash: H256,
index: Index,
) -> Result<Option<RichBlock>> {
Err(internal_rpc_err("unimplemented"))
Ok(EthApi::ommer_by_block_and_index(self, hash, index).await?)
}
/// Handler for: `eth_getUncleByBlockNumberAndIndex`
async fn uncle_by_block_number_and_index(
&self,
_number: BlockNumberOrTag,
_index: Index,
number: BlockNumberOrTag,
index: Index,
) -> Result<Option<RichBlock>> {
Err(internal_rpc_err("unimplemented"))
Ok(EthApi::ommer_by_block_and_index(self, number, index).await?)
}
/// Handler for: `eth_getTransactionByHash`