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