1use crate::{
2 TempoPayloadTypes,
3 engine::TempoEngineValidator,
4 rpc::{
5 TempoAdminApi, TempoAdminApiServer, TempoEthApi, TempoEthApiBuilder, TempoEthExt,
6 TempoEthExtApiServer, TempoForkScheduleApiServer, TempoForkScheduleRpc,
7 TempoOperatorApiServer, TempoOperatorRpc, TempoSimulate, TempoSimulateApiServer,
8 TempoToken, TempoTokenApiServer,
9 },
10};
11use alloy_primitives::B256;
12use reth_node_api::{
13 AddOnsContext, FullNodeComponents, FullNodeTypes, NodeAddOns, NodeTypes,
14 PayloadAttributesBuilder, PayloadTypes,
15};
16use reth_node_builder::{
17 BuilderContext, DebugNode, Node, NodeAdapter,
18 components::{
19 BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder,
20 PayloadBuilderBuilder, PoolBuilder, spawn_maintenance_tasks,
21 },
22 rpc::{
23 BasicEngineValidatorBuilder, EngineValidatorAddOn, NoopEngineApiBuilder,
24 PayloadValidatorBuilder, RethRpcAddOns, RpcAddOns, RpcHandle, RpcHooks,
25 },
26};
27use reth_node_ethereum::EthereumNetworkBuilder;
28use reth_primitives_traits::SealedHeader;
29use reth_provider::providers::ProviderFactoryBuilder;
30use reth_rpc_builder::{Identity, RethRpcModule};
31use reth_rpc_eth_api::{
32 RpcNodeCore,
33 helpers::config::{EthConfigApiServer, EthConfigHandler},
34};
35use reth_storage_api::{AccountInfoReader, EmptyBodyStorage};
36use reth_tracing::tracing::{debug, info};
37use reth_transaction_pool::{
38 Pool, StatefulValidationFn, StatelessValidationFn, TransactionOrigin,
39 TransactionValidationTaskExecutor, blobstore::InMemoryBlobStore,
40 error::InvalidPoolTransactionError,
41};
42use std::sync::Arc;
43use tempo_chainspec::spec::TempoChainSpec;
44use tempo_evm::{TempoEvmConfig, consensus::TempoConsensus};
45use tempo_payload_builder::{
46 DEFAULT_BUILD_TIME_MULTIPLIER, TempoPayloadBuilder, TempoPayloadBuilderConfig,
47};
48use tempo_payload_types::TempoPayloadAttributes;
49use tempo_primitives::{TempoHeader, TempoPrimitives, TempoTxEnvelope, TempoTxType};
50use tempo_transaction_pool::{
51 AA2dPool, AA2dPoolConfig, TempoTransactionPool,
52 amm::AmmLiquidityCache,
53 ordering::TempoTipOrdering,
54 transaction::TempoPooledTransaction,
55 validator::{
56 DEFAULT_AA_VALID_AFTER_MAX_SECS, DEFAULT_MAX_TEMPO_AUTHORIZATIONS,
57 TempoTransactionValidator,
58 },
59};
60
61#[derive(Debug, Clone, Copy, PartialEq, clap::Args)]
63pub struct TempoNodeArgs {
64 #[arg(long = "txpool.aa-valid-after-max-secs", default_value_t = DEFAULT_AA_VALID_AFTER_MAX_SECS)]
66 pub aa_valid_after_max_secs: u64,
67
68 #[arg(long = "txpool.max-tempo-authorizations", default_value_t = DEFAULT_MAX_TEMPO_AUTHORIZATIONS)]
70 pub max_tempo_authorizations: usize,
71
72 #[arg(long = "builder.state-provider-metrics", default_value_t = false)]
74 pub builder_state_provider_metrics: bool,
75
76 #[arg(long = "builder.disable-prewarming", default_value_t = false)]
78 pub builder_disable_prewarming: bool,
79
80 #[arg(long = "builder.enable-prewarming", default_value_t = true)]
82 pub builder_enable_prewarming: bool,
83
84 #[arg(
86 long = "engine.disable-execution-cache-sharing-with-builder",
87 default_value_t = false
88 )]
89 pub engine_disable_execution_cache_sharing_with_builder: bool,
90
91 #[arg(
97 long = "builder.build-time-multiplier",
98 default_value_t = DEFAULT_BUILD_TIME_MULTIPLIER
99 )]
100 pub builder_build_time_multiplier: f64,
101}
102
103impl Default for TempoNodeArgs {
104 fn default() -> Self {
105 Self {
106 aa_valid_after_max_secs: DEFAULT_AA_VALID_AFTER_MAX_SECS,
107 max_tempo_authorizations: DEFAULT_MAX_TEMPO_AUTHORIZATIONS,
108 builder_state_provider_metrics: false,
109 builder_disable_prewarming: false,
110 builder_enable_prewarming: true,
111 engine_disable_execution_cache_sharing_with_builder: false,
112 builder_build_time_multiplier: DEFAULT_BUILD_TIME_MULTIPLIER,
113 }
114 }
115}
116
117impl TempoNodeArgs {
118 pub fn pool_builder(&self) -> TempoPoolBuilder {
120 TempoPoolBuilder {
121 aa_valid_after_max_secs: self.aa_valid_after_max_secs,
122 max_tempo_authorizations: self.max_tempo_authorizations,
123 ..Default::default()
124 }
125 }
126
127 pub fn payload_builder_builder(&self) -> TempoPayloadBuilderBuilder {
129 TempoPayloadBuilderBuilder {
130 state_provider_metrics: self.builder_state_provider_metrics,
131 enable_prewarming: !self.builder_disable_prewarming,
132 build_time_multiplier: self.builder_build_time_multiplier,
133 }
134 }
135}
136
137#[derive(Debug, Default, Clone)]
139#[non_exhaustive]
140pub struct TempoNode {
141 pool_builder: TempoPoolBuilder,
143 payload_builder_builder: TempoPayloadBuilderBuilder,
145 validator_key: Option<B256>,
147}
148
149impl TempoNode {
150 pub fn new(args: &TempoNodeArgs, validator_key: Option<B256>) -> Self {
152 Self {
153 pool_builder: args.pool_builder(),
154 payload_builder_builder: args.payload_builder_builder(),
155 validator_key,
156 }
157 }
158
159 pub fn components<Node>(
161 pool_builder: TempoPoolBuilder,
162 payload_builder_builder: TempoPayloadBuilderBuilder,
163 ) -> ComponentsBuilder<
164 Node,
165 TempoPoolBuilder,
166 BasicPayloadServiceBuilder<TempoPayloadBuilderBuilder>,
167 EthereumNetworkBuilder,
168 TempoExecutorBuilder,
169 TempoConsensusBuilder,
170 >
171 where
172 Node: FullNodeTypes<Types = Self>,
173 {
174 ComponentsBuilder::default()
175 .node_types::<Node>()
176 .pool(pool_builder)
177 .executor(TempoExecutorBuilder::default())
178 .payload(
179 BasicPayloadServiceBuilder::new(payload_builder_builder)
180 .with_pre_cache_state(false),
182 )
183 .network(EthereumNetworkBuilder::default())
184 .consensus(TempoConsensusBuilder::default())
185 }
186
187 pub fn provider_factory_builder() -> ProviderFactoryBuilder<Self> {
188 ProviderFactoryBuilder::default()
189 }
190
191 pub fn with_pool_builder(mut self, pool_builder: TempoPoolBuilder) -> Self {
193 self.pool_builder = pool_builder;
194 self
195 }
196
197 pub fn map_pool_builder<F>(mut self, f: F) -> Self
199 where
200 F: FnOnce(TempoPoolBuilder) -> TempoPoolBuilder,
201 {
202 self.pool_builder = f(self.pool_builder);
203 self
204 }
205
206 pub fn with_payload_builder_builder(
208 mut self,
209 payload_builder_builder: TempoPayloadBuilderBuilder,
210 ) -> Self {
211 self.payload_builder_builder = payload_builder_builder;
212 self
213 }
214
215 pub fn map_payload_builder_builder<F>(mut self, f: F) -> Self
217 where
218 F: FnOnce(TempoPayloadBuilderBuilder) -> TempoPayloadBuilderBuilder,
219 {
220 self.payload_builder_builder = f(self.payload_builder_builder);
221 self
222 }
223
224 pub fn with_validator_key(mut self, validator_key: Option<B256>) -> Self {
226 self.validator_key = validator_key;
227 self
228 }
229}
230
231impl NodeTypes for TempoNode {
232 type Primitives = TempoPrimitives;
233 type ChainSpec = TempoChainSpec;
234 type Storage = EmptyBodyStorage<TempoTxEnvelope, TempoHeader>;
235 type Payload = TempoPayloadTypes;
236}
237
238#[derive(Debug)]
239pub struct TempoAddOns<N: FullNodeTypes<Types = TempoNode>> {
240 #[allow(clippy::type_complexity)]
241 inner: RpcAddOns<
242 NodeAdapter<N>,
243 TempoEthApiBuilder<NodeAdapter<N>>,
244 TempoEngineValidatorBuilder,
245 NoopEngineApiBuilder,
246 BasicEngineValidatorBuilder<TempoEngineValidatorBuilder>,
247 Identity,
248 >,
249 validator_key: Option<B256>,
250}
251
252impl<N> TempoAddOns<N>
253where
254 N: FullNodeTypes<Types = TempoNode>,
255{
256 pub fn new(validator_key: Option<B256>) -> Self {
258 Self {
259 inner: RpcAddOns::new(
260 TempoEthApiBuilder::new(validator_key),
261 TempoEngineValidatorBuilder,
262 NoopEngineApiBuilder::default(),
263 BasicEngineValidatorBuilder::default(),
264 Identity::default(),
265 Default::default(),
266 ),
267 validator_key,
268 }
269 }
270}
271
272impl<N> NodeAddOns<NodeAdapter<N>> for TempoAddOns<N>
273where
274 N: FullNodeTypes<Types = TempoNode>,
275{
276 type Handle = RpcHandle<NodeAdapter<N>, TempoEthApi<NodeAdapter<N>>>;
277
278 async fn launch_add_ons(
279 self,
280 ctx: AddOnsContext<'_, NodeAdapter<N>>,
281 ) -> eyre::Result<Self::Handle> {
282 let eth_config = EthConfigHandler::new(
283 ctx.node.provider.clone(),
284 ctx.node.components.evm_config.clone(),
285 );
286
287 self.inner
288 .launch_add_ons_with(ctx, move |container| {
289 let reth_node_builder::rpc::RpcModuleContainer {
290 modules, registry, ..
291 } = container;
292
293 let eth_api = registry.eth_api().clone();
294 let token = TempoToken::new(eth_api.clone());
295 let eth_ext = TempoEthExt::new(eth_api.clone());
296 let simulate = TempoSimulate::new(eth_api);
297 let admin = TempoAdminApi::new(self.validator_key);
298 let operator = TempoOperatorRpc::new(registry.admin_api());
299 let fork_schedule =
300 TempoForkScheduleRpc::new(registry.eth_api().provider().clone());
301
302 modules.merge_configured(token.into_rpc())?;
303 modules.merge_configured(eth_ext.into_rpc())?;
304 modules.merge_if_module_configured(RethRpcModule::Eth, simulate.into_rpc())?;
305 modules.merge_configured(fork_schedule.into_rpc())?;
306 modules.merge_if_module_configured(
307 RethRpcModule::Other("operator".to_string()),
308 operator.into_rpc(),
309 )?;
310 modules.merge_if_module_configured(RethRpcModule::Admin, admin.into_rpc())?;
311 modules.merge_if_module_configured(RethRpcModule::Eth, eth_config.into_rpc())?;
312
313 Ok(())
314 })
315 .await
316 }
317}
318
319impl<N> RethRpcAddOns<NodeAdapter<N>> for TempoAddOns<N>
320where
321 N: FullNodeTypes<Types = TempoNode>,
322{
323 type EthApi = TempoEthApi<NodeAdapter<N>>;
324
325 fn hooks_mut(&mut self) -> &mut RpcHooks<NodeAdapter<N>, Self::EthApi> {
326 self.inner.hooks_mut()
327 }
328}
329
330impl<N> EngineValidatorAddOn<NodeAdapter<N>> for TempoAddOns<N>
331where
332 N: FullNodeTypes<Types = TempoNode>,
333{
334 type ValidatorBuilder = BasicEngineValidatorBuilder<TempoEngineValidatorBuilder>;
335
336 fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
337 self.inner.engine_validator_builder()
338 }
339}
340
341impl<N> Node<N> for TempoNode
342where
343 N: FullNodeTypes<Types = Self>,
344{
345 type ComponentsBuilder = ComponentsBuilder<
346 N,
347 TempoPoolBuilder,
348 BasicPayloadServiceBuilder<TempoPayloadBuilderBuilder>,
349 EthereumNetworkBuilder,
350 TempoExecutorBuilder,
351 TempoConsensusBuilder,
352 >;
353
354 type AddOns = TempoAddOns<N>;
355
356 fn components_builder(&self) -> Self::ComponentsBuilder {
357 Self::components(self.pool_builder.clone(), self.payload_builder_builder)
358 }
359
360 fn add_ons(&self) -> Self::AddOns {
361 TempoAddOns::new(self.validator_key)
362 }
363}
364
365impl<N: FullNodeComponents<Types = Self>> DebugNode<N> for TempoNode {
366 type RpcBlock =
367 alloy_rpc_types_eth::Block<alloy_rpc_types_eth::Transaction<TempoTxEnvelope>, TempoHeader>;
368
369 fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> tempo_primitives::Block {
370 rpc_block
371 .into_consensus_block()
372 .map_transactions(|tx| tx.into_inner())
373 }
374
375 fn local_payload_attributes_builder(
376 _chain_spec: &Self::ChainSpec,
377 ) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes, TempoHeader>
378 {
379 TempoPayloadAttributesBuilder::new()
380 }
381}
382
383#[derive(Debug, Default)]
385#[non_exhaustive]
386pub struct TempoPayloadAttributesBuilder;
387
388impl TempoPayloadAttributesBuilder {
389 pub const fn new() -> Self {
391 Self
392 }
393}
394
395impl PayloadAttributesBuilder<TempoPayloadAttributes, TempoHeader>
396 for TempoPayloadAttributesBuilder
397{
398 fn build(&self, _parent: &SealedHeader<TempoHeader>) -> TempoPayloadAttributes {
399 let millis = std::time::SystemTime::now()
400 .duration_since(std::time::UNIX_EPOCH)
401 .unwrap()
402 .as_millis() as u64;
403
404 let (timestamp, timestamp_millis_part) = (millis / 1000, millis % 1000);
405 TempoPayloadAttributes::new(
406 None,
407 timestamp,
408 timestamp_millis_part,
409 Default::default(),
410 None,
411 Vec::new,
412 )
413 }
414}
415
416#[derive(Debug, Default, Clone, Copy)]
418#[non_exhaustive]
419pub struct TempoExecutorBuilder;
420
421impl<Node> ExecutorBuilder<Node> for TempoExecutorBuilder
422where
423 Node: FullNodeTypes<Types = TempoNode>,
424{
425 type EVM = TempoEvmConfig;
426
427 async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
428 let evm_config = TempoEvmConfig::new(ctx.chain_spec());
429 Ok(evm_config)
430 }
431}
432
433#[derive(Debug, Clone, Copy)]
435#[non_exhaustive]
436pub struct TempoConsensusBuilder {
437 pub allow_bal_hashes: bool,
439}
440
441#[allow(clippy::derivable_impls)]
442impl Default for TempoConsensusBuilder {
443 fn default() -> Self {
444 Self {
445 allow_bal_hashes: cfg!(feature = "bal"),
446 }
447 }
448}
449
450impl<Node> ConsensusBuilder<Node> for TempoConsensusBuilder
451where
452 Node: FullNodeTypes<Types = TempoNode>,
453{
454 type Consensus = TempoConsensus;
455
456 async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
457 Ok(TempoConsensus::new_with_bal_hashes(
458 ctx.chain_spec(),
459 self.allow_bal_hashes,
460 ))
461 }
462}
463
464#[derive(Debug, Default, Clone)]
466#[non_exhaustive]
467pub struct TempoEngineValidatorBuilder;
468
469impl<Node> PayloadValidatorBuilder<Node> for TempoEngineValidatorBuilder
470where
471 Node: FullNodeComponents<Types = TempoNode>,
472{
473 type Validator = TempoEngineValidator;
474
475 async fn build(self, _ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
476 Ok(TempoEngineValidator::new())
477 }
478}
479
480#[derive(Clone)]
485#[non_exhaustive]
486pub struct TempoPoolBuilder {
487 pub aa_valid_after_max_secs: u64,
489 pub max_tempo_authorizations: usize,
491 pub additional_stateless_validation: Option<StatelessValidationFn<TempoPooledTransaction>>,
493 pub additional_stateful_validation: Option<StatefulValidationFn<TempoPooledTransaction>>,
495}
496
497impl TempoPoolBuilder {
498 pub const fn with_aa_tx_valid_after_max_secs(mut self, secs: u64) -> Self {
500 self.aa_valid_after_max_secs = secs;
501 self
502 }
503
504 pub const fn with_max_tempo_authorizations(mut self, max: usize) -> Self {
506 self.max_tempo_authorizations = max;
507 self
508 }
509
510 pub fn with_additional_stateless_validation<F>(mut self, f: F) -> Self
522 where
523 F: Fn(
524 TransactionOrigin,
525 &TempoPooledTransaction,
526 ) -> Result<(), InvalidPoolTransactionError>
527 + Send
528 + Sync
529 + 'static,
530 {
531 self.additional_stateless_validation = Some(Arc::new(f));
532 self
533 }
534
535 pub fn with_additional_stateless_validation_fn_opt(
540 mut self,
541 f: Option<StatelessValidationFn<TempoPooledTransaction>>,
542 ) -> Self {
543 self.additional_stateless_validation = f;
544 self
545 }
546
547 pub fn with_additional_stateful_validation<F>(mut self, f: F) -> Self
552 where
553 F: Fn(
554 TransactionOrigin,
555 &TempoPooledTransaction,
556 &dyn AccountInfoReader,
557 ) -> Result<(), InvalidPoolTransactionError>
558 + Send
559 + Sync
560 + 'static,
561 {
562 self.additional_stateful_validation = Some(Arc::new(f));
563 self
564 }
565
566 pub fn with_additional_stateful_validation_fn_opt(
571 mut self,
572 f: Option<StatefulValidationFn<TempoPooledTransaction>>,
573 ) -> Self {
574 self.additional_stateful_validation = f;
575 self
576 }
577}
578
579impl core::fmt::Debug for TempoPoolBuilder {
580 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
581 f.debug_struct("TempoPoolBuilder")
582 .field("aa_valid_after_max_secs", &self.aa_valid_after_max_secs)
583 .field("max_tempo_authorizations", &self.max_tempo_authorizations)
584 .field(
585 "additional_stateless_validation",
586 &self.additional_stateless_validation.as_ref().map(|_| "..."),
587 )
588 .field(
589 "additional_stateful_validation",
590 &self.additional_stateful_validation.as_ref().map(|_| "..."),
591 )
592 .finish()
593 }
594}
595
596impl Default for TempoPoolBuilder {
597 fn default() -> Self {
598 Self {
599 aa_valid_after_max_secs: DEFAULT_AA_VALID_AFTER_MAX_SECS,
600 max_tempo_authorizations: DEFAULT_MAX_TEMPO_AUTHORIZATIONS,
601 additional_stateless_validation: None,
602 additional_stateful_validation: None,
603 }
604 }
605}
606
607impl<Node> PoolBuilder<Node, TempoEvmConfig> for TempoPoolBuilder
608where
609 Node: FullNodeTypes<Types = TempoNode>,
610{
611 type Pool = TempoTransactionPool<Node::Provider>;
612
613 async fn build_pool(
614 self,
615 ctx: &BuilderContext<Node>,
616 evm_config: TempoEvmConfig,
617 ) -> eyre::Result<Self::Pool> {
618 let mut pool_config = ctx.pool_config();
619 pool_config.max_inflight_delegated_slot_limit = pool_config.max_account_slots;
620
621 let blob_store = InMemoryBlobStore::default();
623 let validator =
624 TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone(), evm_config)
625 .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes)
626 .with_local_transactions_config(pool_config.local_transactions_config.clone())
627 .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap)
628 .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit)
629 .set_block_gas_limit(ctx.chain_spec().inner.genesis().gas_limit)
630 .disable_balance_check()
631 .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee)
632 .with_additional_tasks(ctx.config().txpool.additional_validation_tasks)
633 .with_custom_tx_type(TempoTxType::AA as u8)
634 .no_eip4844()
635 .build_with_tasks(ctx.task_executor().clone(), blob_store.clone());
636
637 let aa_2d_config = AA2dPoolConfig {
638 price_bump_config: pool_config.price_bumps,
639 pending_limit: pool_config.pending_limit,
640 queued_limit: pool_config.queued_limit,
641 max_txs_per_sender: pool_config.max_account_slots,
642 };
643 let aa_2d_pool = AA2dPool::new(aa_2d_config);
644 let amm_liquidity_cache = AmmLiquidityCache::new(ctx.provider())?;
645
646 let Self {
647 aa_valid_after_max_secs,
648 max_tempo_authorizations,
649 additional_stateless_validation,
650 additional_stateful_validation,
651 } = self;
652 let validator = validator.map(move |mut v| {
653 v.set_additional_stateless_validation_fn_opt(additional_stateless_validation.clone());
654 v.set_additional_stateful_validation_fn_opt(additional_stateful_validation.clone());
655 TempoTransactionValidator::new(
656 v,
657 aa_valid_after_max_secs,
658 max_tempo_authorizations,
659 amm_liquidity_cache.clone(),
660 )
661 });
662 let protocol_pool = Pool::new(
663 validator,
664 TempoTipOrdering::default(),
665 blob_store,
666 pool_config.clone(),
667 );
668
669 let transaction_pool = TempoTransactionPool::new(protocol_pool, aa_2d_pool);
671
672 spawn_maintenance_tasks(ctx, transaction_pool.clone(), &pool_config)?;
673
674 ctx.task_executor().spawn_critical_os_thread(
677 "tempo-txpool-maintenance",
678 "txpool maintenance - tempo pool",
679 tempo_transaction_pool::maintain::maintain_tempo_pool(transaction_pool.clone()),
680 );
681
682 info!(target: "reth::cli", "Transaction pool initialized");
683 debug!(target: "reth::cli", "Spawned txpool maintenance task");
684
685 Ok(transaction_pool)
686 }
687}
688
689#[derive(Debug, Clone, Copy)]
690#[non_exhaustive]
691pub struct TempoPayloadBuilderBuilder {
692 pub state_provider_metrics: bool,
694 pub enable_prewarming: bool,
696 pub build_time_multiplier: f64,
699}
700
701impl Default for TempoPayloadBuilderBuilder {
702 fn default() -> Self {
703 Self {
704 state_provider_metrics: false,
705 enable_prewarming: true,
706 build_time_multiplier: DEFAULT_BUILD_TIME_MULTIPLIER,
707 }
708 }
709}
710
711impl<Node> PayloadBuilderBuilder<Node, TempoTransactionPool<Node::Provider>, TempoEvmConfig>
712 for TempoPayloadBuilderBuilder
713where
714 Node: FullNodeTypes<Types = TempoNode>,
715{
716 type PayloadBuilder = TempoPayloadBuilder<Node::Provider>;
717
718 async fn build_payload_builder(
719 self,
720 ctx: &BuilderContext<Node>,
721 pool: TempoTransactionPool<Node::Provider>,
722 evm_config: TempoEvmConfig,
723 ) -> eyre::Result<Self::PayloadBuilder> {
724 Ok(TempoPayloadBuilder::new(
725 pool,
726 ctx.provider().clone(),
727 ctx.task_executor().clone(),
728 evm_config,
729 TempoPayloadBuilderConfig {
730 is_dev: ctx.is_dev(),
731 state_provider_metrics: self.state_provider_metrics,
732 enable_prewarming: self.enable_prewarming,
733 build_time_multiplier: self.build_time_multiplier,
734 },
735 ))
736 }
737}
738
739#[cfg(test)]
740mod tests {
741 use super::{TempoNode, TempoNodeArgs, TempoPayloadBuilderBuilder, TempoPoolBuilder};
742
743 #[test]
744 fn tempo_node_maps_pool_builder() {
745 let node = TempoNode::new(
746 &TempoNodeArgs {
747 aa_valid_after_max_secs: 12,
748 ..Default::default()
749 },
750 None,
751 )
752 .map_pool_builder(|pool| pool.with_max_tempo_authorizations(7));
753
754 assert_eq!(node.pool_builder.aa_valid_after_max_secs, 12);
755 assert_eq!(node.pool_builder.max_tempo_authorizations, 7);
756 }
757
758 #[test]
759 fn tempo_node_sets_pool_builder() {
760 let node = TempoNode::default().with_pool_builder(TempoPoolBuilder {
761 aa_valid_after_max_secs: 42,
762 ..Default::default()
763 });
764
765 assert_eq!(node.pool_builder.aa_valid_after_max_secs, 42);
766 }
767
768 #[test]
769 fn tempo_node_maps_payload_builder_builder() {
770 let node = TempoNode::new(&TempoNodeArgs::default(), None).map_payload_builder_builder(
771 |mut payload| {
772 payload.state_provider_metrics = true;
773 payload
774 },
775 );
776
777 assert!(node.payload_builder_builder.state_provider_metrics);
778 assert_eq!(
779 node.payload_builder_builder.build_time_multiplier,
780 TempoNodeArgs::default().builder_build_time_multiplier
781 );
782 }
783
784 #[test]
785 fn tempo_node_sets_payload_builder_builder() {
786 let node = TempoNode::default().with_payload_builder_builder(TempoPayloadBuilderBuilder {
787 state_provider_metrics: true,
788 ..Default::default()
789 });
790
791 assert!(node.payload_builder_builder.state_provider_metrics);
792 }
793}