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