mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-10 07:48:19 -05:00
chore: add rpc-compat feature in reth primitives-traits (#16608)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -9548,6 +9548,7 @@ dependencies = [
|
||||
"alloy-genesis",
|
||||
"alloy-primitives",
|
||||
"alloy-rlp",
|
||||
"alloy-rpc-types-eth",
|
||||
"alloy-trie",
|
||||
"arbitrary",
|
||||
"auto_impl",
|
||||
|
||||
@@ -50,6 +50,7 @@ arbitrary = { workspace = true, features = ["derive"], optional = true }
|
||||
proptest = { workspace = true, optional = true }
|
||||
proptest-arbitrary-interop = { workspace = true, optional = true }
|
||||
rayon = { workspace = true, optional = true }
|
||||
alloy-rpc-types-eth = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
reth-codecs.workspace = true
|
||||
@@ -93,6 +94,7 @@ std = [
|
||||
"reth-chainspec/std",
|
||||
"revm-bytecode/std",
|
||||
"revm-state/std",
|
||||
"alloy-rpc-types-eth?/std",
|
||||
]
|
||||
secp256k1 = ["alloy-consensus/secp256k1"]
|
||||
test-utils = [
|
||||
@@ -115,6 +117,7 @@ arbitrary = [
|
||||
"op-alloy-consensus?/arbitrary",
|
||||
"alloy-trie/arbitrary",
|
||||
"reth-chainspec/arbitrary",
|
||||
"alloy-rpc-types-eth?/arbitrary",
|
||||
]
|
||||
serde-bincode-compat = [
|
||||
"serde",
|
||||
@@ -140,6 +143,7 @@ serde = [
|
||||
"revm-bytecode/serde",
|
||||
"revm-state/serde",
|
||||
"rand_08/serde",
|
||||
"alloy-rpc-types-eth?/serde",
|
||||
]
|
||||
reth-codec = [
|
||||
"dep:reth-codecs",
|
||||
@@ -153,3 +157,4 @@ op = [
|
||||
rayon = [
|
||||
"dep:rayon",
|
||||
]
|
||||
rpc-compat = ["alloy-rpc-types-eth"]
|
||||
|
||||
@@ -532,6 +532,205 @@ impl<B: crate::test_utils::TestBlock> RecoveredBlock<B> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rpc-compat")]
|
||||
mod rpc_compat {
|
||||
use super::{
|
||||
Block as BlockTrait, BlockBody as BlockBodyTrait, RecoveredBlock, SignedTransaction,
|
||||
};
|
||||
use crate::block::error::BlockRecoveryError;
|
||||
use alloc::vec::Vec;
|
||||
use alloy_consensus::{
|
||||
transaction::Recovered, Block as CBlock, BlockBody, BlockHeader, Sealable,
|
||||
};
|
||||
use alloy_primitives::U256;
|
||||
use alloy_rpc_types_eth::{
|
||||
Block, BlockTransactions, BlockTransactionsKind, Header, TransactionInfo,
|
||||
};
|
||||
|
||||
impl<B> RecoveredBlock<B>
|
||||
where
|
||||
B: BlockTrait,
|
||||
{
|
||||
/// Converts the block block into an RPC [`Block`] instance with the given
|
||||
/// [`BlockTransactionsKind`].
|
||||
///
|
||||
/// The `tx_resp_builder` closure is used to build the transaction response for each
|
||||
/// transaction.
|
||||
pub fn into_rpc_block<T, F, E>(
|
||||
self,
|
||||
kind: BlockTransactionsKind,
|
||||
tx_resp_builder: F,
|
||||
) -> Result<Block<T, Header<B::Header>>, E>
|
||||
where
|
||||
F: Fn(
|
||||
Recovered<<<B as BlockTrait>::Body as BlockBodyTrait>::Transaction>,
|
||||
TransactionInfo,
|
||||
) -> Result<T, E>,
|
||||
{
|
||||
match kind {
|
||||
BlockTransactionsKind::Hashes => Ok(self.into_rpc_block_with_tx_hashes()),
|
||||
BlockTransactionsKind::Full => self.into_rpc_block_full(tx_resp_builder),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clones the block and converts it into a [`Block`] response with the given
|
||||
/// [`BlockTransactionsKind`]
|
||||
///
|
||||
/// This is a convenience method that avoids the need to explicitly clone the block
|
||||
/// before calling [`Self::into_rpc_block`]. For transaction hashes, it only clones
|
||||
/// the necessary parts for better efficiency.
|
||||
///
|
||||
/// The `tx_resp_builder` closure is used to build the transaction response for each
|
||||
/// transaction.
|
||||
pub fn clone_into_rpc_block<T, F, E>(
|
||||
&self,
|
||||
kind: BlockTransactionsKind,
|
||||
tx_resp_builder: F,
|
||||
) -> Result<Block<T, Header<B::Header>>, E>
|
||||
where
|
||||
F: Fn(
|
||||
Recovered<<<B as BlockTrait>::Body as BlockBodyTrait>::Transaction>,
|
||||
TransactionInfo,
|
||||
) -> Result<T, E>,
|
||||
{
|
||||
match kind {
|
||||
BlockTransactionsKind::Hashes => Ok(self.to_rpc_block_with_tx_hashes()),
|
||||
BlockTransactionsKind::Full => self.clone().into_rpc_block_full(tx_resp_builder),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Block`] instance from a [`RecoveredBlock`] reference.
|
||||
///
|
||||
/// This will populate the `transactions` field with only the hashes of the transactions in
|
||||
/// the block: [`BlockTransactions::Hashes`]
|
||||
///
|
||||
/// This method only clones the necessary parts and avoids cloning the entire block.
|
||||
pub fn to_rpc_block_with_tx_hashes<T>(&self) -> Block<T, Header<B::Header>> {
|
||||
let transactions = self.body().transaction_hashes_iter().copied().collect();
|
||||
let rlp_length = self.rlp_length();
|
||||
let header = self.clone_sealed_header();
|
||||
let withdrawals = self.body().withdrawals().cloned();
|
||||
|
||||
let transactions = BlockTransactions::Hashes(transactions);
|
||||
let uncles =
|
||||
self.body().ommers().unwrap_or(&[]).iter().map(|h| h.hash_slow()).collect();
|
||||
let header = Header::from_consensus(header.into(), None, Some(U256::from(rlp_length)));
|
||||
|
||||
Block { header, uncles, transactions, withdrawals }
|
||||
}
|
||||
|
||||
/// Create a new [`Block`] response from a [`RecoveredBlock`], 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 into_rpc_block_with_tx_hashes<T>(self) -> Block<T, Header<B::Header>> {
|
||||
let transactions = self.body().transaction_hashes_iter().copied().collect();
|
||||
let rlp_length = self.rlp_length();
|
||||
let (header, body) = self.into_sealed_block().split_sealed_header_body();
|
||||
let BlockBody { ommers, withdrawals, .. } = body.into_ethereum_body();
|
||||
|
||||
let transactions = BlockTransactions::Hashes(transactions);
|
||||
let uncles = ommers.into_iter().map(|h| h.hash_slow()).collect();
|
||||
let header = Header::from_consensus(header.into(), None, Some(U256::from(rlp_length)));
|
||||
|
||||
Block { header, uncles, transactions, withdrawals }
|
||||
}
|
||||
|
||||
/// Create a new [`Block`] response from a [`RecoveredBlock`], using the given closure to
|
||||
/// create the rpc transactions.
|
||||
///
|
||||
/// This will populate the `transactions` field with the _full_
|
||||
/// transaction objects: [`BlockTransactions::Full`]
|
||||
pub fn into_rpc_block_full<T, F, E>(
|
||||
self,
|
||||
tx_resp_builder: F,
|
||||
) -> Result<Block<T, Header<B::Header>>, E>
|
||||
where
|
||||
F: Fn(
|
||||
Recovered<<<B as BlockTrait>::Body as BlockBodyTrait>::Transaction>,
|
||||
TransactionInfo,
|
||||
) -> Result<T, E>,
|
||||
{
|
||||
let block_number = self.header().number();
|
||||
let base_fee = self.header().base_fee_per_gas();
|
||||
let block_length = self.rlp_length();
|
||||
let block_hash = Some(self.hash());
|
||||
|
||||
let (block, senders) = self.split_sealed();
|
||||
let (header, body) = block.split_sealed_header_body();
|
||||
let BlockBody { transactions, ommers, withdrawals } = body.into_ethereum_body();
|
||||
|
||||
let transactions = transactions
|
||||
.into_iter()
|
||||
.zip(senders)
|
||||
.enumerate()
|
||||
.map(|(idx, (tx, sender))| {
|
||||
let tx_info = TransactionInfo {
|
||||
hash: Some(*tx.tx_hash()),
|
||||
block_hash,
|
||||
block_number: Some(block_number),
|
||||
base_fee,
|
||||
index: Some(idx as u64),
|
||||
};
|
||||
|
||||
tx_resp_builder(Recovered::new_unchecked(tx, sender), tx_info)
|
||||
})
|
||||
.collect::<Result<Vec<_>, E>>()?;
|
||||
|
||||
let transactions = BlockTransactions::Full(transactions);
|
||||
let uncles = ommers.into_iter().map(|h| h.hash_slow()).collect();
|
||||
let header =
|
||||
Header::from_consensus(header.into(), None, Some(U256::from(block_length)));
|
||||
|
||||
let block = Block { header, uncles, transactions, withdrawals };
|
||||
|
||||
Ok(block)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RecoveredBlock<CBlock<T>>
|
||||
where
|
||||
T: SignedTransaction,
|
||||
{
|
||||
/// Create a `RecoveredBlock` from an alloy RPC block.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```ignore
|
||||
/// // Works with default Transaction type
|
||||
/// let rpc_block: alloy_rpc_types_eth::Block = get_rpc_block();
|
||||
/// let recovered = RecoveredBlock::from_rpc_block(rpc_block)?;
|
||||
///
|
||||
/// // Also works with custom transaction types that implement From<U>
|
||||
/// let custom_rpc_block: alloy_rpc_types_eth::Block<CustomTx> = get_custom_rpc_block();
|
||||
/// let recovered = RecoveredBlock::from_rpc_block(custom_rpc_block)?;
|
||||
/// ```
|
||||
pub fn from_rpc_block<U>(
|
||||
block: alloy_rpc_types_eth::Block<U>,
|
||||
) -> Result<Self, BlockRecoveryError<alloy_consensus::Block<T>>>
|
||||
where
|
||||
T: From<U>,
|
||||
{
|
||||
// Convert to consensus block and then convert transactions
|
||||
let consensus_block = block.into_consensus().convert_transactions();
|
||||
|
||||
// Try to recover the block
|
||||
consensus_block.try_into_recovered()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TryFrom<alloy_rpc_types_eth::Block<U>> for RecoveredBlock<CBlock<T>>
|
||||
where
|
||||
T: SignedTransaction + From<U>,
|
||||
{
|
||||
type Error = BlockRecoveryError<alloy_consensus::Block<T>>;
|
||||
|
||||
fn try_from(block: alloy_rpc_types_eth::Block<U>) -> Result<Self, Self::Error> {
|
||||
Self::from_rpc_block(block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`RecoveredBlock`] serde implementation.
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub(super) mod serde_bincode_compat {
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
//! types.
|
||||
//! - `reth-codec`: Enables db codec support for reth types including zstd compression for certain
|
||||
//! types.
|
||||
//! - `rpc-compat`: Adds RPC compatibility functions for the types in this crate, e.g. rpc type
|
||||
//! conversions.
|
||||
//! - `serde`: Adds serde support for all types.
|
||||
//! - `secp256k1`: Adds secp256k1 support for transaction signing/recovery. (By default the no-std
|
||||
//! friendly `k256` is used)
|
||||
|
||||
@@ -15,7 +15,7 @@ workspace = true
|
||||
# reth
|
||||
revm = { workspace = true, features = ["optional_block_gas_limit", "optional_eip3607", "optional_no_base_fee"] }
|
||||
revm-inspectors.workspace = true
|
||||
reth-primitives-traits.workspace = true
|
||||
reth-primitives-traits = { workspace = true, features = ["rpc-compat"] }
|
||||
reth-errors.workspace = true
|
||||
reth-evm.workspace = true
|
||||
reth-storage-api.workspace = true
|
||||
|
||||
@@ -13,7 +13,7 @@ use futures::Future;
|
||||
use reth_evm::ConfigureEvm;
|
||||
use reth_node_api::BlockBody;
|
||||
use reth_primitives_traits::{NodePrimitives, RecoveredBlock, SealedBlock};
|
||||
use reth_rpc_types_compat::block::from_block;
|
||||
use reth_rpc_types_compat::TransactionCompat;
|
||||
use reth_storage_api::{BlockIdReader, BlockReader, ProviderHeader, ProviderReceipt, ProviderTx};
|
||||
use reth_transaction_pool::{PoolTransaction, TransactionPool};
|
||||
use std::sync::Arc;
|
||||
@@ -59,7 +59,9 @@ pub trait EthBlocks: LoadBlock {
|
||||
async move {
|
||||
let Some(block) = self.recovered_block(block_id).await? else { return Ok(None) };
|
||||
|
||||
let block = from_block((*block).clone(), full.into(), self.tx_resp_builder())?;
|
||||
let block = block.clone_into_rpc_block(full.into(), |tx, tx_info| {
|
||||
self.tx_resp_builder().fill(tx, tx_info)
|
||||
})?;
|
||||
Ok(Some(block))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ reth-evm.workspace = true
|
||||
reth-execution-types.workspace = true
|
||||
reth-metrics.workspace = true
|
||||
reth-ethereum-primitives.workspace = true
|
||||
reth-primitives-traits.workspace = true
|
||||
reth-primitives-traits = { workspace = true, features = ["rpc-compat"] }
|
||||
reth-storage-api.workspace = true
|
||||
reth-revm.workspace = true
|
||||
reth-rpc-server-types.workspace = true
|
||||
|
||||
@@ -23,7 +23,7 @@ use reth_primitives_traits::{
|
||||
block::BlockTx, BlockBody as _, NodePrimitives, Recovered, RecoveredBlock, SignedTransaction,
|
||||
};
|
||||
use reth_rpc_server_types::result::rpc_err;
|
||||
use reth_rpc_types_compat::{block::from_block, TransactionCompat};
|
||||
use reth_rpc_types_compat::TransactionCompat;
|
||||
use reth_storage_api::noop::NoopProvider;
|
||||
use revm::{
|
||||
context_interface::result::ExecutionResult,
|
||||
@@ -259,6 +259,6 @@ where
|
||||
let txs_kind =
|
||||
if full_transactions { BlockTransactionsKind::Full } else { BlockTransactionsKind::Hashes };
|
||||
|
||||
let block = from_block(block, txs_kind, tx_resp_builder)?;
|
||||
let block = block.into_rpc_block(txs_kind, |tx, tx_info| tx_resp_builder.fill(tx, tx_info))?;
|
||||
Ok(SimulatedBlock { inner: block, calls })
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
//! Compatibility functions for rpc `Block` type.
|
||||
|
||||
use crate::transaction::TransactionCompat;
|
||||
use alloy_consensus::{transaction::Recovered, BlockBody, BlockHeader, Sealable};
|
||||
use alloy_primitives::U256;
|
||||
use alloy_rpc_types_eth::{
|
||||
Block, BlockTransactions, BlockTransactionsKind, Header, TransactionInfo,
|
||||
};
|
||||
use reth_primitives_traits::{
|
||||
Block as BlockTrait, BlockBody as BlockBodyTrait, NodePrimitives, RecoveredBlock,
|
||||
SignedTransaction,
|
||||
};
|
||||
|
||||
/// Converts the given primitive block into a [`Block`] response with the given
|
||||
/// [`BlockTransactionsKind`]
|
||||
///
|
||||
/// If a `block_hash` is provided, then this is used, otherwise the block hash is computed.
|
||||
#[expect(clippy::type_complexity)]
|
||||
pub fn from_block<T, B>(
|
||||
block: RecoveredBlock<B>,
|
||||
kind: BlockTransactionsKind,
|
||||
tx_resp_builder: &T,
|
||||
) -> Result<Block<T::Transaction, Header<B::Header>>, T::Error>
|
||||
where
|
||||
T: TransactionCompat,
|
||||
B: BlockTrait<Body: BlockBodyTrait<Transaction = <T::Primitives as NodePrimitives>::SignedTx>>,
|
||||
{
|
||||
match kind {
|
||||
BlockTransactionsKind::Hashes => Ok(from_block_with_tx_hashes::<T::Transaction, B>(block)),
|
||||
BlockTransactionsKind::Full => from_block_full::<T, B>(block, tx_resp_builder),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Block`] response from a [`RecoveredBlock`], 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_with_tx_hashes<T, B>(block: RecoveredBlock<B>) -> Block<T, Header<B::Header>>
|
||||
where
|
||||
B: BlockTrait,
|
||||
{
|
||||
let transactions = block.body().transaction_hashes_iter().copied().collect();
|
||||
let rlp_length = block.rlp_length();
|
||||
let (header, body) = block.into_sealed_block().split_sealed_header_body();
|
||||
let BlockBody { ommers, withdrawals, .. } = body.into_ethereum_body();
|
||||
|
||||
let transactions = BlockTransactions::Hashes(transactions);
|
||||
let uncles = ommers.into_iter().map(|h| h.hash_slow()).collect();
|
||||
let header = Header::from_consensus(header.into(), None, Some(U256::from(rlp_length)));
|
||||
|
||||
Block { header, uncles, transactions, withdrawals }
|
||||
}
|
||||
|
||||
/// Create a new [`Block`] response from a [`RecoveredBlock`], using the
|
||||
/// total difficulty to populate its field in the rpc response.
|
||||
///
|
||||
/// This will populate the `transactions` field with the _full_
|
||||
/// [`TransactionCompat::Transaction`] objects: [`BlockTransactions::Full`]
|
||||
#[expect(clippy::type_complexity)]
|
||||
pub fn from_block_full<T, B>(
|
||||
block: RecoveredBlock<B>,
|
||||
tx_resp_builder: &T,
|
||||
) -> Result<Block<T::Transaction, Header<B::Header>>, T::Error>
|
||||
where
|
||||
T: TransactionCompat,
|
||||
B: BlockTrait<Body: BlockBodyTrait<Transaction = <T::Primitives as NodePrimitives>::SignedTx>>,
|
||||
{
|
||||
let block_number = block.header().number();
|
||||
let base_fee = block.header().base_fee_per_gas();
|
||||
let block_length = block.rlp_length();
|
||||
let block_hash = Some(block.hash());
|
||||
|
||||
let (block, senders) = block.split_sealed();
|
||||
let (header, body) = block.split_sealed_header_body();
|
||||
let BlockBody { transactions, ommers, withdrawals } = body.into_ethereum_body();
|
||||
|
||||
let transactions = transactions
|
||||
.into_iter()
|
||||
.zip(senders)
|
||||
.enumerate()
|
||||
.map(|(idx, (tx, sender))| {
|
||||
let tx_info = TransactionInfo {
|
||||
hash: Some(*tx.tx_hash()),
|
||||
block_hash,
|
||||
block_number: Some(block_number),
|
||||
base_fee,
|
||||
index: Some(idx as u64),
|
||||
};
|
||||
|
||||
tx_resp_builder.fill(Recovered::new_unchecked(tx, sender), tx_info)
|
||||
})
|
||||
.collect::<Result<Vec<_>, T::Error>>()?;
|
||||
|
||||
let transactions = BlockTransactions::Full(transactions);
|
||||
let uncles = ommers.into_iter().map(|h| h.hash_slow()).collect();
|
||||
let header = Header::from_consensus(header.into(), None, Some(U256::from(block_length)));
|
||||
|
||||
let block = Block { header, uncles, transactions, withdrawals };
|
||||
|
||||
Ok(block)
|
||||
}
|
||||
@@ -10,10 +10,8 @@
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
pub mod block;
|
||||
mod fees;
|
||||
pub mod transaction;
|
||||
|
||||
pub use fees::{CallFees, CallFeesError};
|
||||
pub use transaction::{
|
||||
try_into_op_tx_info, EthTxEnvError, IntoRpcTx, RpcConverter, TransactionCompat,
|
||||
|
||||
Reference in New Issue
Block a user