From 55e75642f3e2c5ea9ac9347073a297e834e501db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Hodul=C3=A1k?= Date: Tue, 12 Aug 2025 14:32:10 +0200 Subject: [PATCH 1/4] feat(rpc): Add `SimTxConverter` to allow for providing custom converters with extra context --- crates/rpc/rpc-convert/src/transaction.rs | 100 ++++++++++++++++++---- 1 file changed, 82 insertions(+), 18 deletions(-) diff --git a/crates/rpc/rpc-convert/src/transaction.rs b/crates/rpc/rpc-convert/src/transaction.rs index cf845067410..f75e44b062c 100644 --- a/crates/rpc/rpc-convert/src/transaction.rs +++ b/crates/rpc/rpc-convert/src/transaction.rs @@ -223,6 +223,46 @@ where } } +/// Converts `TxReq` into `SimTx`. +/// +/// Where: +/// * `TxReq` is a transaction request received from an RPC API +/// * `SimTx` is the corresponding consensus layer transaction for execution simulation +/// +/// The `SimTxConverter` has two blanket implementations: +/// * `()` assuming `TxReq` implements [`TryIntoSimTx`] and is used as default for [`RpcConverter`]. +/// * `Fn(TxReq) -> Result>` and can be applied using +/// [`RpcConverter::with_sim_tx_converter`]. +/// +/// One should prefer to implement [`TryIntoSimTx`] for `TxReq` to get the `SimTxConverter` +/// implementation for free, thanks to the blanket implementation, unless the conversion requires +/// more context. For example, some configuration parameters or access handles to database, network, +/// etc. +pub trait SimTxConverter { + /// Performs the conversion from `tx_req` into `SimTx`. + /// + /// See [`SimTxConverter`] for more information. + fn convert_sim_tx(&self, tx_req: TxReq) -> Result>; +} + +impl SimTxConverter for () +where + TxReq: TryIntoSimTx + Sized, +{ + fn convert_sim_tx(&self, tx_req: TxReq) -> Result> { + tx_req.try_into_sim_tx() + } +} + +impl SimTxConverter for F +where + F: Fn(TxReq) -> Result>, +{ + fn convert_sim_tx(&self, tx_req: TxReq) -> Result> { + self(tx_req) + } +} + /// Converts `self` into `T`. /// /// Should create a fake transaction for simulation using [`TransactionRequest`]. @@ -402,12 +442,13 @@ pub struct TransactionConversionError(String); /// is [`TransactionInfo`] then `()` can be used as `Map` which trivially passes over the input /// object. #[derive(Debug)] -pub struct RpcConverter { +pub struct RpcConverter { network: PhantomData, evm: PhantomData, receipt_converter: Receipt, header_converter: Header, mapper: Map, + sim_tx_converter: SimTx, } impl RpcConverter { @@ -419,20 +460,24 @@ impl RpcConverter { receipt_converter, header_converter: (), mapper: (), + sim_tx_converter: (), } } } -impl RpcConverter { +impl + RpcConverter +{ /// Converts the network type - pub fn with_network(self) -> RpcConverter { - let Self { receipt_converter, header_converter, mapper, evm, .. } = self; + pub fn with_network(self) -> RpcConverter { + let Self { receipt_converter, header_converter, mapper, evm, sim_tx_converter, .. } = self; RpcConverter { receipt_converter, header_converter, mapper, network: Default::default(), evm, + sim_tx_converter, } } @@ -440,27 +485,39 @@ impl RpcConverter( self, header_converter: HeaderNew, - ) -> RpcConverter { - let Self { receipt_converter, header_converter: _, mapper, network, evm } = self; - RpcConverter { receipt_converter, header_converter, mapper, network, evm } + ) -> RpcConverter { + let Self { receipt_converter, header_converter: _, mapper, network, evm, sim_tx_converter } = + self; + RpcConverter { receipt_converter, header_converter, mapper, network, evm, sim_tx_converter } } /// Configures the mapper. pub fn with_mapper( self, mapper: MapNew, - ) -> RpcConverter { - let Self { receipt_converter, header_converter, mapper: _, network, evm } = self; - RpcConverter { receipt_converter, header_converter, mapper, network, evm } + ) -> RpcConverter { + let Self { receipt_converter, header_converter, mapper: _, network, evm, sim_tx_converter } = + self; + RpcConverter { receipt_converter, header_converter, mapper, network, evm, sim_tx_converter } + } + + /// Swaps the simulate transaction converter with `sim_tx_converter`. + pub fn with_sim_tx_converter( + self, + sim_tx_converter: SimTxNew, + ) -> RpcConverter { + let Self { receipt_converter, header_converter, mapper, network, evm, .. } = self; + RpcConverter { receipt_converter, header_converter, mapper, network, evm, sim_tx_converter } } } -impl Default - for RpcConverter +impl Default + for RpcConverter where Receipt: Default, Header: Default, Map: Default, + SimTx: Default, { fn default() -> Self { Self { @@ -469,12 +526,13 @@ where receipt_converter: Default::default(), header_converter: Default::default(), mapper: Default::default(), + sim_tx_converter: Default::default(), } } } -impl Clone - for RpcConverter +impl Clone + for RpcConverter { fn clone(&self) -> Self { Self { @@ -483,18 +541,19 @@ impl Clone receipt_converter: self.receipt_converter.clone(), header_converter: self.header_converter.clone(), mapper: self.mapper.clone(), + sim_tx_converter: self.sim_tx_converter.clone(), } } } -impl RpcConvert - for RpcConverter +impl RpcConvert + for RpcConverter where N: NodePrimitives, Network: RpcTypes + Send + Sync + Unpin + Clone + Debug, Evm: ConfigureEvm + 'static, TxTy: IntoRpcTx + Clone + Debug, - RpcTxReq: TryIntoSimTx> + TryIntoTxEnv>, + RpcTxReq: TryIntoTxEnv>, Receipt: ReceiptConverter< N, RpcReceipt = RpcReceipt, @@ -521,6 +580,8 @@ where + Send + Sync + 'static, + SimTx: + SimTxConverter, TxTy> + Clone + Debug + Unpin + Send + Sync + 'static, { type Primitives = N; type Network = Network; @@ -542,7 +603,10 @@ where &self, request: RpcTxReq, ) -> Result, Self::Error> { - Ok(request.try_into_sim_tx().map_err(|e| TransactionConversionError(e.to_string()))?) + Ok(self + .sim_tx_converter + .convert_sim_tx(request) + .map_err(|e| TransactionConversionError(e.to_string()))?) } fn tx_env( From 7ca8dc535c2817b6bfb26e772172bdf23ebe8e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Hodul=C3=A1k?= Date: Tue, 12 Aug 2025 14:49:37 +0200 Subject: [PATCH 2/4] refactor(rpc): Inherit additional traits that `RpcConverter` requires from `SimTxConverter` --- crates/rpc/rpc-convert/src/transaction.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/rpc/rpc-convert/src/transaction.rs b/crates/rpc/rpc-convert/src/transaction.rs index f75e44b062c..bf619a33849 100644 --- a/crates/rpc/rpc-convert/src/transaction.rs +++ b/crates/rpc/rpc-convert/src/transaction.rs @@ -238,7 +238,7 @@ where /// implementation for free, thanks to the blanket implementation, unless the conversion requires /// more context. For example, some configuration parameters or access handles to database, network, /// etc. -pub trait SimTxConverter { +pub trait SimTxConverter: Clone + Debug + Unpin + Send + Sync + 'static { /// Performs the conversion from `tx_req` into `SimTx`. /// /// See [`SimTxConverter`] for more information. @@ -256,7 +256,13 @@ where impl SimTxConverter for F where - F: Fn(TxReq) -> Result>, + F: Fn(TxReq) -> Result> + + Clone + + Debug + + Unpin + + Send + + Sync + + 'static, { fn convert_sim_tx(&self, tx_req: TxReq) -> Result> { self(tx_req) @@ -580,8 +586,7 @@ where + Send + Sync + 'static, - SimTx: - SimTxConverter, TxTy> + Clone + Debug + Unpin + Send + Sync + 'static, + SimTx: SimTxConverter, TxTy>, { type Primitives = N; type Network = Network; From 15bdc623f2c5ec3ab92b0974ffcb42d6c1ce6dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Hodul=C3=A1k?= Date: Tue, 12 Aug 2025 14:55:22 +0200 Subject: [PATCH 3/4] refactor(rpc): Remove unneeded `Sized` bound on `TxReq` in `SimTxConverter` implementation for `()` --- crates/rpc/rpc-convert/src/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rpc/rpc-convert/src/transaction.rs b/crates/rpc/rpc-convert/src/transaction.rs index bf619a33849..e4dacbbceed 100644 --- a/crates/rpc/rpc-convert/src/transaction.rs +++ b/crates/rpc/rpc-convert/src/transaction.rs @@ -247,7 +247,7 @@ pub trait SimTxConverter: Clone + Debug + Unpin + Send + Sync + 's impl SimTxConverter for () where - TxReq: TryIntoSimTx + Sized, + TxReq: TryIntoSimTx, { fn convert_sim_tx(&self, tx_req: TxReq) -> Result> { tx_req.try_into_sim_tx() From 8577fb8007ab95e9ab801591257cc2c762bfc8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Hodul=C3=A1k?= Date: Wed, 13 Aug 2025 10:42:06 +0200 Subject: [PATCH 4/4] refactor(rpc): Add an associated error type to `SimTxConverter` to account for different errors than `ValueError` --- crates/rpc/rpc-convert/src/transaction.rs | 27 +++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/rpc/rpc-convert/src/transaction.rs b/crates/rpc/rpc-convert/src/transaction.rs index e4dacbbceed..e33d254e272 100644 --- a/crates/rpc/rpc-convert/src/transaction.rs +++ b/crates/rpc/rpc-convert/src/transaction.rs @@ -239,32 +239,35 @@ where /// more context. For example, some configuration parameters or access handles to database, network, /// etc. pub trait SimTxConverter: Clone + Debug + Unpin + Send + Sync + 'static { + /// An associated error that can occur during the conversion. + type Err: Error; + /// Performs the conversion from `tx_req` into `SimTx`. /// /// See [`SimTxConverter`] for more information. - fn convert_sim_tx(&self, tx_req: TxReq) -> Result>; + fn convert_sim_tx(&self, tx_req: TxReq) -> Result; } impl SimTxConverter for () where - TxReq: TryIntoSimTx, + TxReq: TryIntoSimTx + Debug, { - fn convert_sim_tx(&self, tx_req: TxReq) -> Result> { + type Err = ValueError; + + fn convert_sim_tx(&self, tx_req: TxReq) -> Result { tx_req.try_into_sim_tx() } } -impl SimTxConverter for F +impl SimTxConverter for F where - F: Fn(TxReq) -> Result> - + Clone - + Debug - + Unpin - + Send - + Sync - + 'static, + TxReq: Debug, + E: Error, + F: Fn(TxReq) -> Result + Clone + Debug + Unpin + Send + Sync + 'static, { - fn convert_sim_tx(&self, tx_req: TxReq) -> Result> { + type Err = E; + + fn convert_sim_tx(&self, tx_req: TxReq) -> Result { self(tx_req) } }