Skip to content
Snippets Groups Projects
gdev.rs 14.3 KiB
Newer Older
// 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 crate::chain_spec::gen_genesis_data::{
    AuthorityKeys, CommonParameters, GenesisIdentity, SessionKeysProvider,
};
use common_runtime::{constants::*, entities::IdtyData, GenesisIdty, IdtyStatus};
use gdev_runtime::{
    opaque::SessionKeys, pallet_universal_dividend, parameters, Runtime, RuntimeGenesisConfig,
    WASM_BINARY,
use jsonrpsee::core::JsonValue;
use sc_network::config::MultiaddrWithPeerId;
use sc_service::ChainType;
use sc_telemetry::TelemetryEndpoints;
use serde::Deserialize;
use sp_runtime::Perbill;
use std::{env, fs};
pub type ChainSpec = sc_service::GenericChainSpec<RuntimeGenesisConfig>;
type GenesisParameters = gdev_runtime::GenesisParameters<u32, u32, u64, u32>;
const TOKEN_DECIMALS: usize = 2;
const TOKEN_SYMBOL: &str = "ĞD";
static EXISTENTIAL_DEPOSIT: u64 = parameters::ExistentialDeposit::get();
// The URL for the telemetry server.
// const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";

struct GDevSKP;
impl SessionKeysProvider<SessionKeys> for GDevSKP {
    fn session_keys(keys: &AuthorityKeys) -> SessionKeys {
        let cloned = keys.clone();
        SessionKeys {
            grandpa: cloned.1,
            babe: cloned.2,
            im_online: cloned.3,
            authority_discovery: cloned.4,
        }
    }
fn get_parameters(parameters_from_file: &Option<GenesisParameters>) -> CommonParameters {
    let parameters_from_file = parameters_from_file
        .clone()
        .expect("parameters must be defined in file for GDev");
    CommonParameters {
        currency_name: TOKEN_SYMBOL.to_string(),
        decimals: TOKEN_DECIMALS,
        babe_epoch_duration: parameters_from_file.babe_epoch_duration,
        babe_expected_block_time: parameters::ExpectedBlockTime::get(),
        babe_max_authorities: parameters::MaxAuthorities::get(),
        timestamp_minimum_period: parameters::MinimumPeriod::get(),
        balances_existential_deposit: EXISTENTIAL_DEPOSIT,
        authority_members_max_authorities: parameters::MaxAuthorities::get(),
        grandpa_max_authorities: parameters::MaxAuthorities::get(),
        universal_dividend_max_past_reevals:
            <Runtime as pallet_universal_dividend::Config>::MaxPastReeval::get(),
        universal_dividend_square_money_growth_rate: parameters::SquareMoneyGrowthRate::get(),
        universal_dividend_ud_creation_period: parameters_from_file.ud_creation_period,
        universal_dividend_ud_reeval_period: parameters_from_file.ud_reeval_period,
        wot_first_issuable_on: parameters_from_file.wot_first_cert_issuable_on,
        wot_min_cert_for_membership: parameters_from_file.wot_min_cert_for_membership,
        wot_min_cert_for_create_idty_right: parameters_from_file.wot_min_cert_for_create_idty_right,
        identity_confirm_period: parameters_from_file.idty_confirm_period,
        identity_change_owner_key_period: parameters::ChangeOwnerKeyPeriod::get(),
        identity_idty_creation_period: parameters_from_file.idty_creation_period,
        identity_autorevocation_period: parameters::AutorevocationPeriod::get(),
        membership_membership_period: parameters_from_file.membership_period,
        membership_membership_renewal_period: parameters_from_file.membership_renewal_period,
        cert_max_by_issuer: parameters_from_file.cert_max_by_issuer,
        cert_min_received_cert_to_be_able_to_issue_cert: parameters_from_file
            .cert_min_received_cert_to_issue_cert,
        cert_validity_period: parameters_from_file.cert_validity_period,
        distance_min_accessible_referees: Perbill::from_percent(80), // TODO: generalize
        distance_max_depth: 5,                                       // TODO: generalize
        smith_sub_wot_min_cert_for_membership: parameters_from_file
            .smith_wot_min_cert_for_membership,
        smith_cert_max_by_issuer: parameters_from_file.smith_cert_max_by_issuer,
        smith_inactivity_max_duration: parameters_from_file.smith_inactivity_max_duration,
        cert_cert_period: parameters_from_file.cert_period,
        treasury_spend_period: <Runtime as pallet_treasury::Config>::SpendPeriod::get(),
    }
}

/// generate development chainspec with Alice validator
pub fn gdev_development_chain_spec(config_file_path: String) -> Result<ChainSpec, String> {
    Ok(ChainSpec::builder(
        &get_wasm_binary().ok_or_else(|| "Development wasm not available".to_string())?,
    )
    .with_name("Development")
    .with_id("gdev")
    .with_chain_type(ChainType::Development)
    .with_genesis_config_patch({
        let genesis_data = gen_genesis_data::generate_genesis_data::<_, _, SessionKeys, GDevSKP>(
            config_file_path.clone(),
            get_parameters,
            Some("Alice".to_owned()),
        )
        .expect("Genesis Data must be buildable");
        genesis_data_to_gdev_genesis_conf(genesis_data)
    })
    .with_properties(
        serde_json::json!({
            "tokenDecimals": TOKEN_DECIMALS,
            "tokenSymbol": TOKEN_SYMBOL,
        })
        .as_object()
        .expect("must be a map")
        .clone(),
    )
    .build())
// === client specifications ===
/// emulate client specifications to get them from json
#[derive(Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct ClientSpec {
    name: String,
    id: String,
    chain_type: ChainType,
    boot_nodes: Vec<MultiaddrWithPeerId>,
    telemetry_endpoints: Option<TelemetryEndpoints>,
    // protocol_id: Option<String>,
    // #[serde(default = "Default::default", skip_serializing_if = "Option::is_none")]
    // fork_id: Option<String>,
    properties: Option<serde_json::Map<std::string::String, JsonValue>>,
    // #[serde(default)]
    // code_substitutes: BTreeMap<String, Bytes>,
}

/// generate live network chainspecs
pub fn gen_live_conf(
    client_spec: ClientSpec,
    config_file_path: String,
) -> Result<ChainSpec, String> {
    Ok(ChainSpec::builder(
        &get_wasm_binary().ok_or_else(|| "Development wasm not available".to_string())?,
    )
    .with_name(client_spec.name.as_str())
    .with_id(client_spec.id.as_str())
    .with_chain_type(client_spec.chain_type)
    .with_genesis_config_patch({
        let genesis_data = gen_genesis_data::generate_genesis_data::<_, _, SessionKeys, GDevSKP>(
            config_file_path.clone(),
            get_parameters,
            None,
        )
        .expect("Genesis Data must be buildable");
        genesis_data_to_gdev_genesis_conf(genesis_data)
    })
    .with_telemetry_endpoints(client_spec.telemetry_endpoints.unwrap())
    .with_properties(client_spec.properties.unwrap())
    .with_boot_nodes(client_spec.boot_nodes)
    .build())
/// generate local network chainspects
pub fn local_testnet_config(
    initial_authorities_len: usize,
    initial_smiths_len: usize,
    initial_identities_len: usize,
) -> Result<ChainSpec, String> {
    Ok(ChainSpec::builder(
        &get_wasm_binary().ok_or_else(|| "Development wasm not available".to_string())?,
        None,
    )
    .with_name("Ğdev Local Testnet")
    .with_id("gdev_local")
    .with_chain_type(ChainType::Local)
    .with_genesis_config_patch({
        let genesis_data =
            gen_genesis_data::generate_genesis_data_for_local_chain::<_, _, SessionKeys, GDevSKP>(
                // Initial authorities len
                initial_authorities_len,
                // Initial smiths len,
                initial_smiths_len,
                // Initial identities len
                initial_identities_len,
                EXISTENTIAL_DEPOSIT,
                get_local_chain_parameters(),
                // Sudo account
                get_account_id_from_seed::<sr25519::Public>("Alice"),
            .expect("Genesis Data must be buildable");
        genesis_data_to_gdev_genesis_conf(genesis_data)
    })
    .with_properties(
        serde_json::json!({
            "tokenDecimals": TOKEN_DECIMALS,
            "tokenSymbol": TOKEN_SYMBOL,
        })
        .as_object()
        .expect("must be a map")
        .clone(),
    )
    .build())
fn genesis_data_to_gdev_genesis_conf(
    genesis_data: super::gen_genesis_data::GenesisData<GenesisParameters, SessionKeys>,
    let super::gen_genesis_data::GenesisData {
        identities,
        initial_authorities,
        initial_monetary_mass,
        memberships,
        parameters,
        session_keys_map,
    serde_json::json!({
        "account": {
            "accounts": accounts,
            "treasuryBalance": treasury_balance,
        "parameters": {
            "parameters": parameters.expect("mandatory for GDev"),
        "authorityMembers": {
            "initialAuthorities": initial_authorities,
        "balances": {
            "totalIssuance": initial_monetary_mass,
        "babe": {
            "epochConfig": Some(BABE_GENESIS_EPOCH_CONFIG),
        "session": {
            "keys": session_keys_map
                .into_iter()
                .map(|(account_id, session_keys)| (account_id.clone(), account_id, session_keys))
                .collect::<Vec<_>>(),
        },
        "sudo": { "key": sudo_key },
        "technicalCommittee": {
            "members": technical_committee_members,
        "quota": {
            "identities": identities.iter().map(|i| i.idty_index).collect::<Vec<_>>(),
        "identity": {
            "identities": identities
                .map(
                    |GenesisIdentity {
                         idty_index,
                         name,
                         owner_key,
                     }| GenesisIdty {
                        index: idty_index,
                        name: common_runtime::IdtyName::from(name.as_str()),
                        value: common_runtime::IdtyValue {
                            data: IdtyData::new(),
                            next_creatable_identity_on: 0,
                            next_scheduled: match status {
                                IdtyStatus::Unconfirmed | IdtyStatus::Unvalidated => {
                                    panic!("Unconfirmed or Unvalidated identity in genesis")
                                }
                                IdtyStatus::Member => expires_on.expect("must have expires_on set"),
                                IdtyStatus::Revoked => 0,
                                IdtyStatus::NotMember => {
                                    revokes_on.expect("must have revokes_on set")
                                }
                            },
                            status,
                .collect::<Vec<GenesisIdty<gdev_runtime::Runtime>>>(),
        "certification": {
            "applyCertPeriodAtGenesis": false,
            "certsByReceiver": certs_by_receiver,
        "membership": { "memberships": memberships },
        "smithMembers": { "initialSmiths": initial_smiths},
        "universalDividend": {
            "firstReeval": first_ud_reeval,
            "firstUd": first_ud,
            "initialMonetaryMass": initial_monetary_mass,
            "ud": ud,

fn get_local_chain_parameters() -> Option<GenesisParameters> {
    let babe_epoch_duration = get_env("DUNITER_BABE_EPOCH_DURATION", 30) as u64;
    let cert_validity_period = get_env("DUNITER_CERT_VALIDITY_PERIOD", 1_000);
    let membership_period = get_env("DUNITER_MEMBERSHIP_PERIOD", 1_000);
    let membership_renewal_period = get_env("DUNITER_MEMBERSHIP_RENEWAL_PERIOD", 1_000);
    let ud_creation_period = get_env("DUNITER_UD_CREATION_PERIOD", 60_000);
    let ud_reeval_period = get_env("DUNITER_UD_REEEVAL_PERIOD", 1_200_000);
    Some(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,
        ud_creation_period,
        ud_reeval_period,
        smith_cert_max_by_issuer: 8,
        smith_inactivity_max_duration: 48,
        smith_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,
    })
}

/// get environment variable
fn get_env<T: std::str::FromStr>(env_var_name: &'static str, default_value: T) -> T {
    std::env::var(env_var_name)
        .map_or(Ok(default_value), |s| s.parse())
        .unwrap_or_else(|_| panic!("{} must be a {}", env_var_name, std::any::type_name::<T>()))
}

/// Get the WASM bytes either from filesytem (`WASM_FILE` env variable giving the path to the wasm blob)
/// or else get the one compiled from source code.
/// Goal: allow to provide the WASM built with srtool, which is reproductible.
fn get_wasm_binary() -> Option<Vec<u8>> {
    let wasm_bytes_from_file = if let Ok(file_path) = env::var("WASM_FILE") {
        Some(fs::read(file_path).unwrap_or_else(|e| panic!("Could not read wasm file: {}", e)))
    } else {
        None
    };
    wasm_bytes_from_file.or_else(|| WASM_BINARY.map(|bytes| bytes.to_vec()))
}