Skip to content
Snippets Groups Projects
Commit eee8c80d authored by Éloïs's avatar Éloïs
Browse files

feat: define a format for duniter genesis config

parent 80b78ae2
No related branches found
No related tags found
No related merge requests found
...@@ -1239,6 +1239,7 @@ dependencies = [ ...@@ -1239,6 +1239,7 @@ dependencies = [
"jsonrpc-core", "jsonrpc-core",
"log", "log",
"maplit", "maplit",
"memmap2 0.5.0",
"pallet-certification", "pallet-certification",
"pallet-grandpa", "pallet-grandpa",
"pallet-transaction-payment-rpc", "pallet-transaction-payment-rpc",
...@@ -1254,11 +1255,13 @@ dependencies = [ ...@@ -1254,11 +1255,13 @@ dependencies = [
"sc-executor", "sc-executor",
"sc-finality-grandpa", "sc-finality-grandpa",
"sc-keystore", "sc-keystore",
"sc-network",
"sc-rpc-api", "sc-rpc-api",
"sc-service", "sc-service",
"sc-telemetry", "sc-telemetry",
"sc-transaction-pool", "sc-transaction-pool",
"sc-transaction-pool-api", "sc-transaction-pool-api",
"serde",
"serde_json", "serde_json",
"sp-api", "sp-api",
"sp-authority-discovery", "sp-authority-discovery",
......
...@@ -51,6 +51,8 @@ hex = "0.4.3" ...@@ -51,6 +51,8 @@ hex = "0.4.3"
jsonrpc-core = '18.0.0' jsonrpc-core = '18.0.0'
log = "0.4" log = "0.4"
maplit = '1.0.2' maplit = '1.0.2'
memmap2 = "0.5.0"
serde = "1.0"
serde_json = "1.0.64" serde_json = "1.0.64"
structopt = '0.3.8' structopt = '0.3.8'
...@@ -71,6 +73,7 @@ sc-consensus-uncles = { git = "https://github.com/librelois/substrate.git", bran ...@@ -71,6 +73,7 @@ sc-consensus-uncles = { git = "https://github.com/librelois/substrate.git", bran
sc-executor = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sc-executor = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" }
sc-finality-grandpa = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sc-finality-grandpa = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" }
sc-keystore = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sc-keystore = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" }
sc-network = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" }
sc-rpc-api = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sc-rpc-api = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" }
sc-service = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sc-service = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" }
sc-telemetry = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sc-telemetry = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" }
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
pub mod gen_genesis_data;
#[cfg(feature = "g1")] #[cfg(feature = "g1")]
pub mod g1; pub mod g1;
#[cfg(feature = "gdev")] #[cfg(feature = "gdev")]
......
...@@ -16,12 +16,12 @@ ...@@ -16,12 +16,12 @@
use super::*; use super::*;
use common_runtime::constants::*; use common_runtime::constants::*;
use common_runtime::entities::IdtyName; use common_runtime::*;
use gdev_runtime::{ use gdev_runtime::{
opaque::SessionKeys, AccountId, AuthorityMembersConfig, BabeConfig, BalancesConfig, CertConfig, opaque::SessionKeys, AccountId, AuthorityMembersConfig, BabeConfig, BalancesConfig, CertConfig,
GenesisConfig, GenesisParameters, IdentityConfig, IdtyValue, ImOnlineId, MembershipConfig, GenesisConfig, IdentityConfig, ImOnlineId, MembershipConfig, ParametersConfig, SessionConfig,
ParametersConfig, SessionConfig, SmithsCertConfig, SmithsMembershipConfig, SudoConfig, SmithsCertConfig, SmithsMembershipConfig, SudoConfig, SystemConfig, UdAccountsStorageConfig,
SystemConfig, UdAccountsStorageConfig, UniversalDividendConfig, WASM_BINARY, UniversalDividendConfig, WASM_BINARY,
}; };
use sc_service::ChainType; use sc_service::ChainType;
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
...@@ -41,6 +41,8 @@ pub type AuthorityKeys = ( ...@@ -41,6 +41,8 @@ pub type AuthorityKeys = (
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>; pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
type GenesisParameters = gdev_runtime::GenesisParameters<u32, u32, u64>;
const TOKEN_DECIMALS: usize = 2; const TOKEN_DECIMALS: usize = 2;
const TOKEN_SYMBOL: &str = "ĞD"; const TOKEN_SYMBOL: &str = "ĞD";
// The URL for the telemetry server. // The URL for the telemetry server.
...@@ -101,6 +103,49 @@ pub fn development_chain_spec() -> Result<ChainSpec, String> { ...@@ -101,6 +103,49 @@ pub fn development_chain_spec() -> Result<ChainSpec, String> {
)) ))
} }
pub fn gen_live_conf() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "wasm not available".to_string())?;
super::gen_genesis_data::generate_genesis_data(
|genesis_data| {
ChainSpec::from_genesis(
// Name
"Ğdev",
// ID
"gdev",
sc_service::ChainType::Live,
move || genesis_data_to_gdev_genesis_conf(genesis_data.clone(), wasm_binary),
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
None,
// Properties
Some(
serde_json::json!({
"tokenDecimals": TOKEN_DECIMALS,
"tokenSymbol": TOKEN_SYMBOL,
})
.as_object()
.expect("must be a map")
.clone(),
),
// Extensions
None,
)
},
Some(super::gen_genesis_data::ParamsAppliedAtGenesis {
genesis_certs_expire_on: 100_000,
genesis_smith_certs_expire_on: 100_000,
genesis_memberships_expire_on: 100_000,
genesis_memberships_renewable_on: 50,
genesis_smith_memberships_expire_on: 100_000,
genesis_smith_memberships_renewable_on: 50,
}),
)
}
pub fn local_testnet_config( pub fn local_testnet_config(
initial_authorities_len: usize, initial_authorities_len: usize,
initial_smiths_len: usize, initial_smiths_len: usize,
...@@ -256,18 +301,16 @@ fn gen_genesis_conf( ...@@ -256,18 +301,16 @@ fn gen_genesis_conf(
identity: IdentityConfig { identity: IdentityConfig {
identities: initial_identities identities: initial_identities
.iter() .iter()
.map(|(name, owner_key)| { .enumerate()
( .map(|(i, (name, owner_key))| GenesisIdty {
owner_key.clone(), index: i as u32 + 1,
( owner_key: owner_key.clone(),
name.clone(), name: name.clone(),
IdtyValue { value: IdtyValue {
next_creatable_identity_on: Default::default(), next_creatable_identity_on: Default::default(),
removable_on: 0, removable_on: 0,
status: gdev_runtime::IdtyStatus::Validated, status: IdtyStatus::Validated,
}, },
),
)
}) })
.collect(), .collect(),
}, },
...@@ -339,3 +382,83 @@ fn session_keys( ...@@ -339,3 +382,83 @@ fn session_keys(
authority_discovery, authority_discovery,
} }
} }
fn genesis_data_to_gdev_genesis_conf(
genesis_data: super::gen_genesis_data::GenesisData<GenesisParameters, SessionKeys>,
wasm_binary: &[u8],
) -> gdev_runtime::GenesisConfig {
let super::gen_genesis_data::GenesisData {
balances,
certs_by_issuer,
first_ud,
identities,
initial_authorities,
initial_monetary_mass,
memberships,
parameters,
session_keys_map,
smiths_certs_by_issuer,
smiths_memberships,
sudo_key,
ud_accounts,
} = genesis_data;
gdev_runtime::GenesisConfig {
system: SystemConfig {
// Add Wasm runtime to storage.
code: wasm_binary.to_vec(),
},
parameters: ParametersConfig { parameters },
authority_discovery: Default::default(),
authority_members: AuthorityMembersConfig {
initial_authorities,
},
balances: BalancesConfig { balances },
babe: BabeConfig {
authorities: Vec::with_capacity(0),
epoch_config: Some(common_runtime::constants::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: sudo_key },
identity: IdentityConfig {
identities: identities
.into_iter()
.enumerate()
.map(|(i, (name, pubkey))| common_runtime::GenesisIdty {
index: i as u32 + 1,
owner_key: pubkey,
name: common_runtime::IdtyName::from(name.as_str()),
value: common_runtime::IdtyValue {
next_creatable_identity_on: 0,
removable_on: 0,
status: IdtyStatus::Validated,
},
})
.collect(),
},
cert: CertConfig {
apply_cert_period_at_genesis: true,
certs_by_issuer,
},
membership: MembershipConfig { memberships },
smiths_cert: SmithsCertConfig {
apply_cert_period_at_genesis: true,
certs_by_issuer: smiths_certs_by_issuer,
},
smiths_membership: SmithsMembershipConfig {
memberships: smiths_memberships,
},
ud_accounts_storage: UdAccountsStorageConfig { ud_accounts },
universal_dividend: UniversalDividendConfig {
first_ud,
initial_monetary_mass,
},
}
}
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
//
// Substrate-Libre-Currency 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.
//
// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
use common_runtime::*;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use sp_core::Decode;
use std::collections::BTreeMap;
type MembershipData = sp_membership::MembershipData<u32>;
#[derive(Clone)]
pub struct GenesisData<Parameters: DeserializeOwned, SessionKeys: Decode> {
pub balances: Vec<(AccountId, u64)>,
pub certs_by_issuer: BTreeMap<u32, BTreeMap<u32, u32>>,
pub first_ud: u64,
pub identities: Vec<(String, AccountId)>,
pub initial_authorities: BTreeMap<u32, (AccountId, bool)>,
pub initial_monetary_mass: u64,
pub memberships: BTreeMap<u32, MembershipData>,
pub parameters: Parameters,
pub session_keys_map: BTreeMap<AccountId, SessionKeys>,
pub smiths_certs_by_issuer: BTreeMap<u32, BTreeMap<u32, u32>>,
pub smiths_memberships: BTreeMap<u32, MembershipData>,
pub sudo_key: Option<AccountId>,
pub ud_accounts: BTreeMap<AccountId, u32>,
}
#[derive(Default)]
pub struct ParamsAppliedAtGenesis {
pub genesis_certs_expire_on: u32,
pub genesis_smith_certs_expire_on: u32,
pub genesis_memberships_expire_on: u32,
pub genesis_memberships_renewable_on: u32,
pub genesis_smith_memberships_expire_on: u32,
pub genesis_smith_memberships_renewable_on: u32,
}
#[derive(Deserialize, Serialize)]
struct GenesisConfig<Parameters> {
first_ud: u64,
identities: BTreeMap<String, Idty>,
#[serde(default)]
parameters: Parameters,
#[serde(rename = "smiths")]
smith_identities: BTreeMap<String, SmithData>,
sudo_key: Option<AccountId>,
}
#[derive(Clone, Deserialize, Serialize)]
struct Idty {
#[serde(default)]
balance: u64,
#[serde(default)]
certs: Vec<String>,
#[serde(rename = "expire_on")]
membership_expire_on: Option<u64>,
pubkey: AccountId,
}
#[derive(Clone, Deserialize, Serialize)]
struct SmithData {
#[serde(default)]
authority: bool,
session_keys: String,
#[serde(default)]
certs: Vec<String>,
}
pub fn generate_genesis_data<CS, P, SK, F>(
f: F,
params_applied_at_genesis: Option<ParamsAppliedAtGenesis>,
) -> Result<CS, String>
where
P: Default + DeserializeOwned,
SK: Decode,
F: Fn(GenesisData<P, SK>) -> CS,
{
let ParamsAppliedAtGenesis {
genesis_certs_expire_on,
genesis_smith_certs_expire_on,
genesis_memberships_expire_on,
genesis_memberships_renewable_on,
genesis_smith_memberships_expire_on,
genesis_smith_memberships_renewable_on,
} = params_applied_at_genesis.unwrap_or_default();
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))?;
// 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,
parameters,
identities,
smith_identities,
} = genesis_config;
// MONEY AND WOT //
let mut balances = Vec::new();
let mut identities_ = Vec::with_capacity(identities.len());
let mut idty_index: u32 = 1;
let mut idty_index_of = BTreeMap::new();
let mut initial_monetary_mass = 0;
let mut memberships = BTreeMap::new();
let mut ud_accounts = BTreeMap::new();
for (idty_name, identity) in &identities {
if !validate_idty_name(idty_name) {
return Err(format!("Identity name '{}' is invalid", &idty_name));
}
// Money
if identity.balance >= 100 {
balances.push((identity.pubkey.clone(), identity.balance));
}
// We must count the money under the existential deposit because what we count is
// the monetary mass created (for the revaluation of the DU)
initial_monetary_mass += identity.balance;
ud_accounts.insert(identity.pubkey.clone(), idty_index);
// Wot
identities_.push((idty_name.clone(), identity.pubkey.clone()));
memberships.insert(
idty_index,
MembershipData {
expire_on: identity
.membership_expire_on
.map_or(genesis_memberships_expire_on, |expire_on| {
to_bn(genesis_timestamp, expire_on)
}),
renewable_on: genesis_memberships_renewable_on,
},
);
// Identity index
idty_index_of.insert(idty_name, idty_index);
idty_index += 1;
}
// CERTIFICATIONS //
let mut certs_by_issuer = BTreeMap::new();
for (idty_name, identity) in &identities {
let issuer_index = idty_index_of
.get(&idty_name)
.ok_or(format!("Identity '{}' not exist", &idty_name))?;
let mut issuer_certs = BTreeMap::new();
for receiver in &identity.certs {
let receiver_index = idty_index_of
.get(receiver)
.ok_or(format!("Identity '{}' not exist", receiver))?;
issuer_certs.insert(*receiver_index, genesis_certs_expire_on);
}
certs_by_issuer.insert(*issuer_index, issuer_certs);
}
// SMITHS SUB-WOT //
let mut initial_authorities = BTreeMap::new();
let mut session_keys_map = BTreeMap::new();
let mut smiths_memberships = BTreeMap::new();
let mut smiths_certs_by_issuer = BTreeMap::new();
for (idty_name, smith_data) in smith_identities {
let idty_index = idty_index_of
.get(&idty_name)
.ok_or(format!("Identity '{}' not exist", &idty_name))?;
let identity = identities
.get(&idty_name)
.ok_or(format!("Identity '{}' not exist", &idty_name))?;
// Initial authorities
initial_authorities.insert(*idty_index, (identity.pubkey.clone(), smith_data.authority));
// Session keys
let session_keys_bytes = hex::decode(&smith_data.session_keys[2..])
.map_err(|_| format!("invalid session keys for idty {}", &idty_name))?;
session_keys_map.insert(
identity.pubkey.clone(),
SK::decode(&mut &session_keys_bytes[..])
.map_err(|_| format!("invalid session keys for idty {}", &idty_name))?,
);
// Certifications
let mut issuer_certs = BTreeMap::new();
for receiver in &smith_data.certs {
let receiver_index = idty_index_of
.get(receiver)
.ok_or(format!("Identity '{}' not exist", receiver))?;
issuer_certs.insert(*receiver_index, genesis_smith_certs_expire_on);
}
smiths_certs_by_issuer.insert(*idty_index, issuer_certs);
// Memberships
smiths_memberships.insert(
*idty_index,
MembershipData {
expire_on: genesis_smith_memberships_expire_on,
renewable_on: genesis_smith_memberships_renewable_on,
},
);
}
let genesis_data = GenesisData {
balances,
certs_by_issuer,
first_ud,
identities: identities_,
initial_authorities,
initial_monetary_mass,
memberships,
parameters,
session_keys_map,
smiths_certs_by_issuer,
smiths_memberships,
sudo_key,
ud_accounts,
};
Ok(f(genesis_data))
}
// 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
}
fn validate_idty_name(name: &str) -> bool {
name.len() <= 64
}
...@@ -64,6 +64,8 @@ impl SubstrateCli for Cli { ...@@ -64,6 +64,8 @@ impl SubstrateCli for Cli {
#[cfg(feature = "gdev")] #[cfg(feature = "gdev")]
"local4" => Box::new(chain_spec::gdev::local_testnet_config(4, 4, 5)?), "local4" => Box::new(chain_spec::gdev::local_testnet_config(4, 4, 5)?),
#[cfg(feature = "gdev")] #[cfg(feature = "gdev")]
"gdev-gl" | "gdev_gl" => Box::new(chain_spec::gdev::gen_live_conf()?),
#[cfg(feature = "gdev")]
"gdev" => { "gdev" => {
unimplemented!() unimplemented!()
//Box::new(chain_spec::gdev::ChainSpec::from_json_file(file_path)?) //Box::new(chain_spec::gdev::ChainSpec::from_json_file(file_path)?)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment