From ca6c877e1b7459773a0021cdb4087a26a01aff8f Mon Sep 17 00:00:00 2001
From: Hugo Trentesaux <hugo.trentesaux@lilo.org>
Date: Mon, 22 May 2023 20:12:13 +0200
Subject: [PATCH] gtest genesis new format (nodes/rust/duniter-v2s!168)

* fix smith certification validity

* get values of parameters

* tuxmain review

* add checks and improve formatting

* improve genesis parsing

adds info

* fix json with new format

* fix warnings

* new gtest genesis format

* get build working with gtest feature

`cargo build --features gtest --no-default-features`

* update lib.rs

* update cargo.toml

* add readme for runtimes
---
 .vscode/settings.json                |   2 +-
 Cargo.lock                           |   7 +-
 node/src/chain_spec.rs               |   3 +
 node/src/chain_spec/gtest.rs         | 189 ++++++----
 node/src/chain_spec/gtest_genesis.rs | 528 +++++++++++++++++++++++++++
 node/src/service/client.rs           |  19 +-
 resources/gtest.json                 | 331 +++++++++++++++++
 runtime/g1/README.md                 |   4 +
 runtime/gdev/README.md               |   5 +
 runtime/gtest/Cargo.toml             |  46 ++-
 runtime/gtest/README.md              |  10 +
 runtime/gtest/src/lib.rs             |  31 +-
 12 files changed, 1085 insertions(+), 90 deletions(-)
 create mode 100644 node/src/chain_spec/gtest_genesis.rs
 create mode 100644 resources/gtest.json
 create mode 100644 runtime/g1/README.md
 create mode 100644 runtime/gdev/README.md
 create mode 100644 runtime/gtest/README.md

