From 3745d65e4dd4f84476a1b5e2939a5e17599a3266 Mon Sep 17 00:00:00 2001
From: bgallois <benjamin@gallois.cc>
Date: Fri, 31 Mar 2023 13:48:26 +0200
Subject: [PATCH] feat(node): add genesis builder for benchmarking

Added a genesis builder for benchmarking with pre-configured accounts that can be used during the benchmarking and circumvent limitation imposed by pallets coupling.
---
 docs/dev/weights-benchmarking.md |   6 +-
 node/src/chain_spec/gdev.rs      | 257 +++++++++++++++++++++++++++++++
 node/src/command.rs              |   2 +
 3 files changed, 262 insertions(+), 3 deletions(-)

diff --git a/docs/dev/weights-benchmarking.md b/docs/dev/weights-benchmarking.md
index e60893212..330a56394 100644
--- a/docs/dev/weights-benchmarking.md
+++ b/docs/dev/weights-benchmarking.md
@@ -22,13 +22,13 @@ complete real example.
 machine: `cargo build --release --features runtime-benchmarks`
 4. Run the benchmarks on your local machine (to test if it work with a real runtime). The command
 is: `duniter benchmark pallet --chain=CURRENCY-dev --steps=50 --repeat=20 --pallet=pallet_universal_dividend --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=.`
-5. If it worked, use the generated file content to create or update the `WeightInfo` trait and the `()` dummy implementation. Then use the `WeightInfo` tarit in the real code of the pallet. See 79e0fd4bf3b0579279fc957da5e2fdfc6d8a17fa for a
+5. If it worked, use the generated file content to create or update by hand the `WeightInfo` trait and the `()` dummy implementation. Then use the `WeightInfo` tarit in the real code of the pallet. See 79e0fd4bf3b0579279fc957da5e2fdfc6d8a17fa for a
 complete real example.
 6. Redo steps `3.` and `4.` on the reference machine.
-7. Put the generated file on `runtime/common/src/weights` and use it in the runtimes configuration.
+7. Put the automatically generated file on `runtime/common/src/weights` and use it in the runtimes configuration.
 See cee7c3b2763ba402e807f126534d9cd39a8bd025 for a complete real example.
 
-Note 1: You *must* replace `CURRENCY` by the currency type, or for ÄžDev use directly `--chain=dev`.
+Note 1: You *must* replace `CURRENCY` by the currency type, or for ÄžDev use directly `--chain=gdev-benchmark`.
 
 Note 2: If the reference machine does not support wasmtime, you should replace `--wasm-execution=compiled`
 by `--wasm-execution=interpreted-i-know-what-i-do`.
diff --git a/node/src/chain_spec/gdev.rs b/node/src/chain_spec/gdev.rs
index b83bfa370..015813f1b 100644
--- a/node/src/chain_spec/gdev.rs
+++ b/node/src/chain_spec/gdev.rs
@@ -183,6 +183,53 @@ pub fn development_chain_spec() -> Result<ChainSpec, String> {
     }
 }
 
+/// generate chainspecs used for benchmarks
+pub fn benchmark_chain_spec() -> Result<ChainSpec, String> {
+    let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;
+
+    Ok(ChainSpec::from_genesis(
+        // Name
+        "Development",
+        // ID
+        "gdev-benchmark",
+        ChainType::Development,
+        move || {
+            gen_genesis_for_benchmark_chain(
+                wasm_binary,
+                // Initial authorities len
+                1,
+                // Initial smiths members len
+                3,
+                // Inital identities len
+                4,
+                // Sudo account
+                get_account_id_from_seed::<sr25519::Public>("Alice"),
+                true,
+            )
+        },
+        // Bootnodes
+        vec![],
+        // Telemetry
+        None,
+        // Protocol ID
+        None,
+        //Fork ID
+        None,
+        // Properties
+        Some(
+            serde_json::json!({
+                    "tokenDecimals": TOKEN_DECIMALS,
+                    "tokenSymbol": TOKEN_SYMBOL,
+            })
+            .as_object()
+            .expect("must be a map")
+            .clone(),
+        ),
+        // Extensions
+        None,
+    ))
+}
+
 /// generate live network chainspecs
 pub fn gen_live_conf() -> Result<ChainSpec, String> {
     let wasm_binary = WASM_BINARY.ok_or_else(|| "wasm not available".to_string())?;
@@ -443,6 +490,216 @@ fn gen_genesis_for_local_chain(
     }
 }
 
