Newer
Older
// Copyright 2021 Axiom-Team
//
// This file is part of Duniter-v2S.
// Duniter-v2S is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Duniter-v2S is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use crate::chain_spec::{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_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use sp_consensus_babe::AuthorityId as BabeId;
use sp_consensus_grandpa::AuthorityId as GrandpaId;
use sp_core::crypto::AccountId32;
use sp_core::{blake2_256, ed25519, sr25519, Decode, Encode, H256};
use sp_runtime::traits::{IdentifyAccount, Verify};
use sp_runtime::{MultiSignature, Perbill};
use std::collections::{BTreeMap, HashMap};
use std::fmt::{Display, Formatter};
use std::fs;
use std::ops::{Add, Sub};
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
type MembershipData = sp_membership::MembershipData<u32>;
#[derive(Clone)]
pub struct GenesisData<Parameters: DeserializeOwned, SessionKeys: Decode> {
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: Option<Parameters>,
pub common_parameters: Option<CommonParameters>,
pub session_keys_map: BTreeMap<AccountId, SessionKeys>,
pub smith_certs_by_receiver: BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
pub smith_memberships: BTreeMap<u32, MembershipData>,
pub sudo_key: Option<AccountId>,
pub technical_committee_members: Vec<AccountId>,
#[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 old_owner_key: Option<AccountId>,
pub active: bool,
}
#[derive(Deserialize, Serialize)]
struct GenesisInput<Parameters> {
first_ud: Option<u64>,
first_ud_reeval: Option<u64>,
parameters: Option<Parameters>,
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>,
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>,
technical_committee: Vec<String>,
transactions_history: Option<BTreeMap<AccountId, Vec<TransactionV2>>>,
#[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>,
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
wallets: BTreeMap<PubkeyV1, u64>,
transactions_history: Option<BTreeMap<PubkeyV1, Vec<TransactionV1>>>,
}
// 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 next cert can be emitted
next_cert_issuable_on: TimestampV1, // TODO: unused?
/// balance of the account of this identity
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/// certs received with their expiration timestamp
certs_received: HashMap<String, TimestampV1>,
}
/// identities
#[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,
/// optional ss58 address in the Ğ1v1
old_owner_key: Option<AccountId>,
/// block at which the membership is set to expire (0 for expired members)
membership_expire_on: u32,
/// block at which the next cert can be emitted
next_cert_issuable_on: u32,
/// balance of the account of this identity
balance: u64,
/// 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>,
/// optional pre-set account migration
migration_address: Option<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>,
certs_received: Vec<String>,
}
#[derive(Clone, Deserialize, Serialize)]
struct CliqueSmith {
name: String,
migration_address: Option<AccountId>,
session_keys: Option<String>,
struct SmithWoT<SK: Decode> {
smith_certs_by_receiver: BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
smith_memberships: BTreeMap<u32, sp_membership::MembershipData<u32>>,
session_keys_map:
BTreeMap<<<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId, SK>,
}
struct GenesisInfo<'a> {
accounts: &'a BTreeMap<AccountId32, GenesisAccountData<u64, u32>>,
genesis_data_wallets_count: &'a usize,
inactive_identities: &'a HashMap<u32, String>,
identities: &'a Vec<GenesisIdentity>,
identity_index: &'a HashMap<u32, String>,
smith_memberships: &'a BTreeMap<u32, MembershipData>,
counter_online_authorities: &'a u32,
counter_cert: &'a u32,
counter_smith_cert: &'a u32,
technical_committee_members: &'a Vec<AccountId32>,
common_parameters: &'a CommonParameters,
/// 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,
SKP: SessionKeysProvider<SessionKeys>,
let genesis_timestamp: u64 = get_genesis_timestamp()?;
// Per network input
let GenesisInput {
treasury_funder_pubkey,
treasury_funder_address,
first_ud_reeval,
parameters,
smith_identities,
clique_smiths,
technical_committee,
} = 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(¶meters);
// Per network smiths (without link to an account yet — identified by their pseudonym)
let mut smiths = build_smiths_wot(&clique_smiths, smith_identities)?;
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
// 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, &smiths);
check_identities_v2(&identities_v2, &common_parameters);
// 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>::new();
// 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();
// 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;
}
// 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;
}
}
let (was_fatal, identities) = feed_identities(
&mut accounts,
&mut identity_index,
&mut monetary_mass,
&mut inactive_identities,
&mut memberships,
&identities_v2,
)?;
if was_fatal {
fatal = true;
}
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
// 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,
SmithWoT {
smith_certs_by_receiver,
smith_memberships,
session_keys_map,
},
) = create_smith_wot(
&mut initial_authorities,
&identities_v2,
&smiths,
&common_parameters,
&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.min_cert 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.min_cert
);
fatal = true;
}
// Verify smith certifications coherence
for (idty_index, certs) in &smith_certs_by_receiver {
if certs.len() < common_parameters.smith_min_cert as usize {
log::error!(
"[{}] has received only {}/{} smith certifications",
identity_index.get(idty_index).unwrap(),
certs.len(),
common_parameters.smith_min_cert
);
fatal = true;
}
}
// 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;
}
}
// 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.existential_deposit)
.expect("should have enough money to fund Treasury");
treasury_balance = common_parameters.existential_deposit;
}
if treasury_balance < common_parameters.existential_deposit {
log::error!(
"Treasury balance {} is inferior to existential deposit {}",
treasury_balance,
common_parameters.existential_deposit
);
fatal = true;
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 {
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
accounts: &accounts,
genesis_data_wallets_count: &genesis_data_wallets_count,
identities: &identities,
inactive_identities: &inactive_identities,
identity_index: &identity_index,
smith_memberships: &smith_memberships,
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.c2.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.existential_deposit != G1_DUNITER_V1_EXISTENTIAL_DEPOSIT {
warn!(
"parameter `existential_deposit` value ({}) is different from Ğ1 value ({})",
common_parameters.existential_deposit, G1_DUNITER_V1_EXISTENTIAL_DEPOSIT
)
}
if common_parameters.membership_period / DAYS != G1_DUNITER_V1_MSVALIDITY / DUNITER_V1_DAYS
{
warn!(
"parameter `membership_period` ({} days) is different from Ğ1's ({} days)",
common_parameters.membership_period as f32 / DAYS as f32,
G1_DUNITER_V1_MSVALIDITY as f32 / DUNITER_V1_DAYS as f32
)
}
if common_parameters.cert_period / DAYS != G1_DUNITER_V1_SIGPERIOD / DUNITER_V1_DAYS {
warn!(
"parameter `cert_period` ({} days) is different from Ğ1's ({} days)",
common_parameters.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.min_cert != G1_DUNITER_V1_SIGQTY {
warn!(
"parameter `min_cert` value ({}) is different from Ğ1 value ({})",
common_parameters.min_cert, 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.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.ud_creation_period as f32 / DAYS as f32, G1_DUNITER_V1_DT as f32 / DUNITER_V1_DAYS as f32
)
}
if common_parameters.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.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.max_depth != G1_DUNITER_V1_STEPMAX {
warn!(
"parameter `max_depth` value ({}) is different from Ğ1 value ({})",
common_parameters.max_depth, G1_DUNITER_V1_STEPMAX
)
}
let count_uds = common_parameters.ud_reeval_period / common_parameters.ud_creation_period;
if count_uds == 0 {
error!(
"the `ud_reeval_period / ud_creation_period` is zero ({} days/{} days)",
common_parameters.ud_reeval_period / DAYS as u64,
common_parameters.ud_creation_period / DAYS as u64
);
fatal = true;
// some more checks
assert_eq!(
identities.len() - inactive_identities.len(),
memberships.len()
);
assert_eq!(smith_memberships.len(), initial_authorities.len());
assert_eq!(smith_memberships.len(), session_keys_map.len());
assert_eq!(identity_index.len(), identities.len());
assert_eq!(
accounts.len(),
identity_index.len() + genesis_data_wallets_count.sub(invalid_wallets)
);
smiths_and_technical_committee_checks(&inactive_identities, &technical_committee, &smiths);
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
// 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
if let Ok(path) = std::env::var("DUNITER_GENESIS_EXPORT") {
// genesis_certs_min_received => min_cert
// genesis_memberships_expire_on => membership_period
// genesis_smith_certs_min_received => smith_min_cert
// genesis_smith_memberships_expire_on => smith_membership_period
let export = GenesisIndexerExport {
first_ud,
first_ud_reeval,
genesis_parameters: common_parameters.clone(),
identities: identities_v2,
sudo_key: sudo_key.clone(),
technical_committee,
ud,
wallets: accounts
.iter()
.map(|(account_id, data)| (account_id.clone(), data.balance))
.collect(),
smiths: (smiths)
.iter()
.map(|smith| {
(
smith.name.clone(),
SmithData {
idty_index: smith.idty_index,
name: smith.name.clone(),
account: smith.account.clone(),
session_keys: smith.session_keys.clone(),
certs_received: smith.certs_received.clone(),
},
)
})
.collect::<BTreeMap<String, SmithData>>(),
transactions_history: genesis_data.transactions_history.map(|history| {
history
.iter()
// Avoid wrong pubkeys in tx history
.filter(|(pubkey, _)| v1_pubkey_to_account_id((*pubkey).clone()).is_ok())
.map(|(pubkey, txs)| {
(
v1_pubkey_to_account_id(pubkey.clone())
.expect("already checked account"),
txs.iter()
// Avoid wrong pubkeys in tx history
.filter(|tx| v1_pubkey_to_account_id(tx.issuer.clone()).is_ok())
.map(|tx| TransactionV2 {
issuer: v1_pubkey_to_account_id(tx.issuer.clone())
.expect("already checked tx.issuer"),
amount: tx.amount.clone(),
written_time: tx.written_time,
comment: tx.comment.clone(),
})
.collect::<Vec<TransactionV2>>(),
)
})
.collect::<BTreeMap<AccountId, Vec<TransactionV2>>>()
}),
};
fs::write(
&path,
serde_json::to_string_pretty(&export).expect("should be serializable"),
)
.unwrap_or_else(|_| panic!("Could not export genesis data to {}", &path));
}
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,
smith_certs_by_receiver,
smith_memberships,
sudo_key,
technical_committee_members,
ud,
};
Ok(genesis_data)
}
fn dump_genesis_info(info: GenesisInfo) {
// give genesis info
log::info!(
"prepared genesis with:
- {} accounts ({} identities, {} simple wallets)
- {} total identities ({} active, {} inactive)
- {} smiths
- {} initial online authorities
- {} certifications
- {} smith certifications
- {} members in technical committee",
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.len(),
info.smith_memberships.len(),
info.counter_online_authorities,
info.counter_cert,
info.counter_smith_cert,
info.technical_committee_members.len(),
);
let (membership_period, membership_period_unit) =
get_best_unit_and_diviser_for_blocks(info.common_parameters.membership_period);
let (cert_period, cert_period_unit) =
get_best_unit_and_diviser_for_blocks(info.common_parameters.cert_period);
let (cert_validity_period, cert_validity_period_unit) =
get_best_unit_and_diviser_for_blocks(info.common_parameters.cert_validity_period);
let (smith_membership_period, smith_membership_period_unit) =
get_best_unit_and_diviser_for_blocks(info.common_parameters.smith_membership_period);
let (smith_certs_validity_period, smith_certs_validity_period_unit) =
get_best_unit_and_diviser_for_blocks(info.common_parameters.smith_certs_validity_period);
let (ud_reeval_period, ud_reeval_period_unit) =
get_best_unit_and_diviser_for_ms(info.common_parameters.ud_reeval_period as f32);
let (ud_creation_period, ud_creation_period_unit) =
get_best_unit_and_diviser_for_ms(info.common_parameters.ud_creation_period as f32);
// give genesis info
log::info!(
"currency parameters:
- existential deposit: {} {}
- currency decimals: {}
- membership validity: {} {}
- certification period: {} {}
- certification validity duration: {} {}
- smith membership validity: {} {}
- smith certification validity: {} {}
- required certifications: {}
- smith required certifications: {}
- max certifications by issuer: {}
- money growth rate: {}% every {} {}
- UD creation period: {} {}
- distance percent of required referees: {}%
- distance max depth: {}",
info.common_parameters.existential_deposit as f64 / 100.0,
info.common_parameters.currency_name,
info.common_parameters.decimals,
membership_period,
membership_period_unit,
cert_period,
cert_period_unit,
cert_validity_period,
cert_validity_period_unit,
smith_membership_period,
smith_membership_period_unit,
smith_certs_validity_period,
smith_certs_validity_period_unit,
info.common_parameters.min_cert,
info.common_parameters.smith_min_cert,
info.common_parameters.cert_max_by_issuer,
f32::sqrt(info.common_parameters.c2.deconstruct() as f32 / 1_000_000_000f32) * 100f32,
ud_reeval_period,
ud_reeval_period_unit,
ud_creation_period,
ud_creation_period_unit,
info.common_parameters
.distance_min_accessible_referees
.deconstruct() as f32
/ 1_000_000_000f32
* 100f32,
info.common_parameters.max_depth,
);
}
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
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 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 {
"second".to_string()
};
let plural = if qty > 1f32 { "s" } else { "" };
format!("{}{}", unit, plural)
}
fn get_best_diviser(ms_value: f32) -> f32 {
let one_minute: f32 = 1000.0 * 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 {
one_minute
}
}
fn smiths_and_technical_committee_checks(
inactive_identities: &HashMap<u32, String>,
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(|v| v == tech_com_member);
if inactive_commitee_member {
log::error!(
"{} is an inactive technical commitee member",
tech_com_member
);
assert!(!inactive_commitee_member);
}
// no inactive smith
for SmithData { name: smith, .. } in smiths {
let inactive_smiths: Vec<_> = inactive_identities
.values()
.filter(|v| *v == smith)
.collect();
inactive_smiths
.iter()
.for_each(|s| log::warn!("Smith {} is inactive", s));
assert_eq!(inactive_smiths.len(), 0);
}
}
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
fn create_smith_wot<SK: Decode>(
initial_authorities: &mut BTreeMap<u32, (AccountId32, bool)>,
identities_v2: &HashMap<String, IdentityV2>,
smiths: &Vec<SmithData>,
common_parameters: &CommonParameters,
clique_smiths: &Option<Vec<CliqueSmith>>,
) -> Result<(bool, u32, u32, SmithWoT<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 smith_memberships = BTreeMap::new();
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,
common_parameters,
)?;
// smith memberships
smith_memberships.insert(
identity.index,
MembershipData {
expire_on: common_parameters.smith_membership_period,
},
);
log::error!(
"Smith '{}' does not correspond to exising identity",
&smith.name
);
fatal = true;
}
}
Ok((
fatal,
counter_online_authorities,
counter_smith_cert,
SmithWoT {
smith_certs_by_receiver,
smith_memberships,
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.existential_deposit {
log::error!(
"wallet {pubkey} has {balance} cǦT which is below {}",
common_parameters.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(
owner_key.clone(),
GenesisAccountData {
random_id: H256(blake2_256(&(balance, &owner_key).encode())),
balance,
idty_id: None,
},
);
} else {
log::warn!("wallet {pubkey} has wrong format");
invalid_wallets = invalid_wallets.add(1);
}
}
(fatal, monetary_mass, accounts, invalid_wallets)
}