1mod block_builder;
20pub use block_builder::*;
21use codec::{Decode, Encode};
22pub use cumulus_test_runtime as runtime;
23use cumulus_test_runtime::AuraId;
24pub use polkadot_parachain_primitives::primitives::{
25 BlockData, HeadData, ValidationParams, ValidationResult,
26};
27use runtime::{
28 Balance, Block, BlockHashCount, Runtime, RuntimeCall, Signature, SignedPayload, TxExtension,
29 UncheckedExtrinsic, VERSION,
30};
31use sc_consensus_aura::{
32 find_pre_digest,
33 standalone::{seal, slot_author},
34};
35pub use sc_executor::error::Result as ExecutorResult;
36use sc_executor::HeapAllocStrategy;
37use sc_executor_common::runtime_blob::RuntimeBlob;
38use sp_api::ProvideRuntimeApi;
39use sp_application_crypto::AppCrypto;
40use sp_blockchain::HeaderBackend;
41use sp_consensus_aura::AuraApi;
42use sp_core::Pair;
43use sp_io::TestExternalities;
44use sp_keystore::testing::MemoryKeystore;
45use sp_runtime::{generic::Era, traits::Header, BuildStorage, MultiAddress, SaturatedConversion};
46use std::sync::Arc;
47pub use substrate_test_client::*;
48
49pub type ParachainBlockData = cumulus_primitives_core::ParachainBlockData<Block>;
50
51pub type Backend = substrate_test_client::Backend<Block>;
53
54pub type Executor = client::LocalCallExecutor<
56 Block,
57 Backend,
58 WasmExecutor<(
59 sp_io::SubstrateHostFunctions,
60 cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
61 )>,
62>;
63
64pub type TestClientBuilder =
66 substrate_test_client::TestClientBuilder<Block, Executor, Backend, GenesisParameters>;
67
68pub type LongestChain = sc_consensus::LongestChain<Backend, Block>;
70
71pub type Client = client::Client<Backend, Executor, Block, runtime::RuntimeApi>;
73
74#[derive(Default)]
76pub struct GenesisParameters {
77 pub endowed_accounts: Vec<cumulus_test_runtime::AccountId>,
78 pub wasm: Option<Vec<u8>>,
79}
80
81impl substrate_test_client::GenesisInit for GenesisParameters {
82 fn genesis_storage(&self) -> Storage {
83 cumulus_test_service::chain_spec::get_chain_spec_with_extra_endowed(
84 None,
85 self.endowed_accounts.clone(),
86 self.wasm.as_deref().unwrap_or_else(|| {
87 cumulus_test_runtime::WASM_BINARY.expect("WASM binary not compiled!")
88 }),
89 )
90 .build_storage()
91 .expect("Builds test runtime genesis storage")
92 }
93}
94
95pub trait TestClientBuilderExt: Sized {
97 fn build(self) -> Client {
99 self.build_with_longest_chain().0
100 }
101
102 fn build_with_longest_chain(self) -> (Client, LongestChain);
104}
105
106impl TestClientBuilderExt for TestClientBuilder {
107 fn build_with_longest_chain(self) -> (Client, LongestChain) {
108 self.build_with_native_executor(None)
109 }
110}
111
112pub trait DefaultTestClientBuilderExt: Sized {
114 fn new() -> Self;
116}
117
118impl DefaultTestClientBuilderExt for TestClientBuilder {
119 fn new() -> Self {
120 Self::with_default_backend()
121 }
122}
123
124pub fn generate_unsigned(function: impl Into<RuntimeCall>) -> UncheckedExtrinsic {
126 UncheckedExtrinsic::new_bare(function.into())
127}
128
129pub fn generate_extrinsic_with_pair(
132 client: &Client,
133 origin: sp_core::sr25519::Pair,
134 function: impl Into<RuntimeCall>,
135 nonce: Option<u32>,
136) -> UncheckedExtrinsic {
137 let current_block_hash = client.info().best_hash;
138 let current_block = client.info().best_number.saturated_into();
139 let genesis_block = client.hash(0).unwrap().unwrap();
140 let nonce = nonce.unwrap_or_default();
141 let period =
142 BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64;
143 let tip = 0;
144 let tx_ext: TxExtension = (
145 frame_system::AuthorizeCall::<Runtime>::new(),
146 frame_system::CheckNonZeroSender::<Runtime>::new(),
147 frame_system::CheckSpecVersion::<Runtime>::new(),
148 frame_system::CheckGenesis::<Runtime>::new(),
149 frame_system::CheckEra::<Runtime>::from(Era::mortal(period, current_block)),
150 frame_system::CheckNonce::<Runtime>::from(nonce),
151 frame_system::CheckWeight::<Runtime>::new(),
152 pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
153 )
154 .into();
155
156 let function = function.into();
157
158 let raw_payload = SignedPayload::from_raw(
159 function.clone(),
160 tx_ext.clone(),
161 ((), (), VERSION.spec_version, genesis_block, current_block_hash, (), (), ()),
162 );
163 let signature = raw_payload.using_encoded(|e| origin.sign(e));
164
165 UncheckedExtrinsic::new_signed(
166 function,
167 MultiAddress::Id(origin.public().into()),
168 Signature::Sr25519(signature),
169 tx_ext,
170 )
171}
172
173pub fn generate_extrinsic(
175 client: &Client,
176 origin: sp_keyring::Sr25519Keyring,
177 function: impl Into<RuntimeCall>,
178) -> UncheckedExtrinsic {
179 generate_extrinsic_with_pair(client, origin.into(), function, None)
180}
181
182pub fn transfer(
184 client: &Client,
185 origin: sp_keyring::Sr25519Keyring,
186 dest: sp_keyring::Sr25519Keyring,
187 value: Balance,
188) -> UncheckedExtrinsic {
189 let function = RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death {
190 dest: MultiAddress::Id(dest.public().into()),
191 value,
192 });
193
194 generate_extrinsic(client, origin, function)
195}
196
197pub fn validate_block(
199 validation_params: ValidationParams,
200 wasm_blob: &[u8],
201) -> ExecutorResult<ValidationResult> {
202 let mut ext = TestExternalities::default();
203 let mut ext_ext = ext.ext();
204
205 let heap_pages = HeapAllocStrategy::Static { extra_pages: 1024 };
206 let executor = WasmExecutor::<(
207 sp_io::SubstrateHostFunctions,
208 cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
209 )>::builder()
210 .with_execution_method(WasmExecutionMethod::default())
211 .with_max_runtime_instances(1)
212 .with_runtime_cache_size(2)
213 .with_onchain_heap_alloc_strategy(heap_pages)
214 .with_offchain_heap_alloc_strategy(heap_pages)
215 .build();
216
217 executor
218 .uncached_call(
219 RuntimeBlob::uncompress_if_needed(wasm_blob).expect("RuntimeBlob uncompress & parse"),
220 &mut ext_ext,
221 false,
222 "validate_block",
223 &validation_params.encode(),
224 )
225 .map(|v| ValidationResult::decode(&mut &v[..]).expect("Decode `ValidationResult`."))
226}
227
228fn get_keystore() -> sp_keystore::KeystorePtr {
229 let keystore = MemoryKeystore::new();
230 sp_keyring::Sr25519Keyring::iter().for_each(|key| {
231 keystore
232 .sr25519_generate_new(
233 sp_consensus_aura::sr25519::AuthorityPair::ID,
234 Some(&key.to_seed()),
235 )
236 .expect("Key should be created");
237 });
238 Arc::new(keystore)
239}
240
241pub fn seal_block(mut block: Block, client: &Client) -> Block {
245 let parachain_slot =
246 find_pre_digest::<Block, <AuraId as AppCrypto>::Signature>(&block.header).unwrap();
247 let parent_hash = block.header.parent_hash;
248 let authorities = client.runtime_api().authorities(parent_hash).unwrap();
249 let expected_author = slot_author::<<AuraId as AppCrypto>::Pair>(parachain_slot, &authorities)
250 .expect("Should be able to find author");
251
252 let keystore = get_keystore();
253 let seal_digest = seal::<_, sp_consensus_aura::sr25519::AuthorityPair>(
254 &block.header.hash(),
255 expected_author,
256 &keystore,
257 )
258 .expect("Should be able to create seal");
259 block.header.digest_mut().push(seal_digest);
260
261 block
262}
263
264pub fn seal_parachain_block_data(block: ParachainBlockData, client: &Client) -> ParachainBlockData {
268 let (blocks, proof) = block.into_inner();
269
270 ParachainBlockData::new(
271 blocks.into_iter().map(|block| seal_block(block, &client)).collect::<Vec<_>>(),
272 proof,
273 )
274}