diff --git a/changelog.md b/changelog.md index 0ac0d8eea..8c9258ae6 100644 --- a/changelog.md +++ b/changelog.md @@ -34,6 +34,7 @@ This changelog is based on [Keep A Changelog](https://keepachangelog.com/en/1.1. ## Removed ## Fixed +* ETCM-8267 - fixed `partner-chains-cli` missing the native token configuration ## Added * ETCM-7811 - native token movement observability components: `sp-native-token-management` and diff --git a/partner-chains-cli/src/config.rs b/partner-chains-cli/src/config.rs index 9382d3116..10a6cc4b6 100644 --- a/partner-chains-cli/src/config.rs +++ b/partner-chains-cli/src/config.rs @@ -314,6 +314,7 @@ pub struct MainChainAddresses { pub committee_candidates_address: String, pub d_parameter_policy_id: String, pub permissioned_candidates_policy_id: String, + pub native_token: NativeTokenConfig, } #[derive(Deserialize, PartialEq, Clone, Debug)] pub struct CardanoParameters { @@ -376,6 +377,18 @@ pub struct SidechainParams { pub governance_authority: MainchainAddressHash, } +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct AssetConfig { + policy_id: String, + asset_name: String, +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct NativeTokenConfig { + pub asset: AssetConfig, + pub illiquid_supply_address: String, +} + #[derive(Deserialize)] pub struct ChainConfig { pub cardano: CardanoParameters, @@ -408,6 +421,32 @@ pub mod config_fields { use super::*; use sidechain_domain::{MainchainAddressHash, UtxoId}; + pub const NATIVE_TOKEN_POLICY: ConfigFieldDefinition<'static, String> = ConfigFieldDefinition { + config_file: CHAIN_CONFIG_FILE_PATH, + path: &["cardano_addresses", "native_token", "asset", "policy_id"], + name: "native token policy ID", + default: None, + _marker: PhantomData, + }; + + pub const NATIVE_TOKEN_ASSET_NAME: ConfigFieldDefinition<'static, String> = + ConfigFieldDefinition { + config_file: CHAIN_CONFIG_FILE_PATH, + path: &["cardano_addresses", "native_token", "asset", "asset_name"], + name: "native token asset name in hex", + default: None, + _marker: PhantomData, + }; + + pub const ILLIQUID_SUPPLY_ADDRESS: ConfigFieldDefinition<'static, String> = + ConfigFieldDefinition { + config_file: CHAIN_CONFIG_FILE_PATH, + path: &["cardano_addresses", "native_token", "illiquid_supply_address"], + name: "native token illiquid token supply address", + default: None, + _marker: PhantomData, + }; + pub const SUBSTRATE_NODE_DATA_BASE_PATH: ConfigFieldDefinition<'static, String> = ConfigFieldDefinition { config_file: RESOURCES_CONFIG_FILE_PATH, diff --git a/partner-chains-cli/src/create_chain_spec/mod.rs b/partner-chains-cli/src/create_chain_spec/mod.rs index ed61e9b31..dadc4cd31 100644 --- a/partner-chains-cli/src/create_chain_spec/mod.rs +++ b/partner-chains-cli/src/create_chain_spec/mod.rs @@ -71,6 +71,10 @@ impl CreateChainSpecCmd { ) .as_str(), ); + context.print("Native Token Management Configuration (unused if empty):"); + context.print(&format!("- asset name: {}", config.native_token_asset_name)); + context.print(&format!("- asset policy ID: {}", config.native_token_policy)); + context.print(&format!("- illiquid supply address: {}", config.illiquid_supply_address)); use colored::Colorize; if config.initial_permissioned_candidates_raw.is_empty() { context.print("WARNING: The list of initial permissioned candidates is empty. Generated chain spec will not allow the chain to start.".red().to_string().as_str()); @@ -105,6 +109,9 @@ impl CreateChainSpecCmd { "PERMISSIONED_CANDIDATES_POLICY_ID", &config.permissioned_candidates_policy_id.to_string(), ); + context.set_env_var("NATIVE_TOKEN_POLICY_ID", &config.native_token_policy); + context.set_env_var("NATIVE_TOKEN_ASSET_NAME", &config.native_token_asset_name); + context.set_env_var("ILLIQUID_SUPPLY_VALIDATOR_ADDRESS", &config.illiquid_supply_address); context.run_command( format!("{node_executable} build-spec --disable-default-bootnode > chain-spec.json") .to_string() @@ -172,6 +179,9 @@ struct CreateChainSpecConfig { committee_candidate_address: String, d_parameter_policy_id: String, permissioned_candidates_policy_id: String, + native_token_policy: String, + native_token_asset_name: String, + illiquid_supply_address: String, } impl CreateChainSpecConfig { @@ -200,6 +210,9 @@ impl CreateChainSpecConfig { c, &config_fields::PERMISSIONED_CANDIDATES_POLICY_ID, )?, + native_token_policy: load_config_field(c, &config_fields::NATIVE_TOKEN_POLICY)?, + native_token_asset_name: load_config_field(c, &config_fields::NATIVE_TOKEN_ASSET_NAME)?, + illiquid_supply_address: load_config_field(c, &config_fields::ILLIQUID_SUPPLY_ADDRESS)?, }) } } diff --git a/partner-chains-cli/src/create_chain_spec/tests.rs b/partner-chains-cli/src/create_chain_spec/tests.rs index c0e3c4204..ecd5bf6d4 100644 --- a/partner-chains-cli/src/create_chain_spec/tests.rs +++ b/partner-chains-cli/src/create_chain_spec/tests.rs @@ -128,7 +128,7 @@ fn test_config_content() -> serde_json::Value { "sidechain_pub_key": "0x0390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27" } ], - "cardano_addresses": cardano_addresses_json() + "cardano_addresses": cardano_addresses_json(), }) } @@ -174,7 +174,7 @@ fn test_config_content_with_empty_initial_permissioned_candidates() -> serde_jso serde_json::json!({ "chain_parameters": chain_parameters_json(), "initial_permissioned_candidates": [], - "cardano_addresses": cardano_addresses_json() + "cardano_addresses": cardano_addresses_json(), }) } @@ -192,7 +192,14 @@ fn cardano_addresses_json() -> serde_json::Value { serde_json::json!({ "committee_candidates_address": "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc", "d_parameter_policy_id": "d0ebb61e2ba362255a7c4a253c6578884603b56fb0a68642657602d6", - "permissioned_candidates_policy_id": "58b4ba68f641d58f7f1bba07182eca9386da1e88a34d47a14638c3fe" + "permissioned_candidates_policy_id": "58b4ba68f641d58f7f1bba07182eca9386da1e88a34d47a14638c3fe", + "native_token": { + "asset": { + "policy_id": "ada83ddd029614381f00e28de0922ab0dec6983ea9dd29ae20eef9b4", + "asset_name": "5043546f6b656e44656d6f", + }, + "illiquid_supply_address": "addr_test1wrhvtvx3f0g9wv9rx8kfqc60jva3e07nqujk2cspekv4mqs9rjdvz" + }, }) } @@ -213,6 +220,10 @@ fn show_chain_parameters() -> MockIO { MockIO::print("- committee_candidate_address: addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc"), MockIO::print("- d_parameter_policy_id: d0ebb61e2ba362255a7c4a253c6578884603b56fb0a68642657602d6"), MockIO::print("- permissioned_candidates_policy_id: 58b4ba68f641d58f7f1bba07182eca9386da1e88a34d47a14638c3fe"), + MockIO::print("Native Token Management Configuration (unused if empty):"), + MockIO::print("- asset name: 5043546f6b656e44656d6f"), + MockIO::print("- asset policy ID: ada83ddd029614381f00e28de0922ab0dec6983ea9dd29ae20eef9b4"), + MockIO::print("- illiquid supply address: addr_test1wrhvtvx3f0g9wv9rx8kfqc60jva3e07nqujk2cspekv4mqs9rjdvz"), ]) } @@ -249,6 +260,15 @@ fn set_env_vars_io() -> MockIO { "PERMISSIONED_CANDIDATES_POLICY_ID", "58b4ba68f641d58f7f1bba07182eca9386da1e88a34d47a14638c3fe", ), + MockIO::set_env_var( + "NATIVE_TOKEN_POLICY_ID", + "ada83ddd029614381f00e28de0922ab0dec6983ea9dd29ae20eef9b4", + ), + MockIO::set_env_var("NATIVE_TOKEN_ASSET_NAME", "5043546f6b656e44656d6f"), + MockIO::set_env_var( + "ILLIQUID_SUPPLY_VALIDATOR_ADDRESS", + "addr_test1wrhvtvx3f0g9wv9rx8kfqc60jva3e07nqujk2cspekv4mqs9rjdvz", + ), ]) } @@ -349,5 +369,8 @@ fn read_chain_config_io() -> MockIO { MockIO::file_read(CHAIN_CONFIG_FILE_PATH), // committee candidates address MockIO::file_read(CHAIN_CONFIG_FILE_PATH), // d parameter policy id MockIO::file_read(CHAIN_CONFIG_FILE_PATH), // permissioned candidates policy id + MockIO::file_read(CHAIN_CONFIG_FILE_PATH), // native token policy id + MockIO::file_read(CHAIN_CONFIG_FILE_PATH), // native token asset name + MockIO::file_read(CHAIN_CONFIG_FILE_PATH), // illiquid supply validator address ]) } diff --git a/partner-chains-cli/src/generate_keys/mod.rs b/partner-chains-cli/src/generate_keys/mod.rs index 648ee0d39..c0076ccf8 100644 --- a/partner-chains-cli/src/generate_keys/mod.rs +++ b/partner-chains-cli/src/generate_keys/mod.rs @@ -107,6 +107,18 @@ pub fn set_dummy_env_vars(context: &C) { "PERMISSIONED_CANDIDATES_POLICY_ID", "00000000000000000000000000000000000000000000000000000000", ); + context.set_env_var( + "NATIVE_TOKEN_POLICY_ID", + "00000000000000000000000000000000000000000000000000000000", + ); + context.set_env_var( + "NATIVE_TOKEN_ASSET_NAME", + "00000000000000000000000000000000000000000000000000000000", + ); + context.set_env_var( + "ILLIQUID_SUPPLY_VALIDATOR_ADDRESS", + "00000000000000000000000000000000000000000000000000000000", + ); } pub fn generate_spo_keys( diff --git a/partner-chains-cli/src/generate_keys/tests.rs b/partner-chains-cli/src/generate_keys/tests.rs index d0797a48e..89991f052 100644 --- a/partner-chains-cli/src/generate_keys/tests.rs +++ b/partner-chains-cli/src/generate_keys/tests.rs @@ -153,6 +153,18 @@ pub mod scenarios { "PERMISSIONED_CANDIDATES_POLICY_ID", "00000000000000000000000000000000000000000000000000000000", ), + MockIO::set_env_var( + "NATIVE_TOKEN_POLICY_ID", + "00000000000000000000000000000000000000000000000000000000", + ), + MockIO::set_env_var( + "NATIVE_TOKEN_ASSET_NAME", + "00000000000000000000000000000000000000000000000000000000", + ), + MockIO::set_env_var( + "ILLIQUID_SUPPLY_VALIDATOR_ADDRESS", + "00000000000000000000000000000000000000000000000000000000", + ), ]) } } diff --git a/partner-chains-cli/src/prepare_configuration/prepare_main_chain_config.rs b/partner-chains-cli/src/prepare_configuration/prepare_main_chain_config.rs index 8cf31d276..b5d2e5dbf 100644 --- a/partner-chains-cli/src/prepare_configuration/prepare_main_chain_config.rs +++ b/partner-chains-cli/src/prepare_configuration/prepare_main_chain_config.rs @@ -1,6 +1,7 @@ use crate::config::config_fields::{ - CARDANO_NETWORK, COMMITTEE_CANDIDATES_ADDRESS, D_PARAMETER_POLICY_ID, - INITIAL_PERMISSIONED_CANDIDATES, PERMISSIONED_CANDIDATES_POLICY_ID, + CARDANO_NETWORK, COMMITTEE_CANDIDATES_ADDRESS, D_PARAMETER_POLICY_ID, ILLIQUID_SUPPLY_ADDRESS, + INITIAL_PERMISSIONED_CANDIDATES, NATIVE_TOKEN_ASSET_NAME, NATIVE_TOKEN_POLICY, + PERMISSIONED_CANDIDATES_POLICY_ID, }; use crate::config::{ get_cardano_network_from_file, CardanoNetwork, SidechainParams, PC_CONTRACTS_CLI_PATH, @@ -13,6 +14,7 @@ use crate::prepare_configuration::prepare_cardano_params::prepare_cardano_params use crate::smart_contracts; use anyhow::anyhow; use serde_json::Value; +use sidechain_domain::PolicyId; // pc-contracts-cli addresses command requires providing a signing key, but the key has no influence on // its output, thus using a dummy key @@ -41,6 +43,7 @@ pub fn prepare_main_chain_config( if INITIAL_PERMISSIONED_CANDIDATES.load_from_file(context).is_none() { INITIAL_PERMISSIONED_CANDIDATES.save_to_file(&vec![], context) } + prepare_native_token(context)?; context.eprint(OUTRO); Ok(()) } @@ -126,6 +129,31 @@ fn run_pc_contracts_cli_addresses( .ok_or(anyhow!("Permissioned candidates policy id from pc-contracts-cli addresses command output cannot be converted to string"))? .to_string(), context); + ILLIQUID_SUPPLY_ADDRESS.save_to_file( + &addresses_json.pointer("/addresses/IlliquidCirculationSupplyValidator") + .ok_or(anyhow!("Illiquid circulation supply validator address is missing from pc-contracts-cli addresses command output"))? + .as_str() + .ok_or(anyhow!("Illiquid circulation supply validator address from pc-contracts-cli addresses command output cannot be converted to string"))? + .to_string(), + context, + ); + Ok(()) +} + +fn prepare_native_token(context: &C) -> anyhow::Result<()> { + context.print( + "Partner Chains can store their initial token supply on Cardano as Cardano native tokens.", + ); + context.print("Creation of the native token is not supported by this wizard and must be performed manually before this step."); + if context.prompt_yes_no("Do you want to configure a native token for you Partner Chain?", true) + { + NATIVE_TOKEN_POLICY.prompt_with_default_from_file_and_save(context); + NATIVE_TOKEN_ASSET_NAME.prompt_with_default_from_file_and_save(context); + } else { + NATIVE_TOKEN_POLICY.save_to_file(&PolicyId::default().to_hex_string(), context); + NATIVE_TOKEN_ASSET_NAME.save_to_file(&"0x".into(), context); + } + Ok(()) } @@ -211,6 +239,8 @@ mod tests { "addr_test1wz5fe8fmxx4v83gzfsdlnhgxm8x7zpldegrqh2wakl3wteqe834r4"; const TEST_PERMISSIONED_CANDIDATES_POLICY_ID: &str = "13db1ba564b3b264f45974fece44b2beb0a2326b10e65a0f7f300dfb"; + const TEST_ILLIQUID_SUPPLY_ADDRESS: &str = + "addr_test1wqn2pkvvmesmxtfa4tz7w8gh8vumr52lpkrhcs4dkg30uqq77h5z4"; const PC_CONTRACTS_CLI_VERSION_CMD_OUTPUT: &str = "Version: 5.0.0, a770e9bbdcc9410575f8d47c0890801b4ae5c31a"; const PC_CONTRACTS_CLI: &str = "./pc-contracts-cli"; @@ -229,6 +259,38 @@ mod tests { ), ]) } + + pub fn prompt_and_save_native_asset_scripts() -> MockIO { + MockIO::Group(vec![ + MockIO::print("Partner Chains can store their initial token supply on Cardano as Cardano native tokens."), + MockIO::print("Creation of the native token is not supported by this wizard and must be performed manually before this step."), + MockIO::prompt_yes_no( + "Do you want to configure a native token for you Partner Chain?", + true, + true, + ), + MockIO::file_read(NATIVE_TOKEN_POLICY.config_file), + MockIO::prompt( + NATIVE_TOKEN_POLICY.name, + None, + "ada83ddd029614381f00e28de0922ab0dec6983ea9dd29ae20eef9b4", + ), + MockIO::file_read(NATIVE_TOKEN_POLICY.config_file), + MockIO::file_write_json_contains( + NATIVE_TOKEN_POLICY.config_file, + &NATIVE_TOKEN_POLICY.json_pointer(), + "ada83ddd029614381f00e28de0922ab0dec6983ea9dd29ae20eef9b4", + ), + MockIO::file_read(NATIVE_TOKEN_ASSET_NAME.config_file), + MockIO::prompt(NATIVE_TOKEN_ASSET_NAME.name, None, "5043546f6b656e44656d6f"), + MockIO::file_read(NATIVE_TOKEN_ASSET_NAME.config_file), + MockIO::file_write_json_contains( + NATIVE_TOKEN_ASSET_NAME.config_file, + &NATIVE_TOKEN_ASSET_NAME.json_pointer(), + "5043546f6b656e44656d6f", + ), + ]) + } } #[test] @@ -281,12 +343,19 @@ mod tests { PERMISSIONED_CANDIDATES_POLICY_ID, TEST_PERMISSIONED_CANDIDATES_POLICY_ID, ), + save_to_existing_file( + ILLIQUID_SUPPLY_ADDRESS, + TEST_ILLIQUID_SUPPLY_ADDRESS, + ), MockIO::file_read(INITIAL_PERMISSIONED_CANDIDATES.config_file), MockIO::file_read(INITIAL_PERMISSIONED_CANDIDATES.config_file), MockIO::file_write_json( INITIAL_PERMISSIONED_CANDIDATES.config_file, test_chain_config(), ), + + scenarios::prompt_and_save_native_asset_scripts(), + MockIO::eprint(OUTRO), ]); prepare_main_chain_config(&mock_context, test_sidechain_params()).expect("should succeed"); @@ -349,7 +418,12 @@ mod tests { PERMISSIONED_CANDIDATES_POLICY_ID, TEST_PERMISSIONED_CANDIDATES_POLICY_ID, ), + save_to_existing_file( + ILLIQUID_SUPPLY_ADDRESS, + TEST_ILLIQUID_SUPPLY_ADDRESS, + ), MockIO::file_read(INITIAL_PERMISSIONED_CANDIDATES.config_file), + scenarios::prompt_and_save_native_asset_scripts(), MockIO::eprint(OUTRO), ]); prepare_main_chain_config(&mock_context, test_sidechain_params()).expect("should succeed"); @@ -393,7 +467,10 @@ mod tests { "cardano_addresses": { "committee_candidates_address": TEST_COMMITTEE_CANDIDATES_ADDRESS, "d_parameter_policy_id": TEST_D_PARAMETER_POLICY_ID, - "permissioned_candidates_policy_id": TEST_PERMISSIONED_CANDIDATES_POLICY_ID + "permissioned_candidates_policy_id": TEST_PERMISSIONED_CANDIDATES_POLICY_ID, + "native_token": { + "illiquid_supply_address": TEST_ILLIQUID_SUPPLY_ADDRESS, + } }, "initial_permissioned_candidates": [] }) @@ -411,7 +488,8 @@ mod tests { "DParameterValidator": "addr_test1wpqqhw53gnqqpv0t694qlnafkvv2yl4fkusvq5lue9z4srqrqjdh3", "MerkleRootTokenValidator": "addr_test1wz3gsl0z2wsrqeav0mr869avzln5kuz5dl8wal4mlm6w5nsast866", "CheckpointValidator": "addr_test1wrln5wjm88f3sg7yg58nt4mlefr45qq89pg3yf6l39kavlqu5jnwk", - "CommitteeHashValidator": "addr_test1wrpf0tq92fgwc3t3y2fe8pf7tgzuneehfh0p6hvp8y5kemsyrkaa5" + "CommitteeHashValidator": "addr_test1wrpf0tq92fgwc3t3y2fe8pf7tgzuneehfh0p6hvp8y5kemsyrkaa5", + "IlliquidCirculationSupplyValidator": TEST_ILLIQUID_SUPPLY_ADDRESS }, "validatorHashes": { "CommitteeCandidateValidator": "a89c9d3b31aac3c5024c1bf9dd06d9cde107edca060ba9ddb7e2e5e4", diff --git a/partner-chains-cli/src/register/register3.rs b/partner-chains-cli/src/register/register3.rs index 9c1daae42..2e1665226 100644 --- a/partner-chains-cli/src/register/register3.rs +++ b/partner-chains-cli/src/register/register3.rs @@ -368,6 +368,13 @@ mod tests { "committee_candidates_address": "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc", "d_parameter_policy_id": "d0ebb61e2ba362255a7c4a253c6578884603b56fb0a68642657602d6", "permissioned_candidates_policy_id": "58b4ba68f641d58f7f1bba07182eca9386da1e88a34d47a14638c3fe", + "native_token": { + "asset": { + "policy_id": "ada83ddd029614381f00e28de0922ab0dec6983ea9dd29ae20eef9b4", + "asset_name": "5043546f6b656e44656d6f", + }, + "illiquid_supply_address": "addr_test1wqn2pkvvmesmxtfa4tz7w8gh8vumr52lpkrhcs4dkg30uqq77h5z4" + }, }, "initial_permissioned_candidates": [ { @@ -380,7 +387,7 @@ mod tests { "grandpa_pub_key": "0xd17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69", "sidechain_pub_key": "0x0390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27" } - ] + ], }) } diff --git a/partner-chains-cli/src/setup_main_chain_state/tests.rs b/partner-chains-cli/src/setup_main_chain_state/tests.rs index fdd2d6513..f7e20a5d8 100644 --- a/partner-chains-cli/src/setup_main_chain_state/tests.rs +++ b/partner-chains-cli/src/setup_main_chain_state/tests.rs @@ -349,6 +349,13 @@ fn test_chain_config_content() -> serde_json::Value { "committee_candidates_address": "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc", "d_parameter_policy_id": "d0ebb61e2ba362255a7c4a253c6578884603b56fb0a68642657602d6", "permissioned_candidates_policy_id": "58b4ba68f641d58f7f1bba07182eca9386da1e88a34d47a14638c3fe", + "native_token": { + "asset": { + "policy_id": "ada83ddd029614381f00e28de0922ab0dec6983ea9dd29ae20eef9b4", + "asset_name": "5043546f6b656e44656d6f", + }, + "illiquid_supply_address": "addr_test1wqn2pkvvmesmxtfa4tz7w8gh8vumr52lpkrhcs4dkg30uqq77h5z4" + }, }, "initial_permissioned_candidates": [ { @@ -361,7 +368,7 @@ fn test_chain_config_content() -> serde_json::Value { "grandpa_pub_key": "0xd17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69", "sidechain_pub_key": "0x0390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27" } - ] + ], }) } diff --git a/primitives/domain/src/lib.rs b/primitives/domain/src/lib.rs index 7d33bb63d..6531f653b 100644 --- a/primitives/domain/src/lib.rs +++ b/primitives/domain/src/lib.rs @@ -178,6 +178,7 @@ const POLICY_ID_LEN: usize = 28; /// Cardano Policy Id #[derive(Clone, Default, PartialEq, Eq, Encode, Decode, ToDatum, TypeInfo, MaxEncodedLen, Hash)] #[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)] +#[cfg_attr(feature = "std", byte_string(to_hex_string))] pub struct PolicyId(pub [u8; POLICY_ID_LEN]); pub const MAX_ASSET_NAME_LEN: u32 = 32;