From 6b12a718769f8faa5243c5e622f8df2d6b5f00f7 Mon Sep 17 00:00:00 2001
From: cgeek <cem.moreau@gmail.com>
Date: Wed, 1 Nov 2023 11:09:28 +0100
Subject: [PATCH] fix(#125): refactoring of `gen_genesis_data`

---
 node/src/chain_spec/gen_genesis_data.rs | 1664 +++++++++++++----------
 1 file changed, 975 insertions(+), 689 deletions(-)

diff --git a/node/src/chain_spec/gen_genesis_data.rs b/node/src/chain_spec/gen_genesis_data.rs
index f2a58694c..613f5cb94 100644
--- a/node/src/chain_spec/gen_genesis_data.rs
+++ b/node/src/chain_spec/gen_genesis_data.rs
@@ -25,10 +25,11 @@ use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
 use sp_consensus_babe::AuthorityId as BabeId;
+use sp_core::crypto::AccountId32;
 use sp_core::{blake2_256, ed25519, sr25519, Decode, Encode, H256};
 use sp_finality_grandpa::AuthorityId as GrandpaId;
-use sp_runtime::traits::IdentifyAccount;
-use sp_runtime::Perbill;
+use sp_runtime::traits::{IdentifyAccount, Verify};
+use sp_runtime::{MultiSignature, Perbill};
 use std::collections::{BTreeMap, HashMap};
 use std::fmt::{Display, Formatter};
 use std::fs;
@@ -251,162 +252,31 @@ where
     } = get_genesis_input::<P>(
         std::env::var("DUNITER_GENESIS_CONFIG").unwrap_or_else(|_| json_file_path.to_owned()),
     )?;
-
     let treasury_funder = v1_pubkey_to_account_id(treasury_funder)
         .expect("treasury founder must have a valid public key");
-
     let common_parameters = get_common_parameters(&parameters);
-
-    if smith_identities.is_some() && clique_smiths.is_some() {
-        return Err(
-            "'smiths' and 'clique_smiths' cannot be both defined at the same time".to_string(),
-        );
-    }
-
+    check_smiths_vs_clique_smiths(&smith_identities, &clique_smiths)?;
     let mut genesis_data = get_genesis_migration_data()?;
-
     check_parameters_consistency(&genesis_data.wallets, &first_ud, &first_ud_reeval, &ud)?;
-
-    // Remove expired certs since export
-    genesis_data
-        .identities
-        .iter_mut()
-        .for_each(|(receiver, i)| {
-            i.certs_received.retain(|issuer, v| {
-                let retain = (v.0 as u64) >= genesis_timestamp;
-                if !retain {
-                    log::warn!("{} -> {} cert expired since export", issuer, receiver);
-                }
-                retain
-            });
-        });
-
-    genesis_data.identities.iter_mut().for_each(|(name, i)| {
-        if (i.membership_expire_on.0 as u64) < genesis_timestamp {
-            i.membership_expire_on = TimestampV1(0);
-            log::warn!("{} membership expired since export", name);
-        }
-    });
-
-    genesis_data.identities.iter_mut().for_each(|(name, i)| {
-        if i.membership_expire_on.0 != 0
-            && i.certs_received.len() < common_parameters.min_cert as usize
-        {
-            i.membership_expire_on = TimestampV1(0);
-            log::warn!(
-                "{} lost membership because of lost certifications since export",
-                name
-            );
-        }
-    });
-
-    genesis_data.identities.iter().for_each(|(name, i)| {
-        if i.owner_pubkey.is_some() && i.owner_address.is_some() {
-            log::warn!(
-                "{} both has a pubkey and an address defined - address will be used",
-                name
-            );
-        }
-        if i.owner_pubkey.is_none() && i.owner_address.is_none() {
-            log::error!("{} neither has a pubkey and an address defined", name);
-        }
-    });
-
-    let mut identities_v2: HashMap<String, IdentityV2> = genesis_data
-        .identities
-        .into_iter()
-        .map(|(name, i)| {
-            (
-                name.clone(),
-                IdentityV2 {
-                    index: i.index,
-                    owner_key: i
-                        .owner_pubkey
-                        .map(|pubkey| {
-                            v1_pubkey_to_account_id(pubkey)
-                                .expect("a G1 identity necessarily has a valid pubkey")
-                        })
-                        .unwrap_or_else(|| {
-                            i.owner_address.unwrap_or_else(|| {
-                                panic!("neither pubkey nor address is defined for {}", name)
-                            })
-                        }),
-                    old_owner_key: None,
-                    membership_expire_on: timestamp_to_relative_blocs(
-                        i.membership_expire_on,
-                        genesis_timestamp,
-                    ),
-                    next_cert_issuable_on: timestamp_to_relative_blocs(
-                        i.next_cert_issuable_on,
-                        genesis_timestamp,
-                    ),
-                    balance: i.balance,
-                    certs_received: i
-                        .certs_received
-                        .into_iter()
-                        .map(|(issuer, timestamp)| {
-                            (
-                                issuer,
-                                timestamp_to_relative_blocs(timestamp, genesis_timestamp),
-                            )
-                        })
-                        .collect(),
-                },
-            )
-        })
-        .collect();
-
-    // // Identities whose membership was lost since export
-    // identities_v2.iter_mut()
-    //     .filter(|(name, i)| (i.membership_expire_on as u64) < genesis_timestamp)
-    //     .for_each(|(name, i)| {
-    //         log::warn!("{} membership expired since export", name);
-    //         i.membership_expire_on = 0;
-    //     });
-
-    // // Identities that are no more members because of a lack of certs
-    // identities_v2.iter_mut()
-    //     .filter(|(name, i)| i.membership_expire_on != 0 && (i.certs_received.len() as u32) < common_parameters.min_cert)
-    //     .for_each(|(name, i)| {
-    //         log::warn!("{} lost membership because of lost certifications since export", name);
-    //         i.membership_expire_on = 0;
-    //     });
-
-    // Check that members have enough certs
-    identities_v2
-        .iter()
-        .filter(|(_, i)| i.membership_expire_on != 0)
-        .for_each(|(name, i)| {
-            let nb_certs = i.certs_received.len() as u32;
-            if nb_certs < common_parameters.min_cert {
-                log::warn!("{} has only {} valid certifications", name, nb_certs);
-            }
-        });
+    check_genesis_data_filter_certs(&mut genesis_data, genesis_timestamp, &common_parameters);
+    let mut identities_v2: HashMap<String, IdentityV2> =
+        genesis_data_to_identities_v2(genesis_data.identities, genesis_timestamp);
+    check_identities_v2(&identities_v2, &common_parameters);
 
     // MONEY AND WOT //
     // declare variables for building genesis
     // -------------------------------------
     // track if fatal error occured, but let processing continue
     let mut fatal = false;
-    // monetary mass for double check
-    let mut monetary_mass = 0u64;
     // initial Treasury balance
     let mut treasury_balance = 0;
-    // counter for online authorities at genesis
-    let mut counter_online_authorities = 0;
     // track identity index
     let mut identity_index = HashMap::new();
-    // counter for certifications
-    let mut counter_cert = 0u32;
-    // counter for smith certifications
-    let mut counter_smith_cert = 0u32;
     // track inactive identities
-    let mut inactive_identities = HashMap::<u32, &str>::new();
+    let mut inactive_identities = HashMap::<u32, String>::new();
 
     // declare variables to fill in genesis
     // -------------------------------------
-    // account inserted in genesis
-    let mut accounts: BTreeMap<AccountId, GenesisAccountData<u64>> = BTreeMap::new();
     // members of technical committee
     let mut technical_committee_members: Vec<AccountId> = Vec::new();
     // memberships
@@ -419,118 +289,27 @@ where
     let mut initial_authorities = BTreeMap::new();
     // session keys
     let mut session_keys_map = BTreeMap::new();