+/// genesis used for benchmark
+/// contain identities prepared for testing
+fn gen_genesis_for_benchmark_chain(
+    wasm_binary: &[u8],
+    initial_authorities_len: usize,
+    initial_smiths_len: usize,
+    initial_identities_len: usize,
+    root_key: AccountId,
+    _enable_println: bool,
+) -> gdev_runtime::GenesisConfig {
+    assert!(initial_identities_len <= 6);
+    assert!(initial_smiths_len <= initial_identities_len);
+    assert!(initial_authorities_len <= initial_smiths_len);
+
+    let babe_epoch_duration = get_env_u32("DUNITER_BABE_EPOCH_DURATION", 30) as u64;
+    let cert_validity_period = get_env_u32("DUNITER_CERT_VALIDITY_PERIOD", 1_000);
+    let first_ud = 1_000;
+    let membership_period = get_env_u32("DUNITER_MEMBERSHIP_PERIOD", 1_000);
+    let smith_cert_validity_period = get_env_u32("DUNITER_SMITH_CERT_VALIDITY_PERIOD", 1_000);
+    let smith_membership_period = get_env_u32("DUNITER_SMITH_MEMBERSHIP_PERIOD", 1_000);
+    let ud_creation_period = get_env_u32("DUNITER_UD_CREATION_PERIOD", 10);
+    let ud_reeval_period = get_env_u32("DUNITER_UD_REEEVAL_PERIOD", 200);
+
+    let initial_smiths = (0..initial_smiths_len)
+        .map(|i| get_authority_keys_from_seed(NAMES[i]))
+        .collect::<Vec<AuthorityKeys>>();
+    let initial_identities = (0..initial_identities_len)
+        .map(|i| {
+            (
+                IdtyName::from(NAMES[i]),
+                get_account_id_from_seed::<sr25519::Public>(NAMES[i]),
+            )
+        })
+        .collect::<BTreeMap<IdtyName, AccountId>>();
+
+    gdev_runtime::GenesisConfig {
+        system: SystemConfig {
+            // Add Wasm runtime to storage.
+            code: wasm_binary.to_vec(),
+        },
+        account: AccountConfig {
+            accounts: initial_identities
+                .iter()
+                .enumerate()
+                .map(|(i, (_, owner_key))| {
+                    (
+                        owner_key.clone(),
+                        GenesisAccountData {
+                            random_id: H256(blake2_256(&(i as u32, owner_key).encode())),
+                            balance: first_ud,
+                            is_identity: true,
+                        },
+                    )
+                })
+                .collect(),
+        },
+        parameters: ParametersConfig {
+            parameters: GenesisParameters {
+                babe_epoch_duration,
+                cert_period: 15,
+                cert_max_by_issuer: 10,
+                cert_min_received_cert_to_issue_cert: 2,
+                cert_validity_period,
+                idty_confirm_period: 40,
+                idty_creation_period: 50,
+                membership_period,
+                pending_membership_period: 500,
+                ud_creation_period,
+                ud_reeval_period,
+                smith_cert_period: 15,
+                smith_cert_max_by_issuer: 8,
+                smith_cert_min_received_cert_to_issue_cert: 2,
+                smith_cert_validity_period,
+                smith_membership_period,
+                smith_pending_membership_period: 500,
+                smiths_wot_first_cert_issuable_on: 20,
+                smiths_wot_min_cert_for_membership: 2,
+                wot_first_cert_issuable_on: 20,
+                wot_min_cert_for_create_idty_right: 2,
+                wot_min_cert_for_membership: 2,
+            },
+        },
+        authority_discovery: Default::default(),
+        authority_members: AuthorityMembersConfig {
+            initial_authorities: initial_smiths
+                .iter()
+                .enumerate()
+                .map(|(i, keys)| (i as u32 + 1, (keys.0.clone(), i < initial_authorities_len)))
+                .collect(),
+        },
+        balances: BalancesConfig {
+            balances: Default::default(),
+        },
+        babe: BabeConfig {
+            authorities: Vec::with_capacity(0),
+            epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG),
+        },
+        grandpa: Default::default(),
+        im_online: Default::default(),
+        session: SessionConfig {
+            keys: initial_smiths
+                .iter()
+                .map(|x| {
+                    (
+                        x.0.clone(),
+                        x.0.clone(),
+                        session_keys(x.1.clone(), x.2.clone(), x.3.clone(), x.4.clone()),
+                    )
+                })
+                .collect::<Vec<_>>(),
+        },
+        sudo: SudoConfig {
+            // Assign network admin rights.
+            key: Some(root_key),
+        },
+        technical_committee: TechnicalCommitteeConfig {
+            members: initial_smiths
+                .iter()
+                .map(|x| x.0.clone())
+                .collect::<Vec<_>>(),
+            ..Default::default()
+        },
+        identity: IdentityConfig {
+            identities: initial_identities
+                .iter()
+                .enumerate()
+                .map(|(i, (name, owner_key))| {
+                    if i != initial_identities_len - 1 {
+                        GenesisIdty {
+                            index: i as u32 + 1,
+                            name: name.clone(),
+                            value: IdtyValue {
+                                data: IdtyData::new(),
+                                next_creatable_identity_on: Default::default(),
+                                old_owner_key: None,
+                                owner_key: owner_key.clone(),
+                                removable_on: 0,
+                                status: IdtyStatus::Validated,
+                            },
+                        }
+                    } else {
+                        GenesisIdty {
+                            index: i as u32 + 1,
+                            name: name.clone(),
+                            value: IdtyValue {
+                                data: IdtyData::new(),
+                                next_creatable_identity_on: Default::default(),
+                                old_owner_key: None,
+                                owner_key: owner_key.clone(),
+                                removable_on: 0,
+                                status: IdtyStatus::Created,
+                            },
+                        }
+                    }
+                })
+                .collect(),
+        },
+        membership: MembershipConfig {
+            memberships: (1..=initial_identities.len())
+                .filter_map(|i| {
+                    if i != initial_identities_len {
+                        Some((i as u32, MembershipData { expire_on: 0 }))
+                    } else {
+                        None
+                    }
+                })
+                .collect(),
+        },
+        cert: CertConfig {
+            apply_cert_period_at_genesis: false,
+            certs_by_receiver: clique_wot(initial_identities.len()),
+        },
+        smiths_membership: SmithsMembershipConfig {
+            memberships: (1..=initial_smiths_len)
+                .map(|i| (i as u32, MembershipData { expire_on: 0 }))
+                .collect(),
+        },
+        smiths_cert: SmithsCertConfig {
+            apply_cert_period_at_genesis: false,
+            certs_by_receiver: clique_wot(initial_smiths_len),
+        },
+        universal_dividend: UniversalDividendConfig {
+            first_reeval: 100,
+            first_ud,
+            initial_monetary_mass: initial_identities_len as u64 * first_ud,
+        },
+        treasury: Default::default(),
+    }
+}
+
+fn get_env_u32(env_var_name: &'static str, default_value: u32) -> u32 {
+    std::env::var(env_var_name)
+        .map_or(Ok(default_value), |s| s.parse())
+        .unwrap_or_else(|_| panic!("{} must be a number", env_var_name))
+}
+
+fn session_keys(
+    babe: BabeId,
+    grandpa: GrandpaId,
+    im_online: ImOnlineId,
+    authority_discovery: AuthorityDiscoveryId,
+) -> SessionKeys {
+    SessionKeys {
+        babe,
+        grandpa,
+        im_online,
+        authority_discovery,
+    }
+}
+
 /// custom genesis
 fn genesis_data_to_gdev_genesis_conf(
     genesis_data: super::gen_genesis_data::GenesisData<GenesisParameters, SessionKeys>,
diff --git a/node/src/command.rs b/node/src/command.rs
index 4ed633276..ce6f6d500 100644
--- a/node/src/command.rs
+++ b/node/src/command.rs
@@ -105,6 +105,8 @@ impl SubstrateCli for Cli {
             "gdev" => Box::new(chain_spec::gdev::ChainSpec::from_json_bytes(
                 &include_bytes!("../specs/gdev-raw.json")[..],
             )?),
+            #[cfg(feature = "gdev")]
+            "gdev-benchmark" => Box::new(chain_spec::gdev::benchmark_chain_spec()?),
             #[cfg(feature = "gtest")]
             "gtest_dev" => Box::new(chain_spec::gtest::development_chain_spec()?),
             #[cfg(feature = "gtest")]
-- 
GitLab