diff --git a/node/src/chain_spec/initial_balances.rs b/node/src/chain_spec/initial_balances.rs new file mode 100644 index 0000000000..e0f4a898fb --- /dev/null +++ b/node/src/chain_spec/initial_balances.rs @@ -0,0 +1,18 @@ +use node_runtime::{AccountId, Balance}; +use serde::Deserialize; +use std::{fs, path::Path}; + +#[derive(Deserialize)] +struct SerializedInitialBalances { + balances: Vec<(AccountId, Balance)>, +} + +fn parse_json(data_file: &Path) -> SerializedInitialBalances { + let data = fs::read_to_string(data_file).expect("Failed reading file"); + serde_json::from_str(&data).expect("failed parsing balances data") +} + +/// Deserializes initial balances from json file +pub fn from_json(data_file: &Path) -> Vec<(AccountId, Balance)> { + parse_json(data_file).balances +} diff --git a/node/src/chain_spec/mod.rs b/node/src/chain_spec/mod.rs index e006c2d761..a520e547d7 100644 --- a/node/src/chain_spec/mod.rs +++ b/node/src/chain_spec/mod.rs @@ -42,6 +42,7 @@ pub use node_runtime::{AccountId, GenesisConfig}; pub mod content_config; pub mod forum_config; +pub mod initial_balances; pub mod initial_members; pub mod proposals_config; @@ -138,6 +139,7 @@ impl Alternative { content_config::empty_versioned_store_permissions_config(), content_config::empty_data_directory_config(), content_config::empty_content_working_group_config(), + vec![], ) }, Vec::new(), @@ -178,6 +180,7 @@ impl Alternative { content_config::empty_versioned_store_permissions_config(), content_config::empty_data_directory_config(), content_config::empty_content_working_group_config(), + vec![], ) }, Vec::new(), @@ -202,7 +205,8 @@ pub fn chain_spec_properties() -> json::map::Map { ); properties } - +// This method should be refactored after Alexandria to reduce number of arguments +// as more args will likely be needed #[allow(clippy::too_many_arguments)] pub fn testnet_genesis( initial_authorities: Vec<( @@ -222,6 +226,7 @@ pub fn testnet_genesis( versioned_store_permissions_config: VersionedStorePermissionsConfig, data_directory_config: DataDirectoryConfig, content_working_group_config: ContentWorkingGroupConfig, + initial_balances: Vec<(AccountId, Balance)>, ) -> GenesisConfig { const STASH: Balance = 5_000; const ENDOWMENT: Balance = 100_000_000; @@ -239,6 +244,11 @@ pub fn testnet_genesis( .cloned() .map(|k| (k, ENDOWMENT)) .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) + .chain( + initial_balances + .iter() + .map(|(account, balance)| (account.clone(), *balance)), + ) .collect(), }), pallet_staking: Some(StakingConfig { diff --git a/utils/chain-spec-builder/src/main.rs b/utils/chain-spec-builder/src/main.rs index 0193d3ae60..8bd17fb625 100644 --- a/utils/chain-spec-builder/src/main.rs +++ b/utils/chain-spec-builder/src/main.rs @@ -24,8 +24,8 @@ use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; use structopt::StructOpt; use joystream_node::chain_spec::{ - self, chain_spec_properties, content_config, forum_config, initial_members, proposals_config, - AccountId, + self, chain_spec_properties, content_config, forum_config, initial_balances, initial_members, + proposals_config, AccountId, }; use sc_chain_spec::ChainType; @@ -88,6 +88,9 @@ enum ChainSpecBuilder { /// The path to an initial content directory data file #[structopt(long, short)] initial_content_path: Option, + /// The path to an initial balances file + #[structopt(long, short)] + initial_balances_path: Option, /// Deployment type: dev, local, staging, live #[structopt(long, short, default_value = "live")] deployment: String, @@ -120,6 +123,9 @@ enum ChainSpecBuilder { /// The path to an initial content directory data file #[structopt(long, short)] initial_content_path: Option, + /// The path to an initial balances file + #[structopt(long, short)] + initial_balances_path: Option, /// Deployment type: dev, local, staging, live #[structopt(long, short, default_value = "live")] deployment: String, @@ -179,6 +185,21 @@ impl ChainSpecBuilder { } } + /// Returns the path to load initial platform content from + fn initial_balances_path(&self) -> &Option { + match self { + ChainSpecBuilder::New { + initial_balances_path, + .. + } => initial_balances_path, + ChainSpecBuilder::Generate { + initial_balances_path, + .. + } => initial_balances_path, + } + } + + /// Returns the chain deployment fn chain_deployment(&self) -> ChainDeployment { match self { ChainSpecBuilder::New { deployment, .. } => deployment @@ -191,6 +212,9 @@ impl ChainSpecBuilder { } } +// TODO: This method should be refactored after Alexandria to reduce number of arguments +// as more args will likely be needed +#[allow(clippy::too_many_arguments)] fn genesis_constructor( deployment: &ChainDeployment, authority_seeds: &[String], @@ -199,6 +223,7 @@ fn genesis_constructor( initial_members_path: &Option, initial_forum_path: &Option, initial_content_path: &Option, + initial_balances_path: &Option, ) -> chain_spec::GenesisConfig { let authorities = authority_seeds .iter() @@ -206,17 +231,15 @@ fn genesis_constructor( .map(chain_spec::get_authority_keys_from_seed) .collect::>(); - let members = if let Some(path) = initial_members_path { - initial_members::from_json(path.as_path()) - } else { - initial_members::none() - }; + let members = initial_members_path + .as_ref() + .map(|path| initial_members::from_json(path.as_path())) + .unwrap_or_else(initial_members::none); - let forum_cfg = if let Some(path) = initial_forum_path { - forum_config::from_json(sudo_account.clone(), path.as_path()) - } else { - forum_config::empty(sudo_account.clone()) - }; + let forum_cfg = initial_forum_path + .as_ref() + .map(|path| forum_config::from_json(sudo_account.clone(), path.as_path())) + .unwrap_or_else(|| forum_config::empty(sudo_account.clone())); let ( versioned_store_cfg, @@ -241,6 +264,11 @@ fn genesis_constructor( ) }; + let initial_account_balances = initial_balances_path + .as_ref() + .map(|path| initial_balances::from_json(path.as_path())) + .unwrap_or_else(Vec::new); + let proposals_cfg = match deployment { ChainDeployment::live => proposals_config::production(), ChainDeployment::staging => proposals_config::staging(), @@ -258,9 +286,13 @@ fn genesis_constructor( versioned_store_permissions_cfg, data_directory_config, content_working_group_config, + initial_account_balances, ) } +// TODO: This method should be refactored after Alexandria to reduce number of arguments +// as more args will likely be needed +#[allow(clippy::too_many_arguments)] fn generate_chain_spec( deployment: ChainDeployment, authority_seeds: Vec, @@ -269,6 +301,7 @@ fn generate_chain_spec( initial_members_path: Option, initial_forum_path: Option, initial_content_path: Option, + initial_balances_path: Option, ) -> Result { let parse_account = |address: &String| { AccountId::from_string(address) @@ -302,6 +335,7 @@ fn generate_chain_spec( &initial_members_path, &initial_forum_path, &initial_content_path, + &initial_balances_path, ) }, vec![], @@ -376,6 +410,7 @@ fn main() -> Result<(), String> { let initial_members_path = builder.initial_members_path().clone(); let initial_forum_path = builder.initial_forum_path().clone(); let initial_content_path = builder.initial_content_path().clone(); + let initial_balances_path = builder.initial_balances_path().clone(); let deployment = builder.chain_deployment(); let (authority_seeds, endowed_accounts, sudo_account) = match builder { @@ -427,6 +462,7 @@ fn main() -> Result<(), String> { initial_members_path, initial_forum_path, initial_content_path, + initial_balances_path, )?; fs::write(chain_spec_path, json).map_err(|err| err.to_string())