mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(rpc): impl block_by_hash (#1460)
This commit is contained in:
@ -69,13 +69,11 @@ where
|
||||
EthApiClient::balance(client, address, None).await.unwrap();
|
||||
EthApiClient::transaction_count(client, address, None).await.unwrap();
|
||||
EthApiClient::storage_at(client, address, U256::default(), None).await.unwrap();
|
||||
EthApiClient::block_by_hash(client, hash, false).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::block_by_hash(client, hash, false).await.err().unwrap()
|
||||
));
|
||||
assert!(is_unimplemented(
|
||||
EthApiClient::block_by_number(client, block_number, false).await.err().unwrap()
|
||||
));
|
||||
|
||||
@ -18,6 +18,28 @@ pub enum BlockTransactions {
|
||||
Full(Vec<Transaction>),
|
||||
}
|
||||
|
||||
/// Determines how the `transactions` field of [Block] should be filled.
|
||||
///
|
||||
/// This essentially represents the `full:bool` argument in RPC calls that determine whether the
|
||||
/// response should include full transaction objects or just the hashes.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum BlockTransactionsKind {
|
||||
/// Only include hashes: [BlockTransactions::Hashes]
|
||||
Hashes,
|
||||
/// Include full transaction objects: [BlockTransactions::Full]
|
||||
Full,
|
||||
}
|
||||
|
||||
impl From<bool> for BlockTransactionsKind {
|
||||
fn from(is_full: bool) -> Self {
|
||||
if is_full {
|
||||
BlockTransactionsKind::Full
|
||||
} else {
|
||||
BlockTransactionsKind::Hashes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can occur when converting other types to blocks
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum BlockError {
|
||||
@ -49,20 +71,49 @@ pub struct Block {
|
||||
}
|
||||
|
||||
impl Block {
|
||||
/// Create a new block response from a [primitive block](reth_primitives::Block), using the
|
||||
/// Converts the given primitive block into a [Block] response with the given
|
||||
/// [BlockTransactionsKind]
|
||||
pub fn from_block(
|
||||
block: PrimitiveBlock,
|
||||
total_difficulty: U256,
|
||||
kind: BlockTransactionsKind,
|
||||
) -> Result<Self, BlockError> {
|
||||
match kind {
|
||||
BlockTransactionsKind::Hashes => {
|
||||
Ok(Self::from_block_hashes_only(block, total_difficulty))
|
||||
}
|
||||
BlockTransactionsKind::Full => Self::from_block_full(block, total_difficulty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [Block] response from a [primitive block](reth_primitives::Block), using the
|
||||
/// total difficulty to populate its field in the rpc response.
|
||||
///
|
||||
/// This will populate the `transactions` field with only the hashes of the transactions in the
|
||||
/// block: [BlockTransactions::Hashes]
|
||||
pub fn from_block_hashes_only(block: PrimitiveBlock, total_difficulty: U256) -> Self {
|
||||
let block_hash = block.header.hash_slow();
|
||||
let transactions = block.body.iter().map(|tx| tx.hash).collect();
|
||||
|
||||
Self::from_block_with_transactions(
|
||||
block_hash,
|
||||
block,
|
||||
total_difficulty,
|
||||
BlockTransactions::Hashes(transactions),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new [Block] response from a [primitive block](reth_primitives::Block), using the
|
||||
/// total difficulty to populate its field in the rpc response.
|
||||
///
|
||||
/// This will populate the `transactions` field with the _full_ [Transaction] objects:
|
||||
/// [BlockTransactions::Full]
|
||||
pub fn from_block_full(
|
||||
block: PrimitiveBlock,
|
||||
total_difficulty: U256,
|
||||
) -> Result<Self, BlockError> {
|
||||
let block_hash = block.header.hash_slow();
|
||||
let block_length = block.length();
|
||||
let block_number = block.number;
|
||||
let uncles = block.ommers.into_iter().map(|h| h.hash_slow()).collect();
|
||||
let base_fee_per_gas = block.header.base_fee_per_gas;
|
||||
|
||||
let header = Header::from_primitive_with_hash(block.header, block_hash);
|
||||
|
||||
let mut transactions = Vec::with_capacity(block.body.len());
|
||||
for (idx, tx) in block.body.iter().enumerate() {
|
||||
let signed_tx = tx.clone().into_ecrecovered().ok_or(BlockError::InvalidSignature)?;
|
||||
@ -74,15 +125,35 @@ impl Block {
|
||||
))
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
Ok(Self::from_block_with_transactions(
|
||||
block_hash,
|
||||
block,
|
||||
total_difficulty,
|
||||
BlockTransactions::Full(transactions),
|
||||
))
|
||||
}
|
||||
|
||||
fn from_block_with_transactions(
|
||||
block_hash: H256,
|
||||
block: PrimitiveBlock,
|
||||
total_difficulty: U256,
|
||||
transactions: BlockTransactions,
|
||||
) -> Self {
|
||||
let block_length = block.length();
|
||||
let uncles = block.ommers.into_iter().map(|h| h.hash_slow()).collect();
|
||||
let base_fee_per_gas = block.header.base_fee_per_gas;
|
||||
|
||||
let header = Header::from_primitive_with_hash(block.header, block_hash);
|
||||
|
||||
Self {
|
||||
header,
|
||||
uncles,
|
||||
transactions: BlockTransactions::Full(transactions),
|
||||
transactions,
|
||||
base_fee_per_gas: base_fee_per_gas.map(U256::from),
|
||||
total_difficulty,
|
||||
size: Some(U256::from(block_length)),
|
||||
withdrawals: block.withdrawals,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,3 +312,17 @@ impl<T: Serialize> Serialize for Rich<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_full_conversion() {
|
||||
let full = true;
|
||||
assert_eq!(BlockTransactionsKind::Full, full.into());
|
||||
|
||||
let full = false;
|
||||
assert_eq!(BlockTransactionsKind::Hashes, full.into());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
//! Contains RPC handler implementations specific to blocks.
|
||||
|
||||
use crate::{eth::error::EthResult, EthApi};
|
||||
use crate::{
|
||||
eth::error::{EthApiError, EthResult},
|
||||
EthApi,
|
||||
};
|
||||
use reth_primitives::{rpc::BlockId, H256};
|
||||
use reth_provider::{BlockProvider, StateProviderFactory};
|
||||
use reth_rpc_types::RichBlock;
|
||||
use reth_rpc_types::{Block, RichBlock};
|
||||
|
||||
impl<Client, Pool, Network> EthApi<Client, Pool, Network>
|
||||
where
|
||||
@ -12,14 +15,13 @@ where
|
||||
pub(crate) async fn block_by_hash(
|
||||
&self,
|
||||
hash: H256,
|
||||
_full: bool,
|
||||
full: bool,
|
||||
) -> EthResult<Option<RichBlock>> {
|
||||
let block = self.client().block(BlockId::Hash(hash.0.into()))?;
|
||||
if let Some(_block) = block {
|
||||
// TODO: GET TD FOR BLOCK - needs block provider? or header provider?
|
||||
// let total_difficulty = todo!();
|
||||
// let rich_block = Block::from_block_full(block, total_difficulty);
|
||||
todo!()
|
||||
if let Some(block) = self.client().block_by_hash(hash)? {
|
||||
let total_difficulty =
|
||||
self.client().header_td(&hash)?.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
|
||||
let block = Block::from_block(block, total_difficulty, full.into())?;
|
||||
Ok(Some(block.into()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@ -56,8 +56,8 @@ where
|
||||
Ok(Some(EthApiSpec::chain_id(self)))
|
||||
}
|
||||
|
||||
async fn block_by_hash(&self, _hash: H256, _full: bool) -> Result<Option<RichBlock>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
async fn block_by_hash(&self, hash: H256, full: bool) -> Result<Option<RichBlock>> {
|
||||
Ok(EthApi::block_by_hash(self, hash, full).await?)
|
||||
}
|
||||
|
||||
async fn block_by_number(
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
//! Error variants for the `eth_` namespace.
|
||||
|
||||
use jsonrpsee::{core::Error as RpcError, types::error::INVALID_PARAMS_CODE};
|
||||
use reth_rpc_types::BlockError;
|
||||
use reth_transaction_pool::error::PoolError;
|
||||
|
||||
use crate::result::{internal_rpc_err, rpc_err};
|
||||
@ -25,6 +26,9 @@ pub(crate) enum EthApiError {
|
||||
UnknownBlockNumber,
|
||||
#[error("Invalid block range")]
|
||||
InvalidBlockRange,
|
||||
/// Thrown when constructing an RPC block from a primitive block data failed.
|
||||
#[error(transparent)]
|
||||
InvalidBlockData(#[from] BlockError),
|
||||
/// Other internal error
|
||||
#[error(transparent)]
|
||||
Internal(#[from] reth_interfaces::Error),
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
//! Additional helpers for converting errors.
|
||||
|
||||
use crate::eth::error::EthApiError;
|
||||
use jsonrpsee::core::{Error as RpcError, RpcResult};
|
||||
use reth_interfaces::Result as RethResult;
|
||||
use reth_primitives::Block;
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::eth::error::EthApiError;
|
||||
|
||||
/// Helper trait to easily convert various `Result` types into [`RpcResult`]
|
||||
pub(crate) trait ToRpcResult<Ok, Err> {
|
||||
/// Converts the error of the [Result] to an [RpcResult] via the `Err` [Display] impl.
|
||||
|
||||
Reference in New Issue
Block a user