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 request_logger_limit: 1024,
144 }
145 }
146 None => RpcConfiguration {
147 addr: None,
148 max_request_size: 0,
149 max_response_size: 0,
150 id_provider: None,
151 max_subs_per_conn: 0,
152 port: 0,
153 message_buffer_capacity: 0,
154 batch_config: RpcBatchRequestConfig::Disabled,
155 max_connections: 0,
156 cors: None,
157 methods: Default::default(),
158 rate_limit: None,
159 rate_limit_whitelisted_ips: vec![],
160 rate_limit_trust_proxy_headers: false,
161 request_logger_limit: 1024,
162 },
163 };
164
165 Ok(ServiceConfiguration {
166 impl_name: "domain-test-node".to_string(),
167 impl_version: "0.1".to_string(),
168 role,
169 tokio_handle,
170 transaction_pool: Default::default(),
171 network: network_config,
172 keystore: KeystoreConfig::InMemory,
173 database: DatabaseSource::ParityDb {
174 path: root.join("paritydb"),
175 },
176 trie_cache_maximum_size: Some(16 * 1024 * 1024),
177 state_pruning: Some(PruningMode::ArchiveAll),
178 blocks_pruning: BlocksPruning::KeepAll,
179 chain_spec,
180 executor: ExecutorConfiguration {
181 wasm_method: WasmExecutionMethod::Compiled {
182 instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite,
183 },
184 max_runtime_instances: 8,
185 default_heap_pages: None,
186 runtime_cache_size: 2,
187 },
188 rpc: rpc_configuration,
189 prometheus_config: None,
190 telemetry_endpoints: None,
191 offchain_worker: OffchainWorkerConfig {
192 enabled: true,
193 indexing_enabled: false,
194 },
195 force_authoring: false,
196 disable_grandpa: false,
197 dev_key_seed: Some(key_seed),
198 tracing_targets: None,
199 tracing_receiver: Default::default(),
200 announce_block: true,
201 data_path: base_path.path().into(),
202 base_path,
203 wasm_runtime_overrides: None,
204 warm_up_trie_cache: None,
205 })
206}
207
208type SignedExtraFor<Runtime> = (
209 frame_system::CheckNonZeroSender<Runtime>,
210 frame_system::CheckSpecVersion<Runtime>,
211 frame_system::CheckTxVersion<Runtime>,
212 frame_system::CheckGenesis<Runtime>,
213 frame_system::CheckMortality<Runtime>,
214 frame_system::CheckNonce<Runtime>,
215 domain_check_weight::CheckWeight<Runtime>,
216 pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
217);
218
219type UncheckedExtrinsicFor<Runtime> = generic::UncheckedExtrinsic<
220 <Runtime as DomainRuntime>::Address,
221 <Runtime as frame_system::Config>::RuntimeCall,
222 <Runtime as DomainRuntime>::Signature,
223 SignedExtraFor<Runtime>,
224>;
225
226type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;
227
228pub fn construct_extrinsic_raw_payload<Runtime, Client>(
229 client: impl AsRef<Client>,
230 function: RuntimeCallFor<Runtime>,
231 immortal: bool,
232 nonce: u32,
233 tip: BalanceOf<Runtime>,
234) -> (
235 SignedPayload<RuntimeCallFor<Runtime>, SignedExtraFor<Runtime>>,
236 SignedExtraFor<Runtime>,
237)
238where
239 Runtime: frame_system::Config<Hash = H256> + pallet_transaction_payment::Config + Send + Sync,
240 RuntimeCallFor<Runtime>:
241 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + Send + Sync,
242 BalanceOf<Runtime>: Send + Sync + From<u64> + sp_runtime::FixedPointOperand,
243 u64: From<BlockNumberFor<Runtime>>,
244 Client: HeaderBackend<Block>,
245 <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
246 AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + Clone,
247{
248 let current_block_hash = client.as_ref().info().best_hash;
249 let current_block = client.as_ref().info().best_number.saturated_into();
250 let genesis_block = client.as_ref().hash(0).unwrap().unwrap();
251 let period = u64::from(<Runtime as frame_system::Config>::BlockHashCount::get())
252 .checked_next_power_of_two()
253 .map(|c| c / 2)
254 .unwrap_or(2);
255 let extra: SignedExtraFor<Runtime> = (
256 frame_system::CheckNonZeroSender::<Runtime>::new(),
257 frame_system::CheckSpecVersion::<Runtime>::new(),
258 frame_system::CheckTxVersion::<Runtime>::new(),
259 frame_system::CheckGenesis::<Runtime>::new(),
260 frame_system::CheckMortality::<Runtime>::from(if immortal {
261 generic::Era::Immortal
262 } else {
263 generic::Era::mortal(period, current_block)
264 }),
265 frame_system::CheckNonce::<Runtime>::from(nonce.into()),
266 domain_check_weight::CheckWeight::<Runtime>::new(),
267 pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
268 );
269 (
270 generic::SignedPayload::<RuntimeCallFor<Runtime>, SignedExtraFor<Runtime>>::from_raw(
271 function,
272 extra.clone(),
273 ((), 1, 0, genesis_block, current_block_hash, (), (), ()),
274 ),
275 extra,
276 )
277}
278
279pub trait DomainRuntime {
280 type Keyring: Copy;
281 type AccountId: DeserializeOwned
282 + Encode
283 + Decode
284 + Clone
285 + Debug
286 + Display
287 + FromStr
288 + Sync
289 + Send
290 + 'static;
291 type Address: Encode + Decode;
292 type Signature: Encode + Decode;
293 fn sign(key: Self::Keyring, payload: &[u8]) -> Self::Signature;
294 fn account_id(key: Self::Keyring) -> Self::AccountId;
295 fn address(key: Self::Keyring) -> Self::Address;
296 fn to_seed(key: Self::Keyring) -> String;
297}
298
299impl DomainRuntime for evm_domain_test_runtime::Runtime {
300 type Keyring = EcdsaKeyring;
301 type AccountId = evm_domain_test_runtime::AccountId;
302 type Address = evm_domain_test_runtime::Address;
303 type Signature = evm_domain_test_runtime::Signature;
304
305 fn sign(key: Self::Keyring, payload: &[u8]) -> Self::Signature {
306 evm_domain_test_runtime::Signature::new(key.sign(payload))
307 }
308
309 fn account_id(key: Self::Keyring) -> Self::AccountId {
310 key.to_account_id()
311 }
312
313 fn address(key: Self::Keyring) -> Self::Address {
314 key.to_account_id()
315 }
316
317 fn to_seed(key: Self::Keyring) -> String {
318 key.to_seed()
319 }
320}
321
322impl DomainRuntime for auto_id_domain_test_runtime::Runtime {
323 type Keyring = Sr25519Keyring;
324 type AccountId = auto_id_domain_test_runtime::AccountId;
325 type Address = auto_id_domain_test_runtime::Address;
326 type Signature = auto_id_domain_test_runtime::Signature;
327
328 fn sign(key: Self::Keyring, payload: &[u8]) -> Self::Signature {
329 key.sign(payload).into()
330 }
331
332 fn account_id(key: Self::Keyring) -> Self::AccountId {
333 key.to_account_id()
334 }
335
336 fn address(key: Self::Keyring) -> Self::Address {
337 sp_runtime::MultiAddress::Id(key.to_account_id())
338 }
339
340 fn to_seed(key: Self::Keyring) -> String {
341 key.to_seed()
342 }
343}
344
345pub fn construct_extrinsic_generic<Runtime, Client>(
347 client: impl AsRef<Client>,
348 function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
349 caller: Runtime::Keyring,
350 immortal: bool,
351 nonce: u32,
352 tip: BalanceOf<Runtime>,
353) -> UncheckedExtrinsicFor<Runtime>
354where
355 Runtime: frame_system::Config<Hash = H256>
356 + pallet_transaction_payment::Config
357 + DomainRuntime
358 + Send
359 + Sync,
360 Runtime::RuntimeCall:
361 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + Send + Sync,
362 BalanceOf<Runtime>: Send + Sync + From<u64> + sp_runtime::FixedPointOperand,
363 u64: From<BlockNumberFor<Runtime>>,
364 Client: HeaderBackend<Block>,
365 <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
366 AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + Clone,
367{
368 let function = function.into();
369 let (raw_payload, extra) =
370 construct_extrinsic_raw_payload(client, function.clone(), immortal, nonce, tip);
371 let signature = raw_payload.using_encoded(|e| Runtime::sign(caller, e));
372 let address = Runtime::address(caller);
373 UncheckedExtrinsicFor::<Runtime>::new_signed(function, address, signature, extra)
374}
375
376pub fn construct_unsigned_extrinsic<Runtime>(
378 function: impl Into<<Runtime as frame_system::Config>::RuntimeCall>,
379) -> UncheckedExtrinsicFor<Runtime>
380where
381 Runtime: frame_system::Config<Hash = H256>
382 + pallet_transaction_payment::Config
383 + DomainRuntime
384 + Send
385 + Sync,
386 Runtime::RuntimeCall:
387 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + Send + Sync,
388 BalanceOf<Runtime>: Send + Sync + From<u64> + sp_runtime::FixedPointOperand,
389{
390 let function = function.into();
391 UncheckedExtrinsicFor::<Runtime>::new_bare(function)
392}