mirror of
https://github.com/pezkuwichain/pezkuwi-runtime-templates.git
synced 2026-04-22 02:07:55 +00:00
314 lines
11 KiB
Rust
314 lines
11 KiB
Rust
use std::{
|
|
iter,
|
|
time::{Duration, Instant},
|
|
};
|
|
|
|
use frame_support::{
|
|
dispatch::GetDispatchInfo,
|
|
traits::{IntegrityTest, TryState, TryStateSelect},
|
|
weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight},
|
|
};
|
|
use frame_system::Account;
|
|
// Local Imports
|
|
use generic_runtime_template::{
|
|
configs::MaxCandidates, constants::SLOT_DURATION, AllPalletsWithSystem, Balance, Balances,
|
|
Executive, Runtime, RuntimeCall, RuntimeOrigin, SudoConfig, UncheckedExtrinsic,
|
|
};
|
|
use pallet_balances::{Holds, TotalIssuance};
|
|
use parachains_common::AccountId;
|
|
use parity_scale_codec::{DecodeLimit, Encode};
|
|
use sp_consensus_aura::{Slot, AURA_ENGINE_ID};
|
|
use sp_runtime::{
|
|
testing::H256,
|
|
traits::{Dispatchable, Header},
|
|
Digest, DigestItem, Storage,
|
|
};
|
|
use sp_state_machine::BasicExternalities;
|
|
|
|
fn generate_genesis(accounts: &[AccountId]) -> Storage {
|
|
use generic_runtime_template::{
|
|
BalancesConfig, CollatorSelectionConfig, RuntimeGenesisConfig, SessionConfig, SessionKeys,
|
|
};
|
|
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
|
use sp_runtime::{app_crypto::ByteArray, BuildStorage};
|
|
|
|
// Configure endowed accounts with initial balance of 1 << 60.
|
|
let balances = accounts.iter().cloned().map(|k| (k, 1 << 60)).collect();
|
|
let invulnerables: Vec<AccountId> = vec![[0; 32].into()];
|
|
let session_keys = vec![(
|
|
[0; 32].into(),
|
|
[0; 32].into(),
|
|
SessionKeys { aura: AuraId::from_slice(&[0; 32]).unwrap() },
|
|
)];
|
|
let root: AccountId = [0; 32].into();
|
|
|
|
RuntimeGenesisConfig {
|
|
system: Default::default(),
|
|
balances: BalancesConfig { balances },
|
|
aura: Default::default(),
|
|
session: SessionConfig { keys: session_keys },
|
|
collator_selection: CollatorSelectionConfig {
|
|
invulnerables,
|
|
candidacy_bond: 1 << 57,
|
|
desired_candidates: 1,
|
|
},
|
|
aura_ext: Default::default(),
|
|
parachain_info: Default::default(),
|
|
parachain_system: Default::default(),
|
|
polkadot_xcm: Default::default(),
|
|
assets: Default::default(),
|
|
transaction_payment: Default::default(),
|
|
sudo: SudoConfig { key: Some(root) },
|
|
treasury: Default::default(),
|
|
..Default::default()
|
|
}
|
|
.build_storage()
|
|
.unwrap()
|
|
}
|
|
|
|
fn process_input(accounts: &[AccountId], genesis: &Storage, data: &[u8]) {
|
|
let mut data = data;
|
|
// We build the list of extrinsics we will execute
|
|
let extrinsics: Vec<(/* lapse */ u8, /* origin */ u8, RuntimeCall)> =
|
|
iter::from_fn(|| DecodeLimit::decode_with_depth_limit(64, &mut data).ok())
|
|
.filter(|(_, _, x)| !matches!(x, RuntimeCall::System(_)))
|
|
.collect();
|
|
if extrinsics.is_empty() {
|
|
return;
|
|
}
|
|
|
|
let mut block: u32 = 1;
|
|
let mut weight: Weight = 0.into();
|
|
let mut elapsed: Duration = Duration::ZERO;
|
|
|
|
BasicExternalities::execute_with_storage(&mut genesis.clone(), || {
|
|
let initial_total_issuance = TotalIssuance::<Runtime>::get();
|
|
|
|
initialize_block(block);
|
|
|
|
for (lapse, origin, extrinsic) in extrinsics {
|
|
// If the lapse is in the range [0, MAX_BLOCK_LAPSE] we finalize the block and initialize
|
|
// a new one.
|
|
let origin_no = origin as usize % accounts.len();
|
|
if !recursive_call_filter(&extrinsic, origin_no) {
|
|
continue;
|
|
}
|
|
|
|
if lapse > 0 {
|
|
finalize_block(elapsed);
|
|
|
|
block += u32::from(lapse) * 393; // 393 * 256 = 100608 which nearly corresponds to a week
|
|
weight = 0.into();
|
|
elapsed = Duration::ZERO;
|
|
|
|
initialize_block(block);
|
|
}
|
|
|
|
weight.saturating_accrue(extrinsic.get_dispatch_info().weight);
|
|
if weight.ref_time() >= 2 * WEIGHT_REF_TIME_PER_SECOND {
|
|
println!("Extrinsic would exhaust block weight, skipping");
|
|
continue;
|
|
}
|
|
|
|
let origin = accounts[origin_no].clone();
|
|
|
|
println!("\n origin: {origin:?}");
|
|
println!(" call: {extrinsic:?}");
|
|
|
|
let now = Instant::now(); // We get the current time for timing purposes.
|
|
#[allow(unused_variables)]
|
|
let res = extrinsic.dispatch(RuntimeOrigin::signed(origin));
|
|
elapsed += now.elapsed();
|
|
|
|
println!(" result: {res:?}");
|
|
}
|
|
|
|
finalize_block(elapsed);
|
|
|
|
check_invariants(block, initial_total_issuance);
|
|
});
|
|
}
|
|
|
|
fn initialize_block(block: u32) {
|
|
println!("\ninitializing block {}", block);
|
|
|
|
let current_timestamp = u64::from(block) * SLOT_DURATION;
|
|
|
|
let prev_header = match block {
|
|
1 => None,
|
|
_ => Some(Executive::finalize_block()),
|
|
};
|
|
|
|
let parent_header = &Header::new(
|
|
block,
|
|
H256::default(),
|
|
H256::default(),
|
|
prev_header.clone().map(|x| x.hash()).unwrap_or_default(),
|
|
Digest {
|
|
logs: vec![DigestItem::PreRuntime(
|
|
AURA_ENGINE_ID,
|
|
Slot::from(u64::from(block)).encode(),
|
|
)],
|
|
},
|
|
);
|
|
|
|
Executive::initialize_block(parent_header);
|
|
|
|
// We apply the timestamp extrinsic for the current block.
|
|
Executive::apply_extrinsic(UncheckedExtrinsic::new_unsigned(RuntimeCall::Timestamp(
|
|
pallet_timestamp::Call::set { now: current_timestamp },
|
|
)))
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
let parachain_validation_data = {
|
|
use cumulus_primitives_core::{relay_chain::HeadData, PersistedValidationData};
|
|
use cumulus_primitives_parachain_inherent::ParachainInherentData;
|
|
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
|
|
|
|
let parent_head = HeadData(prev_header.clone().unwrap_or(parent_header.clone()).encode());
|
|
let sproof_builder = RelayStateSproofBuilder {
|
|
para_id: 100.into(),
|
|
current_slot: Slot::from(2 * current_timestamp / SLOT_DURATION),
|
|
included_para_head: Some(parent_head.clone()),
|
|
..Default::default()
|
|
};
|
|
|
|
let (relay_parent_storage_root, relay_chain_state) =
|
|
sproof_builder.into_state_root_and_proof();
|
|
let data = ParachainInherentData {
|
|
validation_data: PersistedValidationData {
|
|
parent_head,
|
|
relay_parent_number: block,
|
|
relay_parent_storage_root,
|
|
max_pov_size: 1000,
|
|
},
|
|
relay_chain_state,
|
|
downward_messages: Default::default(),
|
|
horizontal_messages: Default::default(),
|
|
};
|
|
cumulus_pallet_parachain_system::Call::set_validation_data { data }
|
|
};
|
|
|
|
Executive::apply_extrinsic(UncheckedExtrinsic::new_unsigned(RuntimeCall::ParachainSystem(
|
|
parachain_validation_data,
|
|
)))
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
// Calls that need to be called before each block starts (init_calls) go here
|
|
}
|
|
|
|
fn finalize_block(elapsed: Duration) {
|
|
println!("\n time spent: {elapsed:?}");
|
|
assert!(elapsed.as_secs() <= 2, "block execution took too much time");
|
|
|
|
println!(" finalizing block");
|
|
Executive::finalize_block();
|
|
}
|
|
|
|
fn check_invariants(block: u32, initial_total_issuance: Balance) {
|
|
let mut counted_free = 0;
|
|
let mut counted_reserved = 0;
|
|
for (account, info) in Account::<Runtime>::iter() {
|
|
let consumers = info.consumers;
|
|
let providers = info.providers;
|
|
assert!(!(consumers > 0 && providers == 0), "Invalid c/p state");
|
|
counted_free += info.data.free;
|
|
counted_reserved += info.data.reserved;
|
|
let max_lock: Balance =
|
|
Balances::locks(&account).iter().map(|l| l.amount).max().unwrap_or_default();
|
|
assert_eq!(max_lock, info.data.frozen, "Max lock should be equal to frozen balance");
|
|
let sum_holds: Balance = Holds::<Runtime>::get(&account).iter().map(|l| l.amount).sum();
|
|
assert!(
|
|
sum_holds <= info.data.reserved,
|
|
"Sum of all holds ({sum_holds}) should be less than or equal to reserved balance {}",
|
|
info.data.reserved
|
|
);
|
|
}
|
|
let total_issuance = TotalIssuance::<Runtime>::get();
|
|
let counted_issuance = counted_free + counted_reserved;
|
|
assert_eq!(total_issuance, counted_issuance);
|
|
assert!(total_issuance <= initial_total_issuance);
|
|
// We run all developer-defined integrity tests
|
|
AllPalletsWithSystem::integrity_test();
|
|
AllPalletsWithSystem::try_state(block, TryStateSelect::All).unwrap();
|
|
}
|
|
|
|
fn recursive_call_filter(call: &RuntimeCall, origin: usize) -> bool {
|
|
match call {
|
|
//recursion
|
|
RuntimeCall::Sudo(
|
|
pallet_sudo::Call::sudo { call }
|
|
| pallet_sudo::Call::sudo_unchecked_weight { call, weight: _ },
|
|
) if origin == 0 => recursive_call_filter(call, origin),
|
|
RuntimeCall::Utility(
|
|
pallet_utility::Call::with_weight { call, weight: _ }
|
|
| pallet_utility::Call::dispatch_as { as_origin: _, call }
|
|
| pallet_utility::Call::as_derivative { index: _, call },
|
|
) => recursive_call_filter(call, origin),
|
|
RuntimeCall::Utility(
|
|
pallet_utility::Call::force_batch { calls }
|
|
| pallet_utility::Call::batch { calls }
|
|
| pallet_utility::Call::batch_all { calls },
|
|
) => calls.iter().map(|call| recursive_call_filter(call, origin)).all(|e| e),
|
|
RuntimeCall::Scheduler(
|
|
pallet_scheduler::Call::schedule_named_after {
|
|
id: _,
|
|
after: _,
|
|
maybe_periodic: _,
|
|
priority: _,
|
|
call,
|
|
}
|
|
| pallet_scheduler::Call::schedule { when: _, maybe_periodic: _, priority: _, call }
|
|
| pallet_scheduler::Call::schedule_named {
|
|
when: _,
|
|
id: _,
|
|
maybe_periodic: _,
|
|
priority: _,
|
|
call,
|
|
}
|
|
| pallet_scheduler::Call::schedule_after {
|
|
after: _,
|
|
maybe_periodic: _,
|
|
priority: _,
|
|
call,
|
|
},
|
|
) => recursive_call_filter(call, origin),
|
|
RuntimeCall::Multisig(
|
|
pallet_multisig::Call::as_multi_threshold_1 { other_signatories: _, call }
|
|
| pallet_multisig::Call::as_multi {
|
|
threshold: _,
|
|
other_signatories: _,
|
|
maybe_timepoint: _,
|
|
call,
|
|
max_weight: _,
|
|
},
|
|
) => recursive_call_filter(call, origin),
|
|
RuntimeCall::Whitelist(
|
|
pallet_whitelist::Call::dispatch_whitelisted_call_with_preimage { call },
|
|
) => recursive_call_filter(call, origin),
|
|
|
|
// restrictions
|
|
RuntimeCall::Sudo(_) if origin != 0 => false,
|
|
RuntimeCall::System(
|
|
frame_system::Call::set_code { .. } | frame_system::Call::kill_prefix { .. },
|
|
) => false,
|
|
RuntimeCall::CollatorSelection(
|
|
pallet_collator_selection::Call::set_desired_candidates { max },
|
|
) => *max < MaxCandidates::get(),
|
|
RuntimeCall::Balances(pallet_balances::Call::force_adjust_total_issuance { .. }) => false,
|
|
|
|
_ => true,
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let accounts: Vec<AccountId> = (0..5).map(|i| [i; 32].into()).collect();
|
|
let genesis = generate_genesis(&accounts);
|
|
|
|
ziggy::fuzz!(|data: &[u8]| {
|
|
process_input(&accounts, &genesis, data);
|
|
});
|
|
}
|