1use crate::{
2 TempoPayloadTypes,
3 engine::TempoEngineValidator,
4 rpc::{
5 TempoAdminApi, TempoAdminApiServer, TempoEthApiBuilder, TempoEthExt, TempoEthExtApiServer,
6 TempoToken, TempoTokenApiServer,
7 },
8};
9use alloy_primitives::B256;
10use reth_engine_local::LocalPayloadAttributesBuilder;
11use reth_evm::revm::primitives::Address;
12use reth_node_api::{
13 AddOnsContext, FullNodeComponents, FullNodeTypes, NodeAddOns, NodePrimitives, NodeTypes,
14 PayloadAttributesBuilder, PayloadTypes,
15};
16use reth_node_builder::{
17 BuilderContext, DebugNode, Node, NodeAdapter,
18 components::{
19 BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder,
20 PayloadBuilderBuilder, PoolBuilder, TxPoolBuilder, spawn_maintenance_tasks,
21 },
22 rpc::{
23 BasicEngineValidatorBuilder, EngineValidatorAddOn, EngineValidatorBuilder, EthApiBuilder,
24 NoopEngineApiBuilder, PayloadValidatorBuilder, RethRpcAddOns, RpcAddOns,
25 },
26};
27use reth_node_ethereum::EthereumNetworkBuilder;
28use reth_primitives_traits::SealedHeader;
29use reth_provider::{EthStorage, providers::ProviderFactoryBuilder};
30use reth_rpc_builder::{Identity, RethRpcModule};
31use reth_rpc_eth_api::{
32 RpcNodeCore,
33 helpers::config::{EthConfigApiServer, EthConfigHandler},
34};
35use reth_tracing::tracing::{debug, info};
36use reth_transaction_pool::{TransactionValidationTaskExecutor, blobstore::InMemoryBlobStore};
37use std::{default::Default, sync::Arc};
38use tempo_chainspec::spec::TempoChainSpec;
39use tempo_consensus::TempoConsensus;
40use tempo_evm::TempoEvmConfig;
41use tempo_payload_builder::TempoPayloadBuilder;
42use tempo_payload_types::TempoPayloadAttributes;
43use tempo_primitives::{TempoHeader, TempoPrimitives, TempoTxEnvelope, TempoTxType};
44use tempo_transaction_pool::{
45 AA2dPool, AA2dPoolConfig, TempoTransactionPool,
46 amm::AmmLiquidityCache,
47 validator::{
48 DEFAULT_AA_VALID_AFTER_MAX_SECS, DEFAULT_MAX_TEMPO_AUTHORIZATIONS,
49 TempoTransactionValidator,
50 },
51};
52
53#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, clap::Args)]
55pub struct TempoNodeArgs {
56 #[arg(long = "txpool.aa-valid-after-max-secs", default_value_t = DEFAULT_AA_VALID_AFTER_MAX_SECS)]
58 pub aa_valid_after_max_secs: u64,
59
60 #[arg(long = "txpool.max-tempo-authorizations", default_value_t = DEFAULT_MAX_TEMPO_AUTHORIZATIONS)]
62 pub max_tempo_authorizations: usize,
63
64 #[arg(long = "builder.state-provider-metrics", default_value_t = false)]
66 pub builder_state_provider_metrics: bool,
67
68 #[arg(long = "builder.disable-state-cache", default_value_t = false)]
70 pub builder_disable_state_cache: bool,
71}
72
73impl TempoNodeArgs {
74 pub fn pool_builder(&self) -> TempoPoolBuilder {
76 TempoPoolBuilder {
77 aa_valid_after_max_secs: self.aa_valid_after_max_secs,
78 max_tempo_authorizations: self.max_tempo_authorizations,
79 }
80 }
81
82 pub fn payload_builder_builder(&self) -> TempoPayloadBuilderBuilder {
84 TempoPayloadBuilderBuilder {
85 state_provider_metrics: self.builder_state_provider_metrics,
86 disable_state_cache: self.builder_disable_state_cache,
87 }
88 }
89}
90
91#[derive(Debug, Default, Clone)]
93#[non_exhaustive]
94pub struct TempoNode {
95 pool_builder: TempoPoolBuilder,
97 payload_builder_builder: TempoPayloadBuilderBuilder,
99 validator_key: Option<B256>,
101}
102
103impl TempoNode {
104 pub fn new(args: &TempoNodeArgs, validator_key: Option<B256>) -> Self {
106 Self {
107 pool_builder: args.pool_builder(),
108 payload_builder_builder: args.payload_builder_builder(),
109 validator_key,
110 }
111 }
112
113 pub fn components<Node>(
115 pool_builder: TempoPoolBuilder,
116 payload_builder_builder: TempoPayloadBuilderBuilder,
117 ) -> ComponentsBuilder<
118 Node,
119 TempoPoolBuilder,
120 BasicPayloadServiceBuilder<TempoPayloadBuilderBuilder>,
121 EthereumNetworkBuilder,
122 TempoExecutorBuilder,
123 TempoConsensusBuilder,
124 >
125 where
126 Node: FullNodeTypes<Types = Self>,
127 {
128 ComponentsBuilder::default()
129 .node_types::<Node>()
130 .pool(pool_builder)
131 .executor(TempoExecutorBuilder::default())
132 .payload(BasicPayloadServiceBuilder::new(payload_builder_builder))
133 .network(EthereumNetworkBuilder::default())
134 .consensus(TempoConsensusBuilder::default())
135 }
136
137 pub fn provider_factory_builder() -> ProviderFactoryBuilder<Self> {
138 ProviderFactoryBuilder::default()
139 }
140
141 pub fn with_validator_key(mut self, validator_key: Option<B256>) -> Self {
143 self.validator_key = validator_key;
144 self
145 }
146}
147
148impl NodeTypes for TempoNode {
149 type Primitives = TempoPrimitives;
150 type ChainSpec = TempoChainSpec;
151 type Storage = EthStorage<TempoTxEnvelope, TempoHeader>;
152 type Payload = TempoPayloadTypes;
153}
154
155#[derive(Debug)]
156pub struct TempoAddOns<
157 N: FullNodeComponents,
158 EthB: EthApiBuilder<N> = TempoEthApiBuilder,
159 PVB = TempoEngineValidatorBuilder,
160 EVB = BasicEngineValidatorBuilder<PVB>,
161 RpcMiddleware = Identity,
162> {
163 inner: RpcAddOns<N, EthB, PVB, NoopEngineApiBuilder, EVB, RpcMiddleware>,
164 validator_key: Option<B256>,
165}
166
167impl<N> TempoAddOns<NodeAdapter<N>, TempoEthApiBuilder>
168where
169 N: FullNodeTypes<Types = TempoNode>,
170{
171 pub fn new(validator_key: Option<B256>) -> Self {
173 Self {
174 inner: RpcAddOns::new(
175 TempoEthApiBuilder::new(validator_key),
176 TempoEngineValidatorBuilder,
177 NoopEngineApiBuilder::default(),
178 BasicEngineValidatorBuilder::default(),
179 Identity::default(),
180 ),
181 validator_key,
182 }
183 }
184}
185
186impl<N, EthB, PVB, EVB> NodeAddOns<N> for TempoAddOns<N, EthB, PVB, EVB>
187where
188 N: FullNodeComponents<Types = TempoNode, Evm = TempoEvmConfig>,
189 EthB: EthApiBuilder<N>,
190 PVB: Send + PayloadValidatorBuilder<N>,
191 EVB: EngineValidatorBuilder<N>,
192 EthB::EthApi:
193 RpcNodeCore<Evm = TempoEvmConfig, Primitives: NodePrimitives<BlockHeader = TempoHeader>>,
194{
195 type Handle = <RpcAddOns<N, EthB, PVB, NoopEngineApiBuilder, EVB> as NodeAddOns<N>>::Handle;
196
197 async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> {
198 let eth_config =
199 EthConfigHandler::new(ctx.node.provider().clone(), ctx.node.evm_config().clone());
200
201 self.inner
202 .launch_add_ons_with(ctx, move |container| {
203 let reth_node_builder::rpc::RpcModuleContainer {
204 modules, registry, ..
205 } = container;
206
207 let eth_api = registry.eth_api().clone();
208 let token = TempoToken::new(eth_api.clone());
209 let eth_ext = TempoEthExt::new(eth_api);
210 let admin = TempoAdminApi::new(self.validator_key);
211
212 modules.merge_configured(token.into_rpc())?;
213 modules.merge_configured(eth_ext.into_rpc())?;
214 modules.merge_if_module_configured(RethRpcModule::Admin, admin.into_rpc())?;
215 modules.merge_if_module_configured(RethRpcModule::Eth, eth_config.into_rpc())?;
216
217 Ok(())
218 })
219 .await
220 }
221}
222
223impl<N, EthB, PVB, EVB> RethRpcAddOns<N> for TempoAddOns<N, EthB, PVB, EVB>
224where
225 N: FullNodeComponents<Types = TempoNode, Evm = TempoEvmConfig>,
226 EthB: EthApiBuilder<N>,
227 PVB: PayloadValidatorBuilder<N>,
228 EVB: EngineValidatorBuilder<N>,
229 EthB::EthApi:
230 RpcNodeCore<Evm = TempoEvmConfig, Primitives: NodePrimitives<BlockHeader = TempoHeader>>,
231{
232 type EthApi = EthB::EthApi;
233
234 fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> {
235 self.inner.hooks_mut()
236 }
237}
238
239impl<N, EthB, PVB, EVB> EngineValidatorAddOn<N> for TempoAddOns<N, EthB, PVB, EVB>
240where
241 N: FullNodeComponents<Types = TempoNode, Evm = TempoEvmConfig>,
242 EthB: EthApiBuilder<N>,
243 PVB: Send,
244 EVB: EngineValidatorBuilder<N>,
245{
246 type ValidatorBuilder = EVB;
247
248 fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
249 self.inner.engine_validator_builder()
250 }
251}
252
253impl<N> Node<N> for TempoNode
254where
255 N: FullNodeTypes<Types = Self>,
256{
257 type ComponentsBuilder = ComponentsBuilder<
258 N,
259 TempoPoolBuilder,
260 BasicPayloadServiceBuilder<TempoPayloadBuilderBuilder>,
261 EthereumNetworkBuilder,
262 TempoExecutorBuilder,
263 TempoConsensusBuilder,
264 >;
265
266 type AddOns = TempoAddOns<NodeAdapter<N>>;
267
268 fn components_builder(&self) -> Self::ComponentsBuilder {
269 Self::components(self.pool_builder, self.payload_builder_builder)
270 }
271
272 fn add_ons(&self) -> Self::AddOns {
273 TempoAddOns::new(self.validator_key)
274 }
275}
276
277impl<N: FullNodeComponents<Types = Self>> DebugNode<N> for TempoNode {
278 type RpcBlock =
279 alloy_rpc_types_eth::Block<alloy_rpc_types_eth::Transaction<TempoTxEnvelope>, TempoHeader>;
280
281 fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> tempo_primitives::Block {
282 rpc_block
283 .into_consensus_block()
284 .map_transactions(|tx| tx.into_inner())
285 }
286
287 fn local_payload_attributes_builder(
288 chain_spec: &Self::ChainSpec,
289 ) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes, TempoHeader>
290 {
291 TempoPayloadAttributesBuilder::new(Arc::new(chain_spec.clone()))
292 }
293}
294
295#[derive(Debug)]
297#[non_exhaustive]
298pub struct TempoPayloadAttributesBuilder {
299 inner: LocalPayloadAttributesBuilder<TempoChainSpec>,
301}
302
303impl TempoPayloadAttributesBuilder {
304 pub fn new(chain_spec: Arc<TempoChainSpec>) -> Self {
306 Self {
307 inner: LocalPayloadAttributesBuilder::new(chain_spec).without_increasing_timestamp(),
308 }
309 }
310}
311
312impl PayloadAttributesBuilder<TempoPayloadAttributes, TempoHeader>
313 for TempoPayloadAttributesBuilder
314{
315 fn build(&self, parent: &SealedHeader<TempoHeader>) -> TempoPayloadAttributes {
316 let mut inner = self.inner.build(parent);
317 inner.suggested_fee_recipient = Address::ZERO;
318
319 let timestamp_millis_part = std::time::SystemTime::now()
320 .duration_since(std::time::UNIX_EPOCH)
321 .unwrap()
322 .as_millis() as u64
323 % 1000;
324
325 TempoPayloadAttributes {
326 inner,
327 timestamp_millis_part,
328 }
329 }
330}
331
332#[derive(Debug, Default, Clone, Copy)]
334#[non_exhaustive]
335pub struct TempoExecutorBuilder;
336
337impl<Node> ExecutorBuilder<Node> for TempoExecutorBuilder
338where
339 Node: FullNodeTypes<Types = TempoNode>,
340{
341 type EVM = TempoEvmConfig;
342
343 async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
344 let evm_config = TempoEvmConfig::new(ctx.chain_spec());
345 Ok(evm_config)
346 }
347}
348
349#[derive(Debug, Default, Clone, Copy)]
351#[non_exhaustive]
352pub struct TempoConsensusBuilder;
353
354impl<Node> ConsensusBuilder<Node> for TempoConsensusBuilder
355where
356 Node: FullNodeTypes<Types = TempoNode>,
357{
358 type Consensus = TempoConsensus;
359
360 async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
361 Ok(TempoConsensus::new(ctx.chain_spec()))
362 }
363}
364
365#[derive(Debug, Default, Clone)]
367#[non_exhaustive]
368pub struct TempoEngineValidatorBuilder;
369
370impl<Node> PayloadValidatorBuilder<Node> for TempoEngineValidatorBuilder
371where
372 Node: FullNodeComponents<Types = TempoNode>,
373{
374 type Validator = TempoEngineValidator;
375
376 async fn build(self, _ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
377 Ok(TempoEngineValidator::new())
378 }
379}
380
381#[derive(Debug, Clone, Copy)]
386#[non_exhaustive]
387pub struct TempoPoolBuilder {
388 pub aa_valid_after_max_secs: u64,
390 pub max_tempo_authorizations: usize,
392}
393
394impl TempoPoolBuilder {
395 pub const fn with_aa_tx_valid_after_max_secs(mut self, secs: u64) -> Self {
397 self.aa_valid_after_max_secs = secs;
398 self
399 }
400
401 pub const fn with_max_tempo_authorizations(mut self, max: usize) -> Self {
403 self.max_tempo_authorizations = max;
404 self
405 }
406}
407
408impl Default for TempoPoolBuilder {
409 fn default() -> Self {
410 Self {
411 aa_valid_after_max_secs: DEFAULT_AA_VALID_AFTER_MAX_SECS,
412 max_tempo_authorizations: DEFAULT_MAX_TEMPO_AUTHORIZATIONS,
413 }
414 }
415}
416
417impl<Node> PoolBuilder<Node, TempoEvmConfig> for TempoPoolBuilder
418where
419 Node: FullNodeTypes<Types = TempoNode>,
420{
421 type Pool = TempoTransactionPool<Node::Provider>;
422
423 async fn build_pool(
424 self,
425 ctx: &BuilderContext<Node>,
426 evm_config: TempoEvmConfig,
427 ) -> eyre::Result<Self::Pool> {
428 let mut pool_config = ctx.pool_config();
429 pool_config.max_inflight_delegated_slot_limit = pool_config.max_account_slots;
430
431 let blob_store = InMemoryBlobStore::default();
433 let validator =
434 TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone(), evm_config)
435 .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes)
436 .with_local_transactions_config(pool_config.local_transactions_config.clone())
437 .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap)
438 .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit)
439 .set_block_gas_limit(ctx.chain_spec().inner.genesis().gas_limit)
440 .disable_balance_check()
441 .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee)
442 .with_additional_tasks(ctx.config().txpool.additional_validation_tasks)
443 .with_custom_tx_type(TempoTxType::AA as u8)
444 .no_eip4844()
445 .build_with_tasks(ctx.task_executor().clone(), blob_store.clone());
446
447 let aa_2d_config = AA2dPoolConfig {
448 price_bump_config: pool_config.price_bumps,
449 pending_limit: pool_config.pending_limit,
450 queued_limit: pool_config.queued_limit,
451 max_txs_per_sender: pool_config.max_account_slots,
452 };
453 let aa_2d_pool = AA2dPool::new(aa_2d_config);
454 let amm_liquidity_cache = AmmLiquidityCache::new(ctx.provider())?;
455
456 let validator = validator.map(|v| {
457 TempoTransactionValidator::new(
458 v,
459 self.aa_valid_after_max_secs,
460 self.max_tempo_authorizations,
461 amm_liquidity_cache.clone(),
462 )
463 });
464 let protocol_pool = TxPoolBuilder::new(ctx)
465 .with_validator(validator)
466 .build(blob_store, pool_config.clone());
467
468 let transaction_pool = TempoTransactionPool::new(protocol_pool, aa_2d_pool);
470
471 spawn_maintenance_tasks(ctx, transaction_pool.clone(), &pool_config)?;
472
473 ctx.task_executor().spawn_critical_task(
476 "txpool maintenance - tempo pool",
477 tempo_transaction_pool::maintain::maintain_tempo_pool(transaction_pool.clone()),
478 );
479
480 info!(target: "reth::cli", "Transaction pool initialized");
481 debug!(target: "reth::cli", "Spawned txpool maintenance task");
482
483 Ok(transaction_pool)
484 }
485}
486
487#[derive(Debug, Default, Clone, Copy)]
488#[non_exhaustive]
489pub struct TempoPayloadBuilderBuilder {
490 pub state_provider_metrics: bool,
492 pub disable_state_cache: bool,
494}
495
496impl<Node> PayloadBuilderBuilder<Node, TempoTransactionPool<Node::Provider>, TempoEvmConfig>
497 for TempoPayloadBuilderBuilder
498where
499 Node: FullNodeTypes<Types = TempoNode>,
500{
501 type PayloadBuilder = TempoPayloadBuilder<Node::Provider>;
502
503 async fn build_payload_builder(
504 self,
505 ctx: &BuilderContext<Node>,
506 pool: TempoTransactionPool<Node::Provider>,
507 evm_config: TempoEvmConfig,
508 ) -> eyre::Result<Self::PayloadBuilder> {
509 Ok(TempoPayloadBuilder::new(
510 pool,
511 ctx.provider().clone(),
512 evm_config,
513 self.state_provider_metrics,
514 self.disable_state_cache,
515 ))
516 }
517}