Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 1000i100-test
  • 105_gitlab_container_registry
  • cgeek/issue-297-cpu
  • ci_cache
  • debug/podman
  • elois-compose-metrics
  • elois-duniter-storage
  • elois-smoldot
  • feature/dc-dump
  • feature/distance-rule
  • feature/show_milestone
  • fix-252
  • fix_picked_up_file_in_runtime_release
  • gdev-800-tests
  • hugo-release/runtime-701
  • hugo-tmp-dockerfile-cache
  • hugo/195-doc
  • hugo/195-graphql-schema
  • hugo/distance-precompute
  • hugo/endpoint-gossip
  • hugo/tmp-0.9.1
  • master
  • network/gdev-800
  • network/gdev-802
  • network/gdev-803
  • network/gdev-900
  • network/gtest-1000
  • pini-check-password
  • release/client-800.2
  • release/hugo-chainspec-gdev5
  • release/poka-chainspec-gdev5
  • release/poka-chainspec-gdev5-pini-docker
  • release/runtime-100
  • release/runtime-200
  • release/runtime-300
  • release/runtime-400
  • release/runtime-401
  • release/runtime-500
  • release/runtime-600
  • release/runtime-700
  • release/runtime-701
  • release/runtime-800
  • runtime/gtest-1000
  • tests/distance-with-oracle
  • tuxmain/anonymous-tx
  • tuxmain/benchmark-distance
  • tuxmain/fix-change-owner-key
  • update-docker-compose-rpc-squid-names
  • upgradable-multisig
  • gdev-800
  • gdev-800-0.8.0
  • gdev-802
  • gdev-803
  • gdev-900-0.10.0
  • gdev-900-0.10.1
  • gdev-900-0.9.0
  • gdev-900-0.9.1
  • gdev-900-0.9.2
  • gtest-1000
  • gtest-1000-0.11.0
  • gtest-1000-0.11.1
  • runtime-100
  • runtime-101
  • runtime-102
  • runtime-103
  • runtime-104
  • runtime-105
  • runtime-200
  • runtime-201
  • runtime-300
  • runtime-301
  • runtime-302
  • runtime-303
  • runtime-400
  • runtime-401
  • runtime-500
  • runtime-600
  • runtime-700
  • runtime-701
  • runtime-800
  • runtime-800-backup
  • runtime-800-bis
  • runtime-801
  • v0.1.0
  • v0.2.0
  • v0.3.0
  • v0.4.0
  • v0.4.1
88 results

Target

Select target project
  • nodes/rust/duniter-v2s
  • llaq/lc-core-substrate
  • pini-gh/duniter-v2s
  • vincentux/duniter-v2s
  • mildred/duniter-v2s
  • d0p1/duniter-v2s
  • bgallois/duniter-v2s
  • Nicolas80/duniter-v2s
8 results
Select Git revision
  • distance
  • elois-ci-binary-release
  • elois-compose-metrics
  • elois-duniter-storage
  • elois-fix-85
  • elois-opti-cert
  • elois-remove-renewable-period
  • elois-rework-certs
  • elois-smoldot
  • elois-substrate-v0.9.23
  • elois-technical-commitee
  • hugo-cucumber-identity
  • master
  • no-bootnodes
  • poc-oneshot-accounts
  • release/runtime-100
  • release/runtime-200
  • ts-types
  • ud-time-64
  • runtime-100
  • runtime-101
  • runtime-102
  • runtime-103
  • runtime-104
  • runtime-105
  • runtime-200
  • runtime-201
  • v0.1.0