diff --git a/.vscode/settings.json b/.vscode/settings.json
index fe6423f76..3070b8018 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,7 +4,7 @@
     100
   ],
   "[json]": {
-    "editor.defaultFormatter": "esbenp.prettier-vscode"
+    "editor.defaultFormatter": "vscode.json-language-features"
   },
   "[yaml]": {
     "editor.defaultFormatter": "esbenp.prettier-vscode"
diff --git a/Cargo.lock b/Cargo.lock
index 0c68f2968..7c5fc58d6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2865,7 +2865,6 @@ dependencies = [
  "pallet-certification",
  "pallet-collective",
  "pallet-duniter-account",
- "pallet-duniter-test-parameters",
  "pallet-duniter-wot",
  "pallet-grandpa",
  "pallet-identity",
@@ -2879,6 +2878,7 @@ dependencies = [
  "pallet-proxy",
  "pallet-scheduler",
  "pallet-session",
+ "pallet-session-benchmarking",
  "pallet-sudo",
  "pallet-timestamp",
  "pallet-transaction-payment",
@@ -2890,6 +2890,7 @@ dependencies = [
  "parity-scale-codec",
  "scale-info",
  "serde",
+ "serde_derive",
  "sp-api",
  "sp-arithmetic",
  "sp-authority-discovery",
@@ -9722,9 +9723,9 @@ version = "1.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
  "digest 0.10.6",
- "rand 0.7.3",
+ "rand 0.8.5",
  "static_assertions",
 ]
 
diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs
index 8bdef9198..580e076e4 100644
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -14,6 +14,7 @@
 // You should have received a copy of the GNU Affero General Public License
 // along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
 
+#[cfg(feature = "gdev")]
 pub mod gen_genesis_data;
 
 #[cfg(feature = "g1")]
@@ -22,6 +23,8 @@ pub mod g1;
 pub mod gdev;
 #[cfg(feature = "gtest")]
 pub mod gtest;
+#[cfg(feature = "gtest")]
+pub mod gtest_genesis;
 
 use common_runtime::{AccountId, IdtyIndex, Signature};
 use sp_core::{Pair, Public};
diff --git a/node/src/chain_spec/gtest.rs b/node/src/chain_spec/gtest.rs
index 180dce2b2..d45890721 100644
--- a/node/src/chain_spec/gtest.rs
+++ b/node/src/chain_spec/gtest.rs
@@ -19,10 +19,10 @@ use common_runtime::constants::*;
 use common_runtime::entities::IdtyData;
 use common_runtime::*;
 use gtest_runtime::{
-    opaque::SessionKeys, AccountConfig, AccountId, AuthorityMembersConfig, BabeConfig,
-    BalancesConfig, CertConfig, GenesisConfig, IdentityConfig, IdtyValue, ImOnlineId,
-    MembershipConfig, SessionConfig, SmithCertConfig, SmithMembershipConfig, SudoConfig,
-    SystemConfig, TechnicalCommitteeConfig, UniversalDividendConfig, WASM_BINARY,
+    opaque::SessionKeys, AccountConfig, AccountId, AuthorityMembersConfig, BabeConfig, CertConfig,
+    GenesisConfig, IdentityConfig, ImOnlineId, MembershipConfig, SessionConfig, SmithCertConfig,
+    SmithMembershipConfig, SudoConfig, SystemConfig, TechnicalCommitteeConfig,
+    UniversalDividendConfig, WASM_BINARY,
 };
 use sc_service::ChainType;
 use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
@@ -34,8 +34,8 @@ use std::collections::BTreeMap;
 
 pub type AuthorityKeys = (
     AccountId,
-    BabeId,
     GrandpaId,
+    BabeId,
     ImOnlineId,
     AuthorityDiscoveryId,
 );
@@ -51,57 +51,136 @@ const TOKEN_SYMBOL: &str = "ÄžT";
 pub fn get_authority_keys_from_seed(s: &str) -> AuthorityKeys {
     (
         get_account_id_from_seed::<sr25519::Public>(s),
-        get_from_seed::<BabeId>(s),
         get_from_seed::<GrandpaId>(s),
+        get_from_seed::<BabeId>(s),
         get_from_seed::<ImOnlineId>(s),
         get_from_seed::<AuthorityDiscoveryId>(s),
     )
 }
 
+/// Generate session keys
+fn get_session_keys_from_seed(s: &str) -> SessionKeys {
+    let authority_keys = get_authority_keys_from_seed(s);
+    session_keys(
+        authority_keys.1,
+        authority_keys.2,
+        authority_keys.3,
+        authority_keys.4,
+    )
+}
+
+/// make session keys struct
+fn session_keys(
+    grandpa: GrandpaId,
+    babe: BabeId,
+    im_online: ImOnlineId,
+    authority_discovery: AuthorityDiscoveryId,
+) -> SessionKeys {
+    SessionKeys {
+        grandpa,
+        babe,
+        im_online,
+        authority_discovery,
+    }
+}
+
+/// generate development chainspec with Alice validator
 pub fn development_chain_spec() -> Result<ChainSpec, String> {
     let wasm_binary = WASM_BINARY.ok_or_else(|| "wasm not available".to_string())?;
 
-    Ok(ChainSpec::from_genesis(
-        // Name
-        "Äžtest Development",
-        // ID
-        "gtest_dev",
-        ChainType::Development,
-        move || {
-            gen_genesis_for_local_chain(
-                wasm_binary,
-                // Initial authorities len
-                1,
-                // Initial smith 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!({
+    // custom genesis when DUNITER_GTEST_GENESIS is set
+    if let Ok(genesis_json_path) = std::env::var("DUNITER_GTEST_GENESIS") {
+        // log
+        log::info!("loading genesis from {genesis_json_path}");
+        // return chainspecs
+        Ok(ChainSpec::from_genesis(
+            // Name
+            "ÄžTest Development",
+            // ID
+            "gtest_dev",
+            // chain type
+            sc_service::ChainType::Development,
+            // genesis config constructor
+            move || {
+                super::gtest_genesis::build_genesis(
+                    // path of json genesis
+                    &genesis_json_path,
+                    // wasm binary
+                    wasm_binary,
+                    // replace authority by Alice
+                    Some(get_session_keys_from_seed("Alice").encode()),
+                )
+                .expect("genesis building failed")
+            },
+            // 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,
-    ))
+                })
+                .as_object()
+                .expect("must be a map")
+                .clone(),
+            ),
+            // Extensions
+            None,
+        ))
+    } else {
+        // log
+        log::info!("generating genesis");
+        // generated genesis
+        Ok(ChainSpec::from_genesis(
+            // Name
+            "ÄžTest Development",
+            // ID
+            "gtest_dev",
+            // chain type
+            ChainType::Development,
+            // constructor
+            move || {
+                gen_genesis_for_local_chain(
+                    wasm_binary,
+                    // Initial authorities len
+                    1,
+                    // Initial smith 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,
+        ))
+    }
 }
 
 pub fn local_testnet_config(
@@ -113,7 +192,7 @@ pub fn local_testnet_config(
 
     Ok(ChainSpec::from_genesis(
         // Name
-        "Äžtest Local Testnet",
+        "ÄžTest Local Testnet",
         // ID
         "gtest_local",
         ChainType::Local,
@@ -209,9 +288,7 @@ fn gen_genesis_for_local_chain(
                 .map(|(i, keys)| (i as u32 + 1, (keys.0.clone(), true)))
                 .collect(),
         },
-        balances: BalancesConfig {
-            balances: Vec::with_capacity(0),
-        },
+        balances: Default::default(),
         babe: BabeConfig {
             authorities: Vec::with_capacity(0),
             epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG),
@@ -299,17 +376,3 @@ fn gen_genesis_for_local_chain(
         treasury: Default::default(),
     }
 }
-
-fn session_keys(
-    babe: BabeId,
-    grandpa: GrandpaId,
-    im_online: ImOnlineId,
-    authority_discovery: AuthorityDiscoveryId,
-) -> SessionKeys {
-    SessionKeys {
-        babe,
-        grandpa,
-        im_online,
-        authority_discovery,
-    }
-}
diff --git a/node/src/chain_spec/gtest_genesis.rs b/node/src/chain_spec/gtest_genesis.rs
new file mode 100644
index 000000000..9aa6f737f
--- /dev/null
+++ b/node/src/chain_spec/gtest_genesis.rs
@@ -0,0 +1,528 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Duniter-v2S.
+//
+// Duniter-v2S is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// Duniter-v2S is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
+
+use common_runtime::constants::*;
+use common_runtime::entities::IdtyData;
+use common_runtime::*;
+use gtest_runtime::{
+    opaque::SessionKeys, parameters, AccountConfig, AccountId, AuthorityMembersConfig, BabeConfig,
+    CertConfig, GenesisConfig, IdentityConfig, MembershipConfig, SessionConfig, SmithCertConfig,
+    SmithMembershipConfig, SudoConfig, SystemConfig, TechnicalCommitteeConfig,
+    UniversalDividendConfig,
+};
+use serde::Deserialize;
+use sp_core::{blake2_256, Decode, Encode, H256};
+use std::collections::{BTreeMap, HashMap};
+
+type MembershipData = sp_membership::MembershipData<u32>;
+
+// get values of parameters
+static EXISTENTIAL_DEPOSIT: u64 = parameters::ExistentialDeposit::get();
+static SMITH_MEMBERSHIP_EXPIRE_ON: u32 = parameters::SmithMembershipPeriod::get();
+static SMITH_CERTS_EXPIRE_ON: u32 = parameters::SmithValidityPeriod::get();
+static MIN_CERT: u32 = parameters::WotMinCertForMembership::get();
+static SMITH_MIN_CERT: u32 = parameters::SmithWotMinCertForMembership::get();
+
+// define structure of json
+#[derive(Clone, Deserialize)]
+struct GenesisJson {
+    identities: HashMap<String, Identity>,
+    smiths: HashMap<String, Smith>,
+    first_ud: u64,
+    first_ud_reeval: u32,
+    initial_monetary_mass: u64,
+    wallets: HashMap<AccountId, u64>, // u128
+    sudo_key: Option<AccountId>,
+    technical_committee: Vec<String>,
+}
+
+/// identities
+#[derive(Clone, Deserialize)]
+struct Identity {
+    /// indentity index matching the order of appearance in the Ǧ1v1 blockchain
+    index: u32,
+    /// ss58 address in gtest network
+    owner_key: AccountId,
+    /// optional ss58 address in the Äž1v1
+    old_owner_key: Option<AccountId>,
+    /// block at which the membership is set to expire (0 for expired members)
+    membership_expire_on: u32,
+    /// block at which the next cert can be emitted
+    next_cert_issuable_on: u32,
+    /// balance of the account of this identity
+    balance: u64, // u128
+    /// certs received with their expiration block
+    certs_received: HashMap<String, u32>,
+}
+
+/// smith members
+#[derive(Clone, Deserialize)]
+struct Smith {
+    /// optional pre-set session keys (at least for the smith bootstraping the blockchain)
+    session_keys: Option<String>,
+    /// smith certification received
+    certs_received: Vec<String>,
+}
+
+// Timestamp to block number
+// fn to_bn(genesis_timestamp: u64, timestamp: u64) -> u32 {
+//     let duration_in_secs = timestamp.saturating_sub(genesis_timestamp);
+//     (duration_in_secs / 6) as u32
+// }
+
+// copied from duniter primitives
+fn validate_idty_name(idty_name: &str) -> bool {
+    idty_name.len() >= 3
+        && idty_name.len() <= 42 // length smaller than 42
+        // all characters are alphanumeric or - or _
+        && idty_name
+            .chars()
+            .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
+}
+
+/// ============================================================================================ ///
+/// build genesis from json file
+pub fn build_genesis(
+    // path of genesis config
+    genesis_config_path: &str,
+    // wasm binary
+    wasm_binary: &[u8],
+    // useful to enforce Alice authority when developing
+    maybe_force_authority: Option<Vec<u8>>,
+) -> Result<GenesisConfig, String> {
+    // preparatory steps
+
+    // define genesis timestamp
+    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()
+        };
+    log::info!("genesis timestamp: {}", genesis_timestamp);
+
+    // open json genesis file
+    let file = std::fs::File::open(&genesis_config_path).map_err(|e| {
+        format!(
+            "Error opening gen conf file `{}`: {}",
+            genesis_config_path, e
+        )
+    })?;
+
+    // memory map the file to avoid loading it in memory
+    let bytes = unsafe {
+        memmap2::Mmap::map(&file).map_err(|e| {
+            format!(
+                "Error mmaping gen conf file `{}`: {}",
+                genesis_config_path, e
+            )
+        })?
+    };
+
+    // parse the json file
+    let genesis_data: GenesisJson = serde_json::from_slice(&bytes)
+        .map_err(|e| format!("Error parsing gen conf file: {}", e))?;
+
+    // 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; // u128
+                                  // wallet index to generate random id
+    let mut wallet_index: u32 = 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();
+
+    // declare variables to fill in genesis
+    // -------------------------------------
+    // account inserted in genesis
+    let mut accounts = BTreeMap::new();
+    // members of technical committee
+    let mut technical_committee_members = Vec::new();
+    // memberships
+    let mut memberships = BTreeMap::new();
+    // identities
+    let mut identities = Vec::new();
+    // certifications
+    let mut certs_by_receiver = BTreeMap::new();
+    // initial authorities
+    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();
+
+    // SIMPLE WALLETS //
+    for (pubkey, balance) in &genesis_data.wallets {
+        // check existential deposit
+        if balance < &EXISTENTIAL_DEPOSIT {
+            log::warn!("wallet {pubkey} has {balance} cǦT which is below {EXISTENTIAL_DEPOSIT}");
+            fatal = true;
+        }
+
+        // double check the monetary mass
+        monetary_mass += balance;
+
+        wallet_index += 1;
+        // json prevents duplicate wallets
+        accounts.insert(
+            pubkey.clone(),
+            GenesisAccountData {
+                random_id: H256(blake2_256(&(wallet_index, &pubkey).encode())),
+                balance: *balance,
+                is_identity: false,
+            },
+        );
+    }
+
+    // IDENTITIES //
+    for (name, identity) in &genesis_data.identities {
+        // identity name
+        if !validate_idty_name(&name) {
+            return Err(format!("Identity name '{}' is invalid", &name));
+        }
+
+        // check existential deposit
+        if identity.balance < EXISTENTIAL_DEPOSIT {
+            if identity.membership_expire_on != 0 {
+                log::warn!(
+                    "expired identity {name} has {} cǦT which is below {EXISTENTIAL_DEPOSIT}",
+                    identity.balance
+                );
+                fatal = true;
+            } else {
+                // member identities can still be below existential deposit thanks to sufficient
+                log::info!(
+                    "identity {name} has {} cǦT which is below {EXISTENTIAL_DEPOSIT}",
+                    identity.balance
+                );
+            }
+        }
+
+        // Money
+        // check that wallet with same owner_key does not exist
+        if accounts.get(&identity.owner_key).is_some() {
+            log::warn!(
+                "{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::warn!(
+                "{other_name} already has identity index {} of {name}",
+                identity.index
+            );
+            fatal = true;
+        }
+        identity_index.insert(identity.index, name);
+
+        // only add the identity if not expired
+        if identity.membership_expire_on != 0 {
+            identities.push(GenesisIdty {
+                index: identity.index,
+                name: common_runtime::IdtyName::from(name.as_str()),
+                value: common_runtime::IdtyValue {
+                    data: IdtyData::new(),
+                    next_creatable_identity_on: identity.next_cert_issuable_on,
+                    old_owner_key: match identity.old_owner_key.clone() {
+                        Some(address) => Some((address, 0)), // FIXME old owner key expiration
+                        None => None,
+                    },
+                    // old_owner_key: None,
+                    owner_key: identity.owner_key.clone(),
+                    // TODO remove the removable_on field of identity
+                    removable_on: 0,
+                    status: IdtyStatus::Validated,
+                },
+            });
+        } else {
+            inactive_identities.insert(identity.index, name);
+        };
+
+        // insert the membershup data (only if not expired)
+        if identity.membership_expire_on != 0 {
+            memberships.insert(
+                identity.index,
+                MembershipData {
+                    expire_on: identity.membership_expire_on,
+                },
+            );
+        }
+    }
+
+    // Technical Comittee //
+    // NOTE : when changing owner key, the technical committee is not changed
+    for name in &genesis_data.technical_committee {
+        if let Some(identity) = &genesis_data.identities.get(name) {
+            technical_committee_members.push(identity.owner_key.clone());
+        } else {
+            log::error!("Identity '{}' does not exist", name);
+            fatal = true;
+        }
+    }
+
+    // CERTIFICATIONS //
+    for (_, identity) in &genesis_data.identities {
+        let mut certs = BTreeMap::new();
+        for (issuer, expire_on) in &identity.certs_received {
+            if let Some(issuer) = &genesis_data.identities.get(issuer) {
+                certs.insert(issuer.index, Some(expire_on.clone()));
+                counter_cert += 1;
+            } else {
+                log::error!("Identity '{}' does not exist", issuer);
+                fatal = true;
+            };
+        }
+        certs_by_receiver.insert(identity.index, certs);
+    }
+
+    // SMITHS SUB-WOT //
+    for (name, smith_data) in &genesis_data.smiths {
+        // check that smith exists
+        if let Some(identity) = &genesis_data.identities.get(&name.clone()) {
+            // Initial authorities and session keys
+            let session_keys_bytes = if let Some(declared_session_keys) = &smith_data.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
+                match maybe_force_authority {
+                    Some(ref bytes) => bytes.clone(),
+                    None => hex::decode(&declared_session_keys[2..])
+                        .map_err(|_| format!("invalid session keys for idty {}", &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(),
+                SessionKeys::decode(&mut &session_keys_bytes[..]).unwrap(),
+            );
+
+            // smith certifications
+            let mut certs = BTreeMap::new();
+            for issuer in &smith_data.certs_received {
+                let issuer_index = &genesis_data
+                    .identities
+                    .get(issuer)
+                    .ok_or(format!("Identity '{}' does not exist", issuer))?
+                    .index;
+                certs.insert(*issuer_index, Some(SMITH_CERTS_EXPIRE_ON));
+                counter_smith_cert += 1;
+            }
+            smith_certs_by_receiver.insert(identity.index, certs);
+
+            // smith memberships
+            smith_memberships.insert(
+                identity.index,
+                MembershipData {
+                    expire_on: SMITH_MEMBERSHIP_EXPIRE_ON,
+                },
+            );
+        } else {
+            log::error!("Smith '{}' does not correspond to exising identity", &name);
+            fatal = true;
+        }
+    }
+
+    // Verify certifications coherence (can be ignored for old users)
+    for (idty_index, receiver_certs) in &certs_by_receiver {
+        if receiver_certs.len() < MIN_CERT as usize {
+            let name = identity_index.get(idty_index).unwrap();
+            let identity = genesis_data.identities.get(name.clone()).unwrap();
+            if identity.membership_expire_on != 0 {
+                log::warn!(
+                    "[{}] has received only {}/{} certifications",
+                    name,
+                    receiver_certs.len(),
+                    MIN_CERT
+                );
+                fatal = true;
+            }
+        }
+    }
+
+    // Verify smith certifications coherence
+    for (idty_index, certs) in &smith_certs_by_receiver {
+        if certs.len() < SMITH_MIN_CERT as usize {
+            log::warn!(
+                "[{}] has received only {}/{} smith certifications",
+                identity_index.get(idty_index).unwrap(),
+                certs.len(),
+                SMITH_MIN_CERT
+            );
+            fatal = true;
+        }
+    }
+
+    // check number of online authorities
+    if counter_online_authorities != 1 {
+        log::error!("one and only one smith must be online, not {counter_online_authorities}");
+    }
+
+    // check monetary mass
+    if monetary_mass != genesis_data.initial_monetary_mass {
+        log::warn!(
+            "actuel monetary_mass ({monetary_mass}) and initial_monetary_mass ({}) do not match",
+            genesis_data.initial_monetary_mass
+        );
+        fatal = true;
+    }
+
+    // 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(),
+        identity_index.len(),
+        &genesis_data.wallets.len(),
+        identity_index.len(),
+        identities.len(),
+        inactive_identities.len(),
+        smith_memberships.len(),
+        counter_online_authorities,
+        counter_cert,
+        counter_smith_cert,
+        technical_committee_members.len(),
+    );
+
+    // some more checks
+    assert_eq!(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() + inactive_identities.len()
+    );
+    assert_eq!(
+        accounts.len(),
+        identity_index.len() + &genesis_data.wallets.len()
+    );
+    // no inactive tech comm
+    for tech_com_member in &genesis_data.technical_committee {
+        assert!(!inactive_identities.values().any(|&v| v == tech_com_member));
+    }
+    // no inactive smith
+    for (smith, _) in &genesis_data.smiths {
+        assert!(!inactive_identities.values().any(|&v| v == smith));
+    }
+
+    // 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!();
+    }
+
+    // return genesis config
+    Ok(gtest_runtime::GenesisConfig {
+        system: SystemConfig {
+            // Add Wasm runtime to storage.
+            code: wasm_binary.to_vec(),
+        },
+        account: AccountConfig { accounts },
+        authority_discovery: Default::default(),
+        authority_members: AuthorityMembersConfig {
+            initial_authorities,
+        },
+        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: session_keys_map
+                .into_iter()
+                .map(|(account_id, session_keys)| (account_id.clone(), account_id, session_keys))
+                .collect::<Vec<_>>(),
+        },
+        sudo: SudoConfig {
+            key: genesis_data.sudo_key,
+        },
+        technical_committee: TechnicalCommitteeConfig {
+            members: technical_committee_members,
+            ..Default::default()
+        },
+        identity: IdentityConfig { identities },
+        cert: CertConfig {
+            apply_cert_period_at_genesis: false,
+            certs_by_receiver,
+        },
+        membership: MembershipConfig { memberships },
+        smith_cert: SmithCertConfig {
+            apply_cert_period_at_genesis: true,
+            certs_by_receiver: smith_certs_by_receiver,
+        },
+        smith_membership: SmithMembershipConfig {
+            memberships: smith_memberships,
+        },
+        universal_dividend: UniversalDividendConfig {
+            first_reeval: genesis_data.first_ud_reeval,
+            first_ud: genesis_data.first_ud,
+            initial_monetary_mass: genesis_data.initial_monetary_mass,
+        },
+        treasury: Default::default(),
+    })
+}
diff --git a/node/src/service/client.rs b/node/src/service/client.rs
index 16cb4b412..ca2c0da82 100644
--- a/node/src/service/client.rs
+++ b/node/src/service/client.rs
@@ -307,20 +307,29 @@ trait BenchmarkCallSigner<RuntimeCall: Encode + Clone, Signer: Pair> {
     ) -> sp_runtime::OpaqueExtrinsic;
 }
 
+#[cfg(feature = "g1")]
+use g1_runtime as runtime;
 #[cfg(feature = "gdev")]
-impl BenchmarkCallSigner<gdev_runtime::RuntimeCall, sp_core::sr25519::Pair>
-    for super::FullClient<gdev_runtime::RuntimeApi, super::GDevExecutor>
-{
+use gdev_runtime as runtime;
+#[cfg(feature = "gdev")]
+type FullClient = super::FullClient<runtime::RuntimeApi, super::GDevExecutor>;
+#[cfg(feature = "gtest")]
+use gtest_runtime as runtime;
+#[cfg(feature = "gtest")]
+type FullClient = super::FullClient<runtime::RuntimeApi, super::GTestExecutor>;
+
+#[cfg(any(feature = "gdev", feature = "gtest"))]
+impl BenchmarkCallSigner<runtime::RuntimeCall, sp_core::sr25519::Pair> for FullClient {
     fn sign_call(
         &self,
-        call: gdev_runtime::RuntimeCall,
+        call: runtime::RuntimeCall,
         nonce: u32,
         current_block: u64,
         period: u64,
         genesis: sp_core::H256,
         acc: sp_core::sr25519::Pair,
     ) -> sp_runtime::OpaqueExtrinsic {
-        use gdev_runtime as runtime;
+        // use runtime;
 
         let extra: runtime::SignedExtra = (
             frame_system::CheckNonZeroSender::<runtime::Runtime>::new(),
diff --git a/resources/gtest.json b/resources/gtest.json
new file mode 100644
index 000000000..9050fa621
--- /dev/null
+++ b/resources/gtest.json
@@ -0,0 +1,331 @@
+{
+  "first_ud": 10000,
+  "first_ud_reeval": 100800,
+  "initial_monetary_mass": 531481,
+  "identities": {
+    "old_user1": {
+      "index": 7777,
+      "owner_key": "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw",
+      "membership_expire_on": 0,
+      "next_cert_issuable_on": 0,
+      "balance": 12,
+      "certs_received": {
+        "elois": 10
+      }
+    },
+    "old_user2": {
+      "index": 8888,
+      "owner_key": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
+      "membership_expire_on": 0,
+      "next_cert_issuable_on": 0,
+      "balance": 12,
+      "certs_received": {
+        "elois": 10
+      }
+    },
+    "old_user3": {
+      "index": 6666,
+      "owner_key": "5Fxune7f71ZbpP2FoY3mhYcmM596Erhv1gRue4nsPwkxMR4n",
+      "membership_expire_on": 0,
+      "next_cert_issuable_on": 0,
+      "balance": 12,
+      "certs_received": {
+        "elois": 10
+      }
+    },
+    "elois": {
+      "index": 2,
+      "owner_key": "5CUjxa4wVKMj3FqKdqAUf7zcEMr4MYAjXeWmUf44B41neLmJ",
+      "membership_expire_on": 1000,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "poka": 100,
+        "cgeek": 100,
+        "vit": 100,
+        "tuxmain": 100,
+        "HugoTrentesaux": 100,
+        "ManUtopiK": 100,
+        "moul": 100,
+        "1000i100": 100,
+        "kapis": 100,
+        "DavidB": 100,
+        "Gamaliel": 100,
+        "wellno1": 100,
+        "old_user1": 50
+      }
+    },
+    "HugoTrentesaux": {
+      "index": 344,
+      "owner_key": "5Dq8xjvkmbz7q4g2LbZgyExD26VSCutfEc6n4W4AfQeVHZqz",
+      "old_owner_key": "5Dq8xjvkmbz7q4g2LbZgyExD26VSCutfEc6n4W4AfQeVHZqz",
+      "membership_expire_on": 1000,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "poka": 100,
+        "vit": 100,
+        "tuxmain": 100,
+        "elois": 100,
+        "moul": 100,
+        "1000i100": 100,
+        "ManUtopiK": 100,
+        "kapis": 100,
+        "DavidB": 100,
+        "Gamaliel": 100,
+        "wellno1": 100
+      }
+    },
+    "poka": {
+      "index": 62,
+      "owner_key": "5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn",
+      "membership_expire_on": 1000,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "cgeek": 100,
+        "vit": 100,
+        "tuxmain": 100,
+        "elois": 100,
+        "HugoTrentesaux": 100
+      }
+    },
+    "tuxmain": {
+      "index": 1401,
+      "owner_key": "5D2DnScFpxoEUXDwZbJH18tRsQMygBSh1F6YCcWvTYzKY2W7",
+      "membership_expire_on": 1000,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "cgeek": 100,
+        "elois": 100,
+        "HugoTrentesaux": 100,
+        "ManUtopiK": 100,
+        "poka": 100,
+        "moul": 100,
+        "1000i100": 100
+      }
+    },
+    "vit": {
+      "index": 20,
+      "owner_key": "5DqkYGjiT5TFm2pGMUBMuZW4sw13vDEZmxqkxmky5AoQvUwd",
+      "membership_expire_on": 1000,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "cgeek": 100,
+        "elois": 100,
+        "HugoTrentesaux": 100,
+        "ManUtopiK": 100,
+        "poka": 100,
+        "tuxmain": 100,
+        "moul": 100
+      }
+    },
+    "cgeek": {
+      "index": 1,
+      "owner_key": "5DP7ze5cJwtHbqXaP2aNtJ5jkULzcTCqXuMzDvk9JjneFjfq",
+      "membership_expire_on": 1000,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "poka": 100,
+        "vit": 100,
+        "tuxmain": 100,
+        "elois": 100,
+        "HugoTrentesaux": 100,
+        "moul": 100
+      }
+    },
+    "ManUtopiK": {
+      "index": 2316,
+      "owner_key": "5DUjwHRqPayt3tAZk1fqEgU99xZB9jzBHKy2sMSTNcc7m9D1",
+      "membership_expire_on": 1000,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "poka": 100,
+        "vit": 100,
+        "tuxmain": 100,
+        "elois": 100,
+        "HugoTrentesaux": 100,
+        "1000i100": 100
+      }
+    },
+    "1000i100": {
+      "index": 72,
+      "owner_key": "5CCrBS67BrpBx3ihGHc72HZp3eHHbETxWFuNfwbbdoGSJFN8",
+      "membership_expire_on": 1000,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "elois": 100,
+        "poka": 100,
+        "cgeek": 100,
+        "vit": 100,
+        "tuxmain": 100,
+        "HugoTrentesaux": 100,
+        "ManUtopiK": 100,
+        "moul": 100
+      }
+    },
+    "moul": {
+      "index": 32,
+      "owner_key": "5EPGRtBYLwfDinnaAXsscM3FkffRADpJRrkZETqW8N6uhp22",
+      "membership_expire_on": 1000,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "ManUtopiK": 100,
+        "vit": 100,
+        "tuxmain": 100,
+        "elois": 100,
+        "HugoTrentesaux": 100,
+        "1000i100": 100
+      }
+    },
+    "Gamaliel": {
+      "index": 3994,
+      "owner_key": "5DS9iWBXW56N7XbeVyyp6CB7m4LeE5fGJYrUR9HDSStT5JN9",
+      "membership_expire_on": 0,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "DavidB": 100,
+        "wellno1": 100,
+        "kapis": 100
+      }
+    },
+    "kapis": {
+      "index": 3883,
+      "owner_key": "5HJyyim1W8Y1UD8LAbBL7cQQLjGofMoD45RtRSAmhkFQxrvs",
+      "membership_expire_on": 22,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "DavidB": 100,
+        "wellno1": 100,
+        "Gamaliel": 100,
+        "HugoTrentesaux": 100,
+        "elois": 100
+      }
+    },
+    "DavidB": {
+      "index": 2277,
+      "owner_key": "5HKTDdXHj3MojiPRcEsVU9JaHyif5gg2br1sy3JZbsjuQebP",
+      "membership_expire_on": 0,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "kapis": 100,
+        "wellno1": 100,
+        "Gamaliel": 100
+      }
+    },
+    "wellno1": {
+      "index": 9999,
+      "owner_key": "5DyEZNkSuK5i8wZiXtvL63zqpye9zPBsPRauCjQkMuVzZYX7",
+      "membership_expire_on": 0,
+      "next_cert_issuable_on": 100,
+      "balance": 10000,
+      "certs_received": {
+        "DavidB": 100,
+        "kapis": 100,
+        "Gamaliel": 100
+      }
+    }
+  },
+  "smiths": {
+    "elois": {
+      "certs_received": [
+        "poka",
+        "cgeek",
+        "vit",
+        "tuxmain",
+        "HugoTrentesaux",
+        "kapis"
+      ],
+      "session_keys": "0x92743b12d55242276539d7256951cdfda85371caefbd325396da9331011b737d14084d2537f77e786a75b9bcbe351051fad52946ec211724b5e8c93bb8aa7a6f14084d2537f77e786a75b9bcbe351051fad52946ec211724b5e8c93bb8aa7a6f14084d2537f77e786a75b9bcbe351051fad52946ec211724b5e8c93bb8aa7a6f"
+    },
+    "poka": {
+      "certs_received": [
+        "cgeek",
+        "vit",
+        "tuxmain",
+        "elois",
+        "HugoTrentesaux"
+      ]
+    },
+    "tuxmain": {
+      "certs_received": [
+        "cgeek",
+        "elois",
+        "HugoTrentesaux",
+        "poka",
+        "kapis"
+      ]
+    },
+    "vit": {
+      "certs_received": [
+        "cgeek",
+        "elois",
+        "HugoTrentesaux",
+        "poka",
+        "tuxmain"
+      ]
+    },
+    "cgeek": {
+      "certs_received": [
+        "poka",
+        "vit",
+        "tuxmain",
+        "elois",
+        "HugoTrentesaux"
+      ]
+    },
+    "HugoTrentesaux": {
+      "certs_received": [
+        "poka",
+        "vit",
+        "tuxmain",
+        "elois",
+        "kapis"
+      ]
+    },
+    "1000i100": {
+      "certs_received": [
+        "cgeek",
+        "vit",
+        "tuxmain",
+        "elois",
+        "HugoTrentesaux",
+        "poka"
+      ]
+    },
+    "kapis": {
+      "certs_received": [
+        "elois",
+        "tuxmain",
+        "HugoTrentesaux",
+        "1000i100",
+        "vit"
+      ]
+    }
+  },
+  "wallets": {
+    "5C558MohmEJ3Z1o1wv8AAR7dFc3ZsUQZGBUHLxvHetWaN53U": 304,
+    "5C56NAAgLYBXEiWsRhRD2vKZquSgZHULUuiCWpcVg8k21Kex": 5000,
+    "5C59KZ3X6AEt23FSLxsLvtXqN41SHyaWa5CQmXiYLUek3ECd": 24507,
+    "5C5KjPbZfvwFXh24KkKEjeofCNng2tZM4YjJN8mMYhLAzn8H": 1042,
+    "5C5VrnckbfuizDDFn9ubcQDEKSnRFbB3jfa5kMaQxmRFzLJX": 10800,
+    "5C5YfjAyZnLPfyMQNreng5knFW87PRTVphRhPUXpWszF7sB9": 200,
+    "5C5YfjAyZnLPfyMQNreng5knFW87PRTVphRkQoupWszF7pte": 359592
+  },
+  "sudo_key": "5Hm8sBbwuLAU99dBezvgtnRmZCrUy9mhqmbQMFyGTaeATYg7",
+  "technical_committee": [
+    "elois",
+    "cgeek",
+    "tuxmain",
+    "HugoTrentesaux"
+  ]
+}
\ No newline at end of file
diff --git a/runtime/g1/README.md b/runtime/g1/README.md
new file mode 100644
index 000000000..69f65e64a
--- /dev/null
+++ b/runtime/g1/README.md
@@ -0,0 +1,4 @@
+# Äž1 runtime
+
+Äž1 runtime is the one used in the production Äž1 network.
+It is the same as ÄžTest runtime with a bit of delay on updates necessary to test them.
\ No newline at end of file
diff --git a/runtime/gdev/README.md b/runtime/gdev/README.md
new file mode 100644
index 000000000..8047cd84f
--- /dev/null
+++ b/runtime/gdev/README.md
@@ -0,0 +1,5 @@
+# ÄžDev runtime
+
+Gdev runtime exists for development purposes. It can be changed without consequenses for exploration.
+It uses the pallet `duniter-test-parameters` to be able to change parameters values more easily without runtime upgrades.
+This is the runtime used by default when running a local blockchain.
\ No newline at end of file
diff --git a/runtime/gtest/Cargo.toml b/runtime/gtest/Cargo.toml
index 034ab8621..a3802c838 100644
--- a/runtime/gtest/Cargo.toml
+++ b/runtime/gtest/Cargo.toml
@@ -16,34 +16,53 @@ targets = ['x86_64-unknown-linux-gnu']
 [features]
 default = ['std']
 runtime-benchmarks = [
-    'frame-benchmarking',
+    'common-runtime/runtime-benchmarks',
+    'frame-benchmarking/runtime-benchmarks',
     'frame-support/runtime-benchmarks',
     'frame-system-benchmarking',
     'frame-system/runtime-benchmarks',
     'hex-literal',
+    'pallet-authority-members/runtime-benchmarks',
+    'pallet-babe/runtime-benchmarks',
     'pallet-balances/runtime-benchmarks',
+    'pallet-certification/runtime-benchmarks',
+    'pallet-collective/runtime-benchmarks',
+    'pallet-duniter-account/runtime-benchmarks',
+    'pallet-duniter-wot/runtime-benchmarks',
+    'pallet-grandpa/runtime-benchmarks',
     'pallet-identity/runtime-benchmarks',
+    'pallet-membership/runtime-benchmarks',
+    'pallet-provide-randomness/runtime-benchmarks',
+    'pallet-im-online/runtime-benchmarks',
+    'pallet-multisig/runtime-benchmarks',
+    'pallet-oneshot-account/runtime-benchmarks',
+    'pallet-preimage/runtime-benchmarks',
+    'pallet-session-benchmarking/runtime-benchmarks',
+    'pallet-proxy/runtime-benchmarks',
+    'pallet-scheduler/runtime-benchmarks',
+    'pallet-timestamp/runtime-benchmarks',
     'pallet-treasury/runtime-benchmarks',
     'pallet-universal-dividend/runtime-benchmarks',
-    'common-runtime/runtime-benchmarks',
+    'pallet-upgrade-origin/runtime-benchmarks',
+    'pallet-utility/runtime-benchmarks',
     'sp-runtime/runtime-benchmarks',
 ]
 std = [
     'codec/std',
+	'common-runtime/std',
     'frame-executive/std',
     'frame-support/std',
     'frame-system-rpc-runtime-api/std',
     'frame-system/std',
     "frame-try-runtime/std",
-    'pallet-atomic-swap/std',
     'log/std',
+    'pallet-atomic-swap/std',
     'pallet-authority-discovery/std',
     'pallet-authority-members/std',
     'pallet-babe/std',
     'pallet-balances/std',
     'pallet-certification/std',
     'pallet-collective/std',
-    'pallet-duniter-test-parameters/std',
     'pallet-duniter-account/std',
     'pallet-duniter-wot/std',
     'pallet-grandpa/std',
@@ -53,8 +72,10 @@ std = [
     'pallet-provide-randomness/std',
     'pallet-im-online/std',
     'pallet-multisig/std',
+	"pallet-offences/std",
     'pallet-preimage/std',
     'pallet-proxy/std',
+	"pallet-scheduler/std",
     'pallet-session/std',
     'pallet-sudo/std',
     'pallet-universal-dividend/std',
@@ -63,8 +84,9 @@ std = [
     'pallet-transaction-payment-rpc-runtime-api/std',
     'pallet-transaction-payment/std',
     'pallet-treasury/std',
-    'common-runtime/std',
-    'serde',
+    'pallet-utility/std',
+	"serde/std",
+	"serde_derive",
     'sp-api/std',
     'sp-arithmetic/std',
     'sp-authority-discovery/std',
@@ -81,6 +103,7 @@ std = [
     'sp-version/std',
 ]
 try-runtime = [
+    "common-runtime/try-runtime",
     "frame-executive/try-runtime",
     "frame-try-runtime",
     "frame-system/try-runtime",
@@ -113,7 +136,6 @@ sp-keyring = { git = 'https://github.com/duniter/substrate', branch = 'duniter-s
 common-runtime = { path = "../common", default-features = false }
 pallet-authority-members = { path = '../../pallets/authority-members', default-features = false }
 pallet-certification = { path = '../../pallets/certification', default-features = false }
-pallet-duniter-test-parameters = { path = '../../pallets/duniter-test-parameters', default-features = false }
 pallet-duniter-account = { path = '../../pallets/duniter-account', default-features = false }
 pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = false }
 pallet-identity = { path = '../../pallets/identity', default-features = false }
@@ -121,6 +143,7 @@ pallet-membership = { path = '../../pallets/membership', default-features = fals
 pallet-oneshot-account = { path = '../../pallets/oneshot-account', default-features = false }
 pallet-provide-randomness = { path = '../../pallets/provide-randomness', default-features = false }
 pallet-universal-dividend = { path = '../../pallets/universal-dividend', default-features = false }
+pallet-session-benchmarking = { path = '../../pallets/session-benchmarking', default-features = false }
 pallet-upgrade-origin = { path = '../../pallets/upgrade-origin', default-features = false }
 sp-membership = { path = '../../primitives/membership', default-features = false }
 
@@ -129,15 +152,14 @@ codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive
 log = { version = "0.4.17", default-features = false }
 hex-literal = { version = '0.3.1', optional = true }
 scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
-serde = { version = "1.0.101", optional = true, features = ["derive"] }
+serde = { version = "1.0.101", default-features = false }
+serde_derive = { version = "1.0.101", optional = true }
 
 # substrate
-frame-benchmarking = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', optional = true }
 frame-try-runtime = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false, optional = true }
 frame-executive = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false }
 frame-support = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false }
 frame-system = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false }
-frame-system-benchmarking = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', optional = true }
 frame-system-rpc-runtime-api = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false}
 pallet-atomic-swap = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false }
 pallet-authority-discovery = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false }
