1pub mod chain_spec;
20pub mod domain;
21pub mod keyring;
22
23pub use domain::*;
24use domain_runtime_primitives::opaque::Block;
25pub use evm_domain_test_runtime;
26use frame_support::dispatch::{DispatchInfo, PostDispatchInfo};
27use frame_system::pallet_prelude::{BlockNumberFor, RuntimeCallFor};
28pub use keyring::Keyring as EcdsaKeyring;
29use sc_network::config::{NonReservedPeerMode, TransportConfig};
30use sc_network::multiaddr;
31use sc_service::config::{
32 DatabaseSource, ExecutorConfiguration, KeystoreConfig, MultiaddrWithPeerId,
33 NetworkConfiguration, OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig,
34 RpcConfiguration, RpcEndpoint, WasmExecutionMethod, WasmtimeInstantiationStrategy,
35};
36use sc_service::{
37 BasePath, BlocksPruning, ChainSpec, Configuration as ServiceConfiguration,
38 Error as ServiceError, Role,
39};
40use serde::de::DeserializeOwned;
41use sp_arithmetic::traits::SaturatedConversion;
42use sp_blockchain::HeaderBackend;
43use sp_core::{Get, H256};
44use sp_domains::DomainId;
45pub use sp_keyring::Sr25519Keyring;
46use sp_runtime::codec::{Decode, Encode};
47use sp_runtime::generic;
48use sp_runtime::generic::SignedPayload;
49use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable};
50use std::fmt::{Debug, Display};
51use std::net::SocketAddr;
52use std::str::FromStr;
53
54pub const EVM_DOMAIN_ID: DomainId = DomainId::new(0u32);
56
57pub const AUTO_ID_DOMAIN_ID: DomainId = DomainId::new(1u32);
59
60#[allow(clippy::too_many_arguments)]
66#[expect(clippy::result_large_err, reason = "Comes from Substrate")]
67pub fn node_config(
68 domain_id: DomainId,
69 tokio_handle: tokio::runtime::Handle,
70 key_seed: String,
71 nodes: Vec<MultiaddrWithPeerId>,
72 nodes_exclusive: bool,
73 role: Role,
74 base_path: BasePath,
75 chain_spec: Box<dyn ChainSpec>,
76 rpc_addr: Option<SocketAddr>,
77 rpc_port: Option<u16>,
78) -> Result<ServiceConfiguration, ServiceError> {
79 let root = base_path.path().to_path_buf();
80
81 let domain_name = format!("{domain_id:?}");
82
83 let mut network_config = NetworkConfiguration::new(
84 format!("{key_seed} ({domain_name})"),
85 "network/test/0.1",
86 Default::default(),
87 None,
88 );
89
90 if nodes_exclusive {
91 network_config.default_peers_set.reserved_nodes = nodes;
92 network_config.default_peers_set.non_reserved_mode = NonReservedPeerMode::Deny;
93 } else {
94 network_config.boot_nodes = nodes;
95 }
96
97 network_config.allow_non_globals_in_dht = true;
98
99 network_config
100 .listen_addresses
101 .push(multiaddr::Protocol::Memory(rand::random()).into());
102
103 network_config.force_synced = true;
107
108 network_config.transport = TransportConfig::MemoryOnly;
109
110 let rpc_configuration = match rpc_addr {
111 Some(listen_addr) => {
112 let port = rpc_port.unwrap_or(9945);
113 RpcConfiguration {
114 addr: Some(vec![RpcEndpoint {
115 batch_config: RpcBatchRequestConfig::Disabled,
116 max_connections: 100,
117 listen_addr,
118 rpc_methods: Default::default(),
119 rate_limit: None,
120 rate_limit_trust_proxy_headers: false,
121 rate_limit_whitelisted_ips: vec![],
122 max_payload_in_mb: 15,
123 max_payload_out_mb: 15,
124 max_subscriptions_per_connection: 100,
125 max_buffer_capacity_per_connection: 100,
126 cors: None,
127 retry_random_port: true,
128 is_optional: false,
129 }]),
130 max_request_size: 15,
131 max_response_size: 15,
132 id_provider: None,
133 max_subs_per_conn: 1024,
134 port,
135 message_buffer_capacity: 1024,
136 batch_config: RpcBatchRequestConfig::Disabled,
137 max_connections: 1000,
138 cors: None,
139 methods: Default::default(),
140 rate_limit: None,
141 rate_limit_whitelisted_ips: vec![],
142 rate_limit_trust_proxy_headers: false,
143 }
144 }
145 None => RpcConfiguration {
146 addr: None,
147 max_request_size: 0,
148 max_response_size: 0,
149 id_provider: None,
150 max_subs_per_conn: 0,
151 port: 0,
152 message_buffer_capacity: 0,
153 batch_config: RpcBatchRequestConfig::Disabled,
154 max_connections: 0,
155 cors: None,
156 methods: Default::default(),
157 rate_limit: None,
158 rate_limit_whitelisted_ips: vec![],
159 rate_limit_trust_proxy_headers: false,
160 },
161 };
162
163 Ok(ServiceConfiguration {
164 impl_name: "domain-test-node".to_string(),
165 impl_version: "0.1".to_string(),
166 role,
167 tokio_handle,
168 transaction_pool: Default::default(),
169 network: network_config,
170 keystore: KeystoreConfig::InMemory,
171 database: DatabaseSource::ParityDb {
172 path: root.join("paritydb"),
173 },
174 trie_cache_maximum_size: Some(16 * 1024 * 1024),
175 state_pruning: Some(PruningMode::ArchiveAll),
176 blocks_pruning: BlocksPruning::KeepAll,
177 chain_spec,
178 executor: ExecutorConfiguration {
179 wasm_method: WasmExecutionMethod::Compiled {
180 instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite,
181 },
182 max_runtime_instances: 8,
183 default_heap_pages: None,
184 runtime_cache_size: 2,
185 },
186 rpc: rpc_configuration,
187 prometheus_config: None,
188 telemetry_endpoints: None,
189 offchain_worker: OffchainWorkerConfig {
190 enabled: true,
191 indexing_enabled: false,
192 },
193 force_authoring: false,
194 disable_grandpa: false,
195 dev_key_seed: Some(key_seed),
196 tracing_targets: None,
197 tracing_receiver: Default::default(),
198 announce_block: true,
199 data_path: base_path.path().into(),
200 base_path,
201 wasm_runtime_overrides: None,
202 })
203}
204
205type SignedExtraFor<Runtime> = (
206 frame_system::CheckNonZeroSender<Runtime>,
207 frame_system::CheckSpecVersion<Runtime>,
208 frame_system::CheckTxVersion<Runtime>,
209 frame_system::CheckGenesis<Runtime>,
210 frame_system::CheckMortality<Runtime>,
211 frame_system::CheckNonce<Runtime>,
212 domain_check_weight::CheckWeight<Runtime>,
213 pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
214);
215
216type UncheckedExtrinsicFor<Runtime> = generic::UncheckedExtrinsic<
217 <Runtime as DomainRuntime>::Address,
218 <Runtime as frame_system::Config>::RuntimeCall,
219 <Runtime as DomainRuntime>::Signature,
220 SignedExtraFor<Runtime>,
221>;
222
223type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;
224
225pub fn construct_extrinsic_raw_payload<Runtime, Client>(
226 client: impl AsRef<Client>,
227 function: RuntimeCallFor<Runtime>,
228 immortal: bool,
229 nonce: u32,
230 tip: BalanceOf<Runtime>,
231) -> (
232 SignedPayload<RuntimeCallFor<Runtime>, SignedExtraFor<Runtime>>,
233 SignedExtraFor<Runtime>,
234)
235where
236 Runtime: frame_system::Config<Hash = H256> + pallet_transaction_payment::Config + Send + Sync,
237 RuntimeCallFor<Runtime>:
238 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + Send + Sync,
239 BalanceOf<Runtime>: Send + Sync + From<u64> + sp_runtime::FixedPointOperand,
240 u64: From<BlockNumberFor<Runtime>>,
241 Client: HeaderBackend<Block>,
242 <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
243 AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + Clone,
244{
245 let current_block_hash = client.as_ref().info().best_hash;
246 let current_block = client.as_ref().info().best_number.saturated_into();
247 let genesis_block = client.as_ref().hash(0).unwrap().unwrap();
248 let period = u64::from(<Runtime as frame_system::Config>::BlockHashCount::get())
249 .checked_next_power_of_two()
250 .map(|c| c / 2)
251 .unwrap_or(2);
252 let extra: SignedExtraFor<Runtime> = (
253 frame_system::CheckNonZeroSender::<Runtime>::new(),
254 frame_system::CheckSpecVersion::<Runtime>::new(),
255 frame_system::CheckTxVersion::<Runtime>::new(),
256 frame_system::CheckGenesis::<Runtime>::new(),
257 frame_system::CheckMortality::<Runtime>::from(if immortal {
258 generic::Era::Immortal
259 } else {
260 generic::Era::mortal(period, current_block)
261 }),
262 frame_system::CheckNonce::<Runtime>::from(nonce.into()),
263 domain_check_weight::CheckWeight::<Runtime>::new(),
264 pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
265 );
266 (
267 generic::SignedPayload::<RuntimeCallFor<Runtime>, SignedExtraFor<Runtime>>::from_raw(
268 function,
269 extra.clone(),
270 ((), 1, 0, genesis_block, current_block_hash, (), (), ()),
271 ),
272 extra,
273 )
274}
275
276pub trait DomainRuntime {
277 type Keyring: Copy;
278 type AccountId: DeserializeOwned
279 + Encode
280 + Decode
281 + Clone
282 + Debug
283 + Display
284 + FromStr
285 + Sync
286 + Send
287 + 'static;
288 type Address: Encode + Decode;
289 type Signature: Encode + Decode;
290 fn sign(key: Self::Keyring, payload: &[u8]) -> Self::Signature;
291 fn account_id(key: Self::Keyring) -> Self::AccountId;
292 fn address(key: Self::Keyring) -> Self::Address;
293 fn to_seed(key: Self::Keyring) -> String;
294}
295
296impl DomainRuntime for evm_domain_test_runtime::Runtime {
297 type Keyring = EcdsaKeyring;
298 type AccountId = evm_domain_test_runtime::AccountId;
299 type Address = evm_domain_test_runtime::Address;
300 type Signature = evm_domain_test_runtime::Signature;
301
302 fn sign(key: Self::Keyring, payload: &[u8]) -> Self::Signature {
303 evm_domain_test_runtime::Signature::new(key.sign(payload))
304 }
305
306 fn account_id(key: Self::Keyring) -> Self::AccountId {
307 key.to_account_id()
308 }
309
310 fn address(key: Self::Keyring) -> Self::Address {
311 key.to_account_id()
312 }
313
314 fn to_seed(key: Self::Keyring) -> String {
315 key.to_seed()
316 }
317}
318
319impl DomainRuntime for auto_id_domain_test_runtime::Runtime {
320 type Keyring = Sr25519Keyring;
321 type AccountId = auto_id_domain_test_runtime::AccountId;
322 type Address = auto_id_domain_test_runtime::Address;
323 type Signature = auto_id_domain_test_runtime::Signature;
324
325 fn sign(key: Self::Keyring, payload: &[u8]) -> Self::Signature {
326 key.sign(payload).into()
327 }
328
329 fn account_id(key: Self::Keyring) -> Self::AccountId {
330 key.to_account_id()
331 }
332
333 fn address(key: Self::Keyring) -> Self::Address {
334 sp_runtime::MultiAddress::Id(key.to_account_id())
335 }
336
337 fn to_seed(key: Self::Keyring) -> String {
338 key.to_seed()
339 }
340}
341
342pub fn construct_extrinsic_generic<Runtime, Client>(
344 client: impl AsRef<Client>,
345 function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
346 caller: Runtime::Keyring,
347 immortal: bool,
348 nonce: u32,
349 tip: BalanceOf<Runtime>,
350) -> UncheckedExtrinsicFor<Runtime>
351where
352 Runtime: frame_system::Config<Hash = H256>
353 + pallet_transaction_payment::Config
354 + DomainRuntime
355 + Send
356 + Sync,
357 Runtime::RuntimeCall:
358 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + Send + Sync,
359 BalanceOf<Runtime>: Send + Sync + From<u64> + sp_runtime::FixedPointOperand,
360 u64: From<BlockNumberFor<Runtime>>,
361 Client: HeaderBackend<Block>,
362 <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
363 AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + Clone,
364{
365 let function = function.into();
366 let (raw_payload, extra) =
367 construct_extrinsic_raw_payload(client, function.clone(), immortal, nonce, tip);
368 let signature = raw_payload.using_encoded(|e| Runtime::sign(caller, e));
369 let address = Runtime::address(caller);
370 UncheckedExtrinsicFor::<Runtime>::new_signed(function, address, signature, extra)
371}
372
373pub fn construct_unsigned_extrinsic<Runtime>(
375 function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
376) -> UncheckedExtrinsicFor<Runtime>
377where
378 Runtime: frame_system::Config<Hash = H256>
379 + pallet_transaction_payment::Config
380 + DomainRuntime
381 + Send
382 + Sync,
383 Runtime::RuntimeCall:
384 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + Send + Sync,
385 BalanceOf<Runtime>: Send + Sync + From<u64> + sp_runtime::FixedPointOperand,
386{
387 let function = function.into();
388 UncheckedExtrinsicFor::<Runtime>::new_bare(function)
389}