28 results
Show changes
Showing
with 430542 additions and 1622 deletions
[package]
authors = ['Axiom-Team Developers <https://axiom-team.fr>']
description = 'duniter live tests.'
edition = "2021"
homepage = 'https://substrate.dev'
license = 'AGPL-3.0'
name = 'duniter-live-tests'
repository = 'https://git.duniter.org/nodes/rust/duniter-v2s'
version = '3.0.0'
authors.workspace = true
description = "duniter live tests"
edition.workspace = true
homepage.workspace = true
license.workspace = true
name = "duniter-live-tests"
repository.workspace = true
version.workspace = true
[dev-dependencies]
anyhow = "1.0"
hex-literal = "0.3"
parity-scale-codec = "3.1.5"
sp-core = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.23' }
subxt = { git = 'https://github.com/duniter/subxt', branch = 'duniter-substrate-v0.9.23' }
tokio = { version = "1.15.0", features = ["macros"] }
anyhow = { workspace = true }
codec = { workspace = true }
countmap = { workspace = true }
sp-core = { workspace = true, features = ["std"] }
sp-runtime = { workspace = true, features = ["std"] }
subxt = { workspace = true, features = [
"native",
"jsonrpsee",
] }
tokio = { workspace = true, features = ["macros", "time", "rt-multi-thread"] }
[features]
runtime-benchmarks = []
try-runtime = []
......@@ -16,7 +16,7 @@ Test suite that verifies the consistency of the onchain storage.
#### Custom RPC endpoint
You can choose to use another RPC endpoint by setting the environment variable `WS_RPC_ENDPOINT`.
This is also the only way to test against a different network that the default one.
This is also the only way to test against a different network that the default one which is `ws://localhost:9944`.
#### run against a specific block
......
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
......@@ -15,22 +15,21 @@
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
#[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")]
pub mod gdev_runtime {}
pub mod gdev {}
use hex_literal::hex;
use sp_core::crypto::AccountId32;
use sp_core::{blake2_128, ByteArray, H256};
use std::collections::HashMap;
use subxt::{extrinsic::PlainTip, ClientBuilder, DefaultConfig};
use countmap::CountMap;
use sp_core::{blake2_128, crypto::AccountId32, ByteArray, H256};
use std::collections::{HashMap, HashSet};
use subxt::{backend::rpc::RpcClient, config::SubstrateConfig as GdevConfig};
const DEFAULT_ENDPOINT: &str = "wss://gdev.librelois.fr:443/ws";
const DEFAULT_ENDPOINT: &str = "ws://localhost:9944";
const TREASURY_ACCOUNT_ID: [u8; 32] =
hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000");
const EXISTENTIAL_DEPOSIT: u64 = 100;
//use hex_literal::hex;
//const TREASURY_ACCOUNT_ID: [u8; 32] =
// hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000");
type Api = gdev_runtime::RuntimeApi<DefaultConfig, BaseExtrinsicParams<DefaultConfig>>;
type BaseExtrinsicParams<T> = subxt::extrinsic::BaseExtrinsicParams<T, PlainTip>;
type Client = subxt::Client<DefaultConfig>;
type Client = subxt::OnlineClient<GdevConfig>;
// define gdev basic types
type Balance = u64;
......@@ -38,40 +37,40 @@ type BlockNumber = u32;
type Index = u32;
// Define gdev types
type AccountInfo = gdev_runtime::runtime_types::frame_system::AccountInfo<
type AccountInfo = gdev::runtime_types::frame_system::AccountInfo<
Index,
gdev_runtime::runtime_types::pallet_duniter_account::types::AccountData<Balance>,
gdev::runtime_types::pallet_duniter_account::types::AccountData<Balance, IdtyIndex>,
>;
type IdtyData = gdev_runtime::runtime_types::common_runtime::entities::IdtyData;
type IdtyData = gdev::runtime_types::common_runtime::entities::IdtyData;
type IdtyIndex = u32;
type IdtyValue = gdev_runtime::runtime_types::pallet_identity::types::IdtyValue<
BlockNumber,
AccountId32,
IdtyData,
>;
use gdev_runtime::runtime_types::pallet_identity::types::IdtyStatus;
type IdtyValue =
gdev::runtime_types::pallet_identity::types::IdtyValue<BlockNumber, AccountId32, IdtyData>;
type MembershipData = gdev::runtime_types::sp_membership::MembershipData<BlockNumber>;
use gdev::runtime_types::pallet_identity::types::{IdtyName, IdtyStatus};
struct Storage {
accounts: HashMap<AccountId32, AccountInfo>,
identities: HashMap<IdtyIndex, IdtyValue>,
identity_index_of: HashMap<[u8; 16], IdtyIndex>,
memberships: HashMap<IdtyIndex, MembershipData>,
identities_names: HashMap<IdtyIndex, IdtyName>,
}
#[tokio::test(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
let ws_rpc_endpoint =
std::env::var("WS_RPC_ENDPOINT").unwrap_or_else(|_| DEFAULT_ENDPOINT.to_owned());
let client: Client = ClientBuilder::new()
.set_url(ws_rpc_endpoint)
.set_page_size(100)
.build()
let rpc = RpcClient::from_url(ws_rpc_endpoint)
.await
.expect("fail to connect to node");
.expect("Failed to create the rpc backend");
let client = Client::from_rpc_client(rpc.clone()).await.unwrap();
let maybe_block_hash = if let Ok(block_number) = std::env::var("AT_BLOCK_NUMBER") {
let block_number: BlockNumber = block_number.parse()?;
println!("Run sanity tests against ĞDev at block #{}.", block_number);
client.rpc().block_hash(Some(block_number.into())).await?
// FIXME
// client.at(block_number).await?
None
} else {
println!("Run sanity tests against ĞDev at last best block");
None
......@@ -80,58 +79,105 @@ async fn main() -> anyhow::Result<()> {
sanity_tests_at(client, maybe_block_hash).await
}
async fn sanity_tests_at(client: Client, maybe_block_hash: Option<H256>) -> anyhow::Result<()> {
// Get API
let api = client.clone().to_runtime_api::<Api>();
async fn sanity_tests_at(client: Client, _maybe_block_hash: Option<H256>) -> anyhow::Result<()> {
// ===== Collect storage ===== //
// Collect accounts
let mut accounts = HashMap::new();
let mut account_iter = api
let mut accounts: HashMap<AccountId32, AccountInfo> = HashMap::new();
let mut account_iter = client
.storage()
.system()
.account_iter(maybe_block_hash)
.at_latest()
.await
.unwrap()
.iter(gdev::storage().system().account_iter())
.await?;
while let Some((key, account_info)) = account_iter.next().await? {
while let Some(Ok(key)) = account_iter.next().await {
let mut account_id_bytes = [0u8; 32];
account_id_bytes.copy_from_slice(&key.0[48..]);
accounts.insert(AccountId32::new(account_id_bytes), account_info);
account_id_bytes.copy_from_slice(&key.key_bytes[48..]);
accounts.insert(AccountId32::new(account_id_bytes), key.value);
}
println!("accounts: {}.", accounts.len());
println!("accounts.len(): {}.", accounts.len());
// Collect identities
let mut identities = HashMap::new();
let mut idty_iter = api
let mut identities: HashMap<IdtyIndex, IdtyValue> = HashMap::new();
let mut idty_iter = client
.storage()
.identity()
.identities_iter(maybe_block_hash)
.at_latest()
.await
.unwrap()
.iter(gdev::storage().identity().identities_iter())
.await?;
while let Some((key, idty_value)) = idty_iter.next().await? {
while let Some(Ok(key)) = idty_iter.next().await {
let mut idty_index_bytes = [0u8; 4];
idty_index_bytes.copy_from_slice(&key.0[40..]);
identities.insert(IdtyIndex::from_le_bytes(idty_index_bytes), idty_value);
idty_index_bytes.copy_from_slice(&key.key_bytes[40..]);
let idty_val = IdtyValue {
data: key.value.data,
next_creatable_identity_on: key.value.next_creatable_identity_on,
old_owner_key: None, // Not used in the live test, skip the conversion
owner_key: AccountId32::from(key.value.owner_key.0),
next_scheduled: key.value.next_scheduled,
status: key.value.status,
};
identities.insert(IdtyIndex::from_le_bytes(idty_index_bytes), idty_val);
}
println!("identities: {}.", identities.len());
println!("identities.len(): {}.", identities.len());
// Collect identity_index_of
let mut identity_index_of = HashMap::new();
let mut idty_index_of_iter = api
let mut identity_index_of: HashMap<[u8; 16], IdtyIndex> = HashMap::new();
let mut idty_index_of_iter = client
.storage()
.identity()
.identity_index_of_iter(maybe_block_hash)
.at_latest()
.await
.unwrap()
.iter(gdev::storage().identity().identity_index_of_iter())
.await?;
while let Some((key, idty_index)) = idty_index_of_iter.next().await? {
while let Some(Ok(key)) = idty_index_of_iter.next().await {
let mut blake2_128_bytes = [0u8; 16];
blake2_128_bytes.copy_from_slice(&key.0[32..]);
identity_index_of.insert(blake2_128_bytes, idty_index);
blake2_128_bytes.copy_from_slice(&key.key_bytes[32..48]);
identity_index_of.insert(blake2_128_bytes, key.value);
}
println!("identity_index_of.len(): {}.", identity_index_of.len());
// Collect identity_names
let mut identities_names: HashMap<IdtyIndex, IdtyName> = HashMap::new();
let mut idty_name_iter = client
.storage()
.at_latest()
.await
.unwrap()
.iter(gdev::storage().identity().identities_names_iter())
.await?;
while let Some(Ok(key)) = idty_name_iter.next().await {
let name = IdtyName(key.key_bytes);
identities_names.insert(key.value, name);
}
println!("identities_names.len(): {}.", identities_names.len());
// Collect memberships
let mut memberships: HashMap<IdtyIndex, MembershipData> = HashMap::new();
let mut membership_iter = client
.storage()
.at_latest()
.await
.unwrap()
.iter(gdev::storage().membership().membership_iter())
.await?;
while let Some(Ok(key)) = membership_iter.next().await {
let mut idty_index_bytes = [0u8; 4];
idty_index_bytes.copy_from_slice(&key.key_bytes[40..]);
let membership_val = MembershipData {
expire_on: key.value.expire_on,
};
memberships.insert(IdtyIndex::from_le_bytes(idty_index_bytes), membership_val);
}
println!("identity_index_of: {}.", identities.len());
println!("memberships.len(): {}.", memberships.len());
let storage = Storage {
accounts,
identities,
identity_index_of,
memberships,
identities_names,
};
// ===== Verify storage ===== //
......@@ -151,12 +197,21 @@ mod verifier {
Self { errors: Vec::new() }
}
/// method to run all storage tests
pub(super) async fn verify_storage(&mut self, storage: &Storage) -> anyhow::Result<()> {
self.verify_accounts(&storage.accounts).await;
self.verify_identities(&storage.accounts, &storage.identities)
.await;
self.verify_identity_index_of(&storage.identities, &storage.identity_index_of)
.await;
self.verify_identity_coherence(&storage.identities, &storage.identity_index_of)
.await;
self.verify_status_coherence(
&storage.identities,
&storage.memberships,
&storage.identities_names,
)
.await;
if self.errors.is_empty() {
Ok(())
......@@ -171,12 +226,19 @@ mod verifier {
}
}
/// assert method to collect errors
fn assert(&mut self, assertion: bool, error: String) {
if !assertion {
self.errors.push(error);
}
}
/// like assert but just push error
fn error(&mut self, error: String) {
self.errors.push(error);
}
/// check accounts sufficients and consumers (specific to duniter-account pallet)
async fn verify_accounts(&mut self, accounts: &HashMap<AccountId32, AccountInfo>) {
for (account_id, account_info) in accounts {
if account_info.sufficients == 0 {
......@@ -187,7 +249,8 @@ mod verifier {
);
// Rule 2: If the account is not sufficient, it should comply to the existential deposit
self.assert(
(account_info.data.free + account_info.data.reserved) >= 200,
(account_info.data.free + account_info.data.reserved)
>= EXISTENTIAL_DEPOSIT,
format!(
"Account {} not respect existential deposit rule.",
account_id
......@@ -195,32 +258,42 @@ mod verifier {
);
}
// Rule 3: If the account have consumers, it shoul have at least one provider
// Rule 3: If the account have consumers, it should have at least one provider
if account_info.consumers > 0 {
// Rule 1: If the account is not s
// Rule 1: If the account is not sufficient [...]
self.assert(
account_info.providers > 0,
format!("Account {} has no providers nor sufficients.", account_id),
);
}
if account_id.as_slice() != TREASURY_ACCOUNT_ID {
// Rule 4: If the account is not a "special account",
// it should have a random id or a consumer
self.assert(
account_info.data.random_id.is_some() || account_info.consumers > 0,
format!("Account {} has no random_id nor consumer.", account_id),
);
}
}
}
/// check list of identities (account existence, sufficient)
async fn verify_identities(
&mut self,
accounts: &HashMap<AccountId32, AccountInfo>,
identities: &HashMap<IdtyIndex, IdtyValue>,
) {
// counts occurence of owner key
let mut countmap = CountMap::<AccountId32, u8>::new();
// list owner key with multiple occurences
let mut duplicates = HashSet::new();
for (idty_index, idty_value) in identities {
countmap.insert_or_increment(idty_value.owner_key.clone());
if let Some(count) = countmap.get_count(&idty_value.owner_key) {
if count > 1 {
self.error(format!(
"address {} is the owner_key of {count} identities",
idty_value.owner_key
));
if count == 2 {
duplicates.insert(idty_value.owner_key.clone());
}
}
}
// Rule 1: each identity should have an account
let maybe_account = accounts.get(&idty_value.owner_key);
self.assert(
......@@ -238,30 +311,19 @@ mod verifier {
),
);
}
match idty_value.status {
IdtyStatus::Validated => {
// Rule 3: If the identity is validated, removable_on shoud be zero
self.assert(
idty_value.removable_on == 0,
format!(
"Identity {} is corrupted: removable_on > 0 on validated idty",
idty_index
),
);
}
_ => {
// Rule 4: If the identity is not validated, next_creatable_identity_on shoud be zero
self.assert(
idty_value.next_creatable_identity_on == 0,
format!("Identity {} is corrupted: next_creatable_identity_on > 0 on non-validated idty",
idty_index)
);
}
for (idty_index, idty_value) in identities {
if duplicates.contains(&idty_value.owner_key) {
self.error(format!(
"duplicate key {} at position {idty_index}",
idty_value.owner_key
));
}
}
}
/// check the identity hashmap (length, identity existence, hash matches owner key)
async fn verify_identity_index_of(
&mut self,
identities: &HashMap<IdtyIndex, IdtyValue>,
......@@ -270,7 +332,11 @@ mod verifier {
// Rule1: identity_index_of should have the same lenght as identities
self.assert(
identities.len() == identity_index_of.len(),
"identities.len() != identity_index_of.len().".to_owned(),
format!(
"identities.len({}) != identity_index_of.len({}).",
identities.len(),
identity_index_of.len()
),
);
for (blake2_128_owner_key, idty_index) in identity_index_of {
......@@ -298,5 +364,139 @@ mod verifier {
}
}
}
/// check identities status and membership coherence
async fn verify_status_coherence(
&mut self,
identities: &HashMap<IdtyIndex, IdtyValue>,
memberships: &HashMap<IdtyIndex, MembershipData>,
names: &HashMap<IdtyIndex, IdtyName>,
) {
for (idty_index, idty_value) in identities {
// Rule 1: each Status::Member
// should have a membership and a name
// membership should be set to expire
// identity should have no scheduled action
if let IdtyStatus::Member = idty_value.status {
self.assert(
memberships.get(idty_index).is_some(),
format!("identity number {idty_index} should have a valid membership"),
);
self.assert(
names.get(idty_index).is_some(),
format!("identity number {idty_index} should have a name"),
);
self.assert(
memberships.get(idty_index).unwrap().expire_on != 0,
format!(
"Member identity number {idty_index} should have a non-null expire_on value"
),
);
self.assert(
identities.get(idty_index).unwrap().next_scheduled == 0,
format!(
"Member identity number {idty_index} should have a null next_scheduled value"
),
);
}
// Rule 2: each Status::NotMember
// should have a name but no membership
// should have a scheduled action (auto-revocation)
if let IdtyStatus::NotMember = idty_value.status {
self.assert(
memberships.get(idty_index).is_none(),
format!("identity number {idty_index} should not have a valid membership"),
);
self.assert(
names.get(idty_index).is_some(),
format!("identity number {idty_index} should have a name"),
);
self.assert(
identities.get(idty_index).unwrap().next_scheduled != 0,
format!("NotMember identity number {idty_index} should have a non-null next_scheduled value"),
);
}
// Rule 3: each Status::Revoked
// should should have a name
// no membership
// should be scheduled for removal
if let IdtyStatus::Revoked = idty_value.status {
self.assert(
memberships.get(idty_index).is_none(),
format!("identity number {idty_index} should not have a valid membership"),
);
self.assert(
names.get(idty_index).is_some(),
format!("identity number {idty_index} should have a name"),
);
self.assert(
identities.get(idty_index).unwrap().next_scheduled != 0,
format!("Revoked identity number {idty_index} should have a non-null next_scheduled value"),
);
}
// Rule 4: each Status::Unvalidaded
// should have a name but no membership.
// should be scheduled for removal
if let IdtyStatus::Unvalidated = idty_value.status {
self.assert(
memberships.get(idty_index).is_none(),
format!("identity number {idty_index} should not have a valid membership"),
);
self.assert(
names.get(idty_index).is_some(),
format!("identity number {idty_index} should have a name"),
);
self.assert(
identities.get(idty_index).unwrap().next_scheduled != 0,
format!("Unvalidated identity number {idty_index} should have a non-null next_scheduled value"),
);
}
// Rule 5: each Status::Unconfirmed
// should not have a name neither a membership.
// should be scheduled for removal soon
if let IdtyStatus::Unconfirmed = idty_value.status {
self.assert(
memberships.get(idty_index).is_none(),
format!("identity number {idty_index} should not have a valid membership"),
);
self.assert(
names.get(idty_index).is_none(),
format!("identity number {idty_index} should not have a name"),
);
self.assert(
identities.get(idty_index).unwrap().next_scheduled != 0,
format!("Unconfirmed identity number {idty_index} should have a non-null next_scheduled value"),
);
}
}
}
/// check coherence between identity list and identity index hashmap
async fn verify_identity_coherence(
&mut self,
identities: &HashMap<IdtyIndex, IdtyValue>,
identity_index_of: &HashMap<[u8; 16], IdtyIndex>,
) {
// each identity should be correcly referenced in the hashmap
for (idty_index, idty_value) in identities {
// hash owner key to get key
let blake2_128_owner_key = &blake2_128(idty_value.owner_key.as_slice());
// get identity index from hashmap
if let Some(index_of) = identity_index_of.get(blake2_128_owner_key) {
self.assert(idty_index == index_of,
format!("identity number {idty_index} with owner key {0} is mapped to identity index {index_of}", idty_value.owner_key));
} else {
self.error(format!(
"identity with owner key {} is not present in hashmap",
idty_value.owner_key
));
}
}
}
}
}
[package]
authors.workspace = true
build = "build.rs"
description = "Crypto-currency software (based on Substrate framework) to operate Ğ1 libre currency"
edition.workspace = true
homepage.workspace = true
license.workspace = true
name = "duniter"
repository.workspace = true
version = "0.11.0"
default-run = "duniter"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[[bin]]
bench = false
name = "duniter"
path = "src/main.rs"
[features]
default = ["distance-oracle", "gdev"]
gdev = ["distance-oracle?/gdev", "gdev-runtime", "std"]
gtest = ["distance-oracle?/gtest", "gtest-runtime", "std"]
g1 = ["g1-runtime", "std"]
constant-fees = [
"common-runtime/constant-fees",
"g1-runtime/constant-fees",
"gdev-runtime/constant-fees",
"gtest-runtime/constant-fees",
]
embed = []
native = []
runtime-benchmarks = [
"common-runtime/runtime-benchmarks",
"dc-distance?/runtime-benchmarks",
"frame-benchmarking-cli/runtime-benchmarks",
"frame-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"g1-runtime/runtime-benchmarks",
"gdev-runtime/runtime-benchmarks",
"gtest-runtime/runtime-benchmarks",
"pallet-grandpa/runtime-benchmarks",
"pallet-oneshot-account/runtime-benchmarks",
"pallet-im-online/runtime-benchmarks",
"pallet-treasury/runtime-benchmarks",
"pallet-transaction-payment/runtime-benchmarks",
"sc-client-db/runtime-benchmarks",
"sc-service/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
try-runtime = [
"runtime-benchmarks",
"common-runtime/try-runtime",
"dc-distance?/try-runtime",
"distance-oracle?/try-runtime",
"frame-system/try-runtime",
"g1-runtime/try-runtime",
"gdev-runtime/try-runtime",
"gtest-runtime/try-runtime",
"pallet-grandpa/try-runtime",
"pallet-oneshot-account/try-runtime",
"pallet-im-online/try-runtime",
"pallet-transaction-payment/try-runtime",
"pallet-treasury/try-runtime",
"sp-distance/try-runtime",
"sp-membership/try-runtime",
"sp-runtime/try-runtime",
]
std = [
"bs58/std",
"common-runtime/std",
"dc-distance/std",
"distance-oracle?/std",
"frame-benchmarking/std",
"frame-system/std",
"futures/std",
"g1-runtime/std",
"gdev-runtime/std",
"gtest-runtime/std",
"hex/std",
"log/std",
"num-format/std",
"pallet-grandpa/std",
"pallet-oneshot-account/std",
"pallet-im-online/std",
"pallet-transaction-payment-rpc-runtime-api/std",
"pallet-transaction-payment/std",
"pallet-treasury/std",
"sc-executor/std",
"serde/std",
"serde_json/std",
"sp-api/std",
"sp-authority-discovery/std",
"sp-block-builder/std",
"sp-consensus-babe/std",
"sp-consensus-grandpa/std",
"sp-core/std",
"sp-distance/std",
"sp-inherents/std",
"sp-io/std",
"sp-keystore/std",
"sp-membership/std",
"sp-offchain/std",
"sp-runtime/std",
"sp-session/std",
"sp-storage/std",
"sp-timestamp/std",
"sp-transaction-pool/std",
"sp-transaction-storage-proof/std",
"sp-trie/std",
]
distance-oracle = ["dep:distance-oracle"]
[dependencies]
async-io = { workspace = true }
bs58 = { workspace = true }
clap = { workspace = true, features = ["derive"] }
clap_complete = { workspace = true }
frame-benchmarking = { workspace = true }
frame-benchmarking-cli = { workspace = true }
frame-system = { workspace = true }
frame-metadata-hash-extension = { workspace = true, default-features = true }
futures = { workspace = true, features = ["compat"] }
hex = { workspace = true }
jsonrpsee = { workspace = true, features = ["server"] }
log = { workspace = true }
memmap2 = { workspace = true }
num-format = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
serde_yaml = { workspace = true }
codec = { workspace = true }
array-bytes = { workspace = true }
parking_lot = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread"] }
# Local
common-runtime = { workspace = true }
g1-runtime = { workspace = true, optional = true }
gdev-runtime = { workspace = true, optional = true }
gtest-runtime = { workspace = true, optional = true }
distance-oracle = { workspace = true, optional = true }
dc-distance = { workspace = true, optional = true }
pallet-oneshot-account = { workspace = true, optional = true }
# Substrate
pallet-grandpa = { workspace = true, default-features = true }
pallet-im-online = { workspace = true, default-features = true }
pallet-transaction-payment = { workspace = true, default-features = true }
pallet-transaction-payment-rpc = { workspace = true, default-features = true }
pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true }
pallet-treasury = { workspace = true, default-features = true }
sc-basic-authorship = { workspace = true, default-features = true }
sc-chain-spec = { workspace = true, default-features = true }
sc-client-api = { workspace = true, default-features = true }
sc-client-db = { workspace = true, default-features = true }
sc-consensus = { workspace = true, default-features = true }
sc-rpc = { workspace = true, default-features = true }
sc-consensus-babe = { workspace = true, default-features = true }
sc-consensus-babe-rpc = { workspace = true, default-features = true }
sc-consensus-grandpa = { workspace = true, default-features = true }
sc-consensus-grandpa-rpc = { workspace = true, default-features = true }
sc-consensus-manual-seal = { workspace = true, default-features = true }
sc-executor = { workspace = true, default-features = true }
sc-keystore = { workspace = true, default-features = true }
sc-network = { workspace = true, default-features = true }
sc-network-sync = { workspace = true, default-features = true }
sc-offchain = { workspace = true, default-features = true }
sc-rpc-api = { workspace = true, default-features = true }
sc-telemetry = { workspace = true, default-features = true }
sc-transaction-pool = { workspace = true, default-features = true }
sc-transaction-pool-api = { workspace = true, default-features = true }
sc-utils = { workspace = true, default-features = true }
sp-api = { workspace = true, default-features = true }
sp-authority-discovery = { workspace = true, default-features = true }
sp-block-builder = { workspace = true, default-features = true }
sp-blockchain = { workspace = true, default-features = true }
sp-consensus = { workspace = true, default-features = true }
sp-consensus-babe = { workspace = true, default-features = true }
sp-consensus-grandpa = { workspace = true, default-features = true }
sp-core = { workspace = true, default-features = true }
sp-distance = { workspace = true, default-features = true }
sp-inherents = { workspace = true, default-features = true }
sp-io = { workspace = true, default-features = true }
sp-keyring = { workspace = true, default-features = true }
sp-keystore = { workspace = true, default-features = true }
sp-membership = { workspace = true, default-features = true }
sp-offchain = { workspace = true, default-features = true }
sp-runtime = { workspace = true, default-features = true }
sp-session = { workspace = true, default-features = true }
sp-storage = { workspace = true, default-features = true }
sp-timestamp = { workspace = true, default-features = true }
sp-transaction-pool = { workspace = true, default-features = true }
sp-transaction-storage-proof = { workspace = true, default-features = true }
substrate-frame-rpc-system = { workspace = true, default-features = true }
[dev-dependencies]
sc-network-test = { workspace = true, default-features = true }
async-trait = { workspace = true }
env_logger = "0.10.2"
async-channel = "2.3.1"
[build-dependencies]
substrate-build-script-utils = { workspace = true, default-features = true }
# Dependencies for specific targets
[target.'cfg(any(target_arch="x86_64", target_arch="aarch64"))'.dependencies]
sc-cli = { workspace = true, default-features = true }
sc-service = { workspace = true, default-features = true }
sp-trie = { workspace = true, default-features = true }
[package.metadata.deb]
maintainer-scripts = "../resources/debian"
systemd-units = [
{ unit-name = "duniter-mirror", enable = false },
{ unit-name = "duniter-smith", enable = false },
{ unit-name = "distance-oracle", enable = false },
]
assets = [
[
"../resources/debian/env_file",
"/etc/duniter/env_file",
"0640",
],
[
"../target/release/duniter",
"/usr/bin/duniter2",
"755",
],
]
[package.metadata.generate-rpm]
assets = [
{ source = "../target/release/duniter", dest = "/usr/bin/duniter2", mode = "755" },
{ source = "../resources/debian/duniter.sysusers", dest = "/usr/lib/sysusers.d/duniter.conf", mode = "0644" },
{ source = "../resources/debian/env_file", dest = "/etc/duniter/env_file", config = true, mode = "0640" },
{ source = "../LICENSE", dest = "/usr/share/licenses/duniter/LICENSE" },
{ source = "../resources/debian/duniter-mirror.service", dest = "/usr/lib/systemd/system/duniter-mirror.service", mode = "0644" },
{ source = "../resources/debian/duniter-smith.service", dest = "/usr/lib/systemd/system/duniter-smith.service", mode = "0644" },
{ source = "../resources/debian/duniter-smith.service", dest = "/usr/lib/systemd/system/distance-oracle.service", mode = "0644" },
]
\ No newline at end of file
# Duniter Node
You can find the autogenerated documentation at: [https://doc-duniter-org.ipns.pagu.re/duniter/index.html](https://doc-duniter-org.ipns.pagu.re/duniter/index.html).
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
fn main() {
//cli::main();
......
Source diff could not be displayed: it is too large. Options to address this: view the blob.
name: "ĞDev"
id: "gdev"
chainType: "Live"
bootNodes:
- "/dns/gdev.cgeek.fr/tcp/30334/p2p/12D3KooWN7QhcPbTZgNMnS7AUZh3ZfnM43VdVKqy4JbAEp5AJh4f"
- "/dns/gdev.coinduf.eu/tcp/30333/p2p/12D3KooWFseA3B66eBzj4NY5ng3Lb2U3VPnKCi3iXYGYUSAahEw7"
telemetryEndpoints:
- ["/dns/telemetry.polkadot.io/tcp/443/x-parity-wss/%2Fsubmit%2F", 0]
properties:
tokenDecimals: 2
tokenSymbol: "ĞD"
Source diff could not be displayed: it is too large. Options to address this: view the blob.
name: "ĞTest"
id: "gtest"
chainType: "Live"
protocolId: "gtest"
bootNodes:
- "/dns/gt.elo.tf/tcp/30333/p2p/12D3KooWNUyHfjozVWP2ne5BN7AEwFNzUQvtK6GULxmgBucnYtjG"
- "/dns/gt.elo.tf/tcp/30334/p2p/12D3KooWRugKTSYDo4SKEWhzatSFQVm5vaG74NpZPmVkXE2W8pJU"
telemetryEndpoints:
- ["/dns/telemetry.polkadot.io/tcp/443/x-parity-wss/%2Fsubmit%2F", 0]
properties:
tokenDecimals: 2
tokenSymbol: "ĞT"
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
// Common to all Duniter blockchains
pub mod gen_genesis_data;
#[cfg(feature = "g1")]
......@@ -23,15 +24,12 @@ pub mod gdev;
#[cfg(feature = "gtest")]
pub mod gtest;
use common_runtime::{AccountId, IdtyIndex, Signature};
use common_runtime::{AccountId, Signature};
use sp_core::{Pair, Public};
use sp_runtime::traits::{IdentifyAccount, Verify};
use std::collections::BTreeMap;
pub type AccountPublic = <Signature as Verify>::Signer;
pub const NAMES: [&str; 6] = ["Alice", "Bob", "Charlie", "Dave", "Eve", "Ferdie"];
/// Generate a crypto pair from seed.
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None)
......@@ -39,14 +37,6 @@ pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Pu
.public()
}
/*/// Generate an account ID from pain.
pub fn get_account_id_from_pair<TPublic: Public>(pair: TPublic::Pair) -> AccountId
where
AccountPublic: From<<TPublic::Pair as Pair>::Public>,
{
AccountPublic::from(pair.public()).into_account()
}*/
/// Generate an account ID from seed.
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
where
......@@ -54,25 +44,3 @@ where
{
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}
fn clique_wot(
initial_identities_len: usize,
cert_validity_period: common_runtime::BlockNumber,
) -> BTreeMap<IdtyIndex, BTreeMap<IdtyIndex, common_runtime::BlockNumber>> {
let mut certs_by_issuer = BTreeMap::new();
for i in 1..=initial_identities_len {
certs_by_issuer.insert(
i as IdtyIndex,
(1..=initial_identities_len)
.filter_map(|j| {
if i != j {
Some((j as IdtyIndex, cert_validity_period))
} else {
None
}
})
.collect(),
);
}
certs_by_issuer
}
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
pub type ChainSpec = sc_service::GenericChainSpec<g1_runtime::GenesisConfig>;
use super::*;
use crate::chain_spec::gen_genesis_data::{
AuthorityKeys, CommonParameters, GenesisIdentity, SessionKeysProvider,
};
use common_runtime::{constants::*, entities::IdtyData, GenesisIdty};
use g1_runtime::{
opaque::SessionKeys, pallet_universal_dividend, parameters, Runtime, RuntimeGenesisConfig,
WASM_BINARY,
};
use sc_service::ChainType;
use serde::Deserialize;
use sp_core::{sr25519, Get};
use std::{env, fs};
pub fn development_chain_spec() -> Result<ChainSpec, String> {
todo!()
pub type ChainSpec = sc_service::GenericChainSpec;
#[derive(Default, Clone, Deserialize)]
// No parameters for G1 (unlike GDev)
struct GenesisParameters {}
const TOKEN_DECIMALS: usize = 2;
const TOKEN_SYMBOL: &str = "Ğ";
static EXISTENTIAL_DEPOSIT: u64 = parameters::ExistentialDeposit::get();
struct G1SKP;
impl SessionKeysProvider<SessionKeys> for G1SKP {
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 {
CommonParameters {
currency_name: TOKEN_SYMBOL.to_string(),
decimals: TOKEN_DECIMALS,
babe_epoch_duration: parameters::EpochDuration::get(),
babe_expected_block_time: parameters::ExpectedBlockTime::get(),
babe_max_authorities: parameters::MaxAuthorities::get(),
timestamp_minimum_period: parameters::MinimumPeriod::get(),
balances_existential_deposit: parameters::ExistentialDeposit::get(),
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::UdCreationPeriod::get() as u64,
universal_dividend_ud_reeval_period: parameters::UdReevalPeriod::get() as u64,
wot_first_issuable_on: parameters::WotFirstCertIssuableOn::get(),
wot_min_cert_for_membership: parameters::WotMinCertForMembership::get(),
wot_min_cert_for_create_idty_right: parameters::WotMinCertForCreateIdtyRight::get(),
identity_confirm_period: parameters::ConfirmPeriod::get(),
identity_change_owner_key_period: parameters::ChangeOwnerKeyPeriod::get(),
identity_idty_creation_period: parameters::IdtyCreationPeriod::get(),
identity_autorevocation_period: parameters::AutorevocationPeriod::get(),
identity_deletion_period: parameters::DeletionPeriod::get(),
membership_membership_period: parameters::MembershipPeriod::get(),
membership_membership_renewal_period: parameters::MembershipRenewalPeriod::get(),
cert_max_by_issuer: parameters::MaxByIssuer::get(),
cert_min_received_cert_to_be_able_to_issue_cert:
parameters::MinReceivedCertToBeAbleToIssueCert::get(),
cert_validity_period: parameters::ValidityPeriod::get(),
distance_min_accessible_referees: parameters::MinAccessibleReferees::get(),
distance_max_depth: parameters::MaxRefereeDistance::get(),
smith_sub_wot_min_cert_for_membership: parameters::SmithWotMinCertForMembership::get(),
smith_inactivity_max_duration: parameters::SmithInactivityMaxDuration::get(),
smith_cert_max_by_issuer: parameters::SmithMaxByIssuer::get(),
cert_cert_period: parameters::CertPeriod::get(),
treasury_spend_period: <Runtime as pallet_treasury::Config>::SpendPeriod::get(),
}
}
/// 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("Ğ1 Local Testnet")
.with_id("g1_local")
.with_chain_type(ChainType::Local)
.with_genesis_config_patch({
let genesis_data =
gen_genesis_data::generate_genesis_data_for_local_chain::<_, _, SessionKeys, G1SKP>(
// Initial authorities len
initial_authorities_len,
// Initial smiths len,
initial_smiths_len,
// Initial identities len
initial_identities_len,
EXISTENTIAL_DEPOSIT,
None,
// Sudo account
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_parameters,
)
.expect("Genesis Data must be buildable");
genesis_data_to_g1_genesis_conf(genesis_data)
})
.with_properties(
serde_json::json!({
"tokenDecimals": TOKEN_DECIMALS,
"tokenSymbol": TOKEN_SYMBOL,
})
.as_object()
.expect("must be a map")
.clone(),
)
.build())
}
/// custom genesis
fn genesis_data_to_g1_genesis_conf(
genesis_data: super::gen_genesis_data::GenesisData<GenesisParameters, SessionKeys>,
) -> serde_json::Value {
let super::gen_genesis_data::GenesisData {
accounts,
treasury_balance,
certs_by_receiver,
first_ud,
first_ud_reeval,
identities,
initial_authorities,
initial_monetary_mass,
memberships,
parameters: _,
common_parameters: _,
session_keys_map,
initial_smiths,
sudo_key,
technical_committee_members,
ud,
} = genesis_data;
serde_json::json!({
"account": {
"accounts": accounts,
"treasuryBalance": treasury_balance,
},
"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
.into_iter()
.map(
|GenesisIdentity {
idty_index,
name,
owner_key,
status,
next_scheduled,
}| GenesisIdty {
index: idty_index,
name: common_runtime::IdtyName::from(name.as_str()),
value: common_runtime::IdtyValue {
data: IdtyData {
first_eligible_ud: match status {
// Only members are eligible to UD.
// The first claimable UD has the minimum index (1).
common_runtime::IdtyStatus::Member => g1_runtime::pallet_universal_dividend::FirstEligibleUd::min(),
_ => g1_runtime::pallet_universal_dividend::FirstEligibleUd(None),
}
},
next_creatable_identity_on: 0,
old_owner_key: None,
owner_key,
next_scheduled,
status,
},
},
)
.collect::<Vec<GenesisIdty<g1_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,
},
})
}
/// 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()))
}
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use common_runtime::constants::*;
use common_runtime::entities::IdtyData;
use common_runtime::*;
use crate::chain_spec::gen_genesis_data::{
AuthorityKeys, CommonParameters, GenesisIdentity, SessionKeysProvider,
};
use common_runtime::{constants::*, entities::IdtyData, GenesisIdty};
use gdev_runtime::{
opaque::SessionKeys, AccountConfig, AccountId, AuthorityMembersConfig, BabeConfig,
BalancesConfig, CertConfig, GenesisConfig, IdentityConfig, ImOnlineId, MembershipConfig,
ParametersConfig, SessionConfig, SmithsCertConfig, SmithsMembershipConfig, SudoConfig,
SystemConfig, TechnicalCommitteeConfig, UniversalDividendConfig, WASM_BINARY,
opaque::SessionKeys, pallet_universal_dividend, parameters, Runtime, WASM_BINARY,
};
use jsonrpsee::core::JsonValue;
use sc_network::config::MultiaddrWithPeerId;
use sc_service::ChainType;
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use sp_consensus_babe::AuthorityId as BabeId;
use sp_core::{blake2_256, sr25519, Encode, H256};
use sp_finality_grandpa::AuthorityId as GrandpaId;
use sp_membership::MembershipData;
use std::collections::BTreeMap;
pub type AuthorityKeys = (
AccountId,
BabeId,
GrandpaId,
ImOnlineId,
AuthorityDiscoveryId,
);
use sc_telemetry::TelemetryEndpoints;
use serde::Deserialize;
use sp_core::{sr25519, Get};
use std::{env, fs};
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
pub type ChainSpec = sc_service::GenericChainSpec;
type GenesisParameters = gdev_runtime::GenesisParameters<u32, u32, u64>;
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/";
/// 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::<BabeId>(s),
get_from_seed::<GrandpaId>(s),
get_from_seed::<ImOnlineId>(s),
get_from_seed::<AuthorityDiscoveryId>(s),
)
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,
}
}
}
pub fn development_chain_spec() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;
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(),
identity_deletion_period: parameters::DeletionPeriod::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: parameters::MinAccessibleReferees::get(),
distance_max_depth: parameters::MaxRefereeDistance::get(),
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(),
}
}
if std::env::var("DUNITER_GENESIS_CONFIG").is_ok() {
super::gen_genesis_data::generate_genesis_data(
|genesis_data| {
ChainSpec::from_genesis(
// Name
"Development",
// ID
"gdev",
sc_service::ChainType::Development,
move || genesis_data_to_gdev_genesis_conf(genesis_data.clone(), wasm_binary),
// 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
/// 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())?,
None,
)
},
Some(get_authority_keys_from_seed("Alice").encode()),
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_smith_memberships_expire_on: 100_000,
}),
.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()),
)
} else {
Ok(ChainSpec::from_genesis(
// Name
"Development",
// ID
"gdev",
ChainType::Development,
move || {
gen_genesis_for_local_chain(
wasm_binary,
// Initial authorities len
1,
// Initial smiths members len
3,
// Inital identities len
4,
// Sudo account
get_account_id_from_seed::<sr25519::Public>("Alice"),
true,
)
},
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
None,
//Fork ID
None,
// Properties
Some(
.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,
......@@ -141,94 +123,89 @@ pub fn development_chain_spec() -> Result<ChainSpec, String> {
.as_object()
.expect("must be a map")
.clone(),
),
// Extensions
None,
))
}
)
.build())
}
pub fn gen_live_conf() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "wasm not available".to_string())?;
// === client specifications ===
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,
//Fork ID
None,
// Properties
Some(
serde_json::json!({
"tokenDecimals": TOKEN_DECIMALS,
"tokenSymbol": TOKEN_SYMBOL,
})
.as_object()
.expect("must be a map")
.clone(),
),
// Extensions
/// 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())?,
None,
)
},
.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,
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_smith_memberships_expire_on: 100_000,
}),
)
.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> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "wasm not available".to_string())?;
Ok(ChainSpec::from_genesis(
// Name
"Ğdev Local Testnet",
// ID
"gdev_local",
ChainType::Local,
move || {
gen_genesis_for_local_chain(
wasm_binary,
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"),
true,
get_parameters,
)
},
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
None,
//Fork ID
None,
// Properties
Some(
.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,
......@@ -236,203 +213,17 @@ pub fn local_testnet_config(
.as_object()
.expect("must be a map")
.clone(),
),
// Extensions
None,
))
}
fn gen_genesis_for_local_chain(
wasm_binary: &[u8],
initial_authorities_len: usize,
initial_smiths_len: usize,
initial_identities_len: usize,
root_key: AccountId,
_enable_println: bool,
) -> gdev_runtime::GenesisConfig {
assert!(initial_identities_len <= 6);
assert!(initial_smiths_len <= initial_identities_len);
assert!(initial_authorities_len <= initial_smiths_len);
let babe_epoch_duration = get_env_u32("DUNITER_BABE_EPOCH_DURATION", 30) as u64;
let cert_validity_period = get_env_u32("DUNITER_CERT_VALIDITY_PERIOD", 1_000);
let first_ud = 1_000;
let membership_period = get_env_u32("DUNITER_MEMBERSHIP_PERIOD", 1_000);
let smith_cert_validity_period = get_env_u32("DUNITER_SMITH_CERT_VALIDITY_PERIOD", 1_000);
let smith_membership_period = get_env_u32("DUNITER_SMITH_MEMBERSHIP_PERIOD", 1_000);
let ud_creation_period = get_env_u32("DUNITER_UD_CREATION_PERIOD", 10);
let ud_reeval_period = get_env_u32("DUNITER_UD_REEEVAL_PERIOD", 200);
let initial_smiths = (0..initial_smiths_len)
.map(|i| get_authority_keys_from_seed(NAMES[i]))
.collect::<Vec<AuthorityKeys>>();
let initial_identities = (0..initial_identities_len)
.map(|i| {
(
IdtyName::from(NAMES[i]),
get_account_id_from_seed::<sr25519::Public>(NAMES[i]),
)
})
.collect::<BTreeMap<IdtyName, AccountId>>();
gdev_runtime::GenesisConfig {
system: SystemConfig {
// Add Wasm runtime to storage.
code: wasm_binary.to_vec(),
},
account: AccountConfig {
accounts: initial_identities
.iter()
.enumerate()
.map(|(i, (_, owner_key))| {
(
owner_key.clone(),
GenesisAccountData {
random_id: H256(blake2_256(&(i as u32, owner_key).encode())),
balance: first_ud,
is_identity: true,
},
)
})
.collect(),
},
parameters: ParametersConfig {
parameters: GenesisParameters {
babe_epoch_duration,
cert_period: 15,
cert_max_by_issuer: 10,
cert_min_received_cert_to_issue_cert: 2,
cert_validity_period,
idty_confirm_period: 40,
idty_creation_period: 50,
membership_period,
pending_membership_period: 500,
ud_creation_period,
ud_reeval_period,
smith_cert_period: 15,
smith_cert_max_by_issuer: 8,
smith_cert_min_received_cert_to_issue_cert: 2,
smith_cert_validity_period: 1_000,
smith_membership_period,
smith_pending_membership_period: 500,
smiths_wot_first_cert_issuable_on: 20,
smiths_wot_min_cert_for_membership: 2,
wot_first_cert_issuable_on: 20,
wot_min_cert_for_create_idty_right: 2,
wot_min_cert_for_membership: 2,
},
},
authority_discovery: Default::default(),
authority_members: AuthorityMembersConfig {
initial_authorities: initial_smiths
.iter()
.enumerate()
.map(|(i, keys)| (i as u32 + 1, (keys.0.clone(), i < initial_authorities_len)))
.collect(),
},
balances: BalancesConfig {
balances: Default::default(),
},
babe: BabeConfig {
authorities: Vec::with_capacity(0),
epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG),
},
grandpa: Default::default(),
im_online: Default::default(),
session: SessionConfig {
keys: initial_smiths
.iter()
.map(|x| {
(
x.0.clone(),
x.0.clone(),
session_keys(x.1.clone(), x.2.clone(), x.3.clone(), x.4.clone()),
)
})
.collect::<Vec<_>>(),
},
sudo: SudoConfig {
// Assign network admin rights.
key: Some(root_key),
},
technical_committee: TechnicalCommitteeConfig {
members: initial_smiths
.iter()
.map(|x| x.0.clone())
.collect::<Vec<_>>(),
..Default::default()
},
identity: IdentityConfig {
identities: initial_identities
.iter()
.enumerate()
.map(|(i, (name, owner_key))| GenesisIdty {
index: i as u32 + 1,
name: name.clone(),
value: IdtyValue {
data: IdtyData::new(),
next_creatable_identity_on: Default::default(),
old_owner_key: None,
owner_key: owner_key.clone(),
removable_on: 0,
status: IdtyStatus::Validated,
},
})
.collect(),
},
membership: MembershipConfig {
memberships: (1..=initial_identities.len())
.map(|i| (i as u32, MembershipData { expire_on: 0 }))
.collect(),
},
cert: CertConfig {
apply_cert_period_at_genesis: false,
certs_by_receiver: clique_wot(initial_identities.len(), cert_validity_period),
},
smiths_membership: SmithsMembershipConfig {
memberships: (1..=initial_smiths_len)
.map(|i| (i as u32, MembershipData { expire_on: 0 }))
.collect(),
},
smiths_cert: SmithsCertConfig {
apply_cert_period_at_genesis: false,
certs_by_receiver: clique_wot(initial_smiths_len, smith_cert_validity_period),
},
universal_dividend: UniversalDividendConfig {
first_reeval: 100,
first_ud,
initial_monetary_mass: initial_identities_len as u64 * first_ud,
},
treasury: Default::default(),
}
}
fn get_env_u32(env_var_name: &'static str, default_value: u32) -> u32 {
std::env::var(env_var_name)
.map_or(Ok(default_value), |s| s.parse())
.unwrap_or_else(|_| panic!("{} must be a number", env_var_name))
}
fn session_keys(
babe: BabeId,
grandpa: GrandpaId,
im_online: ImOnlineId,
authority_discovery: AuthorityDiscoveryId,
) -> SessionKeys {
SessionKeys {
babe,
grandpa,
im_online,
authority_discovery,
}
.build())
}
/// custom genesis
fn genesis_data_to_gdev_genesis_conf(
genesis_data: super::gen_genesis_data::GenesisData<GenesisParameters, SessionKeys>,
wasm_binary: &[u8],
) -> gdev_runtime::GenesisConfig {
) -> serde_json::Value {
let super::gen_genesis_data::GenesisData {
accounts,
treasury_balance,
certs_by_receiver,
first_ud,
first_ud_reeval,
......@@ -441,77 +232,136 @@ fn genesis_data_to_gdev_genesis_conf(
initial_monetary_mass,
memberships,
parameters,
common_parameters: _,
session_keys_map,
smiths_certs_by_receiver,
smiths_memberships,
initial_smiths,
sudo_key,
technical_committee_members,
ud,
} = genesis_data;
gdev_runtime::GenesisConfig {
system: SystemConfig {
// Add Wasm runtime to storage.
code: wasm_binary.to_vec(),
serde_json::json!({
"account": {
"accounts": accounts,
"treasuryBalance": treasury_balance,
},
account: AccountConfig { accounts },
parameters: ParametersConfig { parameters },
authority_discovery: Default::default(),
authority_members: AuthorityMembersConfig {
initial_authorities,
"parameters": {
"parameters": parameters.expect("mandatory for GDev"),
},
"authorityMembers": {
"initialAuthorities": initial_authorities,
},
balances: Default::default(),
babe: BabeConfig {
authorities: Vec::with_capacity(0),
epoch_config: Some(common_runtime::constants::BABE_GENESIS_EPOCH_CONFIG),
"balances": {
"totalIssuance": initial_monetary_mass,
},
grandpa: Default::default(),
im_online: Default::default(),
session: SessionConfig {
keys: session_keys_map
"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: SudoConfig { key: sudo_key },
technical_committee: TechnicalCommitteeConfig {
members: technical_committee_members,
..Default::default()
"sudo": { "key": sudo_key },
"technicalCommittee": {
"members": technical_committee_members,
},
"quota": {
"identities": identities.iter().map(|i| i.idty_index).collect::<Vec<_>>(),
},
identity: IdentityConfig {
identities: identities
"identity": {
"identities": identities
.into_iter()
.enumerate()
.map(|(i, (name, pubkey))| common_runtime::GenesisIdty {
index: i as u32 + 1,
.map(
|GenesisIdentity {
idty_index,
name,
owner_key,
status,
// at this point next_scheduled takes status into account
// so is null for member account
next_scheduled,
}| GenesisIdty {
index: idty_index,
name: common_runtime::IdtyName::from(name.as_str()),
value: common_runtime::IdtyValue {
data: IdtyData::new(),
data: IdtyData {
first_eligible_ud: match status {
// Only members are eligible to UD.
// The first claimable UD has the minimum index (1).
common_runtime::IdtyStatus::Member => gdev_runtime::pallet_universal_dividend::FirstEligibleUd::min(),
_ => gdev_runtime::pallet_universal_dividend::FirstEligibleUd(None),
}
},
next_creatable_identity_on: 0,
old_owner_key: None,
owner_key: pubkey,
removable_on: 0,
status: IdtyStatus::Validated,
owner_key,
next_scheduled,
status,
},
})
.collect(),
},
cert: CertConfig {
apply_cert_period_at_genesis: true,
certs_by_receiver,
},
membership: MembershipConfig { memberships },
smiths_cert: SmithsCertConfig {
apply_cert_period_at_genesis: true,
certs_by_receiver: smiths_certs_by_receiver,
)
.collect::<Vec<GenesisIdty<gdev_runtime::Runtime>>>(),
},
smiths_membership: SmithsMembershipConfig {
memberships: smiths_memberships,
"certification": {
"applyCertPeriodAtGenesis": false,
"certsByReceiver": certs_by_receiver,
},
universal_dividend: UniversalDividendConfig {
first_reeval: first_ud_reeval,
first_ud,
initial_monetary_mass,
"membership": { "memberships": memberships },
"smithMembers": { "initialSmiths": initial_smiths},
"universalDividend": {
"firstReeval": first_ud_reeval,
"firstUd": first_ud,
"initialMonetaryMass": initial_monetary_mass,
"ud": ud,
},
treasury: Default::default(),
})
}
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,
membership_renewal_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()))
}
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use common_runtime::*;
#![allow(unused_imports)]
#![allow(dead_code)]
use crate::chain_spec::{get_account_id_from_seed, get_from_seed, AccountPublic};
use common_runtime::{
constants::{DAYS, MILLISECS_PER_BLOCK},
*,
};
use log::{error, warn};
use num_format::{Locale, ToFormattedString};
use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use sp_core::{blake2_256, Decode, Encode, H256};
use std::collections::BTreeMap;
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use sp_consensus_babe::AuthorityId as BabeId;
use sp_consensus_grandpa::AuthorityId as GrandpaId;
use sp_core::{crypto::AccountId32, ed25519, sr25519, Decode, Encode};
use sp_runtime::{
traits::{IdentifyAccount, Verify},
MultiSignature, Perbill,
};
use std::{
collections::{BTreeMap, HashMap},
fmt::{Display, Formatter},
ops::{Add, Sub},
};
type MembershipData = sp_membership::MembershipData<u32>;
static G1_DUNITER_V1_EXISTENTIAL_DEPOSIT: u64 = 100;
static G1_DUNITER_V1_DECIMALS: usize = 2;
static G1_DUNITER_V1_DT: u64 = 86400;
static G1_DUNITER_V1_SIGPERIOD: u32 = 432000;
static G1_DUNITER_V1_SIGSTOCK: u32 = 100;
static G1_DUNITER_V1_SIGVALIDITY: u32 = 63115200;
static G1_DUNITER_V1_SIGQTY: u32 = 5;
static G1_DUNITER_V1_MSVALIDITY: u32 = 31557600;
static G1_DUNITER_V1_STEPMAX: u32 = 5;
static G1_DUNITER_V1_DTREEVAL: u64 = 15778800;
// Warning: Duniter V1 "days" are expressed in seconds, while V2 are expressed in blocks
static DUNITER_V1_DAYS: u32 = 3600 * 24;
// Not used in V2S
// static G1_DUNITER_V1_SIGWINDOW: u32 = 5259600; // no more pool
// static G1_DUNITER_V1_IDTYWINDOW: u32 = 5259600; // no more pool
// static G1_DUNITER_V1_MSWINDOW: u32 = 5259600; // no more pool
// static G1_DUNITER_V1_PERCENTROT: f32 = 0.67; // no more PoW
// static G1_DUNITER_V1_MEDIANTIMEBLOCKS: u32 = 24; // no more PoW
// static G1_DUNITER_V1_AVGGENTIME: u32 = 300; // no more PoW
// static G1_DUNITER_V1_DTDIFFEVAL: u32 = 12; // no more PoW
// static G1_DUNITER_V1_UD0: u32 = 1000; // new value
// static G1_DUNITER_V1_MSPERIOD: u32 = 5259600; // no more used
// static G1_DUNITER_V1_UDTIME0: u32 = 1488970800; // duniter v1 specific
// static G1_DUNITER_V1_UDREEVALTIME0: u32 = 1490094000; // duniter v1 specific
const EXISTENTIAL_DEPOSIT: u64 = 100;
type MembershipData = sp_membership::MembershipData<u32>;
#[derive(Clone)]
pub struct GenesisData<Parameters: DeserializeOwned, SessionKeys: Decode> {
pub accounts: BTreeMap<AccountId, GenesisAccountData<u64>>,
pub certs_by_receiver: BTreeMap<u32, BTreeMap<u32, u32>>,
pub first_ud: u64,
pub first_ud_reeval: u32,
pub identities: Vec<(String, AccountId)>,
pub accounts: BTreeMap<AccountId, GenesisAccountData<u64, u32>>,
pub treasury_balance: u64,
pub certs_by_receiver: BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
pub first_ud: Option<u64>,
pub first_ud_reeval: Option<u64>,
pub identities: Vec<GenesisIdentity>,
pub initial_authorities: BTreeMap<u32, (AccountId, bool)>,
pub initial_monetary_mass: u64,
pub memberships: BTreeMap<u32, MembershipData>,
pub parameters: Parameters,
pub parameters: Option<Parameters>,
pub common_parameters: Option<CommonParameters>,
pub session_keys_map: BTreeMap<AccountId, SessionKeys>,
pub smiths_certs_by_receiver: BTreeMap<u32, BTreeMap<u32, u32>>,
pub smiths_memberships: BTreeMap<u32, MembershipData>,
pub initial_smiths: BTreeMap<u32, (bool, Vec<u32>)>,
pub sudo_key: Option<AccountId>,
pub technical_committee_members: Vec<AccountId>,
pub ud: u64,
}
#[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_smith_memberships_expire_on: u32,
#[derive(Deserialize, Serialize)]
struct BlockV1 {
number: u32,
#[serde(rename = "medianTime")]
median_time: u64,
}
#[derive(Clone)]
pub struct GenesisIdentity {
pub idty_index: u32,
pub name: String,
pub owner_key: AccountId,
pub status: IdtyStatus,
pub next_scheduled: u32,
}
#[derive(Deserialize, Serialize)]
struct GenesisConfig<Parameters> {
first_ud: u64,
first_ud_reeval: u32,
identities: BTreeMap<String, Idty>,
struct GenesisInput<Parameters> {
first_ud: Option<u64>,
first_ud_reeval: Option<u64>,
#[serde(default)]
parameters: Parameters,
parameters: Option<Parameters>,
#[serde(rename = "smiths")]
smith_identities: BTreeMap<String, SmithData>,
smith_identities: Option<BTreeMap<String, RawSmith>>,
clique_smiths: Option<Vec<CliqueSmith>>,
sudo_key: Option<AccountId>,
treasury_funder_pubkey: Option<PubkeyV1>,
treasury_funder_address: Option<AccountId>,
technical_committee: Vec<String>,
#[serde(default)]
ud: u64,
}
#[derive(Deserialize, Serialize)]
pub struct GenesisIndexerExport {
first_ud: Option<u64>,
first_ud_reeval: Option<u64>,
genesis_parameters: CommonParameters,
identities: HashMap<String, IdentityV2>,
smiths: BTreeMap<String, SmithData>,
sudo_key: Option<AccountId>,
technical_committee: Vec<String>,
ud: u64,
wallets: BTreeMap<AccountId, u64>,
}
#[derive(Clone, Deserialize, Serialize)]
struct Idty {
#[derive(Deserialize, Serialize)]
struct TransactionV1 {
issuer: PubkeyV1,
amount: String,
written_time: Option<u32>,
comment: String,
}
#[derive(Deserialize, Serialize)]
struct TransactionV2 {
issuer: AccountId,
amount: String,
written_time: Option<u32>,
comment: String,
}
#[derive(Deserialize, Serialize)]
struct GenesisMigrationData {
initial_monetary_mass: u64,
current_block: BlockV1,
identities: BTreeMap<String, IdentityV1>,
#[serde(default)]
wallets: BTreeMap<PubkeyV1, u64>,
}
// Base58 encoded Ed25519 public key
#[derive(Clone, Deserialize, Serialize, Ord, PartialOrd, Eq, PartialEq)]
struct PubkeyV1(String);
// Timestamp
#[derive(Clone, Deserialize, Serialize)]
struct TimestampV1(u32);
impl Display for PubkeyV1 {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
/// identities
#[derive(Clone, Deserialize, Serialize)]
struct IdentityV1 {
/// indentity index matching the order of appearance in the Ǧ1v1 blockchain
index: u32,
/// Base58 public key in Ğ1v1
owner_pubkey: Option<PubkeyV1>,
/// Ğ1v2 address
owner_address: Option<AccountId>,
/// Optional Base58 public key in Ğ1v1
old_owner_key: Option<PubkeyV1>,
/// timestamp at which the membership is set to expire (0 for expired members)
membership_expire_on: TimestampV1,
/// timestamp at which the identity would be set as revoked (value in the past for already revoked identities)
membership_revokes_on: TimestampV1,
/// whether the identity is revoked (manually or automatically)
revoked: bool,
/// balance of the account of this identity
balance: u64,
/// certs received with their expiration timestamp
certs_received: HashMap<String, TimestampV1>,
}
/// identities
// note: having membership_expire_on and identity_revoke_on is tricky
// because the model of identity does not take into account the status
// see this forum topic for a suggestion
// https://forum.duniter.org/t/proposition-pour-supprimer-la-pallet-membership/11918
#[derive(Clone, Deserialize, Serialize)]
struct IdentityV2 {
/// indentity index matching the order of appearance in the Ǧ1v1 blockchain
index: u32,
/// ss58 address in gx network
owner_key: AccountId,
/// block at which the membership is set to expire (0 for expired members)
membership_expire_on: u32,
/// block at which the identity should be revoked (value in the past for already revoked identities)
identity_revoke_on: u32,
/// whether the identity is revoked (manually or automatically)
revoked: bool,
/// balance of the account of this identity
balance: u64,
/// certs received with their expiration block
certs_received: HashMap<String, u32>,
}
#[derive(Clone, Deserialize, Serialize)]
struct RawSmith {
name: String,
/// optional pre-set session keys (at least for the smith bootstraping the blockchain)
session_keys: Option<String>,
#[serde(default)]
certs: Vec<String>,
#[serde(rename = "expire_on")]
membership_expire_on: Option<u64>,
pubkey: AccountId,
certs_received: Vec<String>,
}
#[derive(Clone, Deserialize, Serialize)]
struct SmithData {
idty_index: u32,
name: String,
account: AccountId,
/// optional pre-set session keys (at least for the smith bootstraping the blockchain)
session_keys: Option<String>,
#[serde(default)]
certs: Vec<String>,
certs_received: Vec<String>,
}
#[derive(Clone, Deserialize, Serialize)]
struct CliqueSmith {
name: String,
session_keys: Option<String>,
}
struct SmithMembers<SK: Decode> {
initial_smiths_wot: BTreeMap<u32, (bool, Vec<u32>)>,
session_keys_map:
BTreeMap<<<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId, SK>,
}
struct GenesisInfo<'a> {
genesis_timestamp: u64,
accounts: &'a BTreeMap<AccountId32, GenesisAccountData<u64, u32>>,
genesis_data_wallets_count: &'a usize,
inactive_identities: &'a HashMap<u32, (String, IdtyStatus)>,
identities: &'a Vec<GenesisIdentity>,
identity_index: &'a HashMap<u32, String>,
initial_smiths: &'a BTreeMap<u32, (bool, Vec<u32>)>,
counter_online_authorities: &'a u32,
counter_cert: &'a u32,
counter_smith_cert: &'a u32,
technical_committee_members: &'a Vec<AccountId32>,
common_parameters: &'a CommonParameters,
}
pub fn generate_genesis_data<CS, P, SK, F>(
f: F,
maybe_force_authority: Option<Vec<u8>>,
params_applied_at_genesis: Option<ParamsAppliedAtGenesis>,
) -> Result<CS, String>
/// generate genesis data from a json file
/// takes DUNITER_GENESIS_CONFIG env var if present or duniter-gen-conf.json by default
// this function is targeting dev chainspecs, do not use in production network
pub fn generate_genesis_data<P, SK, SessionKeys: Encode, SKP>(
config_file_path: String,
get_common_parameters: fn(&Option<P>) -> CommonParameters,
maybe_force_authority: Option<String>,
) -> Result<GenesisData<P, SK>, String>
where
P: Default + DeserializeOwned,
SK: Decode,
F: Fn(GenesisData<P, SK>) -> CS,
SKP: SessionKeysProvider<SessionKeys>,
{
let ParamsAppliedAtGenesis {
genesis_certs_expire_on,
genesis_smith_certs_expire_on,
genesis_memberships_expire_on,
genesis_smith_memberships_expire_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_timestamp: u64 = get_genesis_timestamp()?;
let genesis_config = serde_json::from_slice(&bytes)
.map_err(|e| format!("Error parsing gen conf file: {}", e))?;
let GenesisConfig {
// Per network input
let GenesisInput {
sudo_key,
treasury_funder_pubkey,
treasury_funder_address,
first_ud,
first_ud_reeval,
parameters,
identities,
smith_identities,
clique_smiths,
technical_committee,
wallets,
} = genesis_config;
ud,
} = get_genesis_input::<P>(
std::env::var("DUNITER_GENESIS_CONFIG").unwrap_or_else(|_| config_file_path.to_owned()),
)?;
// Per network parameters
let common_parameters = get_common_parameters(&parameters);
// Per network smiths (without link to an account yet — identified by their pseudonym)
let mut smiths = build_smiths_wot(&clique_smiths, smith_identities)?;
// G1 data migration (common to all networks)
let mut genesis_data = get_genesis_migration_data()?;
check_parameters_consistency(&genesis_data.wallets, &first_ud, &first_ud_reeval, &ud)?;
check_genesis_data_and_filter_expired_certs_since_export(
&mut genesis_data,
genesis_timestamp,
&common_parameters,
);
let mut identities_v2: HashMap<String, IdentityV2> =
genesis_data_to_identities_v2(genesis_data.identities, genesis_timestamp);
check_identities_v2(&identities_v2, &common_parameters);
// MONEY AND WOT //
// MONEY AND WOT //
// declare variables for building genesis
// -------------------------------------
// track if fatal error occured, but let processing continue
let mut fatal = false;
// initial Treasury balance
let mut treasury_balance = 0;
// track identity index
let mut identity_index = HashMap::new();
// track inactive identities
let mut inactive_identities = HashMap::<u32, (String, IdtyStatus)>::new();
let mut accounts = BTreeMap::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;
// declare variables to fill in genesis
// -------------------------------------
// members of technical committee
let mut technical_committee_members: Vec<AccountId> = Vec::new();
// memberships
let mut memberships = BTreeMap::new();
let mut technical_committee_members = Vec::with_capacity(technical_committee.len());
// certifications
let mut certs_by_receiver = BTreeMap::new();
// initial authorities
let mut initial_authorities = BTreeMap::new();
//let mut total_dust = 0;
// FORCED AUTHORITY //
// If this authority is defined (most likely Alice), then it must exist in both _identities_
// and _smiths_. We create all this, for *development purposes* (used with `gdev_dev` or `gtest_dev` chains).
if let Some(authority_name) = &maybe_force_authority {
make_authority_exist::<SessionKeys, SKP>(
&mut identities_v2,
&mut smiths,
&common_parameters,
authority_name,
);
}
// SIMPLE WALLETS //
let genesis_data_wallets_count = genesis_data.wallets.len();
let (was_fatal, mut monetary_mass, mut accounts, invalid_wallets) =
v1_wallets_to_v2_accounts(genesis_data.wallets, &common_parameters);
if was_fatal {
fatal = true;
}
let mut wallet_index: u32 = 0;
for (pubkey, balance) in wallets {
wallet_index += 1;
accounts.insert(
pubkey.clone(),
GenesisAccountData {
random_id: H256(blake2_256(&(wallet_index, &pubkey).encode())),
balance,
is_identity: false,
// Technical Comittee //
// NOTE : when changing owner key, the technical committee is not changed
for name in &technical_committee {
if let Some(identity) = &identities_v2.get(name) {
technical_committee_members.push(identity.owner_key.clone());
} else {
log::error!("Identity '{}' does not exist", name);
fatal = true;
}
}
// IDENTITIES //
let (was_fatal, identities) = feed_identities(
&mut accounts,
&mut identity_index,
&mut monetary_mass,
&mut inactive_identities,
&mut memberships,
&identities_v2,
&common_parameters,
)?;
if was_fatal {
fatal = true;
}
// CERTIFICATIONS //
// counter for certifications
let (was_fatal, counter_cert) = feed_certs_by_receiver(&mut certs_by_receiver, &identities_v2);
if was_fatal {
fatal = true;
}
// SMITHS SUB-WOT //
// Authorities
if let Some(name) = &maybe_force_authority {
check_authority_exists_in_both_wots(name, &identities_v2, &smiths);
}
let smiths = decorate_smiths_with_identity(smiths, &identity_index, &identities_v2);
// counter for online authorities at genesis
let (
was_fatal,
counter_online_authorities,
counter_smith_cert,
SmithMembers {
initial_smiths_wot,
session_keys_map,
},
) = create_smith_wot(
&mut initial_authorities,
&identities_v2,
&smiths,
&clique_smiths,
)?;
if was_fatal {
fatal = true;
}
// Verify certifications coherence (can be ignored for old users)
for (idty_index, receiver_certs) in &certs_by_receiver {
if receiver_certs.len() < common_parameters.wot_min_cert_for_membership as usize {
let name = identity_index.get(idty_index).unwrap();
let identity = identities_v2.get(&(*name).clone()).unwrap();
if identity.membership_expire_on != 0 {
log::error!(
"[{}] has received only {}/{} certifications",
name,
receiver_certs.len(),
common_parameters.wot_min_cert_for_membership
);
fatal = true;
}
}
}
// Technical Comittee //
// Verify smith certifications coherence
for (idty_index, (_online, certs)) in &initial_smiths_wot {
if certs.len() < common_parameters.smith_sub_wot_min_cert_for_membership as usize {
log::error!(
"[{}] has received only {}/{} smith certifications",
identity_index.get(idty_index).unwrap(),
certs.len(),
common_parameters.smith_sub_wot_min_cert_for_membership
);
fatal = true;
}
}
for idty_name in technical_committee {
if let Some(identity) = identities.get(&idty_name) {
technical_committee_members.push(identity.pubkey.clone());
} else {
return Err(format!("Identity '{}' not exist", idty_name));
// check number of online authorities
if maybe_force_authority.is_none() && 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!(
"actual monetary_mass ({}) and initial_monetary_mass ({}) do not match",
monetary_mass.to_formatted_string(&Locale::en),
genesis_data
.initial_monetary_mass
.to_formatted_string(&Locale::en)
);
if monetary_mass > genesis_data.initial_monetary_mass {
log::error!("money has been created");
fatal = true;
}
}
// IDENTITIES //
// treasury balance must come from existing money
let treasury_funder: AccountId = match (treasury_funder_address, treasury_funder_pubkey) {
(Some(address), None) => address,
(None, Some(pubkey)) => {
v1_pubkey_to_account_id(pubkey).expect("treasury founder must have a valid public key")
}
_ => panic!("One of treasury_funder_address or treasury_funder_pubkey must be set"),
};
if let Some(existing_account) = accounts.get_mut(&treasury_funder) {
existing_account.balance = existing_account
.balance
.checked_sub(common_parameters.balances_existential_deposit)
.expect("should have enough money to fund Treasury");
treasury_balance = common_parameters.balances_existential_deposit;
}
if treasury_balance < common_parameters.balances_existential_deposit {
log::error!(
"Treasury balance {} is inferior to existential deposit {}",
treasury_balance,
common_parameters.balances_existential_deposit
);
fatal = true;
}
for (idty_name, identity) in &identities {
if !validate_idty_name(idty_name) {
return Err(format!("Identity name '{}' is invalid", &idty_name));
smiths.iter().for_each(|smith| {
log::info!(
"[Smith] {} ({} - {})",
smith.idty_index,
smith.account,
smith.name.clone()
);
});
initial_authorities
.iter()
.for_each(|(index, (authority_account, online))| {
log::info!(
"[Authority] {} : {} ({} - {})",
index,
if *online { "online" } else { "offline" },
authority_account,
identity_index
.get(index)
.expect("authority should have an identity")
);
});
let genesis_info = GenesisInfo {
genesis_timestamp,
accounts: &accounts,
genesis_data_wallets_count: &genesis_data_wallets_count,
identities: &identities,
inactive_identities: &inactive_identities,
identity_index: &identity_index,
initial_smiths: &initial_smiths_wot,
counter_online_authorities: &counter_online_authorities,
counter_cert: &counter_cert,
counter_smith_cert: &counter_smith_cert,
technical_committee_members: &technical_committee_members,
common_parameters: &common_parameters,
};
dump_genesis_info(genesis_info);
if parameters.is_some() {
let g1_duniter_v1_c = 0.0488;
let g1_duniter_v1_xpercent: Perbill = Perbill::from_float(0.8);
let c = f32::sqrt(
common_parameters
.universal_dividend_square_money_growth_rate
.deconstruct() as f32
/ 1_000_000_000f32,
);
// static parameters (GTest or G1)
if common_parameters.decimals != G1_DUNITER_V1_DECIMALS {
warn!(
"parameter `decimals` value ({}) is different from Ğ1 value ({})",
common_parameters.decimals, G1_DUNITER_V1_DECIMALS
)
}
if common_parameters.balances_existential_deposit != G1_DUNITER_V1_EXISTENTIAL_DEPOSIT {
warn!(
"parameter `existential_deposit` value ({}) is different from Ğ1 value ({})",
common_parameters.balances_existential_deposit, G1_DUNITER_V1_EXISTENTIAL_DEPOSIT
)
}
if common_parameters.membership_membership_period / DAYS
!= G1_DUNITER_V1_MSVALIDITY / DUNITER_V1_DAYS
{
warn!(
"parameter `membership_period` ({} days) is different from Ğ1's ({} days)",
common_parameters.membership_membership_period as f32 / DAYS as f32,
G1_DUNITER_V1_MSVALIDITY as f32 / DUNITER_V1_DAYS as f32
)
}
if common_parameters.membership_membership_renewal_period / DAYS
!= G1_DUNITER_V1_MSVALIDITY / DUNITER_V1_DAYS
{
warn!(
"parameter `membership_renewal_period` ({} days) is different from Ğ1's ({} days)",
common_parameters.membership_membership_renewal_period as f32 / DAYS as f32,
G1_DUNITER_V1_MSVALIDITY as f32 / DUNITER_V1_DAYS as f32
)
}
if common_parameters.cert_cert_period / DAYS != G1_DUNITER_V1_SIGPERIOD / DUNITER_V1_DAYS {
warn!(
"parameter `cert_period` ({} days) is different from Ğ1's ({} days)",
common_parameters.cert_cert_period as f32 / DAYS as f32,
G1_DUNITER_V1_SIGPERIOD as f32 / DUNITER_V1_DAYS as f32
)
}
if common_parameters.cert_validity_period / DAYS
!= G1_DUNITER_V1_SIGVALIDITY / DUNITER_V1_DAYS
{
warn!(
"parameter `cert_validity_period` ({} days) is different from Ğ1's ({} days)",
common_parameters.cert_validity_period as f32 / DAYS as f32,
G1_DUNITER_V1_SIGVALIDITY as f32 / DUNITER_V1_DAYS as f32
)
}
if common_parameters.wot_min_cert_for_membership != G1_DUNITER_V1_SIGQTY {
warn!(
"parameter `min_cert` value ({}) is different from Ğ1 value ({})",
common_parameters.wot_min_cert_for_membership, G1_DUNITER_V1_SIGQTY
)
}
if common_parameters.cert_max_by_issuer != G1_DUNITER_V1_SIGSTOCK {
warn!(
"parameter `cert_max_by_issuer` value ({}) is different from Ğ1 value ({})",
common_parameters.cert_max_by_issuer, G1_DUNITER_V1_SIGSTOCK
)
}
if c != g1_duniter_v1_c {
warn!(
"parameter `c` value ({}) is different from Ğ1 value ({})",
c, g1_duniter_v1_c
)
}
if common_parameters.universal_dividend_ud_creation_period as f32 / DAYS as f32
!= G1_DUNITER_V1_DT as f32 / DUNITER_V1_DAYS as f32
{
warn!(
"parameter `ud_creation_period` value ({} days) is different from Ğ1 value ({} days)",
common_parameters.universal_dividend_ud_creation_period as f32 / DAYS as f32, G1_DUNITER_V1_DT as f32 / DUNITER_V1_DAYS as f32
)
}
if common_parameters.universal_dividend_ud_reeval_period as f32 / DAYS as f32
!= G1_DUNITER_V1_DTREEVAL as f32 / DUNITER_V1_DAYS as f32
{
warn!(
"parameter `ud_reeval_period` value ({} days) is different from Ğ1 value ({} days)",
common_parameters.universal_dividend_ud_reeval_period as f32 / DAYS as f32,
G1_DUNITER_V1_DTREEVAL as f32 / DUNITER_V1_DAYS as f32
)
}
if common_parameters.distance_min_accessible_referees != g1_duniter_v1_xpercent {
warn!(
"parameter `distance_min_accessible_referees` value ({}) is different from Ğ1 value ({})",
format!("{:?}", common_parameters.distance_min_accessible_referees), format!("{:?}", g1_duniter_v1_xpercent)
)
}
if common_parameters.distance_max_depth != G1_DUNITER_V1_STEPMAX {
warn!(
"parameter `max_depth` value ({}) is different from Ğ1 value ({})",
common_parameters.distance_max_depth, G1_DUNITER_V1_STEPMAX
)
}
let count_uds = common_parameters.universal_dividend_ud_reeval_period
/ common_parameters.universal_dividend_ud_creation_period;
if count_uds == 0 {
error!(
"the `ud_reeval_period / ud_creation_period` is zero ({} days/{} days)",
common_parameters.universal_dividend_ud_reeval_period / DAYS as u64,
common_parameters.universal_dividend_ud_creation_period / DAYS as u64
);
fatal = true;
}
}
// Money
let balance = if identity.balance >= EXISTENTIAL_DEPOSIT {
identity.balance
// some more checks
assert_eq!(
identities.len() - inactive_identities.len(),
memberships.len()
);
assert_eq!(initial_smiths_wot.len(), initial_authorities.len());
assert_eq!(initial_smiths_wot.len(), session_keys_map.len());
assert_eq!(identity_index.len(), identities.len());
assert_eq!(
accounts.len(),
identity_index.len() + genesis_data_wallets_count.sub(invalid_wallets)
);
smiths_and_technical_committee_checks(&inactive_identities, &technical_committee, &smiths);
// 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!();
}
// Indexer output
// handled by indexer directly from py-g1-migrator output
let genesis_data = GenesisData {
accounts,
treasury_balance,
certs_by_receiver,
first_ud,
first_ud_reeval,
identities,
initial_authorities,
initial_monetary_mass: genesis_data.initial_monetary_mass,
memberships,
parameters,
common_parameters: Some(common_parameters),
session_keys_map,
initial_smiths: initial_smiths_wot,
sudo_key,
technical_committee_members,
ud,
};
Ok(genesis_data)
}
fn dump_genesis_info(info: GenesisInfo) {
// give genesis info
log::info!(
"prepared genesis with:
- {} as genesis timestamp
- {} accounts ({} identities, {} simple wallets)
- {} total identities ({} members, {} inactives, {} revoked)
- {} smiths
- {} initial online authorities
- {} certifications
- {} smith certifications
- {} members in technical committee",
info.genesis_timestamp,
info.accounts.len(),
info.identities.len() - info.inactive_identities.len(),
info.genesis_data_wallets_count,
info.identity_index.len(),
info.identities.len() - info.inactive_identities.len(),
info.inactive_identities
.iter()
.filter(|(_, (_, status))| *status == IdtyStatus::NotMember)
.count(),
info.inactive_identities
.iter()
.filter(|(_, (_, status))| *status == IdtyStatus::Revoked)
.count(),
info.initial_smiths.len(),
info.counter_online_authorities,
info.counter_cert,
info.counter_smith_cert,
info.technical_committee_members.len(),
);
let p = info.common_parameters.clone();
let (babe_epoch_duration, babe_epoch_duration_unit) =
get_best_unit_and_diviser_for_blocks(p.babe_epoch_duration as u32);
let (babe_expected_block_time, babe_expected_block_time_unit) =
get_best_unit_and_diviser_for_ms(p.babe_expected_block_time as f32);
let (babe_max_authorities, babe_max_authorities_unit) =
get_best_unit_and_diviser_for_participants(p.babe_max_authorities);
let (timestamp_minimum_period, timestamp_minimum_period_unit) =
get_best_unit_and_diviser_for_ms(p.timestamp_minimum_period as f32);
let (balances_existential_deposit, balances_existential_deposit_unit) =
get_best_unit_and_diviser_for_currency_units(
p.balances_existential_deposit,
p.currency_name.clone(),
);
let (authority_members_max_authorities, authority_members_max_authorities_unit) =
get_best_unit_and_diviser_for_participants(p.authority_members_max_authorities);
let (grandpa_max_authorities, grandpa_max_authorities_unit) =
get_best_unit_and_diviser_for_participants(p.grandpa_max_authorities);
let (universal_dividend_max_past_reevals, universal_dividend_max_past_reevals_unit) =
get_best_unit_and_diviser_for_equinoxes(p.universal_dividend_max_past_reevals);
let (
universal_dividend_square_money_growth_rate,
universal_dividend_square_money_growth_rate_unit,
) = get_best_unit_and_diviser_for_perbill_square(p.universal_dividend_square_money_growth_rate);
let (universal_dividend_ud_creation_period, universal_dividend_ud_creation_period_unit) =
get_best_unit_and_diviser_for_ms(p.universal_dividend_ud_creation_period as f32);
let (universal_dividend_ud_reeval_period, universal_dividend_ud_reeval_period_unit) =
get_best_unit_and_diviser_for_ms(p.universal_dividend_ud_reeval_period as f32);
let (wot_first_issuable_on, wot_first_issuable_on_unit) =
get_best_unit_and_diviser_for_blocks(p.wot_first_issuable_on);
let (wot_min_cert_for_membership, wot_min_cert_for_membership_unit) =
get_best_unit_and_diviser_for_certs(p.wot_min_cert_for_membership);
let (wot_min_cert_for_create_idty_right, wot_min_cert_for_create_idty_right_unit) =
get_best_unit_and_diviser_for_certs(p.wot_min_cert_for_create_idty_right);
let (identity_confirm_period, identity_confirm_period_unit) =
get_best_unit_and_diviser_for_blocks(p.identity_confirm_period);
let (identity_change_owner_key_period, identity_change_owner_key_period_unit) =
get_best_unit_and_diviser_for_blocks(p.identity_change_owner_key_period);
let (identity_idty_creation_period, identity_idty_creation_period_unit) =
get_best_unit_and_diviser_for_blocks(p.identity_idty_creation_period);
let (membership_membership_period, membership_membership_period_unit) =
get_best_unit_and_diviser_for_blocks(p.membership_membership_period);
let (membership_membership_renewal_period, membership_membership_renewal_period_unit) =
get_best_unit_and_diviser_for_blocks(p.membership_membership_renewal_period);
let (cert_cert_period, cert_cert_period_unit) =
get_best_unit_and_diviser_for_blocks(p.cert_cert_period);
let (cert_max_by_issuer, cert_max_by_issuer_unit) =
get_best_unit_and_diviser_for_certs(p.cert_max_by_issuer);
let (
cert_min_received_cert_to_be_able_to_issue_cert,
cert_min_received_cert_to_be_able_to_issue_cert_unit,
) = get_best_unit_and_diviser_for_certs(p.cert_min_received_cert_to_be_able_to_issue_cert);
let (cert_validity_period, cert_validity_period_unit) =
get_best_unit_and_diviser_for_blocks(p.cert_validity_period);
let (distance_min_accessible_referees, distance_min_accessible_referees_unit) =
get_best_unit_and_diviser_for_perbill(p.distance_min_accessible_referees);
let (distance_max_depth, distance_max_depth_unit) =
get_best_unit_and_diviser_for_depth(p.distance_max_depth);
let (smith_members_min_cert_for_membership, smith_members_min_cert_for_membership_unit) =
get_best_unit_and_diviser_for_certs(p.smith_sub_wot_min_cert_for_membership);
let (smith_members_max_by_issuer, smith_members_max_by_issuer_unit) =
get_best_unit_and_diviser_for_certs(p.smith_cert_max_by_issuer);
let (smith_members_inactivity_max_duration, smith_members_inactivity_max_duration_unit) =
get_best_unit_and_diviser_for_sessions(
p.smith_inactivity_max_duration,
p.babe_epoch_duration,
);
let (treasury_spend_period, treasury_spend_period_unit) =
get_best_unit_and_diviser_for_blocks(p.treasury_spend_period);
// give genesis info
log::info!(
"currency parameters:
- babe.epoch_duration: {} {}
- babe.expected_block_time: {} {}
- babe.max_authorities: {} {}
- timestamp.minimum_period: {} {}
- balances.existential_deposit: {} {}
- authority_members.max_authorities: {} {}
- grandpa.max_authorities: {} {}
- universal_dividend.max_past_reevals: {} {}
- universal_dividend.square_money_growth_rate: {} {}/equinox
- universal_dividend.ud_creation_period: {} {}
- universal_dividend.ud_reeval_period: {} {}
- wot.first_issuable_on: {} {}
- wot.min_cert_for_membership: {} {}
- wot.min_cert_for_create_idty_right: {} {}
- identity.confirm_period: {} {}
- identity.change_owner_key_period: {} {}
- identity.idty_creation_period: {} {}
- membership.membership_period: {} {}
- membership.membership_renewal_period: {} {}
- cert.cert_period: {} {}
- cert.max_by_issuer: {} {}
- cert.min_received_cert_to_be_able_to_issue_cert: {} {}
- cert.validity_period: {} {}
- distance.min_accessible_referees: {} {}
- distance.max_depth: {} {},
- smith_members.min_cert_for_membership: {} {}
- smith_members.max_by_issuer: {} {}
- smith_members.smith_inactivity_max_duration: {} {}
- treasury.spend_period: {} {}
- currency decimals: {}",
babe_epoch_duration,
babe_epoch_duration_unit,
babe_expected_block_time,
babe_expected_block_time_unit,
babe_max_authorities,
babe_max_authorities_unit,
timestamp_minimum_period,
timestamp_minimum_period_unit,
balances_existential_deposit,
balances_existential_deposit_unit,
authority_members_max_authorities,
authority_members_max_authorities_unit,
grandpa_max_authorities,
grandpa_max_authorities_unit,
universal_dividend_max_past_reevals,
universal_dividend_max_past_reevals_unit,
universal_dividend_square_money_growth_rate,
universal_dividend_square_money_growth_rate_unit,
universal_dividend_ud_creation_period,
universal_dividend_ud_creation_period_unit,
universal_dividend_ud_reeval_period,
universal_dividend_ud_reeval_period_unit,
wot_first_issuable_on,
wot_first_issuable_on_unit,
wot_min_cert_for_membership,
wot_min_cert_for_membership_unit,
wot_min_cert_for_create_idty_right,
wot_min_cert_for_create_idty_right_unit,
identity_confirm_period,
identity_confirm_period_unit,
identity_change_owner_key_period,
identity_change_owner_key_period_unit,
identity_idty_creation_period,
identity_idty_creation_period_unit,
membership_membership_period,
membership_membership_period_unit,
membership_membership_renewal_period,
membership_membership_renewal_period_unit,
cert_cert_period,
cert_cert_period_unit,
cert_max_by_issuer,
cert_max_by_issuer_unit,
cert_min_received_cert_to_be_able_to_issue_cert,
cert_min_received_cert_to_be_able_to_issue_cert_unit,
cert_validity_period,
cert_validity_period_unit,
distance_min_accessible_referees,
distance_min_accessible_referees_unit,
distance_max_depth,
distance_max_depth_unit,
smith_members_min_cert_for_membership,
smith_members_min_cert_for_membership_unit,
smith_members_max_by_issuer,
smith_members_max_by_issuer_unit,
smith_members_inactivity_max_duration,
smith_members_inactivity_max_duration_unit,
treasury_spend_period,
treasury_spend_period_unit,
info.common_parameters.decimals,
);
}
fn get_best_unit_and_diviser_for_ms(duration_in_ms: f32) -> (f32, String) {
let diviser = get_best_diviser(duration_in_ms);
let qty = duration_in_ms / diviser;
let unit = diviser_to_unit(diviser, qty);
(qty, unit)
}
fn get_best_unit_and_diviser_for_blocks(duration_in_blocks: u32) -> (f32, String) {
let duration_in_ms = duration_in_blocks as f32 * (MILLISECS_PER_BLOCK as u32) as f32;
get_best_unit_and_diviser_for_ms(duration_in_ms)
}
fn get_best_unit_and_diviser_for_sessions(
duration_in_sessions: u32,
babe_epoch_duration_in_blocks: u64,
) -> (f32, String) {
let duration_in_ms = duration_in_sessions as f32
* babe_epoch_duration_in_blocks as f32
* (MILLISECS_PER_BLOCK as u32) as f32;
get_best_unit_and_diviser_for_ms(duration_in_ms)
}
fn get_best_unit_and_diviser_for_perbill_square(qty: Perbill) -> (f32, String) {
let qty = f32::sqrt(qty.deconstruct() as f32 / 1_000_000_000f32) * 100f32;
(qty, "%".to_string())
}
fn get_best_unit_and_diviser_for_perbill(qty: Perbill) -> (f32, String) {
let qty = qty.deconstruct() as f32 / 1_000_000_000f32 * 100f32;
(qty, "%".to_string())
}
fn get_best_unit_and_diviser_for_participants(qty: u32) -> (u32, String) {
(qty, "participants".to_string())
}
fn get_best_unit_and_diviser_for_equinoxes(qty: u32) -> (u32, String) {
(qty, "equinoxes".to_string())
}
fn get_best_unit_and_diviser_for_certs(qty: u32) -> (u32, String) {
(qty, "certs".to_string())
}
fn get_best_unit_and_diviser_for_currency_units(qty: u64, currency_name: String) -> (f64, String) {
let qty = qty as f64 / 100.0;
(qty, currency_name.clone())
}
fn get_best_unit_and_diviser_for_depth(qty: u32) -> (u32, String) {
(qty, "steps".to_string())
}
fn diviser_to_unit(value_in_ms: f32, qty: f32) -> String {
let unit = if value_in_ms >= 24.0 * 3600.0 * 1000.0 {
"day".to_string()
} else if value_in_ms >= 3600.0 * 1000.0 {
"hour".to_string()
} else if value_in_ms >= 60.0 * 1000.0 {
"minute".to_string()
} else {
//total_dust += identity.balance;
0
"second".to_string()
};
let plural = if qty > 1f32 { "s" } else { "" };
format!("{}{}", unit, plural)
}
fn get_best_diviser(ms_value: f32) -> f32 {
let one_second: f32 = 1000.0;
let one_minute: f32 = one_second * 60.0;
let one_hour: f32 = one_minute * 60.0;
let one_day: f32 = one_hour * 24.0;
if ms_value >= one_day {
one_day
} else if ms_value >= one_hour {
one_hour
} else if ms_value >= one_minute {
one_minute
} else {
one_second
}
}
fn smiths_and_technical_committee_checks(
inactive_identities: &HashMap<u32, (String, IdtyStatus)>,
technical_committee: &Vec<String>,
smiths: &Vec<SmithData>,
) {
// no inactive tech comm
for tech_com_member in technical_committee {
let inactive_commitee_member = inactive_identities
.values()
.any(|(name, _)| name == tech_com_member);
if inactive_commitee_member {
panic!(
"{} is an inactive technical commitee member",
tech_com_member
);
}
}
// no inactive smith
for SmithData { name: smith, .. } in smiths {
let inactive_smiths: Vec<_> = inactive_identities
.values()
.filter(|(name, _)| name == smith)
.collect();
inactive_smiths
.iter()
.for_each(|(name, _)| log::warn!("Smith {} is inactive", name));
assert_eq!(inactive_smiths.len(), 0);
}
}
fn create_smith_wot<SK: Decode>(
initial_authorities: &mut BTreeMap<u32, (AccountId32, bool)>,
identities_v2: &HashMap<String, IdentityV2>,
smiths: &Vec<SmithData>,
clique_smiths: &Option<Vec<CliqueSmith>>,
) -> Result<(bool, u32, u32, SmithMembers<SK>), String> {
let mut fatal = false;
let mut counter_online_authorities = 0;
// counter for smith certifications
let mut counter_smith_cert = 0;
let mut smith_certs_by_receiver = BTreeMap::new();
// smith memberships
let mut session_keys_map = BTreeMap::new();
// Then create the smith WoT
for smith in smiths {
// check that smith exists
let identities_v2_clone = identities_v2.clone();
if let Some(identity) = identities_v2.get(&smith.name.clone()) {
counter_online_authorities = set_smith_session_keys_and_authority_status(
initial_authorities,
&mut session_keys_map,
&smith,
identity,
)?;
// smith certifications
counter_smith_cert += feed_smith_certs_by_receiver(
&mut smith_certs_by_receiver,
clique_smiths,
&smith,
identity,
&identities_v2_clone,
)?;
} else {
log::error!(
"Smith '{}' does not correspond to exising identity",
&smith.name
);
fatal = true;
}
}
Ok((
fatal,
counter_online_authorities,
counter_smith_cert,
SmithMembers {
initial_smiths_wot: smith_certs_by_receiver
.iter()
.map(|(k, v)| {
let is_online = initial_authorities
.iter()
.filter(|(k2, (_, online))| *k2 == k && *online)
.count()
> 0;
(*k, (is_online, v.clone()))
})
.collect(),
session_keys_map,
},
))
}
fn v1_wallets_to_v2_accounts(
wallets: BTreeMap<PubkeyV1, u64>,
common_parameters: &CommonParameters,
) -> (
bool,
u64,
BTreeMap<AccountId32, GenesisAccountData<u64, u32>>,
usize,
) {
// monetary mass for double check
let mut monetary_mass = 0u64;
// account inserted in genesis
let mut accounts: BTreeMap<AccountId, GenesisAccountData<u64, u32>> = BTreeMap::new();
let mut invalid_wallets = 0;
let mut fatal = false;
for (pubkey, balance) in wallets {
// check existential deposit
if balance < common_parameters.balances_existential_deposit {
log::error!(
"wallet {pubkey} has {balance} cǦT which is below {}",
common_parameters.balances_existential_deposit
);
fatal = true;
}
// double check the monetary mass
monetary_mass += balance;
// json prevents duplicate wallets
if let Ok(owner_key) = v1_pubkey_to_account_id(pubkey.clone()) {
accounts.insert(
identity.pubkey.clone(),
owner_key.clone(),
GenesisAccountData {
random_id: H256(blake2_256(&(idty_index, &identity.pubkey).encode())),
balance,
is_identity: true,
idty_id: None,
},
);
} else {
log::warn!("wallet {pubkey} has wrong format");
invalid_wallets = invalid_wallets.add(1);
}
}
(fatal, monetary_mass, accounts, invalid_wallets)
}
// 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;
fn check_identities_v2(
identities_v2: &HashMap<String, IdentityV2>,
common_parameters: &CommonParameters,
) {
// // Identities whose membership was lost since export
// identities_v2.iter_mut()
// .filter(|(name, i)| (i.membership_expire_on as u64) < genesis_timestamp)
// .for_each(|(name, i)| {
// log::warn!("{} membership expired since export", name);
// i.membership_expire_on = 0;
// });
// 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)
}),
},
);
// // Identities that are no more members because of a lack of certs
// identities_v2.iter_mut()
// .filter(|(name, i)| i.membership_expire_on != 0 && (i.certs_received.len() as u32) < common_parameters.min_cert)
// .for_each(|(name, i)| {
// log::warn!("{} lost membership because of lost certifications since export", name);
// i.membership_expire_on = 0;
// });
// Identity index
idty_index_of.insert(idty_name, idty_index);
idty_index += 1;
// Check that members have enough certs
identities_v2
.iter()
.filter(|(_, i)| i.membership_expire_on != 0)
.for_each(|(name, i)| {
let nb_certs = i.certs_received.len() as u32;
if nb_certs < common_parameters.wot_min_cert_for_membership {
log::warn!("{} has only {} valid certifications", name, nb_certs);
}
});
}
// CERTIFICATIONS //
fn check_genesis_data_and_filter_expired_certs_since_export(
genesis_data: &mut GenesisMigrationData,
genesis_timestamp: u64,
common_parameters: &CommonParameters,
) {
// Remove expired certs since export
genesis_data
.identities
.iter_mut()
.for_each(|(receiver, i)| {
i.certs_received.retain(|issuer, v| {
let retain = (v.0 as u64) >= genesis_timestamp;
if !retain {
log::warn!("{} -> {} cert expired since export", issuer, receiver);
}
retain
});
});
let mut certs_by_receiver = 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 receiver_certs = BTreeMap::new();
for issuer in &identity.certs {
let issuer_index = idty_index_of
.get(issuer)
.ok_or(format!("Identity '{}' not exist", issuer))?;
receiver_certs.insert(*issuer_index, genesis_certs_expire_on);
genesis_data.identities.iter_mut().for_each(|(name, i)| {
if (i.membership_expire_on.0 as u64) < genesis_timestamp {
if (i.membership_expire_on.0 as u64) >= genesis_data.current_block.median_time {
log::warn!("{} membership expired since export", name);
}
certs_by_receiver.insert(*issuer_index, receiver_certs);
i.membership_expire_on = TimestampV1(0);
}
});
// SMITHS SUB-WOT //
genesis_data.identities.iter_mut().for_each(|(name, i)| {
if i.membership_expire_on.0 != 0
&& i.certs_received.len() < common_parameters.wot_min_cert_for_membership as usize
{
i.membership_expire_on = TimestampV1(0);
log::warn!(
"{} lost membership because of lost certifications since export",
name
);
}
});
let mut initial_authorities = BTreeMap::new();
let mut online_authorities_counter = 0;
let mut session_keys_map = BTreeMap::new();
let mut smiths_memberships = BTreeMap::new();
let mut smiths_certs_by_receiver = 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))?;
if identity.balance < EXISTENTIAL_DEPOSIT {
return Err(format!(
"Identity '{}' have balance '{}' < EXISTENTIAL_DEPOSIT",
idty_name, identity.balance,
));
genesis_data.identities.iter().for_each(|(name, i)| {
if i.owner_pubkey.is_some() && i.owner_address.is_some() {
log::warn!(
"{} both has a pubkey and an address defined - address will be used",
name
);
}
if i.owner_pubkey.is_none() && i.owner_address.is_none() {
log::error!("{} neither has a pubkey and an address defined", name);
}
});
}
fn genesis_data_to_identities_v2(
genesis_identities: BTreeMap<String, IdentityV1>,
genesis_timestamp: u64,
) -> HashMap<String, IdentityV2> {
genesis_identities
.into_iter()
.map(|(name, i)| {
let legacy_account = i
.owner_pubkey
.map(|pubkey| {
v1_pubkey_to_account_id(pubkey)
.expect("a G1 identity necessarily has a valid pubkey")
})
.unwrap_or_else(|| {
i.owner_address.unwrap_or_else(|| {
panic!("neither pubkey nor address is defined for {}", name)
})
});
let owner_key = legacy_account.clone();
(
name,
IdentityV2 {
index: i.index,
owner_key,
membership_expire_on: timestamp_to_relative_blocs(
i.membership_expire_on,
genesis_timestamp,
),
identity_revoke_on: timestamp_to_relative_blocs(
i.membership_revokes_on,
genesis_timestamp,
),
revoked: i.revoked,
balance: i.balance,
certs_received: i
.certs_received
.into_iter()
.map(|(issuer, timestamp)| {
(
issuer,
timestamp_to_relative_blocs(timestamp, genesis_timestamp),
)
})
.collect(),
},
)
})
.collect()
}
// Initial authorities
if maybe_force_authority.is_some() {
if smith_data.session_keys.is_some() {
return Err("session_keys field forbidden".to_owned());
fn make_authority_exist<SessionKeys: Encode, SKP: SessionKeysProvider<SessionKeys>>(
identities_v2: &mut HashMap<String, IdentityV2>,
smiths: &mut Vec<RawSmith>,
common_parameters: &CommonParameters,
authority_name: &String,
) {
// The identity might already exist, notably: G1 "Alice" already exists
if let Some(authority) = identities_v2.get_mut(authority_name) {
// Force authority to be active
authority.membership_expire_on = common_parameters.membership_membership_period;
} else {
// Not found: we must create it
identities_v2.insert(
authority_name.clone(),
IdentityV2 {
index: (identities_v2.len() as u32 + 1),
owner_key: get_account_id_from_seed::<sr25519::Public>(authority_name),
balance: common_parameters.balances_existential_deposit,
certs_received: HashMap::new(),
// note: in this context of generating genesis identities
membership_expire_on: common_parameters.membership_membership_period,
identity_revoke_on: common_parameters.membership_membership_period,
revoked: false,
},
);
};
// Forced authority gets its required certs from first "minCert" WoT identities (fake certs)
let mut new_certs: HashMap<String, u32> = HashMap::new();
let certs_of_authority = &identities_v2.get(authority_name).unwrap().certs_received;
identities_v2
.keys()
// Identities which are not the authority and have not already certified her
.filter(|issuer| {
issuer != &authority_name
&& !certs_of_authority
.iter()
.any(|(authority_issuer, _)| issuer == &authority_issuer)
})
.take(common_parameters.wot_min_cert_for_membership as usize)
.map(String::clone)
.for_each(|issuer| {
new_certs.insert(issuer, common_parameters.cert_cert_period);
});
let authority = identities_v2
.get_mut(authority_name)
.expect("authority must exist or be created");
new_certs.into_iter().for_each(|(issuer, c)| {
authority.certs_received.insert(issuer, c);
});
let sk: SessionKeys = SKP::session_keys(&get_authority_keys_from_seed(authority_name.as_str()));
let forced_authority_session_keys = format!("0x{}", hex::encode(sk.encode()));
// Add forced authority to smiths (whether explicit smith WoT or clique)
if let Some(smith) = smiths.iter_mut().find(|s| &s.name == authority_name) {
smith.session_keys = Some(forced_authority_session_keys);
} else {
smiths.push(RawSmith {
name: authority_name.clone(),
session_keys: Some(forced_authority_session_keys),
certs_received: vec![],
})
}
if *idty_index == 1 {
initial_authorities.insert(1, (identity.pubkey.clone(), true));
}
fn feed_identities(
accounts: &mut BTreeMap<AccountId32, GenesisAccountData<u64, u32>>,
identity_index: &mut HashMap<u32, String>,
monetary_mass: &mut u64,
inactive_identities: &mut HashMap<u32, (String, IdtyStatus)>,
memberships: &mut BTreeMap<u32, MembershipData>,
identities_v2: &HashMap<String, IdentityV2>,
common_parameters: &CommonParameters,
) -> Result<(bool, Vec<GenesisIdentity>), String> {
let mut fatal = false;
let mut identities: Vec<GenesisIdentity> = Vec::new();
for (name, identity) in identities_v2 {
// identity name
if !validate_idty_name(name) {
return Err(format!("Identity name '{}' is invalid", &name));
}
// Money
// check that wallet with same owner_key does not exist
if accounts.get(&identity.owner_key).is_some() {
log::error!(
"{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 {
balance: identity.balance,
idty_id: Some(identity.index),
},
);
// 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::error!(
"{other_name} already has identity index {} of {name}",
identity.index
);
fatal = true;
}
identity_index.insert(identity.index, name.to_owned());
let expired = identity.membership_expire_on == 0;
// only add the identity if not expired
if expired {
inactive_identities.insert(
identity.index,
(
name.clone(),
if identity.revoked {
IdtyStatus::Revoked
} else {
initial_authorities.insert(
*idty_index,
(identity.pubkey.clone(), smith_data.session_keys.is_some()),
IdtyStatus::NotMember
},
),
);
};
let status = match (identity.revoked, expired) {
(true, _) => IdtyStatus::Revoked,
(false, true) => IdtyStatus::NotMember,
(false, false) => IdtyStatus::Member,
};
identities.push(GenesisIdentity {
// N.B.: every **non-expired** identity on Genesis is considered to have:
// - removable_on: 0,
// - next_creatable_identity_on: 0,
idty_index: identity.index,
name: name.to_owned().clone(),
owner_key: identity.owner_key.clone(),
// but expired identities will just have their pseudonym reserved in the storage
status,
next_scheduled: match status {
IdtyStatus::Unconfirmed | IdtyStatus::Unvalidated => {
// these intermediary formats are disallowed in the genesis
// since they correspond to offchain v1 data
panic!("Unconfirmed or Unvalidated identity in genesis")
}
// Member identities schedule is managed by membership pallet
IdtyStatus::Member => 0,
// The identity will be scheduled for revocation after the auto-revocation period.
IdtyStatus::NotMember => common_parameters.identity_autorevocation_period,
// The identity will be scheduled for removal at the revoke block plus the deletion period.
IdtyStatus::Revoked => {
identity.identity_revoke_on + common_parameters.identity_deletion_period
}
},
});
// insert the membership data (only if not expired)
if !expired {
memberships.insert(
identity.index,
MembershipData {
// here we are using the correct expire block
expire_on: identity.membership_expire_on,
},
);
}
}
// sort the identities by index for reproducibility (should have been a vec in json)
identities.sort_unstable_by(|a, b| a.idty_index.cmp(&b.idty_index));
Ok((fatal, identities))
}
// Session keys
let session_keys_bytes = if let Some(ref session_keys) = smith_data.session_keys {
online_authorities_counter += 1;
hex::decode(&session_keys[2..])
.map_err(|_| format!("invalid session keys for idty {}", &idty_name))?
} else if let (1, Some(ref session_keys_bytes)) = (*idty_index, &maybe_force_authority) {
session_keys_bytes.clone()
fn set_smith_session_keys_and_authority_status<SK>(
initial_authorities: &mut BTreeMap<
u32,
(
<<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId,
bool,
),
>,
session_keys_map: &mut BTreeMap<
<<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId,
SK,
>,
smith: &&SmithData,
identity: &IdentityV2,
) -> Result<u32, String>
where
SK: Decode,
{
let mut counter_online_authorities = 0;
// Initial authorities and session keys
let session_keys_bytes = if let Some(declared_session_keys) = &smith.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
hex::decode(&declared_session_keys[2..])
.map_err(|_| format!("invalid session keys for idty {}", smith.name))?
} else {
// Create fake session keys (must be unique and deterministic)
let mut fake_session_keys_bytes = Vec::with_capacity(128);
// 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_session_keys_bytes.extend_from_slice(identity.pubkey.as_ref())
fake_bytes.extend_from_slice(identity.owner_key.as_ref())
}
fake_session_keys_bytes
//vec![initial_authorities.len() as u8; std::mem::size_of::<SK>()]
fake_bytes
};
// insert session keys to map
session_keys_map.insert(
identity.pubkey.clone(),
SK::decode(&mut &session_keys_bytes[..])
.map_err(|_| format!("invalid session keys for idty {}", &idty_name))?,
identity.owner_key.clone(),
SK::decode(&mut &session_keys_bytes[..]).unwrap(),
);
// Certifications
let mut receiver_certs = BTreeMap::new();
for receiver in &smith_data.certs {
let issuer_index = idty_index_of
.get(receiver)
.ok_or(format!("Identity '{}' not exist", receiver))?;
receiver_certs.insert(*issuer_index, genesis_smith_certs_expire_on);
Ok(counter_online_authorities)
}
smiths_certs_by_receiver.insert(*idty_index, receiver_certs);
// Memberships
smiths_memberships.insert(
*idty_index,
MembershipData {
expire_on: genesis_smith_memberships_expire_on,
},
fn feed_smith_certs_by_receiver(
smith_certs_by_receiver: &mut BTreeMap<u32, Vec<u32>>,
clique_smiths: &Option<Vec<CliqueSmith>>,
smith: &&SmithData,
identity: &IdentityV2,
identities_v2: &HashMap<String, IdentityV2>,
) -> Result<u32, String> {
let mut counter_smith_cert = 0;
let mut certs = Vec::<u32>::new();
if clique_smiths.is_some() {
// All initial smiths are considered to be certifying all each other
clique_smiths
.as_ref()
.unwrap()
.iter()
.filter(|other_smith| *other_smith.name.as_str() != *smith.name)
.for_each(|other_smith| {
let issuer_index = &identities_v2
.get(other_smith.name.as_str())
.unwrap_or_else(|| {
panic!("Identity '{}' does not exist", other_smith.name.as_str())
})
.index;
certs.push(*issuer_index);
counter_smith_cert += 1;
});
} else {
for issuer in &smith.certs_received {
let issuer_index = &identities_v2
.get(issuer)
.ok_or(format!("Identity '{}' does not exist", issuer))?
.index;
certs.push(*issuer_index);
counter_smith_cert += 1;
}
}
smith_certs_by_receiver.insert(identity.index, certs);
Ok(counter_smith_cert)
}
fn feed_certs_by_receiver(
certs_by_receiver: &mut BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
identities_v2: &HashMap<String, IdentityV2>,
) -> (bool, u32) {
let mut fatal = false;
let mut counter_cert = 0;
for identity in identities_v2.values() {
let mut certs = BTreeMap::new();
for (issuer, expire_on) in &identity.certs_received {
if let Some(issuer) = &identities_v2.get(issuer) {
certs.insert(issuer.index, Some(*expire_on));
counter_cert += 1;
} else {
log::error!("Identity '{}' does not exist", issuer);
fatal = true;
};
}
certs_by_receiver.insert(identity.index, certs);
}
(fatal, counter_cert)
}
fn check_authority_exists_in_both_wots(
name: &String,
identities_v2: &HashMap<String, IdentityV2>,
smiths: &[RawSmith],
) {
identities_v2
.get(name)
.ok_or(format!("Identity '{}' not exist", name))
.expect("Initial authority must have an identity");
smiths
.iter()
.find(|smith| &smith.name == name)
.expect("Forced authority must be present in smiths");
}
fn build_smiths_wot(
clique_smiths: &Option<Vec<CliqueSmith>>,
smith_identities: Option<BTreeMap<String, RawSmith>>,
) -> Result<Vec<RawSmith>, String> {
if smith_identities.is_some() && clique_smiths.is_some() {
return Err(
"'smiths' and 'clique_smiths' cannot be both defined at the same time".to_string(),
);
}
// Create a single source of smiths
let smiths = if let Some(clique) = &clique_smiths {
// From a clique
clique
.iter()
.map(|smith| RawSmith {
name: smith.name.clone(),
session_keys: smith.session_keys.clone(),
certs_received: vec![],
})
.collect::<Vec<RawSmith>>()
} else {
// From explicit smith WoT
smith_identities
.expect("existence has been tested earlier")
.into_values()
.collect::<Vec<RawSmith>>()
};
Ok(smiths)
}
if maybe_force_authority.is_none() && online_authorities_counter == 0 {
return Err("The session_keys field must be filled in for at least one smith.".to_owned());
fn decorate_smiths_with_identity(
smiths: Vec<RawSmith>,
identity_index: &HashMap<u32, String>,
identities_v2: &HashMap<String, IdentityV2>,
) -> Vec<SmithData> {
smiths
.into_iter()
.map(|smith| SmithData {
idty_index: identity_index
.iter()
.find(|(_, v)| ***v == smith.name)
.map(|(k, _)| *k)
.expect("smith must have an identity"),
account: identities_v2
.get(smith.name.as_str())
.map(|i| i.owner_key.clone())
.expect("identity must exist"),
name: smith.name,
session_keys: smith.session_keys,
certs_received: smith.certs_received,
})
.collect()
}
pub fn generate_genesis_data_for_local_chain<P, SK, SessionKeys: Encode, SKP>(
initial_authorities_len: usize,
initial_smiths_len: usize,
initial_identities_len: usize,
treasury_balance: u64,
parameters: Option<P>,
root_key: AccountId,
get_common_parameters: fn(&Option<P>) -> CommonParameters,
) -> Result<GenesisData<P, SK>, String>
where
P: Default + DeserializeOwned,
SK: Decode,
SKP: SessionKeysProvider<SessionKeys>,
{
// For benchmarking, the total length of identities should be at least MinReceivedCertToBeAbleToIssueCert + 1
assert!(initial_identities_len <= 6);
assert!(initial_smiths_len <= initial_identities_len);
assert!(initial_authorities_len <= initial_smiths_len);
let ud = 1_000;
let idty_index_start: u32 = 1;
let common_parameters = get_common_parameters(&parameters);
let names: [&str; 6] = ["Alice", "Bob", "Charlie", "Dave", "Eve", "Ferdie"];
let initial_idty_balance = 1_000 * 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>>();
let mut session_keys_map = BTreeMap::new();
initial_smiths.iter().for_each(|x| {
let session_keys_bytes = SKP::session_keys(x).encode();
let sk = SK::decode(&mut &session_keys_bytes[..])
.map_err(|_| format!("invalid session keys for idty {}", x.0.clone()))
.unwrap();
session_keys_map.insert(x.0.clone(), sk);
});
let identities: Vec<GenesisIdentity> = initial_identities
.iter()
.enumerate()
.map(|(i, (name, owner_key))| GenesisIdentity {
idty_index: i as u32 + idty_index_start,
name: String::from_utf8(name.0.clone()).unwrap(),
owner_key: owner_key.clone(),
status: IdtyStatus::Member,
next_scheduled: 0,
})
.collect();
let (certs_by_receiver, counter_cert) = clique_wot(initial_identities.len());
let accounts = initial_identities
.iter()
.enumerate()
.map(|(i, (_, owner_key))| {
(
owner_key.clone(),
GenesisAccountData {
// Should be sufficient for the overhead benchmark
balance: initial_idty_balance,
idty_id: Some(i as u32 + idty_index_start),
},
)
})
.collect();
let identity_index = identities
.iter()
.map(|i| (i.idty_index, i.name.clone()))
.collect();
let genesis_data_wallets_count = 0;
let inactive_identities: HashMap<u32, (String, IdtyStatus)> = HashMap::new();
let initial_smiths_wot: BTreeMap<u32, (bool, Vec<u32>)> = clique_smith_wot(initial_smiths_len);
let counter_smith_cert = initial_smiths_wot
.iter()
.map(|(_, (_, v))| v.len())
.sum::<usize>() as u32;
let initial_authorities: BTreeMap<
u32,
(
<<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId,
bool,
),
> = initial_smiths
.iter()
.enumerate()
.map(|(i, keys)| {
(
i as u32 + idty_index_start,
(keys.0.clone(), i < initial_authorities_len),
)
})
.collect();
let counter_online_authorities = initial_authorities
.iter()
.filter(|(_, authority)| authority.1)
.count() as u32;
let technical_committee_members = initial_smiths
.iter()
.map(|x| x.0.clone())
.collect::<Vec<_>>();
let genesis_timestamp: u64 = get_genesis_timestamp()?;
let genesis_info = GenesisInfo {
genesis_timestamp,
accounts: &accounts,
genesis_data_wallets_count: &genesis_data_wallets_count,
identities: &identities,
inactive_identities: &inactive_identities,
identity_index: &identity_index,
initial_smiths: &initial_smiths_wot,
counter_online_authorities: &counter_online_authorities,
counter_cert: &counter_cert,
counter_smith_cert: &counter_smith_cert,
technical_committee_members: &technical_committee_members,
common_parameters: &common_parameters,
};
dump_genesis_info(genesis_info);
let genesis_data = GenesisData {
accounts,
// Treasury balance is created out of nothing for local blockchain
treasury_balance,
certs_by_receiver,
first_ud,
first_ud_reeval,
identities: identities_,
first_ud: None,
first_ud_reeval: None,
identities,
initial_authorities,
initial_monetary_mass,
memberships,
initial_monetary_mass: initial_identities_len as u64 * initial_idty_balance,
// when generating data for local chain, we can set membersip expiration to membership period
memberships: (1..=initial_identities.len())
.map(|i| {
(
i as u32,
MembershipData {
expire_on: common_parameters.membership_membership_period,
},
)
})
.collect(),
parameters,
common_parameters: None,
session_keys_map,
smiths_certs_by_receiver,
smiths_memberships,
sudo_key,
initial_smiths: initial_smiths_wot,
sudo_key: Some(root_key),
technical_committee_members,
ud,
};
Ok(f(genesis_data))
Ok(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 clique_wot(
initial_identities_len: usize,
) -> (
BTreeMap<IdtyIndex, BTreeMap<IdtyIndex, Option<common_runtime::BlockNumber>>>,
u32,
) {
let mut certs_by_issuer = BTreeMap::new();
let mut count: u32 = 0;
for i in 1..=initial_identities_len {
count += initial_identities_len as u32;
certs_by_issuer.insert(
i as IdtyIndex,
(1..=initial_identities_len)
.filter_map(|j| {
if i != j {
Some((j as IdtyIndex, None))
} else {
None
}
})
.collect(),
);
}
(certs_by_issuer, count)
}
fn clique_smith_wot(initial_identities_len: usize) -> BTreeMap<IdtyIndex, (bool, Vec<IdtyIndex>)> {
let mut certs_by_issuer = BTreeMap::new();
for i in 1..=initial_identities_len {
certs_by_issuer.insert(
i as IdtyIndex,
(
true,
(1..=initial_identities_len)
.filter_map(|j| if i != j { Some(j as IdtyIndex) } else { None })
.collect(),
),
);
}
certs_by_issuer
}
fn check_parameters_consistency(
wallets: &BTreeMap<PubkeyV1, u64>,
first_ud: &Option<u64>,
first_reeval: &Option<u64>,
ud: &u64,
) -> Result<(), String> {
// No empty wallet
if let Some((account, _)) = wallets.iter().find(|(_, amount)| **amount == 0) {
return Err(format!("Wallet {} is empty", account));
}
if let (Some(first_ud), Some(first_reeval)) = (first_ud, first_reeval) {
if first_ud > first_reeval {
return Err(format!(
"`first_ud` ({}) should be lower than `first_ud_reeval` ({})",
first_ud, first_reeval
));
}
}
if *ud == 0 {
return Err("`ud` is expected to be > 0".to_owned());
}
Ok(())
}
fn get_genesis_input<P: Default + DeserializeOwned>(
config_file_path: String,
) -> Result<GenesisInput<P>, String> {
// 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(&config_file_path)
.map_err(|e| format!("Error opening gen conf file `{}`: {}", config_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 `{}`: {}", config_file_path, e))?
};
if config_file_path.ends_with(".json") {
serde_json::from_slice::<GenesisInput<P>>(&bytes)
.map_err(|e| format!("Error parsing JSON gen conf file: {}", e))
} else {
serde_yaml::from_slice::<GenesisInput<P>>(&bytes)
.map_err(|e| format!("Error parsing YAML gen conf file: {}", e))
}
}
fn get_genesis_migration_data() -> Result<GenesisMigrationData, String> {
let json_file_path = std::env::var("DUNITER_GENESIS_DATA")
.unwrap_or_else(|_| "./resources/g1-data.json".to_owned());
let file = std::fs::File::open(&json_file_path).map_err(|e| {
format!(
"Error opening gen migration file `{}`: {}",
json_file_path, e
)
})?;
let bytes = unsafe {
memmap2::Mmap::map(&file).map_err(|e| {
format!(
"Error mmaping gen migration file `{}`: {}",
json_file_path, e
)
})?
};
serde_json::from_slice::<GenesisMigrationData>(&bytes)
.map_err(|e| format!("Error parsing gen migration file: {}", e))
}
fn get_genesis_timestamp() -> Result<u64, String> {
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;
Ok(SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs())
}
}
fn validate_idty_name(name: &str) -> bool {
name.len() <= 64
}
pub type AuthorityKeys = (
AccountId,
GrandpaId,
BabeId,
ImOnlineId,
AuthorityDiscoveryId,
);
/// Because SessionKeys struct is defined by each Runtime, it cannot be constructed here.
/// Its construction must be provided.
pub trait SessionKeysProvider<SessionKeys: Encode> {
fn session_keys(keys: &AuthorityKeys) -> SessionKeys;
}
#[derive(Default, Deserialize, Serialize, Clone)]
pub struct CommonParameters {
pub babe_epoch_duration: u64,
pub babe_expected_block_time: u64,
pub babe_max_authorities: u32,
pub timestamp_minimum_period: u64,
pub balances_existential_deposit: u64,
pub authority_members_max_authorities: u32,
pub grandpa_max_authorities: u32,
pub universal_dividend_max_past_reevals: u32,
pub universal_dividend_square_money_growth_rate: Perbill,
pub universal_dividend_ud_creation_period: u64,
pub universal_dividend_ud_reeval_period: u64,
pub wot_first_issuable_on: u32,
pub wot_min_cert_for_membership: u32,
pub wot_min_cert_for_create_idty_right: u32,
pub identity_confirm_period: u32,
pub identity_change_owner_key_period: u32,
pub identity_idty_creation_period: u32,
pub identity_autorevocation_period: u32,
pub identity_deletion_period: u32,
pub membership_membership_period: u32,
pub membership_membership_renewal_period: u32,
pub cert_cert_period: u32,
pub cert_max_by_issuer: u32,
pub cert_min_received_cert_to_be_able_to_issue_cert: u32,
pub cert_validity_period: u32,
pub distance_min_accessible_referees: Perbill,
pub distance_max_depth: u32,
pub smith_sub_wot_min_cert_for_membership: u32,
pub smith_cert_max_by_issuer: u32,
pub smith_inactivity_max_duration: u32,
pub treasury_spend_period: u32,
// TODO: replace u32 by BlockNumber when appropriate
pub currency_name: String,
pub decimals: usize,
}
/// Generate an authority keys.
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),
)
}
/// Converts a Duniter v1 public key (Ed25519) to an Account Id.
/// No need to convert to address.
fn v1_pubkey_to_account_id(pubkey: PubkeyV1) -> Result<AccountId, String> {
let bytes = bs58::decode(pubkey.0)
.into_vec()
.expect("Duniter v1 pubkey should be decodable");
if bytes.len() > 32 {
return Err("Pubkey is too long".to_string());
}
let prepend = vec![0u8; 32 - &bytes.len()];
let bytes: [u8; 32] = [prepend.as_slice(), bytes.as_slice()]
.concat()
.as_slice()
.try_into()
.expect("incorrect pubkey length");
Ok(AccountPublic::from(ed25519::Public::from_raw(bytes)).into_account())
}
fn timestamp_to_relative_blocs(timestamp: TimestampV1, start: u64) -> u32 {
let diff = (timestamp.0 as u64).saturating_sub(start);
seconds_to_blocs(diff as u32)
}
/// Converts a number of seconds to a number of 6-seconds blocs
/// use lower approximation
/// example : 2 seconds will be block 0
/// example : 7 seconds will be block 1
fn seconds_to_blocs(seconds: u32) -> u32 {
seconds / 6
}
#[cfg(test)]
mod tests {
use super::*;
use sp_core::{
crypto::{Ss58AddressFormat, Ss58Codec},
ByteArray,
};
use std::str::FromStr;
#[test]
fn test_timestamp_to_relative_blocs() {
assert_eq!(seconds_to_blocs(2), 0);
assert_eq!(seconds_to_blocs(6), 1);
assert_eq!(seconds_to_blocs(7), 1);
}
#[test]
fn test_v1_pubkey_to_v2_address_translation() {
assert_eq!(
v1_pubkey_to_account_id(PubkeyV1(
"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ".to_string()
))
.unwrap()
.to_ss58check_with_version(Ss58AddressFormat::custom(42)),
"5CfdJjEgh3jDkg3bzmZ1ED1xVhXAARtNmZJWbcXh53rU8z5a".to_owned()
);
}
#[test]
fn test_pubkey_with_33_bytes() {
assert_eq!(
v1_pubkey_to_account_id(PubkeyV1(
"d2meevcahfts2gqmvmrw5hzi25jddikk4nc4u1fkwrau".to_string()
)),
Err("Pubkey is too long".to_owned())
);
}
#[test]
fn test_address_to_pubkey_v1() {
let account = AccountId::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
.expect("valid address");
let pubkey = bs58::encode(account.as_slice()).into_vec();
let pubkey = String::from_utf8(pubkey).expect("valid conversion");
assert_eq!(pubkey, "FHNpKmJrUtusuvKPGomAygQqeiks98bdV6yD61Stb6vg");
}
}
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use common_runtime::constants::*;
use common_runtime::entities::IdtyName;
use crate::chain_spec::gen_genesis_data::{CommonParameters, GenesisIdentity, SessionKeysProvider};
use common_runtime::{constants::*, entities::IdtyData, GenesisIdty};
use gtest_runtime::{
opaque::SessionKeys, AccountId, AuthorityMembersConfig, BabeConfig, BalancesConfig, CertConfig,
GenesisConfig, IdentityConfig, IdtyValue, ImOnlineId, MembershipConfig, SessionConfig,
SmithsCertConfig, SmithsMembershipConfig, SudoConfig, SystemConfig, UdAccountsStorageConfig,
UniversalDividendConfig, WASM_BINARY,
opaque::SessionKeys, pallet_universal_dividend, parameters, ImOnlineId, Runtime, WASM_BINARY,
};
use jsonrpsee::core::JsonValue;
use sc_consensus_grandpa::AuthorityId as GrandpaId;
use sc_network::config::MultiaddrWithPeerId;
use sc_service::ChainType;
use sc_telemetry::TelemetryEndpoints;
use serde::Deserialize;
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use sp_consensus_babe::AuthorityId as BabeId;
use sp_core::sr25519;
use sp_finality_grandpa::AuthorityId as GrandpaId;
use sp_membership::MembershipData;
use std::collections::BTreeMap;
use sp_core::{sr25519, Get};
use std::{env, fs};
pub type ChainSpec = sc_service::GenericChainSpec;
pub type AuthorityKeys = (
AccountId,
BabeId,
GrandpaId,
BabeId,
ImOnlineId,
AuthorityDiscoveryId,
);
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
const TOKEN_DECIMALS: usize = 2;
const TOKEN_SYMBOL: &str = "ĞT";
// The URL for the telemetry server.
// const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
static EXISTENTIAL_DEPOSIT: u64 = parameters::ExistentialDeposit::get();
/// 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::<BabeId>(s),
get_from_seed::<GrandpaId>(s),
get_from_seed::<ImOnlineId>(s),
get_from_seed::<AuthorityDiscoveryId>(s),
)
}
#[derive(Default, Clone, Deserialize)]
// No parameters for GTest (unlike GDev)
struct GenesisParameters {}
pub fn development_chain_spec() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "wasm not available".to_string())?;
struct GTestSKP;
impl SessionKeysProvider<SessionKeys> for GTestSKP {
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,
}
}
}
Ok(ChainSpec::from_genesis(
// Name
"Ğtest Development",
// ID
"gtest_dev",
ChainType::Development,
move || {
gen_genesis_for_local_chain(
wasm_binary,
// Initial authorities
1,
// Inital identities
3,
/// 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("ĞTest Local Testnet")
.with_id("gtest_local")
.with_chain_type(ChainType::Local)
.with_genesis_config_patch({
let genesis_data =
gen_genesis_data::generate_genesis_data_for_local_chain::<_, _, SessionKeys, GTestSKP>(
// Initial authorities len
initial_authorities_len,
// Initial smiths len,
initial_smiths_len,
// Initial identities len
initial_identities_len,
EXISTENTIAL_DEPOSIT,
None,
// Sudo account
get_account_id_from_seed::<sr25519::Public>("Alice"),
true,
get_parameters,
)
},
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
None,
//Fork ID
None,
// Properties
Some(
.expect("Genesis Data must be buildable");
genesis_data_to_gtest_genesis_conf(genesis_data)
})
.with_properties(
serde_json::json!({
"tokenDecimals": TOKEN_DECIMALS,
"tokenSymbol": TOKEN_SYMBOL,
......@@ -95,46 +100,92 @@ pub fn development_chain_spec() -> Result<ChainSpec, String> {
.as_object()
.expect("must be a map")
.clone(),
),
// Extensions
None,
))
)
.build())
}
pub fn local_testnet_config(
initial_authorities_len: usize,
initial_identities_len: usize,
) -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "wasm not available".to_string())?;
fn get_parameters(_: &Option<GenesisParameters>) -> CommonParameters {
CommonParameters {
currency_name: TOKEN_SYMBOL.to_string(),
decimals: TOKEN_DECIMALS,
babe_epoch_duration: parameters::EpochDuration::get(),
babe_expected_block_time: parameters::ExpectedBlockTime::get(),
babe_max_authorities: parameters::MaxAuthorities::get(),
timestamp_minimum_period: parameters::MinimumPeriod::get(),
balances_existential_deposit: parameters::ExistentialDeposit::get(),
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::UdCreationPeriod::get() as u64,
universal_dividend_ud_reeval_period: parameters::UdReevalPeriod::get() as u64,
wot_first_issuable_on: parameters::WotFirstCertIssuableOn::get(),
wot_min_cert_for_membership: parameters::WotMinCertForMembership::get(),
wot_min_cert_for_create_idty_right: parameters::WotMinCertForCreateIdtyRight::get(),
identity_confirm_period: parameters::ConfirmPeriod::get(),
identity_change_owner_key_period: parameters::ChangeOwnerKeyPeriod::get(),
identity_idty_creation_period: parameters::IdtyCreationPeriod::get(),
identity_autorevocation_period: parameters::AutorevocationPeriod::get(),
identity_deletion_period: parameters::DeletionPeriod::get(),
membership_membership_period: parameters::MembershipPeriod::get(),
membership_membership_renewal_period: parameters::MembershipRenewalPeriod::get(),
cert_max_by_issuer: parameters::MaxByIssuer::get(),
cert_min_received_cert_to_be_able_to_issue_cert:
parameters::MinReceivedCertToBeAbleToIssueCert::get(),
cert_validity_period: parameters::ValidityPeriod::get(),
distance_min_accessible_referees: parameters::MinAccessibleReferees::get(),
distance_max_depth: parameters::MaxRefereeDistance::get(),
smith_sub_wot_min_cert_for_membership: parameters::SmithWotMinCertForMembership::get(),
smith_inactivity_max_duration: parameters::SmithInactivityMaxDuration::get(),
smith_cert_max_by_issuer: parameters::SmithMaxByIssuer::get(),
cert_cert_period: parameters::CertPeriod::get(),
treasury_spend_period: <Runtime as pallet_treasury::Config>::SpendPeriod::get(),
}
}
Ok(ChainSpec::from_genesis(
// Name
"Ğtest Local Testnet",
// ID
"gtest_local",
ChainType::Local,
move || {
gen_genesis_for_local_chain(
wasm_binary,
// Initial authorities len
initial_authorities_len,
// Initial identities len
initial_identities_len,
// Sudo account
get_account_id_from_seed::<sr25519::Public>("Alice"),
true,
)
},
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
None,
//Fork ID
// === 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(config_file_path: String) -> Result<ChainSpec, String> {
Ok(ChainSpec::builder(
&get_wasm_binary().ok_or_else(|| "Development wasm not available".to_string())?,
None,
// Properties
Some(
)
.with_name("ĞTest Development")
.with_id("gtest_dev")
.with_chain_type(ChainType::Development)
.with_genesis_config_patch({
let genesis_data = gen_genesis_data::generate_genesis_data::<_, _, SessionKeys, GTestSKP>(
config_file_path.clone(),
get_parameters,
Some("Alice".to_owned()),
)
.expect("Genesis Data must be buildable");
genesis_data_to_gtest_genesis_conf(genesis_data)
})
.with_properties(
serde_json::json!({
"tokenDecimals": TOKEN_DECIMALS,
"tokenSymbol": TOKEN_SYMBOL,
......@@ -142,138 +193,145 @@ pub fn local_testnet_config(
.as_object()
.expect("must be a map")
.clone(),
),
// Extensions
None,
))
)
.build())
}
fn gen_genesis_for_local_chain(
wasm_binary: &[u8],
initial_authorities_len: usize,
initial_identities_len: usize,
root_key: AccountId,
_enable_println: bool,
) -> GenesisConfig {
let initial_authorities = (0..initial_authorities_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]),
// === live chainspecs ===
/// live chainspecs
// one smith must have session keys
pub fn live_chainspecs(
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())?,
None,
)
.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, GTestSKP>(
config_file_path.clone(),
get_parameters,
None,
)
.expect("Genesis Data must be buildable");
genesis_data_to_gtest_genesis_conf(genesis_data)
})
.collect::<BTreeMap<IdtyName, AccountId>>();
.with_telemetry_endpoints(client_spec.telemetry_endpoints.unwrap())
.with_properties(client_spec.properties.unwrap())
.with_boot_nodes(client_spec.boot_nodes)
.build())
}
/// custom genesis
fn genesis_data_to_gtest_genesis_conf(
genesis_data: super::gen_genesis_data::GenesisData<GenesisParameters, SessionKeys>,
) -> serde_json::Value {
let super::gen_genesis_data::GenesisData {
accounts,
treasury_balance,
certs_by_receiver,
first_ud,
first_ud_reeval,
identities,
initial_authorities,
initial_monetary_mass,
memberships,
parameters: _,
common_parameters: _,
session_keys_map,
initial_smiths,
sudo_key,
technical_committee_members,
ud,
} = genesis_data;
GenesisConfig {
system: SystemConfig {
// Add Wasm runtime to storage.
code: wasm_binary.to_vec(),
serde_json::json!({
"account": {
"accounts": accounts,
"treasuryBalance": treasury_balance,
},
authority_discovery: Default::default(),
authority_members: AuthorityMembersConfig {
initial_authorities: initial_authorities
.iter()
.enumerate()
.map(|(i, keys)| (i as u32 + 1, (keys.0.clone(), true)))
.collect(),
"authorityMembers": {
"initialAuthorities": initial_authorities,
},
balances: BalancesConfig {
balances: Vec::with_capacity(0),
"balances": {
"totalIssuance": initial_monetary_mass,
},
babe: BabeConfig {
authorities: Vec::with_capacity(0),
epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG),
"babe": {
"epochConfig": Some(BABE_GENESIS_EPOCH_CONFIG),
},
grandpa: Default::default(),
im_online: Default::default(),
session: SessionConfig {
keys: initial_authorities
.iter()
.map(|x| {
(
x.0.clone(),
x.0.clone(),
session_keys(x.1.clone(), x.2.clone(), x.3.clone(), x.4.clone()),
)
})
"session": {
"keys": session_keys_map
.into_iter()
.map(|(account_id, session_keys)| (account_id.clone(), account_id, session_keys))
.collect::<Vec<_>>(),
},
sudo: SudoConfig {
// Assign network admin rights.
key: Some(root_key),
},
identity: IdentityConfig {
identities: initial_identities
.iter()
.map(|(name, account)| IdtyValue {
name: name.clone(),
next_creatable_identity_on: Default::default(),
removable_on: 0,
status: gtest_runtime::IdtyStatus::Validated,
})
.collect(),
"sudo": { "key": sudo_key },
"technicalCommittee": {
"members": technical_committee_members,
},
membership: MembershipConfig {
memberships: (1..=initial_identities.len())
.map(|i| {
(
i as u32,
MembershipData {
expire_on: gtest_runtime::MembershipPeriod::get(),
"quota": {
"identities": identities.iter().map(|i| i.idty_index).collect::<Vec<_>>(),
},
)
})
.collect(),
"identity": {
"identities": identities
.into_iter()
.map(
|GenesisIdentity {
idty_index,
name,
owner_key,
status,
next_scheduled,
}| GenesisIdty {
index: idty_index,
name: common_runtime::IdtyName::from(name.as_str()),
value: common_runtime::IdtyValue {
data: IdtyData {
first_eligible_ud: match status {
// Only members are eligible to UD.
// The first claimable UD has the minimum index (1).
common_runtime::IdtyStatus::Member => gtest_runtime::pallet_universal_dividend::FirstEligibleUd::min(),
_ => gtest_runtime::pallet_universal_dividend::FirstEligibleUd(None),
}
},
cert: CertConfig {
apply_cert_period_at_genesis: false,
certs_by_issuer: clique_wot(
initial_identities.len(),
gtest_runtime::parameters::ValidityPeriod::get(),
),
next_creatable_identity_on: 0,
old_owner_key: None,
owner_key,
next_scheduled,
status,
},
smiths_membership: SmithsMembershipConfig {
memberships: (1..=initial_authorities_len)
.map(|i| {
(
i as u32,
MembershipData {
expire_on: gtest_runtime::SmithMembershipPeriod::get(),
},
)
})
.collect(),
.collect::<Vec<GenesisIdty<gtest_runtime::Runtime>>>(),
},
smiths_cert: SmithsCertConfig {
apply_cert_period_at_genesis: false,
certs_by_issuer: clique_wot(
initial_authorities_len,
gtest_runtime::parameters::SmithValidityPeriod::get(),
),
"certification": {
"applyCertPeriodAtGenesis": false,
"certsByReceiver": certs_by_receiver,
},
ud_accounts_storage: UdAccountsStorageConfig {
ud_accounts: initial_identities.values().cloned().collect(),
"membership": { "memberships": memberships },
"smithMembers": { "initialSmiths": initial_smiths},
"universalDividend": {
"firstReeval": first_ud_reeval,
"firstUd": first_ud,
"initialMonetaryMass": initial_monetary_mass,
"ud": ud,
},
universal_dividend: UniversalDividendConfig {
first_ud: 1_000,
initial_monetary_mass: 0,
},
}
})
}
fn session_keys(
babe: BabeId,
grandpa: GrandpaId,
im_online: ImOnlineId,
authority_discovery: AuthorityDiscoveryId,
) -> SessionKeys {
SessionKeys {
babe,
grandpa,
im_online,
authority_discovery,
}
/// 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()))
}
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
#[derive(Debug, clap::Parser)]
pub struct Cli {
#[clap(subcommand)]
pub subcommand: Option<Subcommand>,
/// substrate base options
#[clap(flatten)]
pub run: sc_cli::RunCmd,
/// duniter specific options
#[clap(flatten)]
pub duniter_options: DuniterConfigExtension,
/// How blocks should be sealed
///
/// Options are "production", "instant", "manual", or timer interval in milliseconds
......@@ -29,6 +34,33 @@ pub struct Cli {
pub sealing: crate::cli::Sealing,
}
/// add options specific to duniter client
#[derive(Debug, Default, Clone, clap::Parser)]
pub struct DuniterConfigExtension {
/// Public RPC endpoint to gossip on the network and make available in the apps.
#[arg(long)]
pub public_rpc: Option<String>,
/// Public Squid graphql endpoint to gossip on the network and make available in the apps.
#[arg(long)]
pub public_squid: Option<String>,
/// Public endpoints from a JSON file, using following format where `protocol` and `address` are
/// strings (value is free) :
///
/// ```json
/// {
/// "endpoints": [
/// { "protocol": "rpc", "address": "wss://gdev.example.com" },
/// { "protocol": "squid", "address": "gdev.example.com/graphql/v1" },
/// { "protocol": "other", "address": "gdev.example.com/other" }
/// ]
/// }
/// ```
#[arg(long, value_name = "JSON_FILE_PATH")]
pub public_endpoints: Option<String>,
}
#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
/// Build a chain specification.
......@@ -37,6 +69,10 @@ pub enum Subcommand {
/// Validate blocks.
CheckBlock(sc_cli::CheckBlockCmd),
/// Run distance oracle.
#[cfg(feature = "distance-oracle")]
DistanceOracle(DistanceOracle),
/// Export blocks.
ExportBlocks(sc_cli::ExportBlocksCmd),
......@@ -75,19 +111,11 @@ pub enum Subcommand {
/// Sub-commands concerned with benchmarking.
/// The pallet benchmarking moved to the `pallet` sub-command.
#[clap(subcommand)]
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
/// Try some command against runtime state.
#[cfg(feature = "try-runtime")]
TryRuntime(try_runtime_cli::TryRuntimeCmd),
/// Try some command against runtime state. Note: `try-runtime` feature must be enabled.
#[cfg(not(feature = "try-runtime"))]
TryRuntime,
Benchmark(Box<frame_benchmarking_cli::BenchmarkCmd>),
}
/// Block authoring scheme to be used by the node
#[derive(Clone, Copy, Debug, PartialEq, Eq, clap::ArgEnum)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, clap::ValueEnum)]
pub enum Sealing {
/// Author a block using normal runtime behavior (mandatory for production networks)
Production,
......@@ -129,6 +157,23 @@ impl std::str::FromStr for Sealing {
#[derive(Debug, clap::Args)]
pub struct Completion {
#[clap(short, long, arg_enum)]
#[clap(short, long, value_enum)]
pub generator: clap_complete::Shell,
}
#[cfg(feature = "distance-oracle")]
#[derive(Debug, clap::Parser)]
pub struct DistanceOracle {
/// Saving path.
#[clap(short = 'd', long, default_value = "/tmp/duniter/chains/gdev/distance")]
pub evaluation_result_dir: String,
/// Number of seconds between two evaluations (oneshot if absent).
#[clap(short = 'i', long)]
pub interval: Option<u64>,
/// Node used for fetching state.
#[clap(short = 'u', long, default_value = "ws://127.0.0.1:9944")]
pub rpc_url: String,
/// Sets the logging level (e.g., debug, error, info, trace, warn).
#[clap(short = 'l', long, default_value = "info")]
pub log: String,
}
......@@ -15,21 +15,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![allow(unused_imports)]
pub mod key;
pub mod utils;
use crate::cli::{Cli, Subcommand};
#[cfg(feature = "g1")]
use crate::service::G1Executor;
#[cfg(feature = "gdev")]
use crate::service::GDevExecutor;
#[cfg(feature = "gtest")]
use crate::service::GTestExecutor;
use crate::service::{IdentifyRuntimeType, RuntimeType};
use crate::{chain_spec, service};
use crate::{
chain_spec,
cli::{Cli, DuniterConfigExtension, Subcommand},
service,
service::{runtime_executor::Executor, RuntimeType},
};
use clap::CommandFactory;
#[cfg(feature = "runtime-benchmarks")]
use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE};
use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli};
use sc_cli::SubstrateCli;
#[cfg(feature = "runtime-benchmarks")]
use sc_executor::{sp_wasm_interface::ExtendedHostFunctions, NativeExecutionDispatch};
// TODO: create our own reference hardware
/*
......@@ -41,21 +43,12 @@ lazy_static! {
};
}*/
/// Unwraps a [`crate::client::Client`] into the concrete runtime client.
/// Unwraps a [`crate::service::client::Client`] into the concrete runtime client.
#[cfg(feature = "runtime-benchmarks")]
macro_rules! unwrap_client {
(
$client:ident,
$code:expr
) => {
($client:ident, $code:expr) => {
match $client.as_ref() {
#[cfg(feature = "g1")]
crate::service::client::Client::G1($client) => $code,
#[cfg(feature = "gtest")]
crate::service::client::Client::GTest($client) => $code,
#[cfg(feature = "gdev")]
crate::service::client::Client::GDev($client) => $code,
#[allow(unreachable_patterns)]
_ => panic!("unknown runtime"),
crate::service::client::Client::Client($client) => $code,
}
};
}
......@@ -78,7 +71,7 @@ impl SubstrateCli for Cli {
}
fn support_url() -> String {
"support.anonymous.an".into()
"https://forum.duniter.org/".into()
}
fn copyright_start_year() -> i32 {
......@@ -87,56 +80,100 @@ impl SubstrateCli for Cli {
fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> {
Ok(match id {
// Development chainspec with generated genesis and Alice as a validator
// For benchmarking, the total length of identities should be at least MinReceivedCertToBeAbleToIssueCert + 1
#[cfg(feature = "gdev")]
"dev" => Box::new(chain_spec::gdev::development_chain_spec()?),
#[cfg(feature = "gdev")]
"local" | "gdev_local" => Box::new(chain_spec::gdev::local_testnet_config(1, 3, 4)?),
#[cfg(feature = "gdev")]
"local2" => Box::new(chain_spec::gdev::local_testnet_config(2, 3, 4)?),
#[cfg(feature = "gdev")]
"local3" => Box::new(chain_spec::gdev::local_testnet_config(3, 3, 4)?),
"dev" => Box::new(chain_spec::gdev::local_testnet_config(1, 5, 6)?),
// Local testnet with G1 data, Gdev configuration (parameters & Smiths), and Alice as a validator.
// Optionally, load configuration from DUNITER_GENESIS_CONFIG file to override default Gdev configuration.
#[cfg(feature = "gdev")]
"local4" => Box::new(chain_spec::gdev::local_testnet_config(4, 4, 5)?),
"gdev_dev" => Box::new(chain_spec::gdev::gdev_development_chain_spec(
"resources/gdev.yaml".to_string(),
)?),
// Chainspecs for live network with G1 data, Gdev configuration (parameters & Smiths).
// A Smith with declared session keys is required.
// Optionally load configuration from DUNITER_GENESIS_CONFIG file to override default Gdev configuration.
#[cfg(feature = "gdev")]
"gdev-gl" | "gdev_gl" => Box::new(chain_spec::gdev::gen_live_conf()?),
"gdev_live" => {
const CLIENT_SPEC: &str = "./node/specs/gdev_client-specs.yaml";
let client_spec: chain_spec::gdev::ClientSpec = serde_yaml::from_slice(
&std::fs::read(
std::env::var("DUNITER_CLIENT_SPEC")
.unwrap_or_else(|_| CLIENT_SPEC.to_string()),
)
.map_err(|e| format!("failed to read {CLIENT_SPEC} {e}"))?[..],
)
.map_err(|e| format!("failed to parse {e}"))?;
Box::new(chain_spec::gdev::gen_live_conf(
client_spec,
"resources/gdev.yaml".to_string(),
)?)
}
// Hardcoded raw chainspecs with previously generated values, resulting in a needlessly heavy binary due to hexadecimal-text-encoded values.
#[cfg(feature = "gdev")]
"gdev" => Box::new(chain_spec::gdev::ChainSpec::from_json_bytes(
&include_bytes!("../specs/gdev-raw.json")[..],
)?),
// For benchmarking, the total length of identities should be at least MinReceivedCertToBeAbleToIssueCert + 1
#[cfg(feature = "gtest")]
"gtest_dev" => Box::new(chain_spec::gtest::development_chain_spec()?),
#[cfg(feature = "gtest")]
"gtest_local" => Box::new(chain_spec::gtest::local_testnet_config(2, 3)?),
#[cfg(feature = "gtest")]
"gtest_local3" => Box::new(chain_spec::gtest::local_testnet_config(3, 4)?),
"dev" => Box::new(chain_spec::gtest::local_testnet_config(1, 5, 6)?),
// Generate development chainspecs with Alice as a validator.
// Provide the DUNITER_GTEST_GENESIS environment variable to build genesis from JSON; otherwise, a local testnet with generated genesis will be used.
#[cfg(feature = "gtest")]
"gtest_local4" => Box::new(chain_spec::gtest::local_testnet_config(4, 5)?),
"gtest_dev" => Box::new(chain_spec::gtest::development_chainspecs(
"resources/gtest.yaml".to_string(),
)?),
// Chainspecs for the live network.
// Required files in the ./node/specs folder or override with environment variables:
// - gtest.json / DUNITER_GTEST_GENESIS
// - gtest_client-specs.json / DUNITER_GTEST_CLIENT_SPEC
#[cfg(feature = "gtest")]
"gtest" => {
unimplemented!()
//Box::new(chain_spec::gtest::ChainSpec::from_json_file(file_path)?)
"gtest_live" => {
const JSON_CLIENT_SPEC: &str = "./node/specs/gtest_client-specs.yaml";
let client_spec: chain_spec::gtest::ClientSpec = serde_yaml::from_slice(
&std::fs::read(
std::env::var("DUNITER_CLIENT_SPEC")
.unwrap_or_else(|_| JSON_CLIENT_SPEC.to_string()),
)
.map_err(|e| format!("failed to read {JSON_CLIENT_SPEC} {e}"))?[..],
)
.map_err(|e| format!("failed to parse {e}"))?;
Box::new(chain_spec::gtest::live_chainspecs(
client_spec,
"resources/gtest.yaml".to_string(),
)?)
}
// Return hardcoded live chainspecs, only with the embed feature enabled.
// Embed client spec and genesis to avoid embedding hexadecimal runtime
// and having hexadecimal runtime in the git history.
// This will only build on a branch that has a file named ./node/specs/gtest-raw.json.
#[cfg(all(feature = "gtest", feature = "embed"))]
"gtest" => Box::new(chain_spec::gtest::ChainSpec::from_json_bytes(
&include_bytes!("../specs/gtest-raw.json")[..],
)?),
// For benchmarking, the total length of identities should be at least MinReceivedCertToBeAbleToIssueCert + 1
#[cfg(feature = "g1")]
"g1" => {
unimplemented!()
//Box::new(chain_spec::g1::ChainSpec::from_json_file(file_path)?)
}
// Specs provided as json specify which runtime to use in their file name. For example,
// `g1-custom.json` uses the g1 runtime.
// `gdev-workshop.json` uses the gdev runtime.
"dev" => Box::new(chain_spec::g1::local_testnet_config(1, 5, 6)?),
path => {
let path = std::path::PathBuf::from(path);
let starts_with = |prefix: &str| {
path.file_name()
.and_then(|f| f.to_str().map(|s| s.starts_with(&prefix)))
.and_then(|f| f.to_str().map(|s| s.starts_with(prefix)))
.unwrap_or(false)
};
let runtime_type = if starts_with("g1") {
RuntimeType::G1
} else if starts_with("gdem") {
RuntimeType::GTest
} else if starts_with("dev") || starts_with("gdev") {
RuntimeType::GDev
} else if starts_with("gt") {
......@@ -161,18 +198,6 @@ impl SubstrateCli for Cli {
}
})
}
fn native_runtime_version(spec: &Box<dyn ChainSpec>) -> &'static RuntimeVersion {
match spec.runtime_type() {
#[cfg(feature = "g1")]
RuntimeType::G1 => &g1_runtime::VERSION,
#[cfg(feature = "gtest")]
RuntimeType::GTest => &gtest_runtime::VERSION,
#[cfg(feature = "gdev")]
RuntimeType::GDev => &gdev_runtime::VERSION,
_ => panic!("unknown runtime"),
}
}
}
/// Parse and run command line arguments
......@@ -185,52 +210,29 @@ pub fn run() -> sc_cli::Result<()> {
match &cli.subcommand {
Some(Subcommand::BuildSpec(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.sync_run(|config| {
if cmd.shared_params.dev {
match config.chain_spec.runtime_type() {
#[cfg(feature = "g1")]
RuntimeType::G1 => cmd.run(
Box::new(chain_spec::g1::development_chain_spec()?),
config.network,
),
#[cfg(feature = "gtest")]
RuntimeType::GTest => cmd.run(
Box::new(chain_spec::gtest::development_chain_spec()?),
config.network,
),
#[cfg(feature = "gdev")]
RuntimeType::GDev => cmd.run(
Box::new(chain_spec::gdev::development_chain_spec()?),
config.network,
),
_ => panic!("unknown runtime"),
}
} else {
cmd.run(config.chain_spec, config.network)
}
})
runner.sync_run(|config| cmd.run(config.chain_spec, config.network))
}
Some(Subcommand::CheckBlock(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|mut config| {
runner.async_run(|config| {
let (client, _, import_queue, task_manager) =
service::new_chain_ops(&mut config, cli.sealing.is_manual_consensus())?;
service::new_chain_ops(&config, cli.sealing.is_manual_consensus())?;
Ok((cmd.run(client, import_queue), task_manager))
})
}
Some(Subcommand::ExportBlocks(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|mut config| {
runner.async_run(|config| {
let (client, _, _, task_manager) =
service::new_chain_ops(&mut config, cli.sealing.is_manual_consensus())?;
service::new_chain_ops(&config, cli.sealing.is_manual_consensus())?;
Ok((cmd.run(client, config.database), task_manager))
})
}
Some(Subcommand::ExportState(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|mut config| {
runner.async_run(|config| {
let (client, _, _, task_manager) =
service::new_chain_ops(&mut config, cli.sealing.is_manual_consensus())?;
service::new_chain_ops(&config, cli.sealing.is_manual_consensus())?;
Ok((cmd.run(client, config.chain_spec), task_manager))
})
}
......@@ -245,7 +247,7 @@ pub fn run() -> sc_cli::Result<()> {
}
let (client, _, import_queue, task_manager) =
service::new_chain_ops(&mut config, cli.sealing.is_manual_consensus())?;
service::new_chain_ops(&config, cli.sealing.is_manual_consensus())?;
Ok((cmd.run(client, import_queue), task_manager))
})
}
......@@ -255,9 +257,9 @@ pub fn run() -> sc_cli::Result<()> {
}
Some(Subcommand::Revert(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|mut config| {
runner.async_run(|config| {
let (client, backend, _, task_manager) =
service::new_chain_ops(&mut config, cli.sealing.is_manual_consensus())?;
service::new_chain_ops(&config, cli.sealing.is_manual_consensus())?;
let aux_revert = Box::new(|client, backend, blocks| {
service::revert_backend(client, backend, blocks)
});
......@@ -278,25 +280,50 @@ pub fn run() -> sc_cli::Result<()> {
);
Ok(())
}
#[cfg(feature = "distance-oracle")]
Some(Subcommand::DistanceOracle(cmd)) => sc_cli::build_runtime()?.block_on(async move {
let mut builder = sc_cli::LoggerBuilder::new("");
builder.with_profiling(sc_cli::TracingReceiver::Log.into(), cmd.log.clone());
builder.init()?;
let client = distance_oracle::api::client(&cmd.rpc_url).await;
let settings = distance_oracle::Settings {
evaluation_result_dir: cmd.evaluation_result_dir.clone().into(),
rpc_url: cmd.rpc_url.clone(),
};
if let Some(duration) = cmd.interval {
let mut interval = tokio::time::interval(std::time::Duration::from_secs(duration));
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
loop {
distance_oracle::run(&client, &settings).await;
interval.tick().await;
}
} else {
distance_oracle::run(&client, &settings).await;
}
Ok(())
}),
#[cfg(feature = "runtime-benchmarks")]
Some(Subcommand::Benchmark(cmd)) => {
let runner = cli.create_runner(cmd)?;
let chain_spec = &runner.config().chain_spec;
let runner = cli.create_runner(&**cmd)?;
let _chain_spec = &runner.config().chain_spec;
match cmd {
BenchmarkCmd::Storage(cmd) => runner.sync_run(|mut config| {
let (client, backend, _, _) = service::new_chain_ops(&mut config, false)?;
match &**cmd {
BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| {
let (client, backend, _, _) = service::new_chain_ops(&config, false)?;
let db = backend.expose_db();
let storage = backend.expose_storage();
unwrap_client!(client, cmd.run(config, client.clone(), db, storage))
}),
BenchmarkCmd::Block(cmd) => runner.sync_run(|mut config| {
let (client, _, _, _) = service::new_chain_ops(&mut config, false)?;
BenchmarkCmd::Block(cmd) => runner.sync_run(|config| {
let (client, _, _, _) = service::new_chain_ops(&config, false)?;
unwrap_client!(client, cmd.run(client.clone()))
}),
BenchmarkCmd::Overhead(cmd) => runner.sync_run(|mut config| {
let (client, _, _, _) = service::new_chain_ops(&mut config, false)?;
BenchmarkCmd::Overhead(cmd) => runner.sync_run(|config| {
let (client, _, _, _) = service::new_chain_ops(&config, false)?;
let wrapped = client.clone();
let inherent_data = crate::service::client::benchmark_inherent_data()
......@@ -304,26 +331,26 @@ pub fn run() -> sc_cli::Result<()> {
unwrap_client!(
client,
cmd.run(config, client.clone(), inherent_data, wrapped)
cmd.run(
config.chain_spec.name().into(),
client.clone(),
inherent_data,
Vec::new(),
wrapped.as_ref(),
false,
)
)
}),
BenchmarkCmd::Pallet(cmd) => {
if cfg!(feature = "runtime-benchmarks") {
match chain_spec.runtime_type() {
#[cfg(feature = "g1")]
RuntimeType::G1 => runner.sync_run(|config| {
cmd.run::<g1_runtime::Block, G1Executor>(config)
}),
#[cfg(feature = "gtest")]
RuntimeType::GTest => runner.sync_run(|config| {
cmd.run::<gtest_runtime::Block, GTestExecutor>(config)
}),
#[cfg(feature = "gdev")]
RuntimeType::GDev => runner.sync_run(|config| {
cmd.run::<gdev_runtime::Block, GDevExecutor>(config)
}),
_ => Err(sc_cli::Error::Application("unknown runtime type".into())),
}
runner.sync_run(|config| {
cmd.run_with_spec::<sp_runtime::traits::HashingFor<
service::runtime_executor::runtime::Block,
>, ExtendedHostFunctions<
sp_io::SubstrateHostFunctions,
<Executor as NativeExecutionDispatch>::ExtendHostFunctions,
>>(Some(config.chain_spec))
})
} else {
Err("Benchmarking wasn't enabled when building the node. \
You can enable it with `--features runtime-benchmarks`."
......@@ -339,49 +366,15 @@ pub fn run() -> sc_cli::Result<()> {
_ => panic!("unknown runtime"),
}
}
#[cfg(feature = "try-runtime")]
Some(Subcommand::TryRuntime(cmd)) => {
let runner = cli.create_runner(cmd)?;
let chain_spec = &runner.config().chain_spec;
use sc_service::TaskManager;
let registry = &runner
.config()
.prometheus_config
.as_ref()
.map(|cfg| &cfg.registry);
let task_manager = TaskManager::new(runner.config().tokio_handle.clone(), *registry)
.map_err(|e| {
sc_cli::Error::Application(format!("Fail to create TaskManager: {}", e).into())
})?;
// Ensure dev spec
if !chain_spec.id().ends_with("dev") {
return Err(sc_cli::Error::Application(
"try-runtime only support dev specs".into(),
));
}
match chain_spec.runtime_type() {
#[cfg(feature = "gdev")]
RuntimeType::GDev => {
//sp_core::crypto::set_default_ss58_version(Ss58AddressFormatRegistry::GDev);
runner.async_run(|config| {
Ok((
cmd.run::<gdev_runtime::Block, GDevExecutor>(config),
task_manager,
))
})
}
_ => Err(sc_cli::Error::Application("unknown runtime type".into())),
}
#[cfg(not(feature = "runtime-benchmarks"))]
Some(Subcommand::Benchmark(_cmd)) => {
Err("Benchmark wasn't enabled when building the node. \
You can enable it with `--features runtime-benchmarks`."
.into())
}
#[cfg(not(feature = "try-runtime"))]
Some(Subcommand::TryRuntime) => Err("TryRuntime wasn't enabled when building the node. \
You can enable it with `--features try-runtime`."
.into()),
None => {
let runner = cli.create_runner(&cli.run)?;
let duniter_options: DuniterConfigExtension = cli.duniter_options;
runner.run_node_until_exit(|mut config| async move {
// Force offchain worker and offchain indexing if we have the role Authority
if config.role.is_authority() {
......@@ -389,28 +382,14 @@ pub fn run() -> sc_cli::Result<()> {
config.offchain_worker.indexing_enabled = true;
}
match config.chain_spec.runtime_type() {
#[cfg(feature = "g1")]
RuntimeType::G1 => {
service::new_full::<g1_runtime::RuntimeApi, G1Executor>(config, cli.sealing)
{
service::new_full::<
service::runtime_executor::runtime::RuntimeApi,
Executor,
sc_network::Litep2pNetworkBackend,
>(config, cli.sealing, duniter_options)
.map_err(sc_cli::Error::Service)
}
#[cfg(feature = "gtest")]
RuntimeType::GTest => service::new_full::<
gtest_runtime::RuntimeApi,
GTestExecutor,
>(config, cli.sealing)
.map_err(sc_cli::Error::Service),
#[cfg(feature = "gdev")]
RuntimeType::GDev => {
service::new_full::<gdev_runtime::RuntimeApi, GDevExecutor>(
config,
cli.sealing,
)
.map_err(sc_cli::Error::Service)
}
_ => Err(sc_cli::Error::Application("unknown runtime".into())),
}
})
}
}
......
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use sc_cli::{
utils, with_crypto_scheme, CryptoScheme, Error, KeystoreParams, SharedParams, SubstrateCli,
......@@ -20,7 +20,7 @@ use sc_cli::{
use sc_keystore::LocalKeystore;
use sc_service::config::{BasePath, KeystoreConfig};
use sp_core::crypto::{AccountId32, KeyTypeId, SecretString};
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
use sp_keystore::{Keystore, KeystorePtr};
use std::sync::Arc;
#[derive(Debug, clap::Subcommand)]
......@@ -73,7 +73,7 @@ impl GenSessionKeysCmd {
let suri = utils::read_uri(self.suri.as_ref())?;
let base_path = self
.shared_params
.base_path()
.base_path()?
.unwrap_or_else(|| BasePath::from_project("", "", &C::executable_name()));
let chain_id = self.shared_params.chain_id(self.shared_params.is_dev());
let chain_spec = cli.load_spec(&chain_id)?;
......@@ -82,29 +82,32 @@ impl GenSessionKeysCmd {
let mut public_keys_bytes = Vec::with_capacity(128);
for (key_type_id, crypto_scheme) in KEY_TYPES {
let (keystore, public) = match self.keystore_params.keystore_config(&config_dir)? {
(_, KeystoreConfig::Path { path, password }) => {
KeystoreConfig::Path { path, password } => {
let public =
with_crypto_scheme!(crypto_scheme, to_vec(&suri, password.clone()))?;
let keystore: SyncCryptoStorePtr =
Arc::new(LocalKeystore::open(path, password)?);
let keystore: KeystorePtr = Arc::new(LocalKeystore::open(path, password)?);
(keystore, public)
}
_ => unreachable!("keystore_config always returns path and password; qed"),
};
SyncCryptoStore::insert_unknown(&*keystore, key_type_id, &suri, &public[..])
.map_err(|_| Error::KeyStoreOperation)?;
Keystore::insert(&*keystore, key_type_id, &suri, &public[..])
.map_err(|_| Error::KeystoreOperation)?;
public_keys_bytes.extend_from_slice(&public[..]);
}
let mut buffer = [0; 32];
// grandpa
buffer.copy_from_slice(&public_keys_bytes[..32]);
println!("grandpa: {}", AccountId32::new(buffer));
// babe
buffer.copy_from_slice(&public_keys_bytes[32..64]);
println!("babe: {}", AccountId32::new(buffer));
// im_online
buffer.copy_from_slice(&public_keys_bytes[64..96]);
println!("im_online: {}", AccountId32::new(buffer));
// authority discovery
buffer.copy_from_slice(&public_keys_bytes[96..]);
println!("authority_discovery: {}", AccountId32::new(buffer));
......
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
// This file is part of Duniter-v2S.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// 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.
//
// Substrate-Libre-Currency is distributed in the hope that it will be useful,
// 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use sc_cli::{Error, SubstrateCli};
use sp_io::hashing::twox_128;
......@@ -50,7 +50,7 @@ impl StorageKeyPrefixCmd {
if let Some(ref pallet_name) = self.pallet_name {
print_key_prefix = true;
let pallet_prefix = twox_128(pallet_name.as_bytes());
println!("Pallet prefix: 0x{}", hex::encode(&pallet_prefix));
println!("Pallet prefix: 0x{}", hex::encode(pallet_prefix));
key_prefix.extend_from_slice(&pallet_prefix[..]);
}
if let Some(ref item_name) = self.item_name {
......