-    // smith memberships
-    let mut smith_memberships = BTreeMap::new();
-    // smith certifications
-    let mut smith_certs_by_receiver = BTreeMap::new();
-    let mut invalid_wallets = 0;
     //let mut total_dust = 0;
 
     // FORCED AUTHORITY //
     // If this authority is defined (most likely Alice), then it must exist in both _identities_
     // and _smiths_. We create all this, for *development purposes* (used with `gdev_dev` or `gtest_dev` chains).
     if let Some(authority_name) = &maybe_force_authority {
-        // The identity might already exist, notably: G1 "Alice" already exists
-        if let Some(authority) = identities_v2.get_mut(authority_name) {
-            // Force authority to be active
-            authority.membership_expire_on = common_parameters.membership_period;
-        } else {
-            // Not found: we must create it
-            identities_v2.insert(
-                authority_name.clone(),
-                IdentityV2 {
-                    index: (identities_v2.len() as u32 + 1),
-                    owner_key: get_account_id_from_seed::<sr25519::Public>(authority_name),
-                    balance: common_parameters.existential_deposit,
-                    certs_received: HashMap::new(),
-                    membership_expire_on: common_parameters.membership_period,
-                    old_owner_key: None,
-                    next_cert_issuable_on: 0,
-                },
-            );
-        };
-        // Forced authority gets its required certs from first "minCert" WoT identities (fake certs)
-        let mut new_certs: HashMap<String, u32> = HashMap::new();
-        let certs_of_authority = &identities_v2.get(authority_name).unwrap().certs_received;
-        identities_v2
-            .keys()
-            // Identities which are not the authority and have not already certified her
-            .filter(|issuer| {
-                issuer != &authority_name
-                    && !certs_of_authority
-                        .iter()
-                        .any(|(authority_issuer, _)| issuer == &authority_issuer)
-            })
-            .take(common_parameters.min_cert as usize)
-            .map(String::clone)
-            .for_each(|issuer| {
-                new_certs.insert(issuer, common_parameters.cert_period);
-            });
-        let authority = identities_v2
-            .get_mut(authority_name)
-            .expect("authority must exist or be created");
-        new_certs.into_iter().for_each(|(issuer, c)| {
-            authority.certs_received.insert(issuer, c);
-        });
-        let sk: SessionKeys =
-            SKP::session_keys(&get_authority_keys_from_seed(authority_name.as_str()));
-        let forced_authority_session_keys = format!("0x{}", hex::encode(sk.encode()));
-        // Add forced authority to smiths (whether explicit smith WoT or clique)
-        if let Some(smith_identities) = &mut smith_identities {
-            smith_identities.insert(
-                authority_name.clone(),
-                SmithData {
-                    idty_index: authority.index,
-                    name: authority_name.clone(),
-                    account: authority.owner_key.clone(),
-                    session_keys: Some(forced_authority_session_keys.clone()),
-                    certs_received: vec![],
-                },
-            );
-        }
-        if let Some(clique_smiths) = &mut clique_smiths {
-            let existing = clique_smiths.iter_mut().find(|s| &s.name == authority_name);
-            if let Some(smith) = existing {
-                // Forced authority is already set in smiths: but we force its session keys
-                smith.session_keys = Some(forced_authority_session_keys);
-            } else {
-                // Forced authority is not known in smiths: we add it with its session keys
-                clique_smiths.push(CliqueSmith {
-                    name: authority_name.clone(),
-                    session_keys: Some(forced_authority_session_keys),
-                });
-            }
-        }
+        make_authority_exist::<SessionKeys, SKP>(
+            &mut identities_v2,
+            &mut smith_identities,
+            &mut clique_smiths,
+            &common_parameters,
+            authority_name,
+        );
     }
 
     // SIMPLE WALLETS //
-    for (pubkey, balance) in &genesis_data.wallets {
-        // check existential deposit
-        if balance < &common_parameters.existential_deposit {
-            log::error!(
-                "wallet {pubkey} has {balance} cǦT which is below {}",
-                common_parameters.existential_deposit
-            );
-            fatal = true;
-        }
-
-        // double check the monetary mass
-        monetary_mass += balance;
-
-        // json prevents duplicate wallets
-        if let Ok(owner_key) = v1_pubkey_to_account_id(pubkey.clone()) {
-            accounts.insert(
-                owner_key.clone(),
-                GenesisAccountData {
-                    random_id: H256(blake2_256(&(balance, &owner_key).encode())),
-                    balance: *balance,
-                    is_identity: false,
-                },
-            );
-        } else {
-            log::warn!("wallet {pubkey} has wrong format");
-            invalid_wallets = invalid_wallets.add(1);
-        }
+    let genesis_data_wallets_count = genesis_data.wallets.len();
+    let (was_fatal, mut monetary_mass, mut accounts, invalid_wallets) =
+        v1_wallets_to_v2_accounts(genesis_data.wallets, &common_parameters);
+    if was_fatal {
+        fatal = true;
     }
 
     // Technical Comittee //
@@ -545,240 +324,63 @@ where
     }
 
     // IDENTITIES //
-    for (name, identity) in &identities_v2 {
-        // identity name
-        if !validate_idty_name(name) {
-            return Err(format!("Identity name '{}' is invalid", &name));
-        }
+    let was_fatal = feed_identities(
+        &mut identities,
+        &mut accounts,
+        &mut identity_index,
+        &mut monetary_mass,
+        &mut inactive_identities,
+        &mut memberships,
+        &identities_v2,
+    )?;
+    if was_fatal {
+        fatal = true;
+    }
 
-        // TODO: re-check this code origin and wether it should be included or not
-        // do not check existential deposit of identities
-        // // check existential deposit
-        // if identity.balance < common_parameters.existencial_deposit {
-        //     if identity.membership_expire_on == 0 {
-        //         log::warn!(
-        //             "expired identity {name} has {} cǦT which is below {}",
-        //             identity.balance, common_parameters.existencial_deposit
-        //         );
-        //         fatal = true;
-        //     } else {
-        //         member identities can still be below existential deposit thanks to sufficient
-        //         log::info!(
-        //             "identity {name} has {} cǦT which is below {}",
-        //             identity.balance, common_parameters.existencial_deposit
-        //         );
-        //     }
-        // }
+    // CERTIFICATIONS //
+    // counter for certifications
+    let (was_fatal, counter_cert) = feed_certs_by_receiver(&mut certs_by_receiver, &identities_v2);
+    if was_fatal {
+        fatal = true;
+    }
 
-        // Money
-        // check that wallet with same owner_key does not exist
-        if accounts.get(&identity.owner_key).is_some() {
-            log::error!(
-                "{name} owner_key {} already exists as a simple wallet",
-                identity.owner_key
-            );
-            fatal = true;
-        }
-        // insert as an account
-        accounts.insert(
-            identity.owner_key.clone(),
-            GenesisAccountData {
-                random_id: H256(blake2_256(&(identity.index, &identity.owner_key).encode())),
-                balance: identity.balance,
-                is_identity: true,
-            },
+    // SMITHS SUB-WOT //
+
+    // Authorities
+    if let Some(name) = &maybe_force_authority {
+        check_authority_exists_in_both_wots(
+            name,
+            &identities_v2,
+            &clique_smiths,
+            &smith_identities,
         );
+    }
 
-        // double check the monetary mass
-        monetary_mass += identity.balance;
+    let smiths = build_smiths_wot(
+        &clique_smiths,
+        &identity_index,
+        &identities_v2,
+        smith_identities,
+    );
 
-        // insert identity
-        // check that index does not already exist
-        if let Some(other_name) = identity_index.get(&identity.index) {
-            log::error!(
-                "{other_name} already has identity index {} of {name}",
-                identity.index
-            );
-            fatal = true;
-        }
-        identity_index.insert(identity.index, name);
-
-        let expired = identity.membership_expire_on == 0;
-        // only add the identity if not expired
-        if expired {
-            inactive_identities.insert(identity.index, name);
-        };
-        identities.push(GenesisMemberIdentity {
-            // N.B.: every **non-expired** identity on Genesis is considered to have:
-            //  - removable_on: 0,
-            //  - next_creatable_identity_on: 0,
-            //  - status: IdtyStatus::Validated,
-            index: identity.index,
-            name: name.clone(),
-            owned_key: identity.owner_key.clone(),
-            old_owned_key: identity.old_owner_key.clone(),
-            // but expired identities will just have their pseudonym reserved in the storage
-            active: !expired,
-        });
-
-        // insert the membershup data (only if not expired)
-        if !expired {
-            memberships.insert(
-                identity.index,
-                MembershipData {
-                    expire_on: identity.membership_expire_on,
-                },
-            );
-        }
-    }
-    // sort the identities by index for reproducibility (should have been a vec in json)
-    identities.sort_unstable_by(|a, b| a.index.cmp(&b.index));
-
-    // CERTIFICATIONS //
-    for identity in identities_v2.values() {
-        let mut certs = BTreeMap::new();
-        for (issuer, expire_on) in &identity.certs_received {
-            if let Some(issuer) = &identities_v2.get(issuer) {
-                certs.insert(issuer.index, Some(*expire_on));
-                counter_cert += 1;
-            } else {
-                log::error!("Identity '{}' does not exist", issuer);
-                fatal = true;
-            };
-        }
-        certs_by_receiver.insert(identity.index, certs);
-    }
-
-    // SMITHS SUB-WOT //
-
-    // Authorities
-    if let Some(name) = &maybe_force_authority {
-        identities_v2
-            .get(name)
-            .ok_or(format!("Identity '{}' not exist", name))
-            .expect("Initial authority must have an identity");
-        if let Some(smiths) = &clique_smiths {
-            smiths
-                .iter()
-                .find(|s| s.name == *name)
-                .expect("Forced authority must be present in smiths clique");
-        }
-        if let Some(smiths) = &smith_identities {
-            smiths
-                .iter()
-                .find(|(other_name, _)| *other_name == name)
-                .expect("Forced authority must be present in smiths");
-        }
-    }
-
-    let is_clique = clique_smiths.is_some();
-    // Create a single source of smiths
-    let smiths = if let Some(clique) = &clique_smiths {
-        // From a clique
-        clique
-            .iter()
-            .map(|smith| {
-                let idty_index = identity_index
-                    .iter()
-                    .find(|(_, v)| ***v == smith.name)
-                    .map(|(k, _)| *k)
-                    .expect("smith must have an identity");
-                SmithData {
-                    idty_index,
-                    name: smith.name.clone(),
-                    account: identities_v2
-                        .get(smith.name.as_str())
-                        .map(|i| i.owner_key.clone())
-                        .expect("identity must exist"),
-                    session_keys: smith.session_keys.clone(),
-                    certs_received: vec![],
-                }
-            })
-            .collect::<Vec<SmithData>>()
-    } else {
-        // From explicit smith WoT
-        smith_identities
-            .expect("existence has been tested earlier")
-            .into_values()
-            .collect::<Vec<SmithData>>()
-    };
-    // Then create the smith WoT
-    for smith in &smiths {
-        // check that smith exists
-        if let Some(identity) = &identities_v2.get(&smith.name.clone()) {
-            // Initial authorities and session keys
-            let session_keys_bytes = if let Some(declared_session_keys) = &smith.session_keys {
-                counter_online_authorities += 1;
-                // insert authority as online
-                initial_authorities.insert(identity.index, (identity.owner_key.clone(), true));
-                // decode session keys or force to given value
-                hex::decode(&declared_session_keys[2..])
-                    .map_err(|_| format!("invalid session keys for idty {}", smith.name))?
-            } else {
-                // still authority but offline
-                initial_authorities.insert(identity.index, (identity.owner_key.clone(), false));
-                // fake session keys
-                let mut fake_bytes = Vec::with_capacity(128);
-                for _ in 0..4 {
-                    fake_bytes.extend_from_slice(identity.owner_key.as_ref())
-                }
-                fake_bytes
-            };
-
-            // insert session keys to map
-            session_keys_map.insert(
-                identity.owner_key.clone(),
-                SK::decode(&mut &session_keys_bytes[..]).unwrap(),
-            );
-
-            // smith certifications
-            let mut certs = BTreeMap::new();
-            if is_clique {
-                // All initial smiths are considered to be certifying all each other
-                clique_smiths
-                    .as_ref()
-                    .unwrap()
-                    .iter()
-                    .filter(|other_smith| *other_smith.name.as_str() != *smith.name)
-                    .for_each(|other_smith| {
-                        let issuer_index = &identities_v2
-                            .get(other_smith.name.as_str())
-                            .unwrap_or_else(|| {
-                                panic!("Identity '{}' does not exist", other_smith.name.as_str())
-                            })
-                            .index;
-                        certs.insert(*issuer_index, None);
-                    });
-            } else {
-                for issuer in &smith.certs_received {
-                    let issuer_index = &identities_v2
-                        .get(issuer)
-                        .ok_or(format!("Identity '{}' does not exist", issuer))?
-                        .index;
-                    certs.insert(
-                        *issuer_index,
-                        Some(common_parameters.smith_certs_validity_period),
-                    );
-                    counter_smith_cert += 1;
-                }
-                smith_certs_by_receiver.insert(identity.index, certs);
-            }
-
-            // smith memberships
-            smith_memberships.insert(
-                identity.index,
-                MembershipData {
-                    expire_on: common_parameters.smith_membership_period,
-                },
-            );
-        } else {
-            log::error!(
-                "Smith '{}' does not correspond to exising identity",
-                &smith.name
-            );
-            fatal = true;
-        }
-    }
+    // counter for online authorities at genesis
+    let (
+        was_fatal,
+        counter_online_authorities,
+        counter_smith_cert,
+        smith_certs_by_receiver,
+        smith_memberships,
+    ) = create_smith_wot(
+        &mut initial_authorities,
+        &mut session_keys_map,
+        &identities_v2,
+        &smiths,
+        &common_parameters,
+        &clique_smiths,
+    )?;
+    if was_fatal {
+        fatal = true;
+    }
 
     // Verify certifications coherence (can be ignored for old users)
     for (idty_index, receiver_certs) in &certs_by_receiver {
@@ -847,231 +449,46 @@ where
         fatal = true;
     }
 
-    smiths.iter().for_each(|smith| {
-        log::info!(
-            "[Smith] {} ({} - {})",
-            smith.idty_index,
-            smith.account,
-            smith.name.clone()
-        );
-    });
-
-    initial_authorities
-        .iter()
-        .for_each(|(index, (authority_account, online))| {
-            log::info!(
-                "[Authority] {} : {} ({} - {})",
-                index,
-                if *online { "online" } else { "offline" },
-                authority_account,
-                identity_index
-                    .get(index)
-                    .expect("authority should have an identity")
-            );
-        });
-
-    // give genesis info
-    log::info!(
-        "prepared genesis with:
-        - {} accounts ({} identities, {} simple wallets)
-        - {} total identities ({} active, {} inactive)
-        - {} smiths
-        - {} initial online authorities
-        - {} certifications
-        - {} smith certifications
-        - {} members in technical committee",
-        accounts.len(),
-        identities.len() - inactive_identities.len(),
-        &genesis_data.wallets.len(),
-        identity_index.len(),
-        identities.len() - inactive_identities.len(),
-        inactive_identities.len(),
-        smith_memberships.len(),
+    let was_fatal = dump_results(
+        &smiths,
+        &initial_authorities,
+        &parameters,
+        &common_parameters,
+        &identity_index,
+        &accounts,
+        &identities,
+        genesis_data_wallets_count,
+        &inactive_identities,
+        &smith_memberships,
         counter_online_authorities,
         counter_cert,
         counter_smith_cert,
-        technical_committee_members.len(),
+        &technical_committee_members,
     );
+    if was_fatal {
+        fatal = true;
+    }
 
-    // give genesis info
-    log::info!(
-        "currency parameters:
-        - existential deposit: {} {}
-        - currency decimals: {}
-        - membership validity: {} days
-        - certification period: {} days
-        - certification validity duration: {} days
-        - smith membership validity: {} days
-        - smith certification validity: {} days
-        - required certifications: {}
-        - smith required certifications: {}
-        - max certifications by issuer: {}
-        - money growth rate: {}% every {} days
-        - UD creation period: {} days
-        - distance percent of required referees: {}%
-        - distance max depth: {}",
-        common_parameters.existential_deposit,
-        common_parameters.currency_name,
-        common_parameters.decimals,
-        common_parameters.membership_period as f32 / DAYS as f32,
-        common_parameters.cert_period as f32 / DAYS as f32,
-        common_parameters.cert_validity_period as f32 / DAYS as f32,
-        common_parameters.smith_membership_period as f32 / DAYS as f32,
-        common_parameters.smith_certs_validity_period as f32 / DAYS as f32,
-        common_parameters.min_cert,
-        common_parameters.smith_min_cert,
-        common_parameters.cert_max_by_issuer,
-        f32::sqrt(common_parameters.c2.deconstruct() as f32 / 1_000_000_000f32) * 100f32,
-        common_parameters.ud_reeval_period as f32 / DAYS as f32,
-        common_parameters.ud_creation_period as f32 / DAYS as f32,
-        common_parameters
-            .distance_min_accessible_referees
-            .deconstruct() as f32
-            / 1_000_000_000f32
-            * 100f32,
-        common_parameters.max_depth,
+    some_more_checks(
+        &identities,
+        &inactive_identities,
+        &memberships,
+        &smith_memberships,
+        &initial_authorities,
+        &session_keys_map,
+        &identity_index,
+        &accounts,
+        genesis_data_wallets_count,
+        &invalid_wallets,
+        &technical_committee,
+        &smiths,
     );
 
-    if parameters.is_some() {
-        let g1_duniter_v1_c = 0.0488;
-        let g1_duniter_v1_xpercent: Perbill = Perbill::from_float(0.8);
-        let c = f32::sqrt(common_parameters.c2.deconstruct() as f32 / 1_000_000_000f32);
-
-        // static parameters (GTest or G1)
-        if common_parameters.decimals != G1_DUNITER_V1_DECIMALS {
-            warn!(
-                "parameter `decimals` value ({}) is different from Ğ1 value ({})",
-                common_parameters.decimals, G1_DUNITER_V1_DECIMALS
-            )
-        }
-        if common_parameters.existential_deposit != G1_DUNITER_V1_EXISTENTIAL_DEPOSIT {
-            warn!(
-                "parameter `existential_deposit` value ({}) is different from Ğ1 value ({})",
-                common_parameters.existential_deposit, G1_DUNITER_V1_EXISTENTIAL_DEPOSIT
-            )
-        }
-        if common_parameters.membership_period / DAYS != G1_DUNITER_V1_MSVALIDITY / DUNITER_V1_DAYS
-        {
-            warn!(
-                "parameter `membership_period` ({} days) is different from Ğ1's ({} days)",
-                common_parameters.membership_period as f32 / DAYS as f32,
-                G1_DUNITER_V1_MSVALIDITY as f32 / DUNITER_V1_DAYS as f32
-            )
-        }
-        if common_parameters.cert_period / DAYS != G1_DUNITER_V1_SIGPERIOD / DUNITER_V1_DAYS {
-            warn!(
-                "parameter `cert_period` ({} days) is different from Ğ1's ({} days)",
-                common_parameters.cert_period as f32 / DAYS as f32,
-                G1_DUNITER_V1_SIGPERIOD as f32 / DUNITER_V1_DAYS as f32
-            )
-        }
-        if common_parameters.cert_validity_period / DAYS
-            != G1_DUNITER_V1_SIGVALIDITY / DUNITER_V1_DAYS
-        {
-            warn!(
-                "parameter `cert_validity_period` ({} days) is different from Ğ1's ({} days)",
-                common_parameters.cert_validity_period as f32 / DAYS as f32,
-                G1_DUNITER_V1_SIGVALIDITY as f32 / DUNITER_V1_DAYS as f32
-            )
-        }
-        if common_parameters.min_cert != G1_DUNITER_V1_SIGQTY {
-            warn!(
-                "parameter `min_cert` value ({}) is different from Ğ1 value ({})",
-                common_parameters.min_cert, G1_DUNITER_V1_SIGQTY
-            )
-        }
-        if common_parameters.cert_max_by_issuer != G1_DUNITER_V1_SIGSTOCK {
-            warn!(
-                "parameter `cert_max_by_issuer` value ({}) is different from Ğ1 value ({})",
-                common_parameters.cert_max_by_issuer, G1_DUNITER_V1_SIGSTOCK
-            )
-        }
-        if c != g1_duniter_v1_c {
-            warn!(
-                "parameter `c` value ({}) is different from Ğ1 value ({})",
-                c, g1_duniter_v1_c
-            )
-        }
-        if common_parameters.ud_creation_period as f32 / DAYS as f32
-            != G1_DUNITER_V1_DT as f32 / DUNITER_V1_DAYS as f32
-        {
-            warn!(
-                "parameter `ud_creation_period` value ({} days) is different from Ğ1 value ({} days)",
-                common_parameters.ud_creation_period as f32 / DAYS as f32, G1_DUNITER_V1_DT as f32 / DUNITER_V1_DAYS as f32
-            )
-        }
-        if common_parameters.ud_reeval_period as f32 / DAYS as f32
-            != G1_DUNITER_V1_DTREEVAL as f32 / DUNITER_V1_DAYS as f32
-        {
-            warn!(
-                "parameter `ud_reeval_period` value ({} days) is different from Ğ1 value ({} days)",
-                common_parameters.ud_reeval_period as f32 / DAYS as f32,
-                G1_DUNITER_V1_DTREEVAL as f32 / DUNITER_V1_DAYS as f32
-            )
-        }
-        if common_parameters.distance_min_accessible_referees != g1_duniter_v1_xpercent {
-            warn!(
-                "parameter `distance_min_accessible_referees` value ({}) is different from Ğ1 value ({})",
-                format!("{:?}", common_parameters.distance_min_accessible_referees), format!("{:?}", g1_duniter_v1_xpercent)
-            )
-        }
-        if common_parameters.max_depth != G1_DUNITER_V1_STEPMAX {
-            warn!(
-                "parameter `max_depth` value ({}) is different from Ğ1 value ({})",
-                common_parameters.max_depth, G1_DUNITER_V1_STEPMAX
-            )
-        }
-        let count_uds = common_parameters.ud_reeval_period / common_parameters.ud_creation_period;
-        if count_uds == 0 {
-            error!(
-                "the `ud_reeval_period / ud_creation_period` is zero ({} days/{} days)",
-                common_parameters.ud_reeval_period / DAYS as u64,
-                common_parameters.ud_creation_period / DAYS as u64
-            );
-            fatal = true;
-        }
-    }
-
-    // some more checks
-    assert_eq!(
-        identities.len() - inactive_identities.len(),
-        memberships.len()
-    );
-    assert_eq!(smith_memberships.len(), initial_authorities.len());
-    assert_eq!(smith_memberships.len(), session_keys_map.len());
-    assert_eq!(identity_index.len(), identities.len());
-    assert_eq!(
-        accounts.len(),
-        identity_index.len() + genesis_data.wallets.len().sub(invalid_wallets)
-    );
-    // no inactive tech comm
-    for tech_com_member in &technical_committee {
-        let inactive_commitee_member = inactive_identities.values().any(|&v| v == tech_com_member);
-        if inactive_commitee_member {
-            log::error!(
-                "{} is an inactive technical commitee member",
-                tech_com_member
-            );
-            assert!(!inactive_commitee_member);
-        }
-    }
-    // no inactive smith
-    for SmithData { name: smith, .. } in &smiths {
-        let inactive_smiths: Vec<_> = inactive_identities
-            .values()
-            .filter(|&v| *v == smith)
-            .collect();
-        inactive_smiths
-            .iter()
-            .for_each(|s| log::warn!("Smith {} is inactive", s));
-        assert_eq!(inactive_smiths.len(), 0);
-    }
-
-    // check the logs to see all the fatal error preventing from starting gtest currency
-    if fatal {
-        log::error!("some previously logged error prevent from building a sane genesis");
-        panic!();
-    }
+    // check the logs to see all the fatal error preventing from starting gtest currency
+    if fatal {
+        log::error!("some previously logged error prevent from building a sane genesis");
+        panic!();
+    }
 
     // Indexer output
     if let Ok(path) = std::env::var("DUNITER_GENESIS_EXPORT") {
@@ -1170,6 +587,875 @@ where
     Ok(genesis_data)
 }
 
+fn some_more_checks<SK: Decode>(
+    identities: &Vec<GenesisMemberIdentity>,
+    inactive_identities: &HashMap<u32, String>,
+    memberships: &BTreeMap<u32, MembershipData>,
+    smith_memberships: &BTreeMap<u32, MembershipData>,
+    initial_authorities: &BTreeMap<u32, (AccountId32, bool)>,
+    session_keys_map: &BTreeMap<AccountId32, SK>,
+    identity_index: &HashMap<u32, String>,
+    accounts: &BTreeMap<AccountId32, GenesisAccountData<u64>>,
+    genesis_data_wallets_count: usize,
+    invalid_wallets: &usize,
+    technical_committee: &Vec<String>,
+    smiths: &Vec<SmithData>,
+) {
+    // some more checks
+    assert_eq!(
+        identities.len() - inactive_identities.len(),
+        memberships.len()
+    );
+    assert_eq!(smith_memberships.len(), initial_authorities.len());
+    assert_eq!(smith_memberships.len(), session_keys_map.len());
+    assert_eq!(identity_index.len(), identities.len());
+    assert_eq!(
+        accounts.len(),
+        identity_index.len() + genesis_data_wallets_count.sub(invalid_wallets)
+    );
+    // no inactive tech comm
+    for tech_com_member in technical_committee {
+        let inactive_commitee_member = inactive_identities.values().any(|v| v == tech_com_member);
+        if inactive_commitee_member {
+            log::error!(
+                "{} is an inactive technical commitee member",
+                tech_com_member
+            );
+            assert!(!inactive_commitee_member);
+        }
+    }
+    // no inactive smith
+    for SmithData { name: smith, .. } in smiths {
+        let inactive_smiths: Vec<_> = inactive_identities
+            .values()
+            .filter(|v| *v == smith)
+            .collect();
+        inactive_smiths
+            .iter()
+            .for_each(|s| log::warn!("Smith {} is inactive", s));
+        assert_eq!(inactive_smiths.len(), 0);
+    }
+}
+
+fn create_smith_wot<SK: Decode>(
+    initial_authorities: &mut BTreeMap<u32, (AccountId32, bool)>,
+    session_keys_map: &mut BTreeMap<AccountId32, SK>,
+    identities_v2: &HashMap<String, IdentityV2>,
+    smiths: &Vec<SmithData>,
+    common_parameters: &CommonParameters,
+    clique_smiths: &Option<Vec<CliqueSmith>>,
+) -> Result<
+    (
+        bool,
+        u32,
+        u32,
+        BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
+        BTreeMap<u32, sp_membership::MembershipData<u32>>,
+    ),
+    String,
+> {
+    let mut fatal = false;
+    let mut counter_online_authorities = 0;
+    // counter for smith certifications
+    let mut counter_smith_cert = 0;
+    let mut smith_certs_by_receiver = BTreeMap::new();
+    // smith memberships
+    let mut smith_memberships = BTreeMap::new();
+    // Then create the smith WoT
+    for smith in smiths {
+        // check that smith exists
+        if let Some(identity) = &identities_v2.get(&smith.name.clone()) {
+            counter_online_authorities = set_session_keys_and_authority_status(
+                initial_authorities,
+                session_keys_map,
+                &smith,
+                &identity,
+            )?;
+
+            // smith certifications
+            counter_smith_cert = feed_smith_certs_by_receiver(
+                &mut smith_certs_by_receiver,
+                &clique_smiths,
+                &smith,
+                &identity,
+                &identities_v2,
+                &common_parameters,
+            )?;
+
+            // smith memberships
+            smith_memberships.insert(
+                identity.index,
+                MembershipData {
+                    expire_on: common_parameters.smith_membership_period,
+                },
+            );
+        } else {
+            log::error!(
+                "Smith '{}' does not correspond to exising identity",
+                &smith.name
+            );
+            fatal = true;
+        }
+    }
+    Ok((
+        fatal,
+        counter_online_authorities,
+        counter_smith_cert,
+        smith_certs_by_receiver,
+        smith_memberships,
+    ))
+}
+
+fn dump_results<P>(
+    smiths: &Vec<SmithData>,
+    initial_authorities: &BTreeMap<u32, (AccountId32, bool)>,
+    parameters: &Option<P>,
+    common_parameters: &CommonParameters,
+    identity_index: &HashMap<u32, String>,
+    accounts: &BTreeMap<AccountId32, GenesisAccountData<u64>>,
+    identities: &Vec<GenesisMemberIdentity>,
+    genesis_data_wallets_count: usize,
+    inactive_identities: &HashMap<u32, String>,
+    smith_memberships: &BTreeMap<u32, MembershipData>,
+    counter_online_authorities: u32,
+    counter_cert: u32,
+    counter_smith_cert: u32,
+    technical_committee_members: &Vec<AccountId32>,
+) -> bool {
+    let mut fatal = false;
+
+    smiths.iter().for_each(|smith| {
+        log::info!(
+            "[Smith] {} ({} - {})",
+            smith.idty_index,
+            smith.account,
+            smith.name.clone()
+        );
+    });
+
+    initial_authorities
+        .iter()
+        .for_each(|(index, (authority_account, online))| {
+            log::info!(
+                "[Authority] {} : {} ({} - {})",
+                index,
+                if *online { "online" } else { "offline" },
+                authority_account,
+                identity_index
+                    .get(index)
+                    .expect("authority should have an identity")
+            );
+        });
+
+    // give genesis info
+    log::info!(
+        "prepared genesis with:
+        - {} accounts ({} identities, {} simple wallets)
+        - {} total identities ({} active, {} inactive)
+        - {} smiths
+        - {} initial online authorities
+        - {} certifications
+        - {} smith certifications
+        - {} members in technical committee",
+        accounts.len(),
+        identities.len() - inactive_identities.len(),
+        genesis_data_wallets_count,
+        identity_index.len(),
+        identities.len() - inactive_identities.len(),
+        inactive_identities.len(),
+        smith_memberships.len(),
+        counter_online_authorities,
+        counter_cert,
+        counter_smith_cert,
+        technical_committee_members.len(),
+    );
+
+    // give genesis info
+    log::info!(
+        "currency parameters:
+        - existential deposit: {} {}
+        - currency decimals: {}
+        - membership validity: {} days
+        - certification period: {} days
+        - certification validity duration: {} days
+        - smith membership validity: {} days
+        - smith certification validity: {} days
+        - required certifications: {}
+        - smith required certifications: {}
+        - max certifications by issuer: {}
+        - money growth rate: {}% every {} days
+        - UD creation period: {} days
+        - distance percent of required referees: {}%
+        - distance max depth: {}",
+        common_parameters.existential_deposit,
+        common_parameters.currency_name,
+        common_parameters.decimals,
+        common_parameters.membership_period as f32 / DAYS as f32,
+        common_parameters.cert_period as f32 / DAYS as f32,
+        common_parameters.cert_validity_period as f32 / DAYS as f32,
+        common_parameters.smith_membership_period as f32 / DAYS as f32,
+        common_parameters.smith_certs_validity_period as f32 / DAYS as f32,
+        common_parameters.min_cert,
+        common_parameters.smith_min_cert,
+        common_parameters.cert_max_by_issuer,
+        f32::sqrt(common_parameters.c2.deconstruct() as f32 / 1_000_000_000f32) * 100f32,
+        common_parameters.ud_reeval_period as f32 / DAYS as f32,
+        common_parameters.ud_creation_period as f32 / DAYS as f32,
+        common_parameters
+            .distance_min_accessible_referees
+            .deconstruct() as f32
+            / 1_000_000_000f32
+            * 100f32,
+        common_parameters.max_depth,
+    );
+
+    if parameters.is_some() {
+        let g1_duniter_v1_c = 0.0488;
+        let g1_duniter_v1_xpercent: Perbill = Perbill::from_float(0.8);
+        let c = f32::sqrt(common_parameters.c2.deconstruct() as f32 / 1_000_000_000f32);
+
+        // static parameters (GTest or G1)
+        if common_parameters.decimals != G1_DUNITER_V1_DECIMALS {
+            warn!(
+                "parameter `decimals` value ({}) is different from Ğ1 value ({})",
+                common_parameters.decimals, G1_DUNITER_V1_DECIMALS
+            )
+        }
+        if common_parameters.existential_deposit != G1_DUNITER_V1_EXISTENTIAL_DEPOSIT {
+            warn!(
+                "parameter `existential_deposit` value ({}) is different from Ğ1 value ({})",
+                common_parameters.existential_deposit, G1_DUNITER_V1_EXISTENTIAL_DEPOSIT
+            )
+        }
+        if common_parameters.membership_period / DAYS != G1_DUNITER_V1_MSVALIDITY / DUNITER_V1_DAYS
+        {
+            warn!(
+                "parameter `membership_period` ({} days) is different from Ğ1's ({} days)",
+                common_parameters.membership_period as f32 / DAYS as f32,
+                G1_DUNITER_V1_MSVALIDITY as f32 / DUNITER_V1_DAYS as f32
+            )
+        }
+        if common_parameters.cert_period / DAYS != G1_DUNITER_V1_SIGPERIOD / DUNITER_V1_DAYS {
+            warn!(
+                "parameter `cert_period` ({} days) is different from Ğ1's ({} days)",
+                common_parameters.cert_period as f32 / DAYS as f32,
+                G1_DUNITER_V1_SIGPERIOD as f32 / DUNITER_V1_DAYS as f32
+            )
+        }
+        if common_parameters.cert_validity_period / DAYS
+            != G1_DUNITER_V1_SIGVALIDITY / DUNITER_V1_DAYS
+        {
+            warn!(
+                "parameter `cert_validity_period` ({} days) is different from Ğ1's ({} days)",
+                common_parameters.cert_validity_period as f32 / DAYS as f32,
+                G1_DUNITER_V1_SIGVALIDITY as f32 / DUNITER_V1_DAYS as f32
+            )
+        }
+        if common_parameters.min_cert != G1_DUNITER_V1_SIGQTY {
+            warn!(
+                "parameter `min_cert` value ({}) is different from Ğ1 value ({})",
+                common_parameters.min_cert, G1_DUNITER_V1_SIGQTY
+            )
+        }
+        if common_parameters.cert_max_by_issuer != G1_DUNITER_V1_SIGSTOCK {
+            warn!(
+                "parameter `cert_max_by_issuer` value ({}) is different from Ğ1 value ({})",
+                common_parameters.cert_max_by_issuer, G1_DUNITER_V1_SIGSTOCK
+            )
+        }
+        if c != g1_duniter_v1_c {
+            warn!(
+                "parameter `c` value ({}) is different from Ğ1 value ({})",
+                c, g1_duniter_v1_c
+            )
+        }
+        if common_parameters.ud_creation_period as f32 / DAYS as f32
+            != G1_DUNITER_V1_DT as f32 / DUNITER_V1_DAYS as f32
+        {
+            warn!(
+                "parameter `ud_creation_period` value ({} days) is different from Ğ1 value ({} days)",
+                common_parameters.ud_creation_period as f32 / DAYS as f32, G1_DUNITER_V1_DT as f32 / DUNITER_V1_DAYS as f32
+            )
+        }
+        if common_parameters.ud_reeval_period as f32 / DAYS as f32
+            != G1_DUNITER_V1_DTREEVAL as f32 / DUNITER_V1_DAYS as f32
+        {
+            warn!(
+                "parameter `ud_reeval_period` value ({} days) is different from Ğ1 value ({} days)",
+                common_parameters.ud_reeval_period as f32 / DAYS as f32,
+                G1_DUNITER_V1_DTREEVAL as f32 / DUNITER_V1_DAYS as f32
+            )
+        }
+        if common_parameters.distance_min_accessible_referees != g1_duniter_v1_xpercent {
+            warn!(
+                "parameter `distance_min_accessible_referees` value ({}) is different from Ğ1 value ({})",
+                format!("{:?}", common_parameters.distance_min_accessible_referees), format!("{:?}", g1_duniter_v1_xpercent)
+            )
+        }
+        if common_parameters.max_depth != G1_DUNITER_V1_STEPMAX {
+            warn!(
+                "parameter `max_depth` value ({}) is different from Ğ1 value ({})",
+                common_parameters.max_depth, G1_DUNITER_V1_STEPMAX
+            )
+        }
+        let count_uds = common_parameters.ud_reeval_period / common_parameters.ud_creation_period;
+        if count_uds == 0 {
+            error!(
+                "the `ud_reeval_period / ud_creation_period` is zero ({} days/{} days)",
+                common_parameters.ud_reeval_period / DAYS as u64,
+                common_parameters.ud_creation_period / DAYS as u64
+            );
+            fatal = true;
+        }
+    }
+    fatal
+}
+
+fn v1_wallets_to_v2_accounts(
+    wallets: BTreeMap<PubkeyV1, u64>,
+    common_parameters: &CommonParameters,
+) -> (
+    bool,
+    u64,
+    BTreeMap<AccountId32, GenesisAccountData<u64>>,
+    usize,
+) {
+    // monetary mass for double check
+    let mut monetary_mass = 0u64;
+    // account inserted in genesis
+    let mut accounts: BTreeMap<AccountId, GenesisAccountData<u64>> = BTreeMap::new();
+    let mut invalid_wallets = 0;
+    let mut fatal = false;
+    for (pubkey, balance) in wallets {
+        // check existential deposit
+        if balance < common_parameters.existential_deposit {
+            log::error!(
+                "wallet {pubkey} has {balance} cǦT which is below {}",
+                common_parameters.existential_deposit
+            );
+            fatal = true;
+        }
+
+        // double check the monetary mass
+        monetary_mass += balance;
+
+        // json prevents duplicate wallets
+        if let Ok(owner_key) = v1_pubkey_to_account_id(pubkey.clone()) {
+            accounts.insert(
+                owner_key.clone(),
+                GenesisAccountData {
+                    random_id: H256(blake2_256(&(balance, &owner_key).encode())),
+                    balance,
+                    is_identity: false,
+                },
+            );
+        } else {
+            log::warn!("wallet {pubkey} has wrong format");
+            invalid_wallets = invalid_wallets.add(1);
+        }
+    }
+    (fatal, monetary_mass, accounts, invalid_wallets)
+}
+
+fn check_smiths_vs_clique_smiths(
+    smith_identities: &Option<BTreeMap<String, SmithData>>,
+    clique_smiths: &Option<Vec<CliqueSmith>>,
+) -> Result<(), String> {
+    if smith_identities.is_some() && clique_smiths.is_some() {
+        return Err(
+            "'smiths' and 'clique_smiths' cannot be both defined at the same time".to_string(),
+        );
+    }
+    Ok(())
+}
+
+fn check_identities_v2(
+    identities_v2: &HashMap<String, IdentityV2>,
+    common_parameters: &CommonParameters,
+) {
+    // // Identities whose membership was lost since export
+    // identities_v2.iter_mut()
+    //     .filter(|(name, i)| (i.membership_expire_on as u64) < genesis_timestamp)
+    //     .for_each(|(name, i)| {
+    //         log::warn!("{} membership expired since export", name);
+    //         i.membership_expire_on = 0;
+    //     });
+
+    // // Identities that are no more members because of a lack of certs
+    // identities_v2.iter_mut()
+    //     .filter(|(name, i)| i.membership_expire_on != 0 && (i.certs_received.len() as u32) < common_parameters.min_cert)
+    //     .for_each(|(name, i)| {
+    //         log::warn!("{} lost membership because of lost certifications since export", name);
+    //         i.membership_expire_on = 0;
+    //     });
+
+    // Check that members have enough certs
+    identities_v2
+        .iter()
+        .filter(|(_, i)| i.membership_expire_on != 0)
+        .for_each(|(name, i)| {
+            let nb_certs = i.certs_received.len() as u32;
+            if nb_certs < common_parameters.min_cert {
+                log::warn!("{} has only {} valid certifications", name, nb_certs);
+            }
+        });
+}
+
+fn check_genesis_data_filter_certs(
+    genesis_data: &mut GenesisMigrationData,
+    genesis_timestamp: u64,
+    common_parameters: &CommonParameters,
+) {
+    // Remove expired certs since export
+    genesis_data
+        .identities
+        .iter_mut()
+        .for_each(|(receiver, i)| {
+            i.certs_received.retain(|issuer, v| {
+                let retain = (v.0 as u64) >= genesis_timestamp;
+                if !retain {
+                    log::warn!("{} -> {} cert expired since export", issuer, receiver);
+                }
+                retain
+            });
+        });
+
+    genesis_data.identities.iter_mut().for_each(|(name, i)| {
+        if (i.membership_expire_on.0 as u64) < genesis_timestamp {
+            i.membership_expire_on = TimestampV1(0);
+            log::warn!("{} membership expired since export", name);
+        }
+    });
+
+    genesis_data.identities.iter_mut().for_each(|(name, i)| {
+        if i.membership_expire_on.0 != 0
+            && i.certs_received.len() < common_parameters.min_cert as usize
+        {
+            i.membership_expire_on = TimestampV1(0);
+            log::warn!(
+                "{} lost membership because of lost certifications since export",
+                name
+            );
+        }
+    });
+
+    genesis_data.identities.iter().for_each(|(name, i)| {
+        if i.owner_pubkey.is_some() && i.owner_address.is_some() {
+            log::warn!(
+                "{} both has a pubkey and an address defined - address will be used",
+                name
+            );
+        }
+        if i.owner_pubkey.is_none() && i.owner_address.is_none() {
+            log::error!("{} neither has a pubkey and an address defined", name);
+        }
+    });
+}
+
+fn genesis_data_to_identities_v2(
+    genesis_identities: BTreeMap<String, IdentityV1>,
+    genesis_timestamp: u64,
+) -> HashMap<String, IdentityV2> {
+    genesis_identities
+        .into_iter()
+        .map(|(name, i)| {
+            (
+                name.clone(),
+                IdentityV2 {
+                    index: i.index,
+                    owner_key: i
+                        .owner_pubkey
+                        .map(|pubkey| {
+                            v1_pubkey_to_account_id(pubkey)
+                                .expect("a G1 identity necessarily has a valid pubkey")
+                        })
+                        .unwrap_or_else(|| {
+                            i.owner_address.unwrap_or_else(|| {
+                                panic!("neither pubkey nor address is defined for {}", name)
+                            })
+                        }),
+                    old_owner_key: None,
+                    membership_expire_on: timestamp_to_relative_blocs(
+                        i.membership_expire_on,
+                        genesis_timestamp,
+                    ),
+                    next_cert_issuable_on: timestamp_to_relative_blocs(
+                        i.next_cert_issuable_on,
+                        genesis_timestamp,
+                    ),
+                    balance: i.balance,
+                    certs_received: i
+                        .certs_received
+                        .into_iter()
+                        .map(|(issuer, timestamp)| {
+                            (
+                                issuer,
+                                timestamp_to_relative_blocs(timestamp, genesis_timestamp),
+                            )
+                        })
+                        .collect(),
+                },
+            )
+        })
+        .collect()
+}
+
+fn make_authority_exist<SessionKeys: Encode, SKP: SessionKeysProvider<SessionKeys>>(
+    identities_v2: &mut HashMap<String, IdentityV2>,
+    smith_identities: &mut Option<BTreeMap<String, SmithData>>,
+    clique_smiths: &mut Option<Vec<CliqueSmith>>,
+    common_parameters: &CommonParameters,
+    authority_name: &String,
+) {
+    // The identity might already exist, notably: G1 "Alice" already exists
+    if let Some(authority) = identities_v2.get_mut(authority_name) {
+        // Force authority to be active
+        authority.membership_expire_on = common_parameters.membership_period;
+    } else {
+        // Not found: we must create it
+        identities_v2.insert(
+            authority_name.clone(),
+            IdentityV2 {
+                index: (identities_v2.len() as u32 + 1),
+                owner_key: get_account_id_from_seed::<sr25519::Public>(authority_name),
+                balance: common_parameters.existential_deposit,
+                certs_received: HashMap::new(),
+                membership_expire_on: common_parameters.membership_period,
+                old_owner_key: None,
+                next_cert_issuable_on: 0,
+            },
+        );
+    };
+    // Forced authority gets its required certs from first "minCert" WoT identities (fake certs)
+    let mut new_certs: HashMap<String, u32> = HashMap::new();
+    let certs_of_authority = &identities_v2.get(authority_name).unwrap().certs_received;
+    identities_v2
+        .keys()
+        // Identities which are not the authority and have not already certified her
+        .filter(|issuer| {
+            issuer != &authority_name
+                && !certs_of_authority
+                    .iter()
+                    .any(|(authority_issuer, _)| issuer == &authority_issuer)
+        })
+        .take(common_parameters.min_cert as usize)
+        .map(String::clone)
+        .for_each(|issuer| {
+            new_certs.insert(issuer, common_parameters.cert_period);
+        });
+    let authority = identities_v2
+        .get_mut(authority_name)
+        .expect("authority must exist or be created");
+    new_certs.into_iter().for_each(|(issuer, c)| {
+        authority.certs_received.insert(issuer, c);
+    });
+    let sk: SessionKeys = SKP::session_keys(&get_authority_keys_from_seed(authority_name.as_str()));
+    let forced_authority_session_keys = format!("0x{}", hex::encode(sk.encode()));
+    // Add forced authority to smiths (whether explicit smith WoT or clique)
+    if let Some(smith_identities) = smith_identities {
+        smith_identities.insert(
+            authority_name.clone(),
+            SmithData {
+                idty_index: authority.index,
+                name: authority_name.clone(),
+                account: authority.owner_key.clone(),
+                session_keys: Some(forced_authority_session_keys.clone()),
+                certs_received: vec![],
+            },
+        );
+    }
+    if let Some(clique_smiths) = clique_smiths {
+        let existing = clique_smiths.iter_mut().find(|s| &s.name == authority_name);
+        if let Some(smith) = existing {
+            // Forced authority is already set in smiths: but we force its session keys
+            smith.session_keys = Some(forced_authority_session_keys);
+        } else {
+            // Forced authority is not known in smiths: we add it with its session keys
+            clique_smiths.push(CliqueSmith {
+                name: authority_name.clone(),
+                session_keys: Some(forced_authority_session_keys),
+            });
+        }
+    }
+}
+
+fn feed_identities(
+    identities: &mut Vec<GenesisMemberIdentity>,
+    accounts: &mut BTreeMap<AccountId32, GenesisAccountData<u64>>,
+    identity_index: &mut HashMap<u32, String>,
+    monetary_mass: &mut u64,
+    inactive_identities: &mut HashMap<u32, String>,
+    memberships: &mut BTreeMap<u32, MembershipData>,
+    identities_v2: &HashMap<String, IdentityV2>,
+) -> Result<bool, String> {
+    let mut fatal = false;
+    for (name, identity) in identities_v2 {
+        // identity name
+        if !validate_idty_name(name) {
+            return Err(format!("Identity name '{}' is invalid", &name));
+        }
+
+        // TODO: re-check this code origin and wether it should be included or not
+        // do not check existential deposit of identities
+        // // check existential deposit
+        // if identity.balance < common_parameters.existencial_deposit {
+        //     if identity.membership_expire_on == 0 {
+        //         log::warn!(
+        //             "expired identity {name} has {} cǦT which is below {}",
+        //             identity.balance, common_parameters.existencial_deposit
+        //         );
+        //         fatal = true;
+        //     } else {
+        //         member identities can still be below existential deposit thanks to sufficient
+        //         log::info!(
+        //             "identity {name} has {} cǦT which is below {}",
+        //             identity.balance, common_parameters.existencial_deposit
+        //         );
+        //     }
+        // }
+
+        // Money
+        // check that wallet with same owner_key does not exist
+        if accounts.get(&identity.owner_key).is_some() {
+            log::error!(
+                "{name} owner_key {} already exists as a simple wallet",
+                identity.owner_key
+            );
+            fatal = true;
+        }
+        // insert as an account
+        accounts.insert(
+            identity.owner_key.clone(),
+            GenesisAccountData {
+                random_id: H256(blake2_256(&(identity.index, &identity.owner_key).encode())),
+                balance: identity.balance,
+                is_identity: true,
+            },
+        );
+
+        // double check the monetary mass
+        *monetary_mass += identity.balance;
+
+        // insert identity
+        // check that index does not already exist
+        if let Some(other_name) = identity_index.get(&identity.index) {
+            log::error!(
+                "{other_name} already has identity index {} of {name}",
+                identity.index
+            );
+            fatal = true;
+        }
+        identity_index.insert(identity.index, name.to_owned());
+
+        let expired = identity.membership_expire_on == 0;
+        // only add the identity if not expired
+        if expired {
+            inactive_identities.insert(identity.index, name.clone());
+        };
+        identities.push(GenesisMemberIdentity {
+            // N.B.: every **non-expired** identity on Genesis is considered to have:
+            //  - removable_on: 0,
+            //  - next_creatable_identity_on: 0,
+            //  - status: IdtyStatus::Validated,
+            index: identity.index,
+            name: name.to_owned().clone(),
+            owned_key: identity.owner_key.clone(),
+            old_owned_key: identity.old_owner_key.clone(),
+            // but expired identities will just have their pseudonym reserved in the storage
+            active: !expired,
+        });
+
+        // insert the membershup data (only if not expired)
+        if !expired {
+            memberships.insert(
+                identity.index,
+                MembershipData {
+                    expire_on: identity.membership_expire_on,
+                },
+            );
+        }
+    }
+    // sort the identities by index for reproducibility (should have been a vec in json)
+    identities.sort_unstable_by(|a, b| a.index.cmp(&b.index));
+
+    Ok(fatal)
+}
+
+fn set_session_keys_and_authority_status<SK>(
+    initial_authorities: &mut BTreeMap<
+        u32,
+        (
+            <<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId,
+            bool,
+        ),
+    >,
+    session_keys_map: &mut BTreeMap<
+        <<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId,
+        SK,
+    >,
+    smith: &&SmithData,
+    identity: &&&IdentityV2,
+) -> Result<u32, String>
+where
+    SK: Decode,
+{
+    let mut counter_online_authorities = 0;
+    // Initial authorities and session keys
+    let session_keys_bytes = if let Some(declared_session_keys) = &smith.session_keys {
+        counter_online_authorities += 1;
+        // insert authority as online
+        initial_authorities.insert(identity.index, (identity.owner_key.clone(), true));
+        // decode session keys or force to given value
+        hex::decode(&declared_session_keys[2..])
+            .map_err(|_| format!("invalid session keys for idty {}", smith.name))?
+    } else {
+        // still authority but offline
+        initial_authorities.insert(identity.index, (identity.owner_key.clone(), false));
+        // fake session keys
+        let mut fake_bytes = Vec::with_capacity(128);
+        for _ in 0..4 {
+            fake_bytes.extend_from_slice(identity.owner_key.as_ref())
+        }
+        fake_bytes
+    };
+
+    // insert session keys to map
+    session_keys_map.insert(
+        identity.owner_key.clone(),
+        SK::decode(&mut &session_keys_bytes[..]).unwrap(),
+    );
+
+    Ok(counter_online_authorities)
+}
+
+fn feed_smith_certs_by_receiver(
+    smith_certs_by_receiver: &mut BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
+    clique_smiths: &Option<Vec<CliqueSmith>>,
+    smith: &&SmithData,
+    identity: &&&IdentityV2,
+    identities_v2: &HashMap<String, IdentityV2>,
+    common_parameters: &CommonParameters,
+) -> Result<u32, String> {
+    let mut counter_smith_cert = 0;
+    let mut certs = BTreeMap::new();
+    if clique_smiths.is_some() {
+        // All initial smiths are considered to be certifying all each other
+        clique_smiths
+            .as_ref()
+            .unwrap()
+            .iter()
+            .filter(|other_smith| *other_smith.name.as_str() != *smith.name)
+            .for_each(|other_smith| {
+                let issuer_index = &identities_v2
+                    .get(other_smith.name.as_str())
+                    .unwrap_or_else(|| {
+                        panic!("Identity '{}' does not exist", other_smith.name.as_str())
+                    })
+                    .index;
+                certs.insert(*issuer_index, None);
+            });
+    } else {
+        for issuer in &smith.certs_received {
+            let issuer_index = &identities_v2
+                .get(issuer)
+                .ok_or(format!("Identity '{}' does not exist", issuer))?
+                .index;
+            certs.insert(
+                *issuer_index,
+                Some(common_parameters.smith_certs_validity_period),
+            );
+            counter_smith_cert += 1;
+        }
+        smith_certs_by_receiver.insert(identity.index, certs);
+    }
+    Ok(counter_smith_cert)
+}
+
+fn feed_certs_by_receiver(
+    certs_by_receiver: &mut BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
+    identities_v2: &HashMap<String, IdentityV2>,
+) -> (bool, u32) {
+    let mut fatal = false;
+    let mut counter_cert = 0;
+    for identity in identities_v2.values() {
+        let mut certs = BTreeMap::new();
+        for (issuer, expire_on) in &identity.certs_received {
+            if let Some(issuer) = &identities_v2.get(issuer) {
+                certs.insert(issuer.index, Some(*expire_on));
+                counter_cert += 1;
+            } else {
+                log::error!("Identity '{}' does not exist", issuer);
+                fatal = true;
+            };
+        }
+        certs_by_receiver.insert(identity.index, certs);
+    }
+    (fatal, counter_cert)
+}
+
+fn check_authority_exists_in_both_wots(
+    name: &String,
+    identities_v2: &HashMap<String, IdentityV2>,
+    clique_smiths: &Option<Vec<CliqueSmith>>,
+    smith_identities: &Option<BTreeMap<String, SmithData>>,
+) {
+    identities_v2
+        .get(name)
+        .ok_or(format!("Identity '{}' not exist", name))
+        .expect("Initial authority must have an identity");
+    if let Some(smiths) = &clique_smiths {
+        smiths
+            .iter()
+            .find(|s| s.name == *name)
+            .expect("Forced authority must be present in smiths clique");
+    }
+    if let Some(smiths) = &smith_identities {
+        smiths
+            .iter()
+            .find(|(other_name, _)| *other_name == name)
+            .expect("Forced authority must be present in smiths");
+    }
+}
+
+fn build_smiths_wot(
+    clique_smiths: &Option<Vec<CliqueSmith>>,
+    identity_index: &HashMap<u32, String>,
+    identities_v2: &HashMap<String, IdentityV2>,
+    smith_identities: Option<BTreeMap<String, SmithData>>,
+) -> Vec<SmithData> {
+    // Create a single source of smiths
+    let smiths = if let Some(clique) = &clique_smiths {
+        // From a clique
+        clique
+            .iter()
+            .map(|smith| {
+                let idty_index = identity_index
+                    .iter()
+                    .find(|(_, v)| ***v == smith.name)
+                    .map(|(k, _)| *k)
+                    .expect("smith must have an identity");
+                SmithData {
+                    idty_index,
+                    name: smith.name.clone(),
+                    account: identities_v2
+                        .get(smith.name.as_str())
+                        .map(|i| i.owner_key.clone())
+                        .expect("identity must exist"),
+                    session_keys: smith.session_keys.clone(),
+                    certs_received: vec![],
+                }
+            })
+            .collect::<Vec<SmithData>>()
+    } else {
+        // From explicit smith WoT
+        smith_identities
+            .expect("existence has been tested earlier")
+            .into_values()
+            .collect::<Vec<SmithData>>()
+    };
+    smiths
+}
+
 pub fn generate_genesis_data_for_local_chain<P, SK, SessionKeys: Encode, SKP>(
     initial_authorities_len: usize,
     initial_smiths_len: usize,
-- 
GitLab