@@ -172,3 +194,7 @@ sp-session = { git = 'https://github.com/duniter/substrate', branch = 'duniter-s
 sp-std = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false }
 sp-transaction-pool = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false }
 sp-version = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false }
+
+# substrate benchmarking
+frame-benchmarking = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false, optional = true }
+frame-system-benchmarking = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.32', default-features = false, optional = true }
diff --git a/runtime/gtest/README.md b/runtime/gtest/README.md
new file mode 100644
index 000000000..f7d7a6dcd
--- /dev/null
+++ b/runtime/gtest/README.md
@@ -0,0 +1,10 @@
+# Äžtest runtime
+
+ĞTest runtime is the one used in ĞTest network, which is a test network for Ǧ1.
+It has a sudo account to be able to test runtime upgrades more easily before deploying them to Äž1 network.
+
+## ÄžTest chainspecs
+
+ÄžTest chainspecs are intended to use with real data.
+There is no particular case for genesis identities. In `gtest.json`:
+- no `genesis_parameters`
\ No newline at end of file
diff --git a/runtime/gtest/src/lib.rs b/runtime/gtest/src/lib.rs
index 3d2282f26..3b94d4add 100644
--- a/runtime/gtest/src/lib.rs
+++ b/runtime/gtest/src/lib.rs
@@ -22,6 +22,10 @@
 #[cfg(feature = "std")]
 include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
 
