From 5d9b08f58419222e2084a55eedb94fcf348746cb Mon Sep 17 00:00:00 2001 From: librelois <c@elo.tf> Date: Tue, 15 Feb 2022 23:49:43 +0100 Subject: [PATCH] chore: add gdev runtime integration tests --- Cargo.lock | 3 + pallets/identity/src/lib.rs | 1 + runtime/gdev/Cargo.toml | 5 + runtime/gdev/tests/common/mod.rs | 425 ++++++++++++++++++++++++ runtime/gdev/tests/integration_tests.rs | 143 ++++++++ 5 files changed, 577 insertions(+) create mode 100644 runtime/gdev/tests/common/mod.rs create mode 100644 runtime/gdev/tests/integration_tests.rs diff --git a/Cargo.lock b/Cargo.lock index e847b8931..ce9d5ea77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2038,8 +2038,11 @@ dependencies = [ "sp-authority-discovery", "sp-block-builder", "sp-consensus-babe", + "sp-consensus-vrf", "sp-core", + "sp-finality-grandpa", "sp-inherents", + "sp-io", "sp-membership", "sp-offchain", "sp-runtime", diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 9602885ba..76d59fd84 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -144,6 +144,7 @@ pub mod pallet { (idty_index, idty.value.status), ) } + // frame_system::Pallet::<T>::dec_providers(&account); frame_system::Pallet::<T>::inc_sufficients(&idty.value.owner_key); <Identities<T>>::insert(idty_index, idty.value.clone()); IdentitiesNames::<T>::insert(idty.name.clone(), ()); diff --git a/runtime/gdev/Cargo.toml b/runtime/gdev/Cargo.toml index f74d42564..fb66fe924 100644 --- a/runtime/gdev/Cargo.toml +++ b/runtime/gdev/Cargo.toml @@ -73,6 +73,11 @@ std = [ 'sp-version/std', ] +[dev-dependencies] +sp-consensus-vrf = { git = 'https://github.com/librelois/substrate.git', branch = 'duniter-monthly-2022-02' } +sp-finality-grandpa = { git = 'https://github.com/librelois/substrate.git', branch = 'duniter-monthly-2022-02' } +sp-io = { git = 'https://github.com/librelois/substrate.git', branch = 'duniter-monthly-2022-02' } + [dependencies] common-runtime = { path = "../common", default-features = false } pallet-authority-members = { path = '../../pallets/authority-members', default-features = false } diff --git a/runtime/gdev/tests/common/mod.rs b/runtime/gdev/tests/common/mod.rs new file mode 100644 index 000000000..8cb21971d --- /dev/null +++ b/runtime/gdev/tests/common/mod.rs @@ -0,0 +1,425 @@ +// Copyright 2021 Axiom-Team +// +// This file is part of Substrate-Libre-Currency. +// +// Substrate-Libre-Currency is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// Substrate-Libre-Currency is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. + +#![allow(dead_code, unused_imports)] // TODO remove this line when we will have more tests + +use common_runtime::constants::*; +use common_runtime::*; +use frame_support::instances::{Instance1, Instance2}; +use frame_support::traits::{GenesisBuild, OnFinalize, OnInitialize}; +use gdev_runtime::opaque::SessionKeys; +use gdev_runtime::*; +use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_consensus_babe::{AuthorityId as BabeId, Slot}; +use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; +use sp_core::crypto::IsWrappedBy; +use sp_core::sr25519; +use sp_core::{Encode, Pair, Public}; +use sp_finality_grandpa::AuthorityId as GrandpaId; +use sp_membership::MembershipData; +use sp_runtime::testing::{Digest, DigestItem}; +use sp_runtime::traits::{IdentifyAccount, Verify}; +use std::collections::BTreeMap; + +pub type AccountPublic = <Signature as Verify>::Signer; + +pub type AuthorityKeys = ( + AccountId, + BabeId, + GrandpaId, + ImOnlineId, + AuthorityDiscoveryId, +); + +pub const NAMES: [&str; 6] = ["Alice", "Bob", "Charlie", "Dave", "Eve", "Ferdie"]; + +//pub const CHARLIE: &str = "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"; +pub const DAVE: &str = "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy"; + +pub struct ExtBuilder { + // endowed accounts with balances + initial_authorities_len: usize, + initial_balances: Vec<(AccountId, Balance)>, + initial_identities: BTreeMap<IdtyName, AccountId>, + initial_smiths: Vec<AuthorityKeys>, + parameters: GenesisParameters<u32, u32, Balance>, +} + +impl ExtBuilder { + pub fn new( + initial_authorities_len: usize, + initial_smiths_len: usize, + initial_identities_len: usize, + ) -> Self { + assert!(initial_identities_len <= 6); + assert!(initial_smiths_len <= initial_identities_len); + assert!(initial_authorities_len <= initial_smiths_len); + + let initial_smiths = (0..initial_smiths_len) + .map(|i| get_authority_keys_from_seed(NAMES[i])) + .collect::<Vec<AuthorityKeys>>(); + let initial_identities = (0..initial_identities_len) + .map(|i| { + ( + IdtyName::from(NAMES[i]), + get_account_id_from_seed::<sr25519::Public>(NAMES[i]), + ) + }) + .collect::<BTreeMap<IdtyName, AccountId>>(); + + Self { + initial_authorities_len, + initial_balances: vec![], + initial_identities, + initial_smiths, + parameters: GenesisParameters { + babe_epoch_duration: 25, + cert_period: 15, + cert_max_by_issuer: 10, + cert_min_received_cert_to_issue_cert: 2, + cert_renewable_period: 50, + cert_validity_period: 10_000, + idty_confirm_period: 40, + idty_creation_period: 50, + idty_max_disabled_period: 1_000, + membership_period: 1_000, + membership_renewable_period: 5, + pending_membership_period: 500, + ud_creation_period: 10, + ud_first_reeval: 100, + ud_reeval_period: 20, + ud_reeval_period_in_blocks: 10 * 20, + smith_cert_period: 15, + smith_cert_max_by_issuer: 8, + smith_cert_min_received_cert_to_issue_cert: 2, + smith_cert_renewable_period: 50, + smith_cert_validity_period: 1_000, + smith_membership_period: 1_000, + smith_membership_renewable_period: 50, + smith_pending_membership_period: 500, + smiths_wot_first_cert_issuable_on: 20, + smiths_wot_min_cert_for_membership: 2, + wot_first_cert_issuable_on: 20, + wot_min_cert_for_create_idty_right: 2, + wot_min_cert_for_membership: 2, + }, + } + } + + /*pub fn with_initial_balances(mut self, initial_balances: Vec<(AccountId, Balance)>) -> Self { + self.initial_balances = initial_balances; + self + } + + pub fn with_parameters(mut self, parameters: GenesisParameters<u32, u32, Balance>) -> Self { + self.parameters = parameters; + self + }*/ + + pub fn build(self) -> sp_io::TestExternalities { + let Self { + initial_authorities_len, + initial_balances, + initial_identities, + initial_smiths, + parameters, + } = self; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::<Runtime>() + .unwrap(); + + pallet_authority_members::GenesisConfig::<Runtime> { + initial_authorities: initial_smiths + .iter() + .enumerate() + .map(|(i, keys)| (i as u32 + 1, (keys.0.clone(), i < initial_authorities_len))) + .collect(), + } + .assimilate_storage(&mut t) + .unwrap(); + + /*pallet_babe::GenesisConfig { + authorities: Vec::with_capacity(0), + epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG), + } + .assimilate_storage(&mut t) + .unwrap();*/ + + pallet_balances::GenesisConfig::<Runtime> { + balances: initial_balances, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_duniter_test_parameters::GenesisConfig::<Runtime> { parameters } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_session::GenesisConfig::<Runtime> { + keys: initial_smiths + .iter() + .map(|x| { + ( + x.0.clone(), + x.0.clone(), + session_keys(x.1.clone(), x.2.clone(), x.3.clone(), x.4.clone()), + ) + }) + .collect::<Vec<_>>(), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_identity::GenesisConfig::<Runtime> { + identities: initial_identities + .iter() + .enumerate() + .map(|(i, (name, owner_key))| GenesisIdty { + index: i as u32 + 1, + name: name.clone(), + value: IdtyValue { + next_creatable_identity_on: Default::default(), + owner_key: owner_key.clone(), + removable_on: 0, + status: IdtyStatus::Validated, + }, + }) + .collect(), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_membership::GenesisConfig::<Runtime, Instance1> { + memberships: (1..=initial_identities.len()) + .map(|i| { + ( + i as u32, + MembershipData { + expire_on: parameters.membership_period, + renewable_on: parameters.membership_renewable_period, + }, + ) + }) + .collect(), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_certification::GenesisConfig::<Runtime, Instance1> { + apply_cert_period_at_genesis: false, + certs_by_issuer: clique_wot(initial_identities.len(), parameters.cert_validity_period), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_membership::GenesisConfig::<Runtime, Instance2> { + memberships: (1..=initial_smiths.len()) + .map(|i| { + ( + i as u32, + MembershipData { + expire_on: parameters.smith_membership_period, + renewable_on: parameters.smith_membership_renewable_period, + }, + ) + }) + .collect(), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_certification::GenesisConfig::<Runtime, Instance2> { + apply_cert_period_at_genesis: false, + certs_by_issuer: clique_wot( + initial_smiths.len(), + parameters.smith_cert_validity_period, + ), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_ud_accounts_storage::GenesisConfig::<Runtime> { + ud_accounts: initial_identities + .values() + .cloned() + .enumerate() + .map(|(i, account)| (account, (i + 1) as u32)) + .collect(), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_universal_dividend::GenesisConfig::<Runtime> { + first_ud: 1_000, + initial_monetary_mass: 0, + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + + ext.execute_with(|| { + System::set_block_number(1); + }); + + ext + } +} + +/// Generate a crypto pair from seed. +pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +/// Generate an account ID from seed. +pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId +where + AccountPublic: From<<TPublic::Pair as Pair>::Public>, +{ + AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account() +} + +/// Generate an authority keys. +pub fn get_authority_keys_from_seed(s: &str) -> AuthorityKeys { + ( + get_account_id_from_seed::<sr25519::Public>(s), + get_from_seed::<BabeId>(s), + get_from_seed::<GrandpaId>(s), + get_from_seed::<ImOnlineId>(s), + get_from_seed::<AuthorityDiscoveryId>(s), + ) +} + +pub fn run_to_block(n: u32) { + while System::block_number() < n { + // Finalize the previous block + //Babe::on_finalize(System::block_number()); + //Timestamp::on_finalize(System::block_number()); + TransactionPayment::on_finalize(System::block_number()); + Authorship::on_finalize(System::block_number()); + Grandpa::on_finalize(System::block_number()); + + // Set the new block number and author + System::reset_events(); + System::set_block_number(System::block_number() + 1); + + // Initialize the new block + Scheduler::on_initialize(System::block_number()); + //Babe::on_initialize(System::block_number()); + Authorship::on_initialize(System::block_number()); + Session::on_initialize(System::block_number()); + + UniversalDividend::on_initialize(System::block_number()); + Wot::on_initialize(System::block_number()); + Identity::on_initialize(System::block_number()); + Membership::on_initialize(System::block_number()); + Cert::on_initialize(System::block_number()); + SmithsSubWot::on_initialize(System::block_number()); + SmithsMembership::on_initialize(System::block_number()); + SmithsCert::on_initialize(System::block_number()); + } +} + +/*pub fn make_primary_pre_digest( + authority_index: sp_consensus_babe::AuthorityIndex, + slot: sp_consensus_babe::Slot, + vrf_output: VRFOutput, + vrf_proof: VRFProof, +) -> Digest { + let digest_data = sp_consensus_babe::digests::PreDigest::Primary( + sp_consensus_babe::digests::PrimaryPreDigest { + authority_index, + slot, + vrf_output, + vrf_proof, + }, + ); + let log = DigestItem::PreRuntime(sp_consensus_babe::BABE_ENGINE_ID, digest_data.encode()); + Digest { logs: vec![log] } +} + +pub fn make_vrf_output( + slot: Slot, + pair: &sp_consensus_babe::AuthorityPair, +) -> (VRFOutput, VRFProof, [u8; 32]) { + let pair = sp_core::sr25519::Pair::from_ref(pair).as_ref(); + let transcript = sp_consensus_babe::make_transcript(&Babe::randomness(), slot, 0); + let vrf_inout = pair.vrf_sign(transcript); + let vrf_randomness: sp_consensus_vrf::schnorrkel::Randomness = vrf_inout + .0 + .make_bytes::<[u8; 32]>(&sp_consensus_babe::BABE_VRF_INOUT_CONTEXT); + let vrf_output = VRFOutput(vrf_inout.0.to_output()); + let vrf_proof = VRFProof(vrf_inout.1); + + (vrf_output, vrf_proof, vrf_randomness) +} + +pub fn make_secondary_vrf_pre_digest( + authority_index: sp_consensus_babe::AuthorityIndex, + slot: sp_consensus_babe::Slot, + vrf_output: VRFOutput, + vrf_proof: VRFProof, +) -> Digest { + let digest_data = sp_consensus_babe::digests::PreDigest::SecondaryVRF( + sp_consensus_babe::digests::SecondaryVRFPreDigest { + authority_index, + slot, + vrf_output, + vrf_proof, + }, + ); + let log = DigestItem::PreRuntime(sp_consensus_babe::BABE_ENGINE_ID, digest_data.encode()); + Digest { logs: vec![log] } +}*/ + +fn clique_wot( + initial_identities_len: usize, + cert_validity_period: common_runtime::BlockNumber, +) -> BTreeMap<IdtyIndex, BTreeMap<IdtyIndex, common_runtime::BlockNumber>> { + let mut certs_by_issuer = BTreeMap::new(); + for i in 1..=initial_identities_len { + certs_by_issuer.insert( + i as IdtyIndex, + (1..=initial_identities_len) + .filter_map(|j| { + if i != j { + Some((j as IdtyIndex, cert_validity_period)) + } else { + None + } + }) + .collect(), + ); + } + certs_by_issuer +} + +fn session_keys( + babe: BabeId, + grandpa: GrandpaId, + im_online: ImOnlineId, + authority_discovery: AuthorityDiscoveryId, +) -> SessionKeys { + SessionKeys { + babe, + grandpa, + im_online, + authority_discovery, + } +} diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs new file mode 100644 index 000000000..655f6186b --- /dev/null +++ b/runtime/gdev/tests/integration_tests.rs @@ -0,0 +1,143 @@ +// Copyright 2021 Axiom-Team +// +// This file is part of Substrate-Libre-Currency. +// +// Substrate-Libre-Currency is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// Substrate-Libre-Currency is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. + +mod common; + +use common::*; +use frame_support::traits::{PalletInfo, StorageInfo, StorageInfoTrait}; +//use frame_support::{assert_err, assert_ok}; +use frame_support::assert_ok; +use frame_support::{StorageHasher, Twox128}; +use gdev_runtime::*; +use sp_core::crypto::Ss58Codec; + +#[test] +fn verify_pallet_prefixes() { + let prefix = |pallet_name, storage_name| { + let mut res = [0u8; 32]; + res[0..16].copy_from_slice(&Twox128::hash(pallet_name)); + res[16..32].copy_from_slice(&Twox128::hash(storage_name)); + res.to_vec() + }; + assert_eq!( + <Timestamp as StorageInfoTrait>::storage_info(), + vec![ + StorageInfo { + pallet_name: b"Timestamp".to_vec(), + storage_name: b"Now".to_vec(), + prefix: prefix(b"Timestamp", b"Now"), + max_values: Some(1), + max_size: Some(8), + }, + StorageInfo { + pallet_name: b"Timestamp".to_vec(), + storage_name: b"DidUpdate".to_vec(), + prefix: prefix(b"Timestamp", b"DidUpdate"), + max_values: Some(1), + max_size: Some(1), + } + ] + ); +} + +#[test] +fn verify_pallet_indices() { + fn is_pallet_index<P: 'static>(index: usize) { + assert_eq!( + <Runtime as frame_system::Config>::PalletInfo::index::<P>(), + Some(index) + ); + } + is_pallet_index::<System>(0); +} + +#[test] +fn verify_proxy_type_indices() { + assert_eq!(ProxyType::Any as u8, 0); +} + +#[test] +fn test_genesis_build() { + ExtBuilder::new(1, 3, 4).build().execute_with(|| { + run_to_block(2); + }); +} + +#[test] +fn test_remove_identity() { + ExtBuilder::new(1, 3, 4).build().execute_with(|| { + run_to_block(2); + + assert_ok!(Identity::remove_identity( + frame_system::RawOrigin::Root.into(), + 4, + None + )); + let events = System::events(); + assert_eq!(events.len(), 3); + assert_eq!( + System::events()[0].event, + Event::Membership(pallet_membership::Event::MembershipRevoked(4)) + ); + /*println!( + "{}", + get_account_id_from_seed::<sp_core::sr25519::Public>("Charlie") + );*/ + assert_eq!( + System::events()[1].event, + Event::System(frame_system::Event::KilledAccount { + account: AccountId::from_ss58check(DAVE).unwrap() + }) + ); + assert_eq!( + System::events()[2].event, + Event::Identity(pallet_identity::Event::IdtyRemoved { idty_index: 4 }) + ); + //println!("{:#?}", events); + }); +} + +#[test] +fn test_remove_smith_identity() { + ExtBuilder::new(1, 3, 4).build().execute_with(|| { + run_to_block(2); + + assert_ok!(Identity::remove_identity( + frame_system::RawOrigin::Root.into(), + 3, + None + )); + let events = System::events(); + assert_eq!(events.len(), 4); + assert_eq!( + System::events()[0].event, + Event::AuthorityMembers(pallet_authority_members::Event::MemberRemoved(3)) + ); + assert_eq!( + System::events()[1].event, + Event::SmithsMembership(pallet_membership::Event::MembershipRevoked(3)) + ); + assert_eq!( + System::events()[2].event, + Event::Membership(pallet_membership::Event::MembershipRevoked(3)) + ); + assert_eq!( + System::events()[3].event, + Event::Identity(pallet_identity::Event::IdtyRemoved { idty_index: 3 }) + ); + //println!("{:#?}", events); + }); +} -- GitLab