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 common_runtime::entities::IdtyData;
use common_runtime::*;
use gtest_genesis::{build_genesis, GenesisJson};
opaque::SessionKeys, AccountConfig, AccountId, AuthorityMembersConfig, BabeConfig,
BalancesConfig, CertConfig, GenesisConfig, IdentityConfig, ImOnlineId, MembershipConfig,
SessionConfig, SmithCertConfig, SmithMembershipConfig, SudoConfig, SystemConfig,
TechnicalCommitteeConfig, UniversalDividendConfig, WASM_BINARY,
use jsonrpsee::core::JsonValue;
use sc_consensus_grandpa::AuthorityId as GrandpaId;
use sc_network::config::MultiaddrWithPeerId;
use sc_telemetry::TelemetryEndpoints;
use serde::Deserialize;
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use sp_consensus_babe::AuthorityId as BabeId;
use sp_core::{blake2_256, sr25519, Encode, H256};
use sp_membership::MembershipData;
use std::collections::BTreeMap;
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
pub type AuthorityKeys = (
AccountId,
GrandpaId,
ImOnlineId,
AuthorityDiscoveryId,
);
/// Generate an authority keys.
pub fn get_authority_keys_from_seed(s: &str) -> AuthorityKeys {
(
get_account_id_from_seed::<sr25519::Public>(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,
}
}
// === 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>,
}
// === development chainspecs ===
/// generate development chainspec with Alice validator
// there is some code duplication because we can not use ClientSpec
pub fn development_chainspecs() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "wasm not available".to_string())?;
// 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}");
// open json genesis file
let file = std::fs::File::open(&genesis_json_path)
.map_err(|e| format!("Error opening gen conf file `{}`: {}", genesis_json_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_json_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))?;
// return chainspecs
Ok(ChainSpec::from_genesis(
// Name
"ĞTest Development",
// ID
"gtest_dev",
// chain type
sc_service::ChainType::Development,
// genesis config constructor
move || {
build_genesis(
// genesis data built from json
genesis_data.clone(),
// 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": 2,
"tokenSymbol": "ĞT",
})
.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 || {
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"),
)
},
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
None,
//Fork ID
None,
// Properties
Some(
serde_json::json!({
"tokenDecimals": 2,
"tokenSymbol": "ĞT",
})
.as_object()
.expect("must be a map")
.clone(),
),
// Extensions
None,
))
}
// === live chainspecs ===
/// live chainspecs
// one smith must have session keys
pub fn live_chainspecs(
client_spec: ClientSpec,
genesis_data: GenesisJson,
let wasm_binary = WASM_BINARY.ok_or_else(|| "wasm not available".to_string())?;
// return chainspecs
Ok(ChainSpec::from_genesis(
// Name
client_spec.name.as_str(),
client_spec.id.as_str(),
// chain type
client_spec.chain_type,
// genesis config constructor
build_genesis(
// genesis data
genesis_data.clone(),
// wasm binary
// do not replace session keys
None,
.expect("genesis building failed")
client_spec.boot_nodes,
// Telemetry (by default, enable telemetry, can be disabled with argument)
client_spec.telemetry_endpoints,
client_spec.properties,
// Extensions
None,
))
}
/// generate a genesis with given number of smith and identities
fn generate_genesis(
root_key: AccountId,
) -> GenesisConfig {
assert!(initial_identities_len <= 6);
assert!(initial_smiths_len <= initial_identities_len);
assert!(initial_authorities_len <= initial_smiths_len);
let monetary_mass = initial_identities_len as u64 * ud;
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>>();
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())),
is_identity: true,
},
)
})
.collect(),
},
authority_members: AuthorityMembersConfig {
.iter()
.enumerate()
.map(|(i, keys)| (i as u32 + 1, (keys.0.clone(), true)))
.collect(),
},
// Necessary to initialize TotalIssuence
balances: BalancesConfig {
total_issuance: monetary_mass,
},
babe: BabeConfig {
authorities: Vec::with_capacity(0),
epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG),
grandpa: Default::default(),
im_online: Default::default(),
session: SessionConfig {
.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.
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))| common_runtime::GenesisIdty {
index: i as u32 + 1,
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,
},
})
.collect(),
},
membership: MembershipConfig {
memberships: (1..=initial_identities.len())
.map(|i| {
(
i as u32,
MembershipData {
expire_on: gtest_runtime::MembershipPeriod::get(),
},
)
certs_by_receiver: clique_wot(initial_identities.len()),
smith_membership: SmithMembershipConfig {
memberships: (1..=initial_smiths_len)
.map(|i| {
(
i as u32,
MembershipData {
expire_on: gtest_runtime::SmithMembershipPeriod::get(),
},
)
})
.collect(),
},
smith_cert: SmithCertConfig {
apply_cert_period_at_genesis: false,
certs_by_receiver: clique_wot(initial_smiths_len),
},
universal_dividend: UniversalDividendConfig {
// TODO FIXME
first_reeval: Some(600_000),
first_ud: Some(6_000),