+#[cfg(feature = "runtime-benchmarks")]
+#[macro_use]
+extern crate frame_benchmarking;
+
 pub mod parameters;
 
 pub use self::parameters::*;
@@ -29,8 +33,8 @@ pub use common_runtime::{
     constants::*, entities::*, handlers::*, AccountId, Address, Balance, BlockNumber,
     FullIdentificationOfImpl, GetCurrentEpochIndex, Hash, Header, IdtyIndex, Index, Signature,
 };
+pub use frame_system::Call as SystemCall;
 pub use pallet_balances::Call as BalancesCall;
-pub use pallet_identity::{IdtyStatus, IdtyValue};
 pub use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
 use pallet_session::historical as session_historical;
 pub use pallet_timestamp::Call as TimestampCall;
@@ -112,7 +116,7 @@ pub type SignedExtra = (
     frame_system::CheckTxVersion<Runtime>,
     frame_system::CheckGenesis<Runtime>,
     frame_system::CheckEra<Runtime>,
-    frame_system::CheckNonce<Runtime>,
+    pallet_oneshot_account::CheckNonce<Runtime>,
     frame_system::CheckWeight<Runtime>,
     pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
 );
@@ -161,14 +165,16 @@ mod benches {
 }
 
 pub struct BaseCallFilter;
+
+// implement filter
 impl Contains<RuntimeCall> for BaseCallFilter {
     fn contains(call: &RuntimeCall) -> bool {
         !matches!(
             call,
-            RuntimeCall::System(
-                frame_system::Call::remark { .. } | frame_system::Call::remark_with_event { .. }
-            ) | RuntimeCall::Membership(
-                pallet_membership::Call::claim_membership { .. }
+            // in main web of trust, membership request and revoke are handeled through identity pallet
+            // the user can not call them directly
+            RuntimeCall::Membership(
+                pallet_membership::Call::request_membership { .. }
                     | pallet_membership::Call::revoke_membership { .. }
             ) | RuntimeCall::Session(_)
         )
@@ -194,6 +200,7 @@ pub enum ProxyType {
     AlmostAny = 0,
     TransferOnly = 1,
     CancelProxy = 2,
+    TechnicalCommitteePropose = 3,
 }
 impl Default for ProxyType {
     fn default() -> Self {
@@ -222,11 +229,19 @@ impl frame_support::traits::InstanceFilter<RuntimeCall> for ProxyType {
                     RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. })
                 )
             }
+            ProxyType::TechnicalCommitteePropose => {
+                matches!(
+                    c,
+                    RuntimeCall::TechnicalCommittee(pallet_collective::Call::propose { .. })
+                )
+            }
         }
     }
 }
 
+// Configure FRAME pallets to include in runtime.
 common_runtime::pallets_config! {
+
     impl pallet_sudo::Config for Runtime {
         type RuntimeEvent = RuntimeEvent;
         type RuntimeCall = RuntimeCall;
@@ -254,7 +269,7 @@ construct_runtime!(
         TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event<T>} = 32,
         OneshotAccount: pallet_oneshot_account::{Pallet, Call, Storage, Event<T>} = 7,
 
-        // Consensus support.
+        // Consensus support
         AuthorityMembers: pallet_authority_members::{Pallet, Call, Storage, Config<T>, Event<T>} = 10,
         Authorship: pallet_authorship::{Pallet, Call, Storage} = 11,
         Offences: pallet_offences::{Pallet, Storage, Event} = 12,
@@ -264,7 +279,7 @@ construct_runtime!(
         ImOnline: pallet_im_online::{Pallet, Call, Storage, Event<T>, ValidateUnsigned, Config<T>} = 16,
         AuthorityDiscovery: pallet_authority_discovery::{Pallet, Config} = 17,
 
-        // Governance stuff.
+        // Governance stuff
         Sudo: pallet_sudo::{Pallet, Call, Config<T>, Storage, Event<T>} = 20,
         UpgradeOrigin: pallet_upgrade_origin::{Pallet, Call, Event} = 21,
         Preimage: pallet_preimage::{Pallet, Call, Storage, Event<T>} = 22,
-- 
GitLab