mirror of
https://github.com/pezkuwichain/pezkuwi-runtime-templates.git
synced 2026-06-15 07:41:01 +00:00
Upgrade polkadot sdk v1.13.0 (#298)
* generic template updated to v1.13.0 * evm dependency upgrades * evm dependency upgrades after moonbeam * importing frontier updates * upgrade fuzzer to v1.11.0 * upgrade fuzzer to v1.12.0 * generic template fuzzer updated * evm fuzzer --------- Co-authored-by: Nikita Khateev <nikita.khateev@openzeppelin.com>
This commit is contained in:
@@ -1,297 +1,207 @@
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{
|
||||
iter,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use cumulus_primitives_core::relay_chain::Slot;
|
||||
// Local Imports
|
||||
use evm_runtime_template::{
|
||||
configs::MaxCandidates, constants::SLOT_DURATION, AccountId, AllPalletsWithSystem, Balance,
|
||||
Balances, BlockNumber, EVMChainIdConfig, Executive, Runtime, RuntimeCall, RuntimeOrigin,
|
||||
SudoConfig, UncheckedExtrinsic,
|
||||
Balances, EVMChainIdConfig, Executive, Runtime, RuntimeCall, RuntimeOrigin, SudoConfig,
|
||||
UncheckedExtrinsic,
|
||||
};
|
||||
use frame_support::{
|
||||
dispatch::GetDispatchInfo,
|
||||
pallet_prelude::Encode,
|
||||
traits::{IntegrityTest, TryState, TryStateSelect},
|
||||
weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight},
|
||||
};
|
||||
use sp_consensus_aura::AURA_ENGINE_ID;
|
||||
use frame_system::Account;
|
||||
use pallet_balances::{Holds, TotalIssuance};
|
||||
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 substrate_runtime_fuzzer::{Data, INITIAL_TIMESTAMP, MAX_TIME_FOR_BLOCK};
|
||||
use sp_state_machine::BasicExternalities;
|
||||
|
||||
pub type Externalities = sp_state_machine::BasicExternalities;
|
||||
|
||||
fn main() {
|
||||
let endowed_accounts: Vec<AccountId> = (0..5).map(|i| [i; 32].into()).collect();
|
||||
|
||||
let genesis_storage: Storage = {
|
||||
use evm_runtime_template::{
|
||||
BalancesConfig, CollatorSelectionConfig, RuntimeGenesisConfig, SessionConfig,
|
||||
SessionKeys,
|
||||
};
|
||||
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
||||
use sp_runtime::{app_crypto::ByteArray, BuildStorage};
|
||||
|
||||
let initial_authorities: Vec<(AccountId, AuraId)> =
|
||||
vec![([0; 32].into(), AuraId::from_slice(&[0; 32]).unwrap())];
|
||||
let root: AccountId = [0; 32].into();
|
||||
|
||||
RuntimeGenesisConfig {
|
||||
system: Default::default(),
|
||||
balances: BalancesConfig {
|
||||
// Configure endowed accounts with initial balance of 1 << 60.
|
||||
balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
|
||||
},
|
||||
aura: Default::default(),
|
||||
session: SessionConfig {
|
||||
keys: initial_authorities
|
||||
.iter()
|
||||
.map(|x| (x.0, x.0, SessionKeys { aura: x.1.clone() }))
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
collator_selection: CollatorSelectionConfig {
|
||||
invulnerables: initial_authorities.iter().map(|x| x.0).collect(),
|
||||
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(),
|
||||
base_fee: Default::default(), // TODO: reconsider default value
|
||||
evm_chain_id: EVMChainIdConfig {
|
||||
chain_id: 1000, // TODO: select a good value
|
||||
..Default::default()
|
||||
},
|
||||
evm: Default::default(),
|
||||
ethereum: Default::default(),
|
||||
}
|
||||
.build_storage()
|
||||
.unwrap()
|
||||
fn generate_genesis(accounts: &[AccountId]) -> Storage {
|
||||
use evm_runtime_template::{
|
||||
BalancesConfig, CollatorSelectionConfig, RuntimeGenesisConfig, SessionConfig, SessionKeys,
|
||||
};
|
||||
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
||||
use sp_runtime::{app_crypto::ByteArray, BuildStorage};
|
||||
|
||||
ziggy::fuzz!(|data: &[u8]| {
|
||||
let mut iterable = Data::from_data(data);
|
||||
// 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();
|
||||
|
||||
// Max weight for a block.
|
||||
let max_weight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND * 2, 0);
|
||||
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(),
|
||||
base_fee: Default::default(), // TODO: reconsider default value
|
||||
evm_chain_id: EVMChainIdConfig {
|
||||
chain_id: 1000, // TODO: select a good value
|
||||
..Default::default()
|
||||
},
|
||||
evm: Default::default(),
|
||||
ethereum: Default::default(),
|
||||
}
|
||||
.build_storage()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
let extrinsics: Vec<(Option<u32>, usize, RuntimeCall)> =
|
||||
iterable.extract_extrinsics::<RuntimeCall>();
|
||||
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;
|
||||
}
|
||||
|
||||
if extrinsics.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut block: u32 = 1;
|
||||
let mut weight: Weight = 0.into();
|
||||
let mut elapsed: Duration = Duration::ZERO;
|
||||
|
||||
// `externalities` represents the state of our mock chain.
|
||||
let mut externalities = Externalities::new(genesis_storage.clone());
|
||||
BasicExternalities::execute_with_storage(&mut genesis.clone(), || {
|
||||
let initial_total_issuance = TotalIssuance::<Runtime>::get();
|
||||
|
||||
let mut current_block: u32 = 1;
|
||||
let mut current_weight: Weight = Weight::zero();
|
||||
// let mut already_seen = 0; // This must be uncommented if you want to print events
|
||||
let mut elapsed: Duration = Duration::ZERO;
|
||||
initialize_block(block);
|
||||
|
||||
let start_block = |block: u32, lapse: u32| {
|
||||
println!("\ninitializing block {}", block + lapse);
|
||||
|
||||
let next_block = block + lapse;
|
||||
let current_timestamp = INITIAL_TIMESTAMP + u64::from(next_block) * SLOT_DURATION;
|
||||
let pre_digest = match current_timestamp {
|
||||
INITIAL_TIMESTAMP => Default::default(),
|
||||
_ => Digest {
|
||||
logs: vec![DigestItem::PreRuntime(
|
||||
AURA_ENGINE_ID,
|
||||
Slot::from(current_timestamp / SLOT_DURATION).encode(),
|
||||
)],
|
||||
},
|
||||
};
|
||||
|
||||
let prev_header = match next_block {
|
||||
1 => None,
|
||||
_ => Some(Executive::finalize_block()),
|
||||
};
|
||||
|
||||
let parent_header = &Header::new(
|
||||
next_block + 1,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
prev_header.clone().map(|x| x.hash()).unwrap_or_default(),
|
||||
pre_digest,
|
||||
);
|
||||
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: next_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
|
||||
};
|
||||
|
||||
externalities.execute_with(|| start_block(current_block, 0));
|
||||
|
||||
for (maybe_lapse, origin, extrinsic) in extrinsics {
|
||||
let origin_no = origin % endowed_accounts.len();
|
||||
for (lapse, origin, extrinsic) in extrinsics {
|
||||
let origin_no = origin as usize % accounts.len();
|
||||
if !recursive_call_filter(&extrinsic, origin_no) {
|
||||
continue;
|
||||
}
|
||||
// If the lapse is in the range [0, MAX_BLOCK_LAPSE] we finalize the block and initialize
|
||||
// a new one.
|
||||
if let Some(lapse) = maybe_lapse {
|
||||
// We update our state variables
|
||||
current_weight = Weight::zero();
|
||||
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;
|
||||
|
||||
// We start the next block
|
||||
externalities.execute_with(|| start_block(current_block, lapse));
|
||||
current_block += lapse;
|
||||
initialize_block(block);
|
||||
}
|
||||
|
||||
// We get the current time for timing purposes.
|
||||
let now = Instant::now();
|
||||
|
||||
let mut call_weight = Weight::zero();
|
||||
// We compute the weight to avoid overweight blocks.
|
||||
externalities.execute_with(|| {
|
||||
call_weight = extrinsic.get_dispatch_info().weight;
|
||||
});
|
||||
|
||||
current_weight = current_weight.saturating_add(call_weight);
|
||||
if current_weight.ref_time() >= max_weight.ref_time() {
|
||||
println!("Skipping because of max weight {max_weight}");
|
||||
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;
|
||||
}
|
||||
|
||||
externalities.execute_with(|| {
|
||||
let origin_account = endowed_accounts[origin_no];
|
||||
{
|
||||
println!("\n origin: {origin_account:?}");
|
||||
println!(" call: {extrinsic:?}");
|
||||
}
|
||||
let _res = extrinsic.clone().dispatch(RuntimeOrigin::signed(origin_account));
|
||||
println!(" result: {_res:?}");
|
||||
let origin = accounts[origin_no].clone();
|
||||
|
||||
// Uncomment to print events for debugging purposes
|
||||
/*
|
||||
#[cfg(not(fuzzing))]
|
||||
{
|
||||
let all_events = statemine_runtime::System::events();
|
||||
let events: Vec<_> = all_events.clone().into_iter().skip(already_seen).collect();
|
||||
already_seen = all_events.len();
|
||||
println!(" events: {:?}\n", events);
|
||||
}
|
||||
*/
|
||||
});
|
||||
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:?}");
|
||||
}
|
||||
|
||||
println!("\n time spent: {elapsed:?}");
|
||||
assert!(elapsed.as_secs() <= MAX_TIME_FOR_BLOCK, "block execution took too much time");
|
||||
finalize_block(elapsed);
|
||||
|
||||
// We end the final block
|
||||
externalities.execute_with(|| {
|
||||
// Finilization
|
||||
Executive::finalize_block();
|
||||
// Invariants
|
||||
println!("\ntesting invariants for block {current_block}");
|
||||
<AllPalletsWithSystem as TryState<BlockNumber>>::try_state(
|
||||
current_block,
|
||||
TryStateSelect::All,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
// After execution of all blocks.
|
||||
externalities.execute_with(|| {
|
||||
// We keep track of the sum of balance of accounts
|
||||
let mut counted_free = 0;
|
||||
let mut counted_reserved = 0;
|
||||
|
||||
for acc in frame_system::Account::<Runtime>::iter() {
|
||||
// Check that the consumer/provider state is valid.
|
||||
let acc_consumers = acc.1.consumers;
|
||||
let acc_providers = acc.1.providers;
|
||||
assert!(!(acc_consumers > 0 && acc_providers == 0), "Invalid state");
|
||||
|
||||
// Increment our balance counts
|
||||
counted_free += acc.1.data.free;
|
||||
counted_reserved += acc.1.data.reserved;
|
||||
// Check that locks and holds are valid.
|
||||
let max_lock: Balance =
|
||||
Balances::locks(acc.0).iter().map(|l| l.amount).max().unwrap_or_default();
|
||||
assert_eq!(
|
||||
max_lock, acc.1.data.frozen,
|
||||
"Max lock should be equal to frozen balance"
|
||||
);
|
||||
let sum_holds: Balance =
|
||||
pallet_balances::Holds::<Runtime>::get(acc.0).iter().map(|l| l.amount).sum();
|
||||
assert!(
|
||||
sum_holds <= acc.1.data.reserved,
|
||||
"Sum of all holds ({sum_holds}) should be less than or equal to reserved \
|
||||
balance {}",
|
||||
acc.1.data.reserved
|
||||
);
|
||||
}
|
||||
|
||||
let total_issuance = pallet_balances::TotalIssuance::<Runtime>::get();
|
||||
let counted_issuance = counted_free + counted_reserved;
|
||||
// The reason we do not simply use `!=` here is that some balance might be transferred to another chain via XCM.
|
||||
// If we find some kind of workaround for this, we could replace `<` by `!=` here and make the check stronger.
|
||||
assert!(
|
||||
total_issuance <= counted_issuance,
|
||||
"Inconsistent total issuance: {total_issuance} but counted {counted_issuance}"
|
||||
);
|
||||
|
||||
println!("running integrity tests");
|
||||
// We run all developer-defined integrity tests
|
||||
<AllPalletsWithSystem as IntegrityTest>::integrity_test();
|
||||
});
|
||||
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 recursive_call_filter(call: &RuntimeCall, origin: usize) -> bool {
|
||||
match call {
|
||||
//recursion
|
||||
@@ -359,3 +269,48 @@ fn recursive_call_filter(call: &RuntimeCall, origin: usize) -> bool {
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
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 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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user