From 6ff1bcd4477a79ff283268c7e64615422a1d501f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lech=20G=C5=82owiak?= Date: Fri, 3 Jan 2025 08:14:28 +0100 Subject: [PATCH] feat: Move run init-goveranance just after choosing the genesis-utxo (#342) --- .../prepare_configuration/init_governance.rs | 111 ++++++++++++++++++ .../src/prepare_configuration/mod.rs | 19 +-- .../prepare_main_chain_config.rs | 108 +++-------------- ...chain_params.rs => select_genesis_utxo.rs} | 13 +- toolkit/primitives/domain/src/lib.rs | 2 +- 5 files changed, 144 insertions(+), 109 deletions(-) create mode 100644 toolkit/partner-chains-cli/src/prepare_configuration/init_governance.rs rename toolkit/partner-chains-cli/src/prepare_configuration/{prepare_chain_params.rs => select_genesis_utxo.rs} (91%) diff --git a/toolkit/partner-chains-cli/src/prepare_configuration/init_governance.rs b/toolkit/partner-chains-cli/src/prepare_configuration/init_governance.rs new file mode 100644 index 000000000..b2e3e5228 --- /dev/null +++ b/toolkit/partner-chains-cli/src/prepare_configuration/init_governance.rs @@ -0,0 +1,111 @@ +use crate::{ + cardano_key, + config::{config_fields, ServiceConfig}, + IOContext, +}; +use ogmios_client::types::OgmiosTx; +use partner_chains_cardano_offchain::csl::MainchainPrivateKeyExt; +use partner_chains_cardano_offchain::init_governance::InitGovernance; +use sidechain_domain::{MainchainAddressHash, MainchainPrivateKey, UtxoId}; + +pub(crate) fn run_init_governance( + genesis_utxo: UtxoId, + ogmios_config: &ServiceConfig, + context: &C, +) -> anyhow::Result { + let offchain = context.offchain_impl(ogmios_config)?; + let (payment_key, governance_authority) = get_private_key_and_key_hash(context)?; + let runtime = tokio::runtime::Runtime::new().map_err(|e| anyhow::anyhow!(e))?; + runtime + .block_on(offchain.init_governance(governance_authority, payment_key, genesis_utxo)) + .map_err(|e| anyhow::anyhow!("Governance initalization failed: {e:?}!")) +} + +fn get_private_key_and_key_hash( + context: &C, +) -> Result<(MainchainPrivateKey, MainchainAddressHash), anyhow::Error> { + let cardano_signing_key_file = config_fields::CARDANO_PAYMENT_SIGNING_KEY_FILE + .prompt_with_default_from_file_and_save(context); + let pkey = cardano_key::get_mc_pkey_from_file(&cardano_signing_key_file, context)?; + let addr_hash = pkey.to_pub_key_hash(); + + Ok((pkey, addr_hash)) +} + +#[cfg(test)] +mod tests { + use super::run_init_governance; + use crate::{ + config::{ + config_fields::{CARDANO_PAYMENT_SIGNING_KEY_FILE, GENESIS_UTXO, OGMIOS_PROTOCOL}, + NetworkProtocol, ServiceConfig, + }, + tests::{MockIO, MockIOContext, OffchainMock, OffchainMocks}, + }; + use hex_literal::hex; + use ogmios_client::types::OgmiosTx; + use serde_json::{json, Value}; + use sidechain_domain::{MainchainAddressHash, MainchainPrivateKey, UtxoId}; + + #[test] + fn happy_path() { + let mock_context = MockIOContext::new() + .with_json_file(GENESIS_UTXO.config_file, serde_json::json!({})) + .with_json_file(OGMIOS_PROTOCOL.config_file, serde_json::json!({})) + .with_json_file("payment.skey", payment_key_content()) + .with_offchain_mocks(preprod_offchain_mocks()) + .with_expected_io(vec![ + MockIO::file_read(CARDANO_PAYMENT_SIGNING_KEY_FILE.config_file), + MockIO::prompt( + "path to the payment signing key file", + Some("payment.skey"), + "payment.skey", + ), + MockIO::file_read(CARDANO_PAYMENT_SIGNING_KEY_FILE.config_file), + MockIO::file_write_json( + CARDANO_PAYMENT_SIGNING_KEY_FILE.config_file, + test_resources_config(), + ), + MockIO::file_read("payment.skey"), + ]); + run_init_governance(TEST_GENESIS_UTXO, &ogmios_config(), &mock_context) + .expect("should succeed"); + } + + fn payment_key_content() -> serde_json::Value { + json!({ + "type": "PaymentSigningKeyShelley_ed25519", + "description": "Payment Signing Key", + "cborHex": "5820d0a6c5c921266d15dc8d1ce1e51a01e929a686ed3ec1a9be1145727c224bf386" + }) + } + const TEST_GENESIS_UTXO: UtxoId = UtxoId::new([0u8; 32], 0); + + fn ogmios_config() -> ServiceConfig { + ServiceConfig { + hostname: "localhost".to_string(), + port: 1337, + protocol: NetworkProtocol::Http, + } + } + + fn test_resources_config() -> Value { + serde_json::json!({ + "cardano_payment_signing_key_file": "payment.skey", + }) + } + + fn preprod_offchain_mocks() -> OffchainMocks { + let mock = OffchainMock::new().with_init_governance( + TEST_GENESIS_UTXO, + MainchainAddressHash(hex!("e8c300330fe315531ca89d4a2e7d0c80211bc70b473b1ed4979dff2b")), + MainchainPrivateKey(hex!( + "d0a6c5c921266d15dc8d1ce1e51a01e929a686ed3ec1a9be1145727c224bf386" + )), + Ok(OgmiosTx { + id: hex!("0000000000000000000000000000000000000000000000000000000000000000"), + }), + ); + OffchainMocks::new_with_mock("http://localhost:1337", mock) + } +} diff --git a/toolkit/partner-chains-cli/src/prepare_configuration/mod.rs b/toolkit/partner-chains-cli/src/prepare_configuration/mod.rs index b3be912b8..7e94f607a 100644 --- a/toolkit/partner-chains-cli/src/prepare_configuration/mod.rs +++ b/toolkit/partner-chains-cli/src/prepare_configuration/mod.rs @@ -1,14 +1,14 @@ +mod init_governance; mod prepare_cardano_params; -mod prepare_chain_params; mod prepare_main_chain_config; +mod select_genesis_utxo; -use crate::config::config_fields; -use crate::config::config_fields::BOOTNODES; +use crate::config::config_fields::{BOOTNODES, SUBSTRATE_NODE_DATA_BASE_PATH}; use crate::config::config_values::DEFAULT_CHAIN_NAME; use crate::generate_keys::network_key_path; use crate::io::IOContext; -use crate::prepare_configuration::prepare_chain_params::prepare_chain_params; use crate::prepare_configuration::prepare_main_chain_config::prepare_main_chain_config; +use crate::prepare_configuration::select_genesis_utxo::select_genesis_utxo; use crate::prepare_configuration::PrepareConfigurationError::NetworkKeyNotFoundError; use crate::CmdRun; use anyhow::Context; @@ -24,8 +24,9 @@ pub struct PrepareConfigurationCmd {} impl CmdRun for PrepareConfigurationCmd { fn run(&self, context: &C) -> anyhow::Result<()> { establish_bootnodes(context)?; - let (chain_params, ogmios_config) = prepare_chain_params(context)?; - prepare_main_chain_config(context, &ogmios_config, chain_params)?; + let (genesis_utxo, ogmios_config) = select_genesis_utxo(context)?; + let _ = init_governance::run_init_governance(genesis_utxo, &ogmios_config, context)?; + prepare_main_chain_config(context, &ogmios_config, genesis_utxo)?; context.eprint("🚀 All done!"); Ok(()) } @@ -102,8 +103,8 @@ fn deconstruct_bootnode(bootnode_opt: Option) -> Option<(Protocol, Strin fn peer_id_from_config(context: &impl IOContext) -> anyhow::Result { let chain_name: String = DEFAULT_CHAIN_NAME.into(); - let substrate_node_base_path = config_fields::SUBSTRATE_NODE_DATA_BASE_PATH - .prompt_with_default_from_file_and_save(context); + let substrate_node_base_path = + SUBSTRATE_NODE_DATA_BASE_PATH.prompt_with_default_from_file_and_save(context); let network_key_path = network_key_path(&substrate_node_base_path, &chain_name); @@ -188,7 +189,7 @@ fn peer_id_from_network_key(key_str: &str) -> anyhow::Result { #[cfg(test)] pub mod tests { use super::*; - use crate::config::config_fields::SUBSTRATE_NODE_DATA_BASE_PATH; + use crate::config::config_fields::{BOOTNODES, SUBSTRATE_NODE_DATA_BASE_PATH}; use crate::config::{ConfigFieldDefinition, SelectOptions, RESOURCES_CONFIG_FILE_PATH}; use crate::prepare_configuration::PrepareConfigurationError::NetworkKeyNotFoundError; use crate::prepare_configuration::Protocol::{Dns, Ipv4}; diff --git a/toolkit/partner-chains-cli/src/prepare_configuration/prepare_main_chain_config.rs b/toolkit/partner-chains-cli/src/prepare_configuration/prepare_main_chain_config.rs index 1a2cdf171..35d12fa87 100644 --- a/toolkit/partner-chains-cli/src/prepare_configuration/prepare_main_chain_config.rs +++ b/toolkit/partner-chains-cli/src/prepare_configuration/prepare_main_chain_config.rs @@ -6,11 +6,8 @@ use crate::config::config_fields::{ use crate::config::ServiceConfig; use crate::io::IOContext; use crate::prepare_configuration::prepare_cardano_params::prepare_cardano_params; -use crate::{config::config_fields, *}; -use partner_chains_cardano_offchain::csl::MainchainPrivateKeyExt; -use partner_chains_cardano_offchain::init_governance::InitGovernance; use partner_chains_cardano_offchain::scripts_data::GetScriptsData; -use sidechain_domain::{MainchainAddressHash, MainchainPrivateKey, PolicyId, UtxoId}; +use sidechain_domain::{PolicyId, UtxoId}; pub fn prepare_main_chain_config( context: &C, @@ -21,14 +18,6 @@ pub fn prepare_main_chain_config( cardano_parameteres.save(context); set_up_cardano_addresses(context, genesis_utxo, ogmios_config)?; - let offchain = context.offchain_impl(ogmios_config)?; - let (payment_key, governance_authority) = get_private_key_and_key_hash(context)?; - - let runtime = tokio::runtime::Runtime::new().map_err(|e| anyhow::anyhow!(e))?; - runtime - .block_on(offchain.init_governance(governance_authority, payment_key, genesis_utxo)) - .map_err(|e| anyhow::anyhow!("Governance initalization failed: {e:?}!"))?; - if INITIAL_PERMISSIONED_CANDIDATES.load_from_file(context).is_none() { INITIAL_PERMISSIONED_CANDIDATES.save_to_file(&vec![], context) } @@ -37,17 +26,6 @@ pub fn prepare_main_chain_config( Ok(()) } -fn get_private_key_and_key_hash( - context: &C, -) -> Result<(MainchainPrivateKey, MainchainAddressHash), anyhow::Error> { - let cardano_signig_key_file = config_fields::CARDANO_PAYMENT_SIGNING_KEY_FILE - .prompt_with_default_from_file_and_save(context); - let pkey = cardano_key::get_mc_pkey_from_file(&cardano_signig_key_file, context)?; - let addr_hash = pkey.to_pub_key_hash(); - - Ok((pkey, addr_hash)) -} - fn set_up_cardano_addresses( context: &C, genesis_utxo: UtxoId, @@ -122,14 +100,11 @@ mod tests { use super::*; use crate::config::config_fields::{GENESIS_UTXO, OGMIOS_PROTOCOL}; use crate::config::NetworkProtocol; + use crate::ogmios::test_values::{preprod_eras_summaries, preprod_shelley_config}; use crate::ogmios::{OgmiosRequest, OgmiosResponse}; use crate::prepare_configuration::prepare_cardano_params::tests::PREPROD_CARDANO_PARAMS; use crate::prepare_configuration::tests::save_to_existing_file; use crate::tests::{MockIO, MockIOContext, OffchainMock, OffchainMocks}; - use config_fields::CARDANO_PAYMENT_SIGNING_KEY_FILE; - use hex_literal::hex; - use ogmios::test_values::{preprod_eras_summaries, preprod_shelley_config}; - use ogmios_client::types::OgmiosTx; use partner_chains_cardano_offchain::scripts_data::{Addresses, PolicyIds, ScriptsData}; use serde_json::json; use serde_json::Value; @@ -259,18 +234,6 @@ mod tests { ), save_to_existing_file(ILLIQUID_SUPPLY_ADDRESS, TEST_ILLIQUID_SUPPLY_ADDRESS), print_addresses_io(), - MockIO::file_read(CARDANO_PAYMENT_SIGNING_KEY_FILE.config_file), - MockIO::prompt( - "path to the payment signing key file", - Some("payment.skey"), - "payment.skey", - ), - MockIO::file_read(CARDANO_PAYMENT_SIGNING_KEY_FILE.config_file), - MockIO::file_write_json( - CARDANO_PAYMENT_SIGNING_KEY_FILE.config_file, - test_resources_config(), - ), - MockIO::file_read("payment.skey"), MockIO::file_read(INITIAL_PERMISSIONED_CANDIDATES.config_file), MockIO::file_read(INITIAL_PERMISSIONED_CANDIDATES.config_file), MockIO::file_write_json( @@ -329,18 +292,6 @@ mod tests { ), save_to_existing_file(ILLIQUID_SUPPLY_ADDRESS, TEST_ILLIQUID_SUPPLY_ADDRESS), print_addresses_io(), - MockIO::file_read(CARDANO_PAYMENT_SIGNING_KEY_FILE.config_file), - MockIO::prompt( - "path to the payment signing key file", - Some("payment.skey"), - "payment.skey", - ), - MockIO::file_read(CARDANO_PAYMENT_SIGNING_KEY_FILE.config_file), - MockIO::file_write_json( - CARDANO_PAYMENT_SIGNING_KEY_FILE.config_file, - test_resources_config(), - ), - MockIO::file_read("payment.skey"), MockIO::file_read(INITIAL_PERMISSIONED_CANDIDATES.config_file), scenarios::prompt_and_save_native_asset_scripts(), MockIO::eprint(OUTRO), @@ -364,47 +315,26 @@ mod tests { } fn preprod_offchain_mocks() -> OffchainMocks { - let mock = OffchainMock::new() - .with_scripts_data( - UtxoId::from_str(TEST_GENESIS_UTXO).unwrap(), - Ok(ScriptsData { - addresses: Addresses { - committee_candidate_validator: TEST_COMMITTEE_CANDIDATES_ADDRESS - .to_string(), - illiquid_circulation_supply_validator: TEST_ILLIQUID_SUPPLY_ADDRESS - .to_string(), - ..Default::default() - }, - policy_ids: PolicyIds { - permissioned_candidates: PolicyId::from_hex_unsafe( - TEST_PERMISSIONED_CANDIDATES_POLICY_ID, - ), - d_parameter: PolicyId::from_hex_unsafe(TEST_D_PARAMETER_POLICY_ID), - ..Default::default() - }, - }), - ) - .with_init_governance( - UtxoId::from_str(TEST_GENESIS_UTXO).unwrap(), - MainchainAddressHash(hex!( - "e8c300330fe315531ca89d4a2e7d0c80211bc70b473b1ed4979dff2b" - )), - MainchainPrivateKey(hex!( - "d0a6c5c921266d15dc8d1ce1e51a01e929a686ed3ec1a9be1145727c224bf386" - )), - Ok(OgmiosTx { - id: hex!("0000000000000000000000000000000000000000000000000000000000000000"), - }), - ); + let mock = OffchainMock::new().with_scripts_data( + UtxoId::from_str(TEST_GENESIS_UTXO).unwrap(), + Ok(ScriptsData { + addresses: Addresses { + committee_candidate_validator: TEST_COMMITTEE_CANDIDATES_ADDRESS.to_string(), + illiquid_circulation_supply_validator: TEST_ILLIQUID_SUPPLY_ADDRESS.to_string(), + ..Default::default() + }, + policy_ids: PolicyIds { + permissioned_candidates: PolicyId::from_hex_unsafe( + TEST_PERMISSIONED_CANDIDATES_POLICY_ID, + ), + d_parameter: PolicyId::from_hex_unsafe(TEST_D_PARAMETER_POLICY_ID), + ..Default::default() + }, + }), + ); OffchainMocks::new_with_mock("http://localhost:1337", mock) } - fn test_resources_config() -> Value { - serde_json::json!({ - "cardano_payment_signing_key_file": "payment.skey", - }) - } - fn test_chain_config() -> Value { serde_json::json!({ "cardano": { diff --git a/toolkit/partner-chains-cli/src/prepare_configuration/prepare_chain_params.rs b/toolkit/partner-chains-cli/src/prepare_configuration/select_genesis_utxo.rs similarity index 91% rename from toolkit/partner-chains-cli/src/prepare_configuration/prepare_chain_params.rs rename to toolkit/partner-chains-cli/src/prepare_configuration/select_genesis_utxo.rs index a5027fac3..6bb4816c4 100644 --- a/toolkit/partner-chains-cli/src/prepare_configuration/prepare_chain_params.rs +++ b/toolkit/partner-chains-cli/src/prepare_configuration/select_genesis_utxo.rs @@ -10,7 +10,7 @@ use partner_chains_cardano_offchain::csl::NetworkTypeExt; use serde::de::DeserializeOwned; use sidechain_domain::{NetworkType, UtxoId}; -pub fn prepare_chain_params(context: &C) -> anyhow::Result<(UtxoId, ServiceConfig)> { +pub fn select_genesis_utxo(context: &C) -> anyhow::Result<(UtxoId, ServiceConfig)> { context.eprint(INTRO); let ogmios_configuration = prompt_ogmios_configuration(context)?; let shelley_config = get_shelley_config(&ogmios_configuration.to_string(), context)?; @@ -25,8 +25,6 @@ pub fn prepare_chain_params(context: &C) -> anyhow::Result<(UtxoId let genesis_utxo = select_from_utxos(context, "Select an UTXO to use as the genesis UTXO", utxo_query_result)?; - context.print(CAUTION); - save_if_missing(GENESIS_UTXO, genesis_utxo, context); Ok((genesis_utxo, ogmios_configuration)) } @@ -55,8 +53,6 @@ fn derive_address( } const INTRO: &str = "Now, let's set up the genesis utxo. It identifies a partner chain. This wizard will query Ogmios for your UTXOs using address derived from the payment verification key. Please provide required data."; -const CAUTION: &str = - "Please do not spend this UTXO, it needs to be consumed by the governance initialization.\n"; #[cfg(test)] mod tests { @@ -67,9 +63,7 @@ mod tests { }; use crate::ogmios::test_values::preview_shelley_config; use crate::ogmios::{OgmiosRequest, OgmiosResponse}; - use crate::prepare_configuration::prepare_chain_params::{ - prepare_chain_params, CAUTION, INTRO, - }; + use crate::prepare_configuration::select_genesis_utxo::{select_genesis_utxo, INTRO}; use crate::select_utxo::tests::{mock_7_valid_utxos_rows, mock_result_7_valid, query_utxos_io}; use crate::tests::{MockIO, MockIOContext}; @@ -100,7 +94,6 @@ mod tests { mock_7_valid_utxos_rows(), "4704a903b01514645067d851382efd4a6ed5d2ff07cf30a538acc78fed7c4c02#93 (1100000 lovelace)" ), - MockIO::print(CAUTION), MockIO::file_write_json_contains( GENESIS_UTXO.config_file, &GENESIS_UTXO.json_pointer(), @@ -108,7 +101,7 @@ mod tests { ), ]); - let result = prepare_chain_params(&mock_context); + let result = select_genesis_utxo(&mock_context); result.expect("should succeed"); } diff --git a/toolkit/primitives/domain/src/lib.rs b/toolkit/primitives/domain/src/lib.rs index 8e01cc258..7dbf08d86 100644 --- a/toolkit/primitives/domain/src/lib.rs +++ b/toolkit/primitives/domain/src/lib.rs @@ -348,7 +348,7 @@ pub struct UtxoId { } impl UtxoId { - pub fn new(hash: [u8; TX_HASH_SIZE], index: u16) -> UtxoId { + pub const fn new(hash: [u8; TX_HASH_SIZE], index: u16) -> UtxoId { UtxoId { tx_hash: McTxHash(hash), index: UtxoIndex(index) } } }