domain_test_service/
domain.rs

1//! Utilities used for testing with the domain.
2#![warn(missing_docs)]
3
4use crate::chain_spec::create_domain_spec;
5use crate::{
6    AUTO_ID_DOMAIN_ID, BalanceOf, DomainRuntime, EVM_DOMAIN_ID, EcdsaKeyring, Sr25519Keyring,
7    UncheckedExtrinsicFor, construct_extrinsic_generic, node_config,
8};
9use cross_domain_message_gossip::ChainMsg;
10use domain_block_preprocessor::inherents::CreateInherentDataProvider;
11use domain_client_operator::snap_sync::ConsensusChainSyncParams;
12use domain_client_operator::{BootstrapResult, OperatorStreams, fetch_domain_bootstrap_info};
13use domain_eth_service::provider::EthProvider;
14use domain_eth_service::{DefaultEthConfig, EthConfiguration};
15use domain_runtime_primitives::opaque::Block;
16use domain_runtime_primitives::{Balance, EthereumAccountId};
17use domain_service::providers::DefaultProvider;
18use domain_service::{FullBackend, FullClient, FullPool};
19use domain_test_primitives::{EvmOnchainStateApi, OnchainStateApi};
20use frame_support::dispatch::{DispatchInfo, PostDispatchInfo};
21use frame_system::pallet_prelude::{BlockNumberFor, RuntimeCallFor};
22use pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi;
23use sc_client_api::HeaderBackend;
24use sc_domains::RuntimeExecutor;
25use sc_network::service::traits::NetworkService;
26use sc_network::{NetworkStateInfo, ReputationChange};
27use sc_network_sync::SyncingService;
28use sc_service::config::MultiaddrWithPeerId;
29use sc_service::{BasePath, Role, RpcHandlers, TFullBackend, TaskManager, TransactionPool};
30use sc_transaction_pool::FullChainApi;
31use sc_transaction_pool_api::OffchainTransactionPoolFactory;
32use sc_utils::mpsc::{TracingUnboundedSender, tracing_unbounded};
33use sp_api::{ApiExt, ConstructRuntimeApi, Metadata, ProvideRuntimeApi};
34use sp_block_builder::BlockBuilder;
35use sp_blockchain::HashAndNumber;
36use sp_consensus_subspace::SubspaceApi;
37use sp_core::{Encode, H256};
38use sp_domains::core_api::DomainCoreApi;
39use sp_domains::{DomainId, DomainsApi, OperatorId, PermissionedActionAllowedBy};
40use sp_messenger::messages::{ChainId, ChannelId};
41use sp_messenger::{MessengerApi, RelayerApi};
42use sp_offchain::OffchainWorkerApi;
43use sp_runtime::OpaqueExtrinsic;
44use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, NumberFor};
45use sp_session::SessionKeys;
46use sp_transaction_pool::runtime_api::TaggedTransactionQueue;
47use std::future::Future;
48use std::net::SocketAddr;
49use std::sync::Arc;
50use std::time::Duration;
51use subspace_runtime_primitives::opaque::Block as CBlock;
52use subspace_runtime_primitives::{BlockHashFor, HeaderFor, Nonce};
53use subspace_test_service::MockConsensusNode;
54use substrate_frame_rpc_system::AccountNonceApi;
55use substrate_test_client::{
56    BlockchainEventsExt, RpcHandlersExt, RpcTransactionError, RpcTransactionOutput,
57};
58use tokio::time::sleep;
59
60/// The backend type used by the test service.
61pub type Backend = TFullBackend<Block>;
62
63type Client<RuntimeApi> = FullClient<Block, RuntimeApi>;
64
65/// Domain executor for the test service.
66pub type DomainOperator<RuntimeApi> =
67    domain_service::DomainOperator<Block, CBlock, subspace_test_client::Client, RuntimeApi>;
68
69/// A generic domain node instance used for testing.
70pub struct DomainNode<Runtime, RuntimeApi>
71where
72    Runtime: DomainRuntime,
73    RuntimeApi: ConstructRuntimeApi<Block, Client<RuntimeApi>> + Send + Sync + 'static,
74    RuntimeApi::RuntimeApi: ApiExt<Block>
75        + Metadata<Block>
76        + BlockBuilder<Block>
77        + OffchainWorkerApi<Block>
78        + SessionKeys<Block>
79        + DomainCoreApi<Block>
80        + MessengerApi<Block, NumberFor<CBlock>, BlockHashFor<CBlock>>
81        + TaggedTransactionQueue<Block>
82        + AccountNonceApi<Block, Runtime::AccountId, Nonce>
83        + TransactionPaymentRuntimeApi<Block, Balance>
84        + RelayerApi<Block, NumberFor<Block>, NumberFor<CBlock>, BlockHashFor<CBlock>>,
85{
86    /// The domain id
87    pub domain_id: DomainId,
88    /// The node's account key
89    pub key: Runtime::Keyring,
90    /// TaskManager's instance.
91    pub task_manager: TaskManager,
92    /// Client's instance.
93    pub client: Arc<Client<RuntimeApi>>,
94    /// Client backend.
95    pub backend: Arc<Backend>,
96    /// Code executor.
97    pub code_executor: Arc<RuntimeExecutor>,
98    /// Network service.
99    pub network_service: Arc<dyn NetworkService>,
100    /// Sync service.
101    pub sync_service: Arc<SyncingService<Block>>,
102    /// The `MultiaddrWithPeerId` to this node. This is useful if you want to pass it as "boot node"
103    /// to other nodes.
104    pub addr: MultiaddrWithPeerId,
105    /// RPCHandlers to make RPC queries.
106    pub rpc_handlers: RpcHandlers,
107    /// Domain oeprator.
108    pub operator: DomainOperator<RuntimeApi>,
109    /// Sink to the node's tx pool
110    pub tx_pool_sink: TracingUnboundedSender<ChainMsg>,
111    /// The node base path
112    pub base_path: BasePath,
113}
114
115impl<Runtime, RuntimeApi> DomainNode<Runtime, RuntimeApi>
116where
117    Runtime: frame_system::Config<Hash = H256>
118        + pallet_transaction_payment::Config
119        + DomainRuntime
120        + Send
121        + Sync,
122    Runtime::RuntimeCall:
123        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + Send + Sync,
124    crate::BalanceOf<Runtime>: Send + Sync + From<u64> + sp_runtime::FixedPointOperand,
125    u64: From<BlockNumberFor<Runtime>>,
126    RuntimeApi: ConstructRuntimeApi<Block, Client<RuntimeApi>> + Send + Sync + 'static,
127    RuntimeApi::RuntimeApi: ApiExt<Block>
128        + Metadata<Block>
129        + BlockBuilder<Block>
130        + OffchainWorkerApi<Block>
131        + SessionKeys<Block>
132        + DomainCoreApi<Block>
133        + TaggedTransactionQueue<Block>
134        + AccountNonceApi<Block, <Runtime as DomainRuntime>::AccountId, Nonce>
135        + TransactionPaymentRuntimeApi<Block, Balance>
136        + MessengerApi<Block, NumberFor<CBlock>, BlockHashFor<CBlock>>
137        + RelayerApi<Block, NumberFor<Block>, NumberFor<CBlock>, BlockHashFor<CBlock>>
138        + OnchainStateApi<Block, <Runtime as DomainRuntime>::AccountId, Balance>,
139    <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
140        AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + Clone,
141{
142    #[allow(clippy::too_many_arguments)]
143    async fn build<Provider>(
144        domain_id: DomainId,
145        tokio_handle: tokio::runtime::Handle,
146        key: Runtime::Keyring,
147        base_path: BasePath,
148        domain_nodes: Vec<MultiaddrWithPeerId>,
149        domain_nodes_exclusive: bool,
150        skip_empty_bundle_production: bool,
151        maybe_operator_id: Option<OperatorId>,
152        role: Role,
153        mock_consensus_node: &mut MockConsensusNode,
154        rpc_addr: Option<SocketAddr>,
155        rpc_port: Option<u16>,
156        provider: Provider,
157    ) -> Self
158    where
159        Provider: domain_service::providers::BlockImportProvider<Block, Client<RuntimeApi>>
160            + domain_service::providers::RpcProvider<
161                Block,
162                Client<RuntimeApi>,
163                FullPool<RuntimeApi>,
164                FullChainApi<Client<RuntimeApi>, Block>,
165                Backend,
166                <Runtime as DomainRuntime>::AccountId,
167                CreateInherentDataProvider<subspace_test_client::Client, CBlock>,
168            > + 'static,
169    {
170        let mut domain_config = node_config(
171            domain_id,
172            tokio_handle.clone(),
173            Runtime::to_seed(key),
174            domain_nodes,
175            domain_nodes_exclusive,
176            role,
177            base_path.clone(),
178            Box::new(create_domain_spec()) as Box<_>,
179            rpc_addr,
180            rpc_port,
181        )
182        .expect("could not generate domain node Configuration");
183
184        let domain_backend = sc_service::new_db_backend::<Block>(domain_config.db_config())
185            .unwrap_or_else(
186                |err| {
187                    tracing::error!("Failed to create domain backend: {domain_id:?} {role:?} {base_path:?} error: {err:?}");
188
189                    // Find out which directory got deleted too soon
190                    for dir_path in base_path.path().ancestors() {
191                        tracing::error!("{dir_path:?} try_exists: {:?}", dir_path.try_exists());
192                    }
193                    panic!("Failed to create domain backend: {domain_id:?} {role:?} {base_path:?} error: {err:?}")
194                }
195            );
196
197        let BootstrapResult {
198            domain_instance_data,
199            domain_created_at,
200            imported_block_notification_stream,
201            ..
202        } = fetch_domain_bootstrap_info::<Block, _, _, _>(
203            &*mock_consensus_node.client,
204            &*domain_backend,
205            domain_id,
206        )
207        .await
208        .expect("Failed to get domain instance data");
209
210        domain_config
211            .chain_spec
212            .set_storage(domain_instance_data.raw_genesis.into_storage());
213
214        let span = sc_tracing::tracing::info_span!(
215            sc_tracing::logging::PREFIX_LOG_SPAN,
216            name = domain_config.network.node_name.as_str()
217        );
218        let _enter = span.enter();
219
220        let multiaddr = domain_config.network.listen_addresses[0].clone();
221
222        let operator_streams = OperatorStreams {
223            // Set `consensus_block_import_throttling_buffer_size` to 0 to ensure the primary chain will not be
224            // ahead of the execution chain by more than one block, thus slot will not be skipped in test.
225            consensus_block_import_throttling_buffer_size: 0,
226            block_importing_notification_stream: mock_consensus_node
227                .block_importing_notification_stream(),
228            imported_block_notification_stream,
229            new_slot_notification_stream: mock_consensus_node.new_slot_notification_stream(),
230            acknowledgement_sender_stream: mock_consensus_node.new_acknowledgement_sender_stream(),
231            _phantom: Default::default(),
232        };
233
234        let (domain_message_sink, domain_message_receiver) =
235            tracing_unbounded("domain_message_channel", 100);
236        let gossip_msg_sink = mock_consensus_node
237            .xdm_gossip_worker_builder()
238            .gossip_msg_sink();
239
240        let maybe_operator_id = role
241            .is_authority()
242            .then_some(maybe_operator_id.unwrap_or(if domain_id == EVM_DOMAIN_ID { 0 } else { 1 }));
243
244        let consensus_best_hash = mock_consensus_node.client.info().best_hash;
245        let runtime_api = mock_consensus_node.client.runtime_api();
246        let chain_constants = runtime_api.chain_constants(consensus_best_hash).unwrap();
247
248        let domain_block_pruning_depth = runtime_api
249            .block_pruning_depth(consensus_best_hash)
250            .unwrap();
251
252        let domain_params = domain_service::DomainParams {
253            domain_id,
254            domain_config,
255            domain_created_at,
256            consensus_client: mock_consensus_node.client.clone(),
257            consensus_offchain_tx_pool_factory: OffchainTransactionPoolFactory::new(
258                mock_consensus_node.transaction_pool.clone(),
259            ),
260            domain_sync_oracle: mock_consensus_node.sync_service.clone(),
261            consensus_network: mock_consensus_node.network_service.clone(),
262            operator_streams,
263            gossip_message_sink: gossip_msg_sink,
264            domain_message_receiver,
265            provider,
266            skip_empty_bundle_production,
267            skip_out_of_order_slot: true,
268            maybe_operator_id,
269            confirmation_depth_k: chain_constants.confirmation_depth_k(),
270            challenge_period: domain_block_pruning_depth,
271            consensus_chain_sync_params: None::<ConsensusChainSyncParams<_, HeaderFor<Block>>>,
272            domain_backend,
273        };
274
275        let domain_node = domain_service::new_full::<
276            _,
277            _,
278            _,
279            _,
280            _,
281            _,
282            RuntimeApi,
283            <Runtime as DomainRuntime>::AccountId,
284            _,
285        >(domain_params)
286        .await
287        .expect("failed to build domain node");
288
289        let domain_service::NewFull {
290            task_manager,
291            client,
292            backend,
293            code_executor,
294            network_service,
295            sync_service,
296            network_starter,
297            rpc_handlers,
298            operator,
299            ..
300        } = domain_node;
301
302        if role.is_authority() {
303            mock_consensus_node
304                .xdm_gossip_worker_builder()
305                .push_chain_sink(ChainId::Domain(domain_id), domain_message_sink.clone());
306        }
307
308        let addr = MultiaddrWithPeerId {
309            multiaddr,
310            peer_id: network_service.local_peer_id(),
311        };
312
313        network_starter.start_network();
314
315        DomainNode {
316            domain_id,
317            key,
318            task_manager,
319            client,
320            backend,
321            code_executor,
322            network_service,
323            sync_service,
324            addr,
325            rpc_handlers,
326            operator,
327            tx_pool_sink: domain_message_sink,
328            base_path,
329        }
330    }
331
332    /// Wait for `count` blocks to be imported in the node and then exit. This function will not
333    /// return if no blocks are ever created, thus you should restrict the maximum amount of time of
334    /// the test execution.
335    pub fn wait_for_blocks(&self, count: usize) -> impl Future<Output = ()> {
336        self.client.wait_for_blocks(count)
337    }
338
339    /// Get the nonce of the node account
340    pub fn account_nonce(&self) -> u32 {
341        self.client
342            .runtime_api()
343            .account_nonce(
344                self.client.info().best_hash,
345                <Runtime as DomainRuntime>::account_id(self.key),
346            )
347            .expect("Fail to get account nonce")
348    }
349
350    /// Get the nonce of the given account
351    pub fn account_nonce_of(&self, account_id: <Runtime as DomainRuntime>::AccountId) -> u32 {
352        self.client
353            .runtime_api()
354            .account_nonce(self.client.info().best_hash, account_id)
355            .expect("Fail to get account nonce")
356    }
357
358    /// Sends a signed system.remark extrinsic to the pool containing the current account nonce.
359    pub async fn send_system_remark(&self) {
360        let nonce = self.account_nonce();
361        let _ = self
362            .construct_and_send_extrinsic(frame_system::Call::remark {
363                remark: nonce.encode(),
364            })
365            .await
366            .map(|_| ());
367    }
368
369    /// Construct a signed extrinsic with the current nonce of the node account and send it to this node.
370    pub async fn construct_and_send_extrinsic(
371        &self,
372        function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
373    ) -> Result<RpcTransactionOutput, RpcTransactionError> {
374        self.construct_and_send_extrinsic_with(self.account_nonce(), 0.into(), function)
375            .await
376    }
377
378    /// Construct a signed extrinsic with the given nonce and tip for the node account and send it to this node.
379    pub async fn construct_and_send_extrinsic_with(
380        &self,
381        nonce: u32,
382        tip: BalanceOf<Runtime>,
383        function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
384    ) -> Result<RpcTransactionOutput, RpcTransactionError> {
385        let extrinsic = construct_extrinsic_generic::<Runtime, _>(
386            &self.client,
387            function,
388            self.key,
389            false,
390            nonce,
391            tip,
392        );
393        self.rpc_handlers.send_transaction(extrinsic.into()).await
394    }
395
396    /// Construct a signed extrinsic.
397    pub fn construct_extrinsic(
398        &self,
399        nonce: u32,
400        function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
401    ) -> UncheckedExtrinsicFor<Runtime> {
402        construct_extrinsic_generic::<Runtime, _>(
403            &self.client,
404            function,
405            self.key,
406            false,
407            nonce,
408            0.into(),
409        )
410    }
411
412    /// Construct a signed extrinsic with the given transaction tip.
413    pub fn construct_extrinsic_with_tip(
414        &self,
415        nonce: u32,
416        tip: BalanceOf<Runtime>,
417        function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
418    ) -> UncheckedExtrinsicFor<Runtime> {
419        construct_extrinsic_generic::<Runtime, _>(
420            &self.client,
421            function,
422            self.key,
423            false,
424            nonce,
425            tip,
426        )
427    }
428
429    /// Send an extrinsic to this node.
430    pub async fn send_extrinsic(
431        &self,
432        extrinsic: impl Into<OpaqueExtrinsic>,
433    ) -> Result<RpcTransactionOutput, RpcTransactionError> {
434        self.rpc_handlers.send_transaction(extrinsic.into()).await
435    }
436
437    /// Get the free balance of the given account
438    pub fn free_balance(&self, account_id: <Runtime as DomainRuntime>::AccountId) -> Balance {
439        self.client
440            .runtime_api()
441            .free_balance(self.client.info().best_hash, account_id)
442            .expect("Fail to get account free balance")
443    }
444
445    /// Returns the open XDM channel for given chain
446    pub fn get_open_channel_for_chain(&self, chain_id: ChainId) -> Option<ChannelId> {
447        self.client
448            .runtime_api()
449            .get_open_channel_for_chain(self.client.info().best_hash, chain_id)
450            .expect("Fail to get open channel for Chain")
451    }
452
453    /// Construct an unsigned extrinsic that can be applied to the test runtime.
454    pub fn construct_unsigned_extrinsic(
455        &self,
456        function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
457    ) -> UncheckedExtrinsicFor<Runtime>
458    where
459        Runtime:
460            frame_system::Config<Hash = H256> + pallet_transaction_payment::Config + Send + Sync,
461        RuntimeCallFor<Runtime>:
462            Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + Send + Sync,
463        BalanceOf<Runtime>: Send + Sync + From<u64> + sp_runtime::FixedPointOperand,
464    {
465        let function = function.into();
466        UncheckedExtrinsicFor::<Runtime>::new_bare(function)
467    }
468
469    /// Construct an unsigned extrinsic and send it to this node.
470    pub async fn construct_and_send_unsigned_extrinsic(
471        &self,
472        function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
473    ) -> Result<RpcTransactionOutput, RpcTransactionError> {
474        let extrinsic = self.construct_unsigned_extrinsic(function);
475        self.rpc_handlers.send_transaction(extrinsic.into()).await
476    }
477
478    /// Give the peer at `addr` the minimum reputation, which will ban it.
479    // TODO: also ban/unban in the DSN
480    pub fn ban_peer(&self, addr: MultiaddrWithPeerId) {
481        // If unban_peer() has been called on the peer, we need to bump it twice
482        // to give it the minimal reputation.
483        self.network_service.report_peer(
484            addr.peer_id,
485            ReputationChange::new_fatal("Peer banned by test (1)"),
486        );
487        self.network_service.report_peer(
488            addr.peer_id,
489            ReputationChange::new_fatal("Peer banned by test (2)"),
490        );
491    }
492
493    /// Give the peer at `addr` a high reputation, which guarantees it is un-banned it.
494    pub fn unban_peer(&self, addr: MultiaddrWithPeerId) {
495        // If ReputationChange::new_fatal() has been called on the peer, we need to bump it twice
496        // to give it a positive reputation.
497        self.network_service.report_peer(
498            addr.peer_id,
499            ReputationChange::new(i32::MAX, "Peer unbanned by test (1)"),
500        );
501        self.network_service.report_peer(
502            addr.peer_id,
503            ReputationChange::new(i32::MAX, "Peer unbanned by test (2)"),
504        );
505    }
506
507    /// Take and stop the domain node and delete its database lock file.
508    ///
509    /// Stopping and restarting a node can cause weird race conditions, with errors like:
510    /// "The system cannot find the path specified".
511    /// If this happens, try increasing the wait time in this method.
512    pub async fn stop(self) -> Result<(), std::io::Error> {
513        let lock_file_path = self.base_path.path().join("paritydb").join("lock");
514        // On Windows, sometimes open files can’t be deleted so `drop` first then delete
515        std::mem::drop(self);
516
517        // Give the node time to cleanup, exit, and release the lock file.
518        // TODO: fix the underlying issue or wait for the actual shutdown instead
519        sleep(Duration::from_secs(2)).await;
520
521        // The lock file already being deleted is not a fatal test error, so just log it
522        if let Err(err) = std::fs::remove_file(lock_file_path) {
523            tracing::error!("deleting paritydb lock file failed: {err:?}");
524        }
525        Ok(())
526    }
527
528    /// Remove all tx from the tx pool
529    pub async fn clear_tx_pool(&self) {
530        let tx_hashes: Vec<_> = self
531            .operator
532            .transaction_pool
533            .ready()
534            .map(|t| self.operator.transaction_pool.hash_of(&t.data))
535            .collect();
536
537        let hash_and_number = HashAndNumber {
538            number: self.client.info().best_number,
539            hash: self.client.info().best_hash,
540        };
541        self.operator
542            .transaction_pool
543            .pool()
544            .prune_known(&hash_and_number, &tx_hashes);
545
546        // `ban_time` have set to 0, explicitly wait 1ms here to ensure `clear_stale` will remove
547        // all the bans as the ban time must be passed.
548        tokio::time::sleep(std::time::Duration::from_millis(1)).await;
549        self.operator
550            .transaction_pool
551            .pool()
552            .validated_pool()
553            .clear_stale(&hash_and_number);
554    }
555}
556
557impl<Runtime, RuntimeApi> DomainNode<Runtime, RuntimeApi>
558where
559    Runtime: frame_system::Config<Hash = H256>
560        + pallet_transaction_payment::Config
561        + DomainRuntime
562        + Send
563        + Sync,
564    RuntimeApi: ConstructRuntimeApi<Block, Client<RuntimeApi>> + Send + Sync + 'static,
565    RuntimeApi::RuntimeApi: Metadata<Block>
566        + BlockBuilder<Block>
567        + OffchainWorkerApi<Block>
568        + SessionKeys<Block>
569        + DomainCoreApi<Block>
570        + TaggedTransactionQueue<Block>
571        + AccountNonceApi<Block, <Runtime as DomainRuntime>::AccountId, Nonce>
572        + TransactionPaymentRuntimeApi<Block, Balance>
573        + MessengerApi<Block, NumberFor<CBlock>, BlockHashFor<CBlock>>
574        + RelayerApi<Block, NumberFor<Block>, NumberFor<CBlock>, BlockHashFor<CBlock>>
575        + EvmOnchainStateApi<Block>,
576{
577    /// Returns the current EVM contract creation allow list.
578    /// Returns `None` if this is not an EVM domain, or if the allow list isn't set (allow all).
579    pub fn evm_contract_creation_allowed_by(
580        &self,
581    ) -> PermissionedActionAllowedBy<EthereumAccountId> {
582        self.client
583            .runtime_api()
584            .evm_contract_creation_allowed_by(self.client.info().best_hash)
585            .expect("Failed to get EVM contact creation allow list")
586            .expect("Should be an EVM domain")
587    }
588}
589
590/// A builder to create a [`DomainNode`].
591pub struct DomainNodeBuilder {
592    tokio_handle: tokio::runtime::Handle,
593    domain_nodes: Vec<MultiaddrWithPeerId>,
594    domain_nodes_exclusive: bool,
595    skip_empty_bundle_production: bool,
596    base_path: BasePath,
597    maybe_operator_id: Option<OperatorId>,
598    rpc_addr: Option<SocketAddr>,
599    rpc_port: Option<u16>,
600}
601
602impl DomainNodeBuilder {
603    /// Create a new instance of `Self`.
604    ///
605    /// `tokio_handle` - The tokio handler to use.
606    /// `base_path` - Where databases will be stored.
607    pub fn new(tokio_handle: tokio::runtime::Handle, base_path: BasePath) -> Self {
608        DomainNodeBuilder {
609            tokio_handle,
610            domain_nodes: Vec::new(),
611            domain_nodes_exclusive: false,
612            skip_empty_bundle_production: false,
613            base_path,
614            maybe_operator_id: None,
615            rpc_addr: None,
616            rpc_port: None,
617        }
618    }
619
620    /// Instruct the node to exclusively connect to registered parachain nodes.
621    ///
622    /// Domain nodes can be registered using [`Self::connect_to_domain_node`].
623    pub fn exclusively_connect_to_registered_parachain_nodes(mut self) -> Self {
624        self.domain_nodes_exclusive = true;
625        self
626    }
627
628    /// Make the node connect to the given domain node.
629    ///
630    /// By default the node will not be connected to any node, and won't be able to discover any
631    /// other nodes.
632    pub fn connect_to_domain_node(mut self, addr: MultiaddrWithPeerId) -> Self {
633        self.domain_nodes.push(addr);
634        self
635    }
636
637    /// Skip empty bundle production when there is no non-empty domain block need to confirm
638    pub fn skip_empty_bundle(mut self) -> Self {
639        self.skip_empty_bundle_production = true;
640        self
641    }
642
643    /// Set the operator id
644    pub fn operator_id(mut self, operator_id: OperatorId) -> Self {
645        self.maybe_operator_id = Some(operator_id);
646        self
647    }
648
649    /// Set RPC address for the domain node
650    pub fn rpc_addr(mut self, addr: SocketAddr) -> Self {
651        self.rpc_addr = Some(addr);
652        self
653    }
654
655    /// Set RPC port for the domain node
656    pub fn rpc_port(mut self, port: u16) -> Self {
657        self.rpc_port = Some(port);
658        self
659    }
660
661    fn evm_eth_provider(
662        base_path: &std::path::Path,
663    ) -> EthProvider<
664        evm_domain_test_runtime::TransactionConverter,
665        DefaultEthConfig<
666            FullClient<Block, evm_domain_test_runtime::RuntimeApi>,
667            FullBackend<Block>,
668        >,
669    > {
670        EthProvider::with_configuration(
671            Some(base_path),
672            EthConfiguration {
673                max_past_logs: 10000,
674                fee_history_limit: 2048,
675                enable_dev_signer: true,
676                target_gas_price: 1,
677                execute_gas_limit_multiplier: 10,
678                eth_log_block_cache: 50,
679                eth_statuses_cache: 50,
680            },
681        )
682    }
683
684    /// Build an EVM domain node
685    pub async fn build_evm_node(
686        self,
687        role: Role,
688        key: EcdsaKeyring,
689        mock_consensus_node: &mut MockConsensusNode,
690    ) -> EvmDomainNode {
691        let domain_base_path = self
692            .base_path
693            .path()
694            .join(format!("domain-{EVM_DOMAIN_ID}"));
695        let eth_provider = Self::evm_eth_provider(&domain_base_path);
696
697        DomainNode::build(
698            EVM_DOMAIN_ID,
699            self.tokio_handle,
700            key,
701            BasePath::new(domain_base_path),
702            self.domain_nodes,
703            self.domain_nodes_exclusive,
704            self.skip_empty_bundle_production,
705            self.maybe_operator_id,
706            role,
707            mock_consensus_node,
708            self.rpc_addr,
709            self.rpc_port,
710            eth_provider,
711        )
712        .await
713    }
714
715    /// Build an Auto ID domain node
716    pub async fn build_auto_id_node(
717        self,
718        role: Role,
719        key: Sr25519Keyring,
720        mock_consensus_node: &mut MockConsensusNode,
721    ) -> AutoIdDomainNode {
722        DomainNode::build(
723            AUTO_ID_DOMAIN_ID,
724            self.tokio_handle,
725            key,
726            BasePath::new(
727                self.base_path
728                    .path()
729                    .join(format!("domain-{AUTO_ID_DOMAIN_ID}")),
730            ),
731            self.domain_nodes,
732            self.domain_nodes_exclusive,
733            self.skip_empty_bundle_production,
734            self.maybe_operator_id,
735            role,
736            mock_consensus_node,
737            self.rpc_addr,
738            self.rpc_port,
739            DefaultProvider,
740        )
741        .await
742    }
743
744    /// Build an EVM domain node with the given domain id
745    pub async fn build_evm_node_with(
746        self,
747        role: Role,
748        key: EcdsaKeyring,
749        mock_consensus_node: &mut MockConsensusNode,
750        domain_id: DomainId,
751    ) -> EvmDomainNode {
752        let eth_provider = Self::evm_eth_provider(self.base_path.path());
753
754        DomainNode::build(
755            domain_id,
756            self.tokio_handle,
757            key,
758            self.base_path,
759            self.domain_nodes,
760            self.domain_nodes_exclusive,
761            self.skip_empty_bundle_production,
762            self.maybe_operator_id,
763            role,
764            mock_consensus_node,
765            self.rpc_addr,
766            self.rpc_port,
767            eth_provider,
768        )
769        .await
770    }
771}
772
773/// The evm domain node
774pub type EvmDomainNode =
775    DomainNode<evm_domain_test_runtime::Runtime, evm_domain_test_runtime::RuntimeApi>;
776
777/// The evm domain client
778pub type EvmDomainClient = Client<evm_domain_test_runtime::RuntimeApi>;
779
780/// The auto-id domain node
781pub type AutoIdDomainNode =
782    DomainNode<auto_id_domain_test_runtime::Runtime, auto_id_domain_test_runtime::RuntimeApi>;
783
784/// The auto-id domain client
785pub type AutoIdDomainClient = Client<auto_id_domain_test_runtime::RuntimeApi>;