From 40823408be482d2d9a4864b46ab79ea04197be84 Mon Sep 17 00:00:00 2001
From: cgeek <cem.moreau@gmail.com>
Date: Mon, 11 Sep 2023 21:19:57 +0200
Subject: [PATCH] fix(#125): dissociate genesis config from genesis migration

---
 node/src/chain_spec/gen_genesis_data.rs | 133 ++++++++++++++++++------
 1 file changed, 101 insertions(+), 32 deletions(-)

diff --git a/node/src/chain_spec/gen_genesis_data.rs b/node/src/chain_spec/gen_genesis_data.rs
index 2fdd6cef0..3d4dd4d71 100644
--- a/node/src/chain_spec/gen_genesis_data.rs
+++ b/node/src/chain_spec/gen_genesis_data.rs
@@ -15,9 +15,12 @@
 // along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
 
 use common_runtime::*;
+use maplit::btreemap;
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
+use sp_core::crypto::AccountId32;
 use sp_core::{blake2_256, Decode, Encode, H256};
 use std::collections::BTreeMap;
+use std::fmt::format;
 
 type MembershipData = sp_membership::MembershipData<u32>;
 
@@ -55,7 +58,6 @@ struct GenesisConfig<Parameters> {
     first_ud: Option<u64>,
     first_ud_reeval: Option<u64>,
     genesis_parameters: ParamsAppliedAtGenesis,
-    identities: BTreeMap<String, Idty>,
     #[serde(default)]
     parameters: Parameters,
     #[serde(rename = "smiths")]
@@ -63,6 +65,11 @@ struct GenesisConfig<Parameters> {
     sudo_key: Option<AccountId>,
     technical_committee: Vec<String>,
     ud: u64,
+}
+
+#[derive(Deserialize, Serialize)]
+struct GenesisMigrationData {
+    identities: BTreeMap<String, Idty>,
     #[serde(default)]
     wallets: BTreeMap<AccountId, u64>,
 }
@@ -112,36 +119,8 @@ where
     SK: Decode,
     F: Fn(GenesisData<P, SK>) -> CS,
 {
-    let genesis_timestamp: u64 =
-        if let Ok(genesis_timestamp) = std::env::var("DUNITER_GENESIS_TIMESTAMP") {
-            genesis_timestamp
-                .parse()
-                .map_err(|_| "DUNITER_GENESIS_TIMESTAMP must be a number".to_owned())?
-        } else {
-            use std::time::SystemTime;
-            SystemTime::now()
-                .duration_since(SystemTime::UNIX_EPOCH)
-                .expect("SystemTime before UNIX EPOCH!")
-                .as_secs()
-        };
-
-    let json_file_path = std::env::var("DUNITER_GENESIS_CONFIG")
-        .unwrap_or_else(|_| "duniter-gen-conf.json".to_owned());
-
-    // We mmap the file into memory first, as this is *a lot* faster than using
-    // `serde_json::from_reader`. See https://github.com/serde-rs/json/issues/160
-    let file = std::fs::File::open(&json_file_path)
-        .map_err(|e| format!("Error opening gen conf file `{}`: {}", json_file_path, e))?;
+    let genesis_timestamp: u64 = get_genesis_timestamp()?;
 
-    // SAFETY: `mmap` is fundamentally unsafe since technically the file can change
-    //         underneath us while it is mapped; in practice it's unlikely to be a problem
-    let bytes = unsafe {
-        memmap2::Mmap::map(&file)
-            .map_err(|e| format!("Error mmaping gen conf file `{}`: {}", json_file_path, e))?
-    };
-
-    let genesis_config = serde_json::from_slice(&bytes)
-        .map_err(|e| format!("Error parsing gen conf file: {}", e))?;
     let GenesisConfig {
         sudo_key,
         first_ud,
@@ -154,12 +133,19 @@ where
                 genesis_smith_memberships_expire_on,
             },
         parameters,
-        identities,
+        // identities,
         smith_identities,
         technical_committee,
         ud,
+        // wallets,
+    } = get_genesis_config::<P>()?;
+
+    let GenesisMigrationData {
+        identities,
         wallets,
-    } = genesis_config;
+    } = get_genesis_migration_data()?;
+
+    check_parameters_consistency(&wallets, &first_ud, &first_ud_reeval, &ud)?;
 
     // MONEY AND WOT //
 
@@ -396,6 +382,89 @@ where
     Ok(f(genesis_data))
 }
 
+fn check_parameters_consistency(
+    wallets: &BTreeMap<AccountId32, u64>,
+    first_ud: &Option<u64>,
+    first_reeval: &Option<u64>,
+    ud: &u64,
+) -> Result<(), String> {
+    // No empty wallet
+    if let Some((account, _)) = wallets.iter().find(|(_, amount)| **amount == 0) {
+        return Err(format!("Wallet {} is empty", account));
+    }
+
+    // TODO: check ud_reeval_period / ud_creation_period (can round to zero)
+
+    if first_reeval.is_none() {
+        return Err("Please provied a value for `first_ud_reeval`".to_owned());
+    }
+    if first_ud.is_none() {
+        return Err("Please provied a value for `first_ud`".to_owned());
+    }
+    if first_ud.unwrap() > first_reeval.unwrap() {
+        return Err(format!(
+            "`first_ud` ({}) should be lower than `first_ud_reeval` ({})",
+            first_ud.unwrap(),
+            first_reeval.unwrap()
+        )
+        .to_owned());
+    }
+    if *ud == 0 {
+        return Err("`ud` is expected to be > 0".to_owned());
+    }
+    Ok(())
+}
+
+fn get_genesis_config<P: Default + DeserializeOwned>() -> Result<GenesisConfig<P>, String> {
+    let json_file_path = std::env::var("DUNITER_GENESIS_CONFIG")
+        .unwrap_or_else(|_| "duniter-gen-conf.json".to_owned());
+    // We mmap the file into memory first, as this is *a lot* faster than using
+    // `serde_json::from_reader`. See https://github.com/serde-rs/json/issues/160
+    let file = std::fs::File::open(&json_file_path)
+        .map_err(|e| format!("Error opening gen conf file `{}`: {}", json_file_path, e))?;
+    // SAFETY: `mmap` is fundamentally unsafe since technically the file can change
+    //         underneath us while it is mapped; in practice it's unlikely to be a problem
+    let bytes = unsafe {
+        memmap2::Mmap::map(&file)
+            .map_err(|e| format!("Error mmaping gen conf file `{}`: {}", json_file_path, e))?
+    };
+    serde_json::from_slice::<GenesisConfig<P>>(&bytes)
+        .map_err(|e| format!("Error parsing gen conf file: {}", e))
+}
+
+fn get_genesis_migration_data() -> Result<GenesisMigrationData, String> {
+    if let Ok(json_file_path) = std::env::var("DUNITER_GENESIS_MIGRATION") {
+        let file = std::fs::File::open(&json_file_path)
+            .map_err(|e| format!("Error opening gen conf file `{}`: {}", json_file_path, e))?;
+        let bytes = unsafe {
+            memmap2::Mmap::map(&file)
+                .map_err(|e| format!("Error mmaping gen conf file `{}`: {}", json_file_path, e))?
+        };
+        serde_json::from_slice::<GenesisMigrationData>(&bytes)
+            .map_err(|e| format!("Error parsing gen conf file: {}", e))
+    } else {
+        // No data
+        Ok(GenesisMigrationData {
+            wallets: btreemap! {},
+            identities: btreemap! {},
+        })
+    }
+}
+
+fn get_genesis_timestamp() -> Result<u64, String> {
+    if let Ok(genesis_timestamp) = std::env::var("DUNITER_GENESIS_TIMESTAMP") {
+        genesis_timestamp
+            .parse()
+            .map_err(|_| "DUNITER_GENESIS_TIMESTAMP must be a number".to_owned())
+    } else {
+        use std::time::SystemTime;
+        Ok(SystemTime::now()
+            .duration_since(SystemTime::UNIX_EPOCH)
+            .expect("SystemTime before UNIX EPOCH!")
+            .as_secs())
+    }
+}
+
 // Timestamp to block number
 fn to_bn(genesis_timestamp: u64, timestamp: u64) -> u32 {
     let duration_in_secs = timestamp.saturating_sub(genesis_timestamp);
-- 
GitLab