From 6736a4f4f9be1e81938062876b66fffb4f10e6e1 Mon Sep 17 00:00:00 2001 From: librelois <c@elo.tf> Date: Sun, 20 Feb 2022 17:46:21 +0100 Subject: [PATCH] feat(runtimes): define random id for genesis accounts at genesis --- Cargo.lock | 1 + node/src/chain_spec/gdev.rs | 17 +++- node/src/chain_spec/gen_genesis_data.rs | 32 +++--- pallets/duniter-account/src/lib.rs | 32 +++++- pallets/duniter-account/src/types.rs | 11 ++ pallets/duniter-wot/src/mock.rs | 5 + pallets/identity/src/lib.rs | 1 - runtime/common/src/lib.rs | 1 + runtime/common/src/pallets_config.rs | 4 +- runtime/gdev/Cargo.toml | 1 + runtime/gdev/tests/common/mod.rs | 48 ++++++--- runtime/gdev/tests/integration_tests.rs | 129 +++++++++++++++++++++++- 12 files changed, 239 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef4a5bba7..c6f2cb862 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2049,6 +2049,7 @@ dependencies = [ "sp-finality-grandpa", "sp-inherents", "sp-io", + "sp-keyring", "sp-membership", "sp-offchain", "sp-runtime", diff --git a/node/src/chain_spec/gdev.rs b/node/src/chain_spec/gdev.rs index 2e0e830ce..229ab5462 100644 --- a/node/src/chain_spec/gdev.rs +++ b/node/src/chain_spec/gdev.rs @@ -26,7 +26,7 @@ use gdev_runtime::{ use sc_service::ChainType; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; -use sp_core::sr25519; +use sp_core::{blake2_256, sr25519, Encode, H256}; use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_membership::MembershipData; use std::collections::BTreeMap; @@ -242,7 +242,17 @@ fn gen_genesis_conf( account: AccountConfig { accounts: initial_identities .iter() - .map(|(_, owner_key)| owner_key.clone()) + .enumerate() + .map(|(i, (_, owner_key))| { + ( + owner_key.clone(), + GenesisAccountData { + random_id: H256(blake2_256(&(i as u32, owner_key).encode())), + balance: 0, + is_identity: true, + }, + ) + }) .collect(), }, parameters: ParametersConfig { @@ -403,7 +413,6 @@ fn genesis_data_to_gdev_genesis_conf( ) -> gdev_runtime::GenesisConfig { let super::gen_genesis_data::GenesisData { accounts, - balances, certs_by_issuer, first_ud, identities, @@ -429,7 +438,7 @@ fn genesis_data_to_gdev_genesis_conf( authority_members: AuthorityMembersConfig { initial_authorities, }, - balances: BalancesConfig { balances }, + balances: Default::default(), babe: BabeConfig { authorities: Vec::with_capacity(0), epoch_config: Some(common_runtime::constants::BABE_GENESIS_EPOCH_CONFIG), diff --git a/node/src/chain_spec/gen_genesis_data.rs b/node/src/chain_spec/gen_genesis_data.rs index 1176adc1e..492402a34 100644 --- a/node/src/chain_spec/gen_genesis_data.rs +++ b/node/src/chain_spec/gen_genesis_data.rs @@ -16,15 +16,14 @@ use common_runtime::*; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use sp_core::Decode; -use std::collections::{BTreeMap, BTreeSet}; +use sp_core::{blake2_256, Decode, Encode, H256}; +use std::collections::BTreeMap; type MembershipData = sp_membership::MembershipData<u32>; #[derive(Clone)] pub struct GenesisData<Parameters: DeserializeOwned, SessionKeys: Decode> { - pub accounts: BTreeSet<AccountId>, - pub balances: Vec<(AccountId, u64)>, + pub accounts: BTreeMap<AccountId, GenesisAccountData<u64>>, pub certs_by_issuer: BTreeMap<u32, BTreeMap<u32, u32>>, pub first_ud: u64, pub identities: Vec<(String, AccountId)>, @@ -138,13 +137,13 @@ where // MONEY ANDÂ WOT // - let mut accounts = BTreeSet::new(); - let mut balances = Vec::new(); + let mut accounts = BTreeMap::new(); let mut identities_ = Vec::with_capacity(identities.len()); let mut idty_index: u32 = 1; let mut idty_index_of = BTreeMap::new(); let mut initial_monetary_mass = 0; let mut memberships = BTreeMap::new(); + //let mut total_dust = 0; let mut ud_accounts = BTreeMap::new(); for (idty_name, identity) in &identities { if !validate_idty_name(idty_name) { @@ -152,13 +151,21 @@ where } // Money - if identity.balance >= 100 { - balances.push((identity.pubkey.clone(), identity.balance)); + let balance = if identity.balance >= 100 { + identity.balance } else { - // If an identity has no currency in genesis, its account will not be created, - // so it must be created explicitly - accounts.insert(identity.pubkey.clone()); - } + //total_dust += identity.balance; + 0 + }; + accounts.insert( + identity.pubkey.clone(), + GenesisAccountData { + random_id: H256(blake2_256(&(idty_index, &identity.pubkey).encode())), + balance, + is_identity: true, + }, + ); + // We must count the money under the existential deposit because what we count is // the monetary mass created (for the revaluation of the DU) initial_monetary_mass += identity.balance; @@ -248,7 +255,6 @@ where let genesis_data = GenesisData { accounts, - balances, certs_by_issuer, first_ud, identities: identities_, diff --git a/pallets/duniter-account/src/lib.rs b/pallets/duniter-account/src/lib.rs index fc862331f..012f9797d 100644 --- a/pallets/duniter-account/src/lib.rs +++ b/pallets/duniter-account/src/lib.rs @@ -73,7 +73,8 @@ pub mod pallet { #[pallet::genesis_config] pub struct GenesisConfig<T: Config> { - pub accounts: sp_std::collections::btree_set::BTreeSet<T::AccountId>, + pub accounts: + sp_std::collections::btree_map::BTreeMap<T::AccountId, GenesisAccountData<T::Balance>>, } #[cfg(feature = "std")] @@ -88,8 +89,26 @@ pub mod pallet { #[pallet::genesis_build] impl<T: Config> GenesisBuild<T> for GenesisConfig<T> { fn build(&self) { - for account_id in &self.accounts { - PendingNewAccounts::<T>::insert(account_id.clone(), ()); + for ( + account_id, + GenesisAccountData { + random_id, + balance, + is_identity, + }, + ) in &self.accounts + { + assert!(!balance.is_zero() || *is_identity); + frame_system::Account::<T>::mutate(account_id, |account| { + account.data.random_id = Some(*random_id); + if !balance.is_zero() { + account.data.free = *balance; + account.providers = 1; + } + if *is_identity { + account.sufficients = 1; + } + }); } } } @@ -155,6 +174,11 @@ pub mod pallet { total_weight += 200_000; } else { // The charges could not be deducted, we slash the account + let res = frame_system::Pallet::<T>::dec_providers(&account_id); + debug_assert!( + res.is_ok(), + "Cannot fail because providers are incremented just before" + ); let account_data = frame_system::Pallet::<T>::get(&account_id); let (imbalance, rest) = pallet_balances::Pallet::<T>::slash( &account_id, @@ -227,7 +251,7 @@ where f: impl FnOnce(&mut Option<pallet_balances::AccountData<Balance>>) -> Result<R, E>, ) -> Result<R, E> { let account = frame_system::Account::<T>::get(account_id); - let was_providing = account.data != Default::default(); + let was_providing = account.data.was_providing(); let mut some_data = if was_providing { Some(account.data.into()) } else { diff --git a/pallets/duniter-account/src/types.rs b/pallets/duniter-account/src/types.rs index 92d5494db..25135c3f6 100644 --- a/pallets/duniter-account/src/types.rs +++ b/pallets/duniter-account/src/types.rs @@ -34,6 +34,9 @@ impl<Balance: Zero> AccountData<Balance> { self.reserved = new_balances.reserved; self.fee_frozen = new_balances.fee_frozen; } + pub fn was_providing(&self) -> bool { + !self.free.is_zero() || !self.reserved.is_zero() + } } impl<Balance: Zero> From<AccountData<Balance>> for pallet_balances::AccountData<Balance> { @@ -46,3 +49,11 @@ impl<Balance: Zero> From<AccountData<Balance>> for pallet_balances::AccountData< } } } + +#[derive(Clone, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] +pub struct GenesisAccountData<Balance> { + pub random_id: H256, + pub balance: Balance, + pub is_identity: bool, +} diff --git a/pallets/duniter-wot/src/mock.rs b/pallets/duniter-wot/src/mock.rs index 123dbf7de..e3c39d5e1 100644 --- a/pallets/duniter-wot/src/mock.rs +++ b/pallets/duniter-wot/src/mock.rs @@ -214,6 +214,11 @@ pub fn new_test_ext(initial_identities_len: usize) -> sp_io::TestExternalities { .unwrap(); frame_support::BasicExternalities::execute_with_storage(&mut t, || { + // manually increment genesis identities sufficient counter + // In real world, this should be handle manually by genesis creator + for i in 1..=initial_identities_len { + frame_system::Pallet::<Test>::inc_sufficients(&(i as u64)); + } // Some dedicated test account frame_system::Pallet::<Test>::inc_providers(&(initial_identities_len as u64)); frame_system::Pallet::<Test>::inc_providers(&(initial_identities_len as u64 + 1)); diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 00f0ed190..f76323b09 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -144,7 +144,6 @@ pub mod pallet { (idty_index, idty.value.status), ) } - frame_system::Pallet::<T>::inc_sufficients(&idty.value.owner_key); <Identities<T>>::insert(idty_index, idty.value.clone()); IdentitiesNames::<T>::insert(idty.name.clone(), ()); IdentityIndexOf::<T>::insert(idty.value.owner_key, idty_index); diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 3bf42004d..68dd55784 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -25,6 +25,7 @@ mod pallets_config; pub mod providers; pub mod weights; +pub use pallet_duniter_account::GenesisAccountData; pub use pallet_identity::{GenesisIdty, IdtyName, IdtyStatus, IdtyValue}; pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index 1dae66af8..1182f72a0 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -114,8 +114,8 @@ macro_rules! pallets_config { impl pallet_duniter_account::Config for Runtime { type AccountIdToSalt = sp_runtime::traits::ConvertInto; type Event = Event; - type MaxNewAccountsPerBlock = frame_support::pallet_prelude::ConstU32<100>; - type NewAccountPrice = frame_support::traits::ConstU64<200>; + type MaxNewAccountsPerBlock = frame_support::pallet_prelude::ConstU32<1>; + type NewAccountPrice = frame_support::traits::ConstU64<300>; } // BLOCK CREATION // diff --git a/runtime/gdev/Cargo.toml b/runtime/gdev/Cargo.toml index 9a325078a..c584dd980 100644 --- a/runtime/gdev/Cargo.toml +++ b/runtime/gdev/Cargo.toml @@ -79,6 +79,7 @@ std = [ 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' } +sp-keyring = { git = 'https://github.com/librelois/substrate.git', branch = 'duniter-monthly-2022-02' } [dependencies] common-runtime = { path = "../common", default-features = false } diff --git a/runtime/gdev/tests/common/mod.rs b/runtime/gdev/tests/common/mod.rs index 8cb21971d..5c1142379 100644 --- a/runtime/gdev/tests/common/mod.rs +++ b/runtime/gdev/tests/common/mod.rs @@ -27,7 +27,7 @@ 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_core::{Encode, Pair, Public, H256}; use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_membership::MembershipData; use sp_runtime::testing::{Digest, DigestItem}; @@ -46,13 +46,10 @@ pub type AuthorityKeys = ( 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_accounts: BTreeMap<AccountId, GenesisAccountData<Balance>>, initial_authorities_len: usize, - initial_balances: Vec<(AccountId, Balance)>, initial_identities: BTreeMap<IdtyName, AccountId>, initial_smiths: Vec<AuthorityKeys>, parameters: GenesisParameters<u32, u32, Balance>, @@ -68,9 +65,18 @@ impl ExtBuilder { 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_accounts = (0..initial_identities_len) + .map(|i| { + ( + get_account_id_from_seed::<sr25519::Public>(NAMES[i]), + GenesisAccountData { + balance: 0, + is_identity: true, + random_id: H256([i as u8; 32]), + }, + ) + }) + .collect::<BTreeMap<_, _>>(); let initial_identities = (0..initial_identities_len) .map(|i| { ( @@ -79,10 +85,13 @@ impl ExtBuilder { ) }) .collect::<BTreeMap<IdtyName, AccountId>>(); + let initial_smiths = (0..initial_smiths_len) + .map(|i| get_authority_keys_from_seed(NAMES[i])) + .collect::<Vec<AuthorityKeys>>(); Self { + initial_accounts, initial_authorities_len, - initial_balances: vec![], initial_identities, initial_smiths, parameters: GenesisParameters { @@ -119,20 +128,28 @@ impl ExtBuilder { } } - /*pub fn with_initial_balances(mut self, initial_balances: Vec<(AccountId, Balance)>) -> Self { - self.initial_balances = initial_balances; + 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 { + random_id: H256(account_id.into()), + ..Default::default() + }) + .balance = balance; + } self } - pub fn with_parameters(mut self, parameters: GenesisParameters<u32, u32, Balance>) -> 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_accounts, initial_authorities_len, - initial_balances, initial_identities, initial_smiths, parameters, @@ -159,8 +176,8 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap();*/ - pallet_balances::GenesisConfig::<Runtime> { - balances: initial_balances, + pallet_duniter_account::GenesisConfig::<Runtime> { + accounts: initial_accounts, } .assimilate_storage(&mut t) .unwrap(); @@ -320,6 +337,7 @@ pub fn run_to_block(n: u32) { System::set_block_number(System::block_number() + 1); // Initialize the new block + Account::on_initialize(System::block_number()); Scheduler::on_initialize(System::block_number()); //Babe::on_initialize(System::block_number()); Authorship::on_initialize(System::block_number()); diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs index 215b1152a..5d95a9f04 100644 --- a/runtime/gdev/tests/integration_tests.rs +++ b/runtime/gdev/tests/integration_tests.rs @@ -18,11 +18,11 @@ 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::{assert_err, assert_ok}; use frame_support::{StorageHasher, Twox128}; use gdev_runtime::*; -use sp_core::crypto::Ss58Codec; +use sp_keyring::AccountKeyring; +use sp_runtime::MultiAddress; #[test] fn verify_pallet_prefixes() { @@ -99,7 +99,7 @@ fn test_remove_identity() { assert_eq!( System::events()[1].event, Event::System(frame_system::Event::KilledAccount { - account: AccountId::from_ss58check(DAVE).unwrap() + account: AccountKeyring::Dave.to_account_id() }) ); assert_eq!( @@ -141,3 +141,124 @@ fn test_remove_smith_identity() { //println!("{:#?}", events); }); } + +#[test] +fn test_create_new_account() { + ExtBuilder::new(1, 3, 4) + .with_initial_balances(vec![(AccountKeyring::Alice.to_account_id(), 1_000)]) + .build() + .execute_with(|| { + run_to_block(2); + + // Should be able to transfer 5 units to a new account + assert_ok!(Balances::transfer( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + MultiAddress::Id(AccountKeyring::Eve.to_account_id()), + 500 + )); + let events = System::events(); + //println!("{:#?}", events); + assert_eq!(events.len(), 2); + assert_eq!( + System::events()[0].event, + Event::Balances(pallet_balances::Event::Endowed { + account: AccountKeyring::Eve.to_account_id(), + free_balance: 500, + }) + ); + assert_eq!( + System::events()[1].event, + Event::Balances(pallet_balances::Event::Transfer { + from: AccountKeyring::Alice.to_account_id(), + to: AccountKeyring::Eve.to_account_id(), + amount: 500, + }) + ); + + // At next bloc, the mew account must be created, + // and new account tax should be collected + run_to_block(3); + let events = System::events(); + //println!("{:#?}", events); + assert_eq!(events.len(), 2); + assert_eq!( + System::events()[0].event, + Event::System(frame_system::Event::NewAccount { + account: AccountKeyring::Eve.to_account_id(), + }) + ); + assert_eq!( + System::events()[1].event, + Event::Balances(pallet_balances::Event::Withdraw { + who: AccountKeyring::Eve.to_account_id(), + amount: 300, + }) + ); + assert_eq!( + Balances::free_balance(AccountKeyring::Eve.to_account_id()), + 200 + ); + + // A random id request should be registered + assert_eq!( + Account::pending_random_id_assignments(0), + Some(AccountKeyring::Eve.to_account_id()) + ); + + // We can't remove the account until the random id is assigned + run_to_block(4); + assert_err!( + Balances::transfer( + frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + MultiAddress::Id(AccountKeyring::Alice.to_account_id()), + 200 + ), + pallet_balances::Error::<Runtime>::KeepAlive, + ); + assert_eq!( + Balances::free_balance(AccountKeyring::Eve.to_account_id()), + 200 + ); + assert_ok!(Balances::transfer_all( + frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + MultiAddress::Id(AccountKeyring::Alice.to_account_id()), + false + ),); + assert_eq!( + Balances::free_balance(AccountKeyring::Eve.to_account_id()), + 200 + ); + }); +} + +#[test] +fn test_create_new_idty() { + ExtBuilder::new(1, 3, 4) + .with_initial_balances(vec![(AccountKeyring::Alice.to_account_id(), 1_000)]) + .build() + .execute_with(|| { + run_to_block(2); + + // Should be able to create an identity + assert_ok!(Balances::transfer( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + MultiAddress::Id(AccountKeyring::Eve.to_account_id()), + 200 + )); + assert_ok!(Identity::create_identity( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + AccountKeyring::Eve.to_account_id(), + )); + + // At next bloc, nothing should be preleved + run_to_block(3); + let events = System::events(); + assert_eq!(events.len(), 0); + + // A random id request should be registered + assert_eq!( + Account::pending_random_id_assignments(0), + Some(AccountKeyring::Eve.to_account_id()) + ); + }); +} -- GitLab