Skip to content
Snippets Groups Projects
mod.rs 17 KiB
Newer Older
// Copyright 2021 Axiom-Team
//
// This file is part of Duniter-v2S.
// Duniter-v2S 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.
//
// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
#![allow(dead_code, unused_imports)]
use common_runtime::{constants::*, *};
use frame_support::traits::{OnFinalize, OnInitialize};
use gdev_runtime::{opaque::SessionKeys, *};
use pallet_authority_members::OnNewSession;
use pallet_smith_members::SmithMeta;
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use sp_consensus_babe::{AuthorityId as BabeId, Slot, VrfInput, VrfProof};
use sp_consensus_grandpa::AuthorityId as GrandpaId;
use sp_core::{crypto::IsWrappedBy, sr25519, Encode, Pair, Public, H256};
use sp_keyring::AccountKeyring;
use sp_membership::MembershipData;
    generic::SignedPayload,
    testing::{Digest, DigestItem},
    traits::{Extrinsic, IdentifyAccount, Verify},
use std::collections::BTreeMap;

pub type AccountPublic = <Signature as Verify>::Signer;

pub type AuthorityKeys = (
    AccountId,
    BabeId,
    GrandpaId,
    ImOnlineId,
    AuthorityDiscoveryId,
);

pub const BLOCK_TIME: u64 = 6_000;
pub const NAMES: [&str; 6] = ["Alice", "Bob", "Charlie", "Dave", "Eve", "Ferdie"];

pub struct ExtBuilder {
    // endowed accounts with balances
    initial_accounts: BTreeMap<AccountId, GenesisAccountData<Balance, u32>>,
    initial_authorities_len: usize,
    initial_identities: BTreeMap<IdtyName, AccountId>,
    initial_smiths: Vec<AuthorityKeys>,
    parameters: GenesisParameters<u32, u32, Balance, u32>,
}

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_accounts = (0..initial_identities_len)
            .map(|i| {
                (
                    get_account_id_from_seed::<sr25519::Public>(NAMES[i]),
                    GenesisAccountData {
                        balance: 0,
                    },
                )
            })
            .collect::<BTreeMap<_, _>>();
        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>>();
        let initial_smiths = (0..initial_smiths_len)
            .map(|i| get_authority_keys_from_seed(NAMES[i]))
            .collect::<Vec<AuthorityKeys>>();
            initial_authorities_len,
            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_validity_period: 10_000,
                idty_confirm_period: 40,
                idty_creation_period: 50,
                membership_renewal_period: 10,
                ud_creation_period: 60_000,
                ud_reeval_period: 60_000 * 20,
                smith_cert_max_by_issuer: 8,
                smith_wot_min_cert_for_membership: 2,
                smith_inactivity_max_duration: 48,
                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 {
        for (account_id, balance) in initial_balances {
            self.initial_accounts
                .entry(account_id.clone())
                .or_insert(GenesisAccountData {
                    ..Default::default()
                })
                .balance = balance;
        }
    /*pub fn with_parameters(mut self, parameters: GenesisParameters<u32, u32, Balance>) -> Self {
        self.parameters = parameters;
        self
    }*/

        F: Fn(&mut pallet_duniter_test_parameters::Parameters<u32, u32, Balance, u32>),
    >(
        mut self,
        f: F,
    ) -> Self {
        f(&mut self.parameters);
        self
    }

    pub fn build(self) -> sp_io::TestExternalities {
        let Self {
            initial_authorities_len,
            initial_identities,
            initial_smiths,
            parameters,
        } = self;

        let mut t = frame_system::GenesisConfig::<Runtime>::default()
            .build_storage()
        // compute total monetary mass
        let monetary_mass = initial_accounts
            .values()
            .map(|balance| balance.balance)
            .sum();

        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_duniter_account::GenesisConfig::<Runtime> {
            accounts: initial_accounts.clone(),
            treasury_balance: <Runtime as pallet_balances::Config>::ExistentialDeposit::get(),
        }
        .assimilate_storage(&mut t)
        .unwrap();

        // Necessary to initialize TotalIssuance
        pallet_balances::GenesisConfig::<Runtime> {
            total_issuance: monetary_mass,
        }
        .assimilate_storage(&mut t)
        .unwrap();

        pallet_duniter_test_parameters::GenesisConfig::<Runtime> {
            parameters: parameters.clone(),
        }
        .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<_>>(),
            non_authority_keys: Vec::new(),
        }
        .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 {
                        data: IdtyData {
                            first_eligible_ud: pallet_universal_dividend::FirstEligibleUd::min(),
                        },
                        next_creatable_identity_on: Default::default(),
                        owner_key: owner_key.clone(),
                        next_scheduled: 0,
                        status: IdtyStatus::Member,
                    },
                })
                .collect(),
        }
        .assimilate_storage(&mut t)
        .unwrap();

        pallet_quota::GenesisConfig::<Runtime> {
            identities: initial_identities
                .iter()
                .enumerate()
                .map(|(i, _)| i as u32 + 1)
                .collect(),
        }
        .assimilate_storage(&mut t)
        .unwrap();

        pallet_membership::GenesisConfig::<Runtime> {
            memberships: (1..=initial_identities.len())
                .map(|i| {
                    (
                        i as u32,
                        MembershipData {
                            expire_on: parameters.membership_period,
                        },
                    )
                })
                .collect(),
        }
        .assimilate_storage(&mut t)
        .unwrap();

        pallet_certification::GenesisConfig::<Runtime> {
            certs_by_receiver: clique_wot(
                initial_identities.len(),
                parameters.cert_validity_period,
            ),
            apply_cert_period_at_genesis: false,
        }
        .assimilate_storage(&mut t)
        .unwrap();

        pallet_smith_members::GenesisConfig::<Runtime> {
            initial_smiths: clique_smith_wot(initial_smiths.len()),
        }
        .assimilate_storage(&mut t)
        .unwrap();

        pallet_universal_dividend::GenesisConfig::<Runtime> {
            first_reeval: Some(600_000),
            first_ud: Some(24_000),
            initial_monetary_mass: monetary_mass,
            ud: 1_000,
        }
        .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),
    )
}

fn mock_babe_initialize(n: u32) {
    let slot: sp_consensus_babe::Slot = (n as u64).into();
    pallet_babe::CurrentSlot::<Runtime>::put(slot);
    // force epoch change based on session
    let session = Session::current_index() as u64;
    pallet_babe::EpochIndex::<Runtime>::put(session);
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());
        Distance::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);
        // Reset the block weight
        System::set_block_consumed_resources(Weight::zero(), 0_usize);
        // Current slot is not incremented by BABE
        pallet_babe::CurrentSlot::<Runtime>::put(pallet_babe::CurrentSlot::<Runtime>::get() + 1);

        // Initialize the new block
        Account::on_initialize(System::block_number());
        Scheduler::on_initialize(System::block_number());
        Session::on_initialize(System::block_number());
        mock_babe_initialize(System::block_number());
        Authorship::on_initialize(System::block_number());

        /*if session_before != session_after {
            Distance::on_new_session(session_after);
        } else {
            use pallet_session::ShouldEndSession;
            assert!(!Babe::should_end_session(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());
        Certification::on_initialize(System::block_number());

        Timestamp::set_timestamp(System::block_number() as u64 * BLOCK_TIME);
        Distance::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, Option<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, Some(cert_validity_period)))
                    } else {
                        None
                    }
                })
                .collect(),
        );
    }
    certs_by_issuer
}

fn clique_smith_wot(initial_identities_len: usize) -> BTreeMap<IdtyIndex, (bool, Vec<IdtyIndex>)> {
    let mut certs_by_issuer = BTreeMap::new();
    for i in 1..=initial_identities_len {
        certs_by_issuer.insert(
            i as IdtyIndex,
            (
                true,
                (1..=initial_identities_len)
                    .filter_map(|j| if i != j { Some(j as IdtyIndex) } 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,
    }
}

/// get extrinsic for given call
pub fn get_unchecked_extrinsic(
    call: RuntimeCall,
    era: u64,
    block: u64,
    signer: AccountKeyring,
    tip: Balance,
    nonce: u32,
) -> gdev_runtime::UncheckedExtrinsic {
    let extra: gdev_runtime::SignedExtra = (
        frame_system::CheckNonZeroSender::<gdev_runtime::Runtime>::new(),
        frame_system::CheckSpecVersion::<gdev_runtime::Runtime>::new(),
        frame_system::CheckTxVersion::<gdev_runtime::Runtime>::new(),
        frame_system::CheckGenesis::<gdev_runtime::Runtime>::new(),
        frame_system::CheckMortality::<gdev_runtime::Runtime>::from(
            sp_runtime::generic::Era::mortal(era, block),
        ),
        frame_system::CheckNonce::<gdev_runtime::Runtime>::from(nonce).into(),
        frame_system::CheckWeight::<gdev_runtime::Runtime>::new(),
        pallet_transaction_payment::ChargeTransactionPayment::<gdev_runtime::Runtime>::from(tip),
    );
    let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap();
    let origin = signer;
    let sig = payload.using_encoded(|payload| origin.pair().sign(payload));

    gdev_runtime::UncheckedExtrinsic::new(
        call,
        Some((origin.to_account_id().into(), sig.into(), extra)),
    )
    .unwrap()
}