From 9f9bc650986ad441990070bae19dee590ff470fe Mon Sep 17 00:00:00 2001 From: Nikolaos Dymitriadis Date: Tue, 10 Dec 2024 10:41:05 +0100 Subject: [PATCH 1/9] add upsert-permissied-candidates trait Signed-off-by: Nikolaos Dymitriadis --- .../offchain/src/permissioned_candidates.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/toolkit/offchain/src/permissioned_candidates.rs b/toolkit/offchain/src/permissioned_candidates.rs index dd1eb7cbc..346971704 100644 --- a/toolkit/offchain/src/permissioned_candidates.rs +++ b/toolkit/offchain/src/permissioned_candidates.rs @@ -24,6 +24,27 @@ use partner_chains_plutus_data::permissioned_candidates::{ }; use sidechain_domain::{McTxHash, PermissionedCandidateData, UtxoId}; +pub trait UpsertPermissionedCandidates { + #[allow(async_fn_in_trait)] + async fn upsert_permissioned_candidates( + &self, + genesis_utxo: UtxoId, + candidates: &[PermissionedCandidateData], + payment_signing_key: [u8; 32], + ) -> anyhow::Result>; +} + +impl UpsertPermissionedCandidates for C { + async fn upsert_permissioned_candidates( + &self, + genesis_utxo: UtxoId, + candidates: &[PermissionedCandidateData], + payment_signing_key: [u8; 32], + ) -> anyhow::Result> { + upsert_permissioned_candidates(genesis_utxo, candidates, payment_signing_key, self).await + } +} + pub async fn upsert_permissioned_candidates( genesis_utxo: UtxoId, candidates: &[PermissionedCandidateData], From f747932228c2ee81208c3be53c3a7a504d094701 Mon Sep 17 00:00:00 2001 From: Nikolaos Dymitriadis Date: Tue, 10 Dec 2024 10:50:32 +0100 Subject: [PATCH 2/9] add upsert-d-param to offchain trait bounds, add mock Signed-off-by: Nikolaos Dymitriadis --- toolkit/partner-chains-cli/src/io.rs | 8 ++++- toolkit/partner-chains-cli/src/tests/mod.rs | 39 +++++++++++++++++++++ toolkit/primitives/domain/src/lib.rs | 2 +- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/toolkit/partner-chains-cli/src/io.rs b/toolkit/partner-chains-cli/src/io.rs index f92929e3c..9c83d4a59 100644 --- a/toolkit/partner-chains-cli/src/io.rs +++ b/toolkit/partner-chains-cli/src/io.rs @@ -4,6 +4,7 @@ use anyhow::{anyhow, Context}; use jsonrpsee::http_client::HttpClient; use partner_chains_cardano_offchain::d_param::UpsertDParam; use partner_chains_cardano_offchain::init_governance::InitGovernance; +use partner_chains_cardano_offchain::permissioned_candidates::UpsertPermissionedCandidates; use partner_chains_cardano_offchain::register::{Deregister, Register}; use partner_chains_cardano_offchain::scripts_data::GetScriptsData; use sp_core::offchain::Timestamp; @@ -17,7 +18,12 @@ use tempfile::{TempDir, TempPath}; pub trait IOContext { /// It should implement all the required traits for offchain operations - type Offchain: GetScriptsData + InitGovernance + UpsertDParam + Register + Deregister; + type Offchain: GetScriptsData + + InitGovernance + + UpsertDParam + + Deregister + + Register + + UpsertPermissionedCandidates; fn run_command(&self, cmd: &str) -> anyhow::Result; fn print(&self, msg: &str); diff --git a/toolkit/partner-chains-cli/src/tests/mod.rs b/toolkit/partner-chains-cli/src/tests/mod.rs index 363df99ba..e07cf279d 100644 --- a/toolkit/partner-chains-cli/src/tests/mod.rs +++ b/toolkit/partner-chains-cli/src/tests/mod.rs @@ -4,6 +4,7 @@ use anyhow::anyhow; use ogmios_client::types::OgmiosTx; use partner_chains_cardano_offchain::d_param::UpsertDParam; use partner_chains_cardano_offchain::init_governance::InitGovernance; +use partner_chains_cardano_offchain::permissioned_candidates::UpsertPermissionedCandidates; use partner_chains_cardano_offchain::register::{Deregister, Register}; use partner_chains_cardano_offchain::scripts_data::{GetScriptsData, ScriptsData}; use partner_chains_cardano_offchain::OffchainError; @@ -260,6 +261,10 @@ pub struct OffchainMock { Result, >, pub upsert_d_param: HashMap<(UtxoId, DParameter, [u8; 32]), Result, String>>, + pub upsert_permissioned_candidates: HashMap< + (UtxoId, Vec, [u8; 32]), + Result, String>, + >, pub register: HashMap< (UtxoId, CandidateRegistration, MainchainPrivateKey), Result, OffchainError>, @@ -334,6 +339,23 @@ impl OffchainMock { ..self } } + + pub(crate) fn with_upsert_permissioned_candidates( + self, + genesis_utxo: UtxoId, + candidates: &[sidechain_domain::PermissionedCandidateData], + payment_key: MainchainPrivateKey, + result: Result, String>, + ) -> Self { + Self { + upsert_permissioned_candidates: [( + (genesis_utxo, candidates.to_vec(), payment_key.0), + result, + )] + .into(), + ..self + } + } } impl GetScriptsData for OffchainMock { @@ -409,6 +431,23 @@ impl Deregister for OffchainMock { } } +impl UpsertPermissionedCandidates for OffchainMock { + async fn upsert_permissioned_candidates( + &self, + genesis_utxo: UtxoId, + candidates: &[sidechain_domain::PermissionedCandidateData], + payment_signing_key: [u8; 32], + ) -> anyhow::Result> { + self.upsert_permissioned_candidates + .get(&(genesis_utxo, candidates.to_vec(), payment_signing_key)) + .cloned() + .unwrap_or_else(|| { + Err(format!("No mock for upsert_permissioned_candidates({genesis_utxo:?}, {candidates:?}, {payment_signing_key:?})\n defined mocks:{:?}", self.upsert_permissioned_candidates)) + }) + .map_err(|err| anyhow!("{err}")) + } +} + impl Drop for MockIOContext { fn drop(&mut self) { if std::thread::panicking() { diff --git a/toolkit/primitives/domain/src/lib.rs b/toolkit/primitives/domain/src/lib.rs index 2dd9c0ed1..ea7eacf30 100644 --- a/toolkit/primitives/domain/src/lib.rs +++ b/toolkit/primitives/domain/src/lib.rs @@ -639,7 +639,7 @@ impl DParameter { } } -#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, TypeInfo, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, TypeInfo, PartialOrd, Ord, Hash)] pub struct PermissionedCandidateData { pub sidechain_public_key: SidechainPublicKey, pub aura_public_key: AuraPublicKey, From 423b6f89a39fc911fba098c64fe6dfb920db4791 Mon Sep 17 00:00:00 2001 From: Nikolaos Dymitriadis Date: Tue, 10 Dec 2024 11:30:42 +0100 Subject: [PATCH 3/9] use rust offchain upsert-permissioned-candidates in wizard Signed-off-by: Nikolaos Dymitriadis --- .../src/permissioned_candidates.rs | 10 --- .../src/setup_main_chain_state/mod.rs | 70 +++++++------------ toolkit/primitives/domain/src/lib.rs | 20 +++++- 3 files changed, 45 insertions(+), 55 deletions(-) diff --git a/toolkit/partner-chains-cli/src/permissioned_candidates.rs b/toolkit/partner-chains-cli/src/permissioned_candidates.rs index 8ab1eaafd..90d73910a 100644 --- a/toolkit/partner-chains-cli/src/permissioned_candidates.rs +++ b/toolkit/partner-chains-cli/src/permissioned_candidates.rs @@ -50,16 +50,6 @@ impl ParsedPermissionedCandidatesKeys { pub fn account_id_32(&self) -> AccountId32 { sp_runtime::MultiSigner::from(self.sidechain).into_account() } - - /// Outputs smart contracts format of keys: triple of hex strings without 0x prefix and separated by colons - pub fn to_smart_contracts_args_triple(&self) -> String { - format!( - "{}:{}:{}", - hex::encode(self.sidechain.0), - hex::encode(self.aura.0), - hex::encode(self.grandpa.0) - ) - } } impl TryFrom<&PermissionedCandidateKeys> for ParsedPermissionedCandidatesKeys { diff --git a/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs b/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs index 084890ad1..40e3d194f 100644 --- a/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs +++ b/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs @@ -1,17 +1,16 @@ use crate::config::config_fields::{CARDANO_PAYMENT_SIGNING_KEY_FILE, POSTGRES_CONNECTION_STRING}; use crate::config::{ - config_fields, get_cardano_network_from_file, ChainConfig, ConfigFieldDefinition, - CHAIN_CONFIG_FILE_PATH, PC_CONTRACTS_CLI_PATH, + config_fields, ChainConfig, ConfigFieldDefinition, CHAIN_CONFIG_FILE_PATH, + PC_CONTRACTS_CLI_PATH, }; use crate::io::IOContext; -use crate::pc_contracts_cli_resources::{ - establish_pc_contracts_cli_configuration, prompt_ogmios_configuration, -}; +use crate::pc_contracts_cli_resources::prompt_ogmios_configuration; use crate::permissioned_candidates::{ParsedPermissionedCandidatesKeys, PermissionedCandidateKeys}; -use crate::{cardano_key, smart_contracts, CmdRun}; +use crate::{cardano_key, CmdRun}; use anyhow::anyhow; use anyhow::Context; use partner_chains_cardano_offchain::d_param::UpsertDParam; +use partner_chains_cardano_offchain::permissioned_candidates::UpsertPermissionedCandidates; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use sidechain_domain::mainchain_epoch::MainchainEpochDerivation; @@ -47,17 +46,6 @@ pub struct PermissionedCandidateData { pub is_valid: bool, } -enum InsertOrUpdate { - Insert, - Update, -} - -impl InsertOrUpdate { - fn permissioned_candidates_command(&self) -> &'static str { - "update-permissioned-candidates --remove-all-candidates" - } -} - impl TryFrom for ParsedPermissionedCandidatesKeys { type Error = anyhow::Error; @@ -84,6 +72,17 @@ impl SortedPermissionedCandidates { keys.sort(); Self(keys) } + + pub fn to_candidate_data(&self) -> Vec { + self.0 + .iter() + .map(|c| sidechain_domain::PermissionedCandidateData { + sidechain_public_key: c.sidechain.into(), + aura_public_key: c.aura.into(), + grandpa_public_key: c.grandpa.into(), + }) + .collect() + } } impl CmdRun for SetupMainChainStateCmd { @@ -119,8 +118,8 @@ impl SetupMainChainStateCmd { context, config_initial_authorities, chain_config.chain_parameters.genesis_utxo, - InsertOrUpdate::Update, - )?; + ) + .await?; } context.print(&format!( "D-Parameter on the main chain is: (P={}, R={})", @@ -138,8 +137,8 @@ impl SetupMainChainStateCmd { context, config_initial_authorities, chain_config.chain_parameters.genesis_utxo, - InsertOrUpdate::Insert, - )?; + ) + .await?; let default_d_parameter = DParameter { num_permissioned_candidates: 0, num_registered_candidates: 0 }; set_d_parameter_on_main_chain( @@ -247,39 +246,22 @@ fn print_on_chain_and_config_permissioned_candidates( } } -fn set_candidates_on_main_chain( +async fn set_candidates_on_main_chain( context: &C, candidates: SortedPermissionedCandidates, genesis_utxo: UtxoId, - insert_or_update: InsertOrUpdate, ) -> anyhow::Result<()> { let update = context.prompt_yes_no("Do you want to set/update the permissioned candidates on the main chain with values from configuration file?", false); if update { - let pc_contracts_cli_resources = establish_pc_contracts_cli_configuration(context)?; + let ogmios_config = prompt_ogmios_configuration(context)?; let payment_signing_key_path = CARDANO_PAYMENT_SIGNING_KEY_FILE.prompt_with_default_from_file_and_save(context); - let command = insert_or_update.permissioned_candidates_command(); - let candidate_keys = candidates - .0 - .iter() - .map(|c| format!("--add-candidate {}", c.to_smart_contracts_args_triple())) - .collect::>() - .join(" "); - - let cardano_network = get_cardano_network_from_file(context)?; + let pkey = cardano_key::get_mc_pkey_from_file(&payment_signing_key_path, context)?; context - .run_command(&format!( - "{PC_CONTRACTS_CLI_PATH} {} --network {} {} {} {}", - command, - cardano_network, - candidate_keys, - smart_contracts::sidechain_params_arguments(genesis_utxo), - smart_contracts::runtime_config_arguments( - &pc_contracts_cli_resources, - &payment_signing_key_path - ) - )) + .offchain_impl(&ogmios_config)? + .upsert_permissioned_candidates(genesis_utxo, &candidates.to_candidate_data(), pkey.0) + .await .context("Permissioned candidates update failed")?; context.print("Permissioned candidates updated. The change will be effective in two main chain epochs."); } diff --git a/toolkit/primitives/domain/src/lib.rs b/toolkit/primitives/domain/src/lib.rs index ea7eacf30..b8a280086 100644 --- a/toolkit/primitives/domain/src/lib.rs +++ b/toolkit/primitives/domain/src/lib.rs @@ -21,7 +21,7 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use plutus::{Datum, ToDatum}; use plutus_datum_derive::*; use scale_info::TypeInfo; -use sp_core::{bounded::BoundedVec, ed25519, sr25519, ConstU32}; +use sp_core::{bounded::BoundedVec, ecdsa, ed25519, sr25519, ConstU32}; #[cfg(feature = "serde")] use { derive_more::FromStr, @@ -320,6 +320,12 @@ impl ScEpochNumber { #[byte_string(debug, hex_serialize, hex_deserialize, decode_hex, as_ref)] pub struct SidechainPublicKey(pub Vec); +impl From for SidechainPublicKey { + fn from(value: ecdsa::Public) -> Self { + Self(value.0.to_vec()) + } +} + #[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Eq, Hash)] #[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)] pub struct SidechainSignature(pub Vec); @@ -617,6 +623,12 @@ impl AuraPublicKey { } } +impl From for AuraPublicKey { + fn from(value: sr25519::Public) -> Self { + Self(value.0.to_vec()) + } +} + #[derive(Clone, PartialEq, Eq, Encode, Decode, TypeInfo, PartialOrd, Ord, Hash)] #[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)] pub struct GrandpaPublicKey(pub Vec); @@ -626,6 +638,12 @@ impl GrandpaPublicKey { } } +impl From for GrandpaPublicKey { + fn from(value: ed25519::Public) -> Self { + Self(value.0.to_vec()) + } +} + #[derive(Debug, Clone, PartialEq, Decode, Encode, MaxEncodedLen, TypeInfo, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct DParameter { From 3db2ce4f16de9d6ba43e26f1a339304bed95c5d7 Mon Sep 17 00:00:00 2001 From: Nikolaos Dymitriadis Date: Tue, 10 Dec 2024 12:01:33 +0100 Subject: [PATCH 4/9] fix tests Signed-off-by: Nikolaos Dymitriadis --- .../src/setup_main_chain_state/tests.rs | 130 ++++++++++++------ 1 file changed, 90 insertions(+), 40 deletions(-) diff --git a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs index 03bddff5c..421059412 100644 --- a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs +++ b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs @@ -13,7 +13,10 @@ use crate::tests::{MockIO, MockIOContext, OffchainMock, OffchainMocks}; use crate::CmdRun; use hex_literal::hex; use serde_json::json; -use sidechain_domain::{DParameter, MainchainPrivateKey, McTxHash, UtxoId}; +use sidechain_domain::{ + AuraPublicKey, DParameter, GrandpaPublicKey, MainchainPrivateKey, McTxHash, SidechainPublicKey, + UtxoId, +}; use sp_core::offchain::Timestamp; #[test] @@ -39,12 +42,19 @@ fn no_ariadne_parameters_on_main_chain_no_updates() { #[test] fn no_ariadne_parameters_on_main_chain_do_updates() { - let offchain_mock = OffchainMock::new().with_upsert_d_param( - UtxoId::default(), - new_d_parameter(), - payment_signing_key(), - Ok(Some(McTxHash::default())), - ); + let offchain_mock = OffchainMock::new() + .with_upsert_d_param( + UtxoId::default(), + new_d_parameter(), + payment_signing_key(), + Ok(Some(McTxHash::default())), + ) + .with_upsert_permissioned_candidates( + genesis_utxo(), + &initial_permissioned_candidates(), + payment_signing_key(), + Ok(Some(McTxHash::default())), + ); let mock_context = MockIOContext::new() .with_file(PC_CONTRACTS_CLI_PATH, "") .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) @@ -58,7 +68,7 @@ fn no_ariadne_parameters_on_main_chain_do_updates() { get_ariadne_parameters_io(ariadne_parameters_not_found_response()), print_ariadne_parameters_not_found_io(), prompt_permissioned_candidates_update_io(true), - insert_permissioned_candidates_io(), + upsert_permissioned_candidates_io(), prompt_d_parameter_update_io(true), insert_d_parameter_io(), print_post_update_info_io(), @@ -90,12 +100,19 @@ fn ariadne_parameters_are_on_main_chain_no_updates() { #[test] fn ariadne_parameters_are_on_main_chain_do_update() { - let offchain_mock = OffchainMock::new().with_upsert_d_param( - UtxoId::default(), - new_d_parameter(), - payment_signing_key(), - Ok(Some(McTxHash::default())), - ); + let offchain_mock = OffchainMock::new() + .with_upsert_d_param( + UtxoId::default(), + new_d_parameter(), + payment_signing_key(), + Ok(Some(McTxHash::default())), + ) + .with_upsert_permissioned_candidates( + genesis_utxo(), + &initial_permissioned_candidates(), + payment_signing_key(), + Ok(Some(McTxHash::default())), + ); let mock_context = MockIOContext::new() .with_file(PC_CONTRACTS_CLI_PATH, "") .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) @@ -109,7 +126,7 @@ fn ariadne_parameters_are_on_main_chain_do_update() { get_ariadne_parameters_io(ariadne_parameters_found_response()), print_main_chain_and_configuration_candidates_difference_io(), prompt_permissioned_candidates_update_io(true), - update_permissioned_candidates_io(), + upsert_permissioned_candidates_io(), print_d_param_from_main_chain_io(), prompt_d_parameter_update_io(true), update_d_parameter_io(), @@ -121,7 +138,14 @@ fn ariadne_parameters_are_on_main_chain_do_update() { #[test] fn fails_if_update_permissioned_candidates_fail() { + let offchain_mock = OffchainMock::new().with_upsert_permissioned_candidates( + genesis_utxo(), + &initial_permissioned_candidates(), + payment_signing_key(), + Err("something went wrong".into()), + ); let mock_context = MockIOContext::new() + .with_offchain_mocks(OffchainMocks::new_with_mock("http://localhost:1337", offchain_mock)) .with_file(PC_CONTRACTS_CLI_PATH, "") .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) .with_json_file(RESOURCES_CONFIG_FILE_PATH, test_resources_config_content()) @@ -132,7 +156,7 @@ fn fails_if_update_permissioned_candidates_fail() { get_ariadne_parameters_io(ariadne_parameters_found_response()), print_main_chain_and_configuration_candidates_difference_io(), prompt_permissioned_candidates_update_io(true), - update_permissioned_candidates_failed_io(), + upsert_permissioned_candidates_failed_io(), ]); let result = SetupMainChainStateCmd.run(&mock_context); result.expect_err("should return error"); @@ -230,40 +254,35 @@ fn prompt_permissioned_candidates_update_io(choice: bool) -> MockIO { MockIO::prompt_yes_no("Do you want to set/update the permissioned candidates on the main chain with values from configuration file?", false, choice) } -fn insert_permissioned_candidates_io() -> MockIO { +fn upsert_permissioned_candidates_io() -> MockIO { MockIO::Group(vec![ - establish_pc_contracts_cli_config_io(), - MockIO::file_read("partner-chains-cli-chain-config.json"), - MockIO::run_command( - "./pc-contracts-cli update-permissioned-candidates --remove-all-candidates --network testnet --add-candidate 020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1:d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d:88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee --add-candidate 0390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27:8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48:d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69 --genesis-utxo 0000000000000000000000000000000000000000000000000000000000000000#0 --kupo-host localhost --kupo-port 1442 --ogmios-host localhost --ogmios-port 1337 --payment-signing-key-file payment.skey", - "{\"endpoint\":\"UpdatePermissionedCandidates\",\"transactionId\":\"bbb5ab1232fde32884678333b44aa4f92a2229d7ba7a65d9eae4cb8b8c87d735\"}" + prompt_ogmios_configuration_io( + &default_ogmios_service_config(), + &default_ogmios_service_config(), ), + prompt_with_default_and_save_to_existing_file( + CARDANO_PAYMENT_SIGNING_KEY_FILE, + Some("payment.skey"), + "payment.skey", + ), + MockIO::file_read("payment.skey"), MockIO::print( "Permissioned candidates updated. The change will be effective in two main chain epochs.", )]) } -fn update_permissioned_candidates_io() -> MockIO { +fn upsert_permissioned_candidates_failed_io() -> MockIO { MockIO::Group(vec![ - establish_pc_contracts_cli_config_io(), - MockIO::file_read("partner-chains-cli-chain-config.json"), - MockIO::run_command( - "./pc-contracts-cli update-permissioned-candidates --remove-all-candidates --network testnet --add-candidate 020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1:d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d:88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee --add-candidate 0390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27:8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48:d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69 --genesis-utxo 0000000000000000000000000000000000000000000000000000000000000000#0 --kupo-host localhost --kupo-port 1442 --ogmios-host localhost --ogmios-port 1337 --payment-signing-key-file payment.skey", - "{\"endpoint\":\"UpdatePermissionedCandidates\",\"transactionId\":\"bbb5ab1232fde32884678333b44aa4f92a2229d7ba7a65d9eae4cb8b8c87d735\"}" + prompt_ogmios_configuration_io( + &default_ogmios_service_config(), + &default_ogmios_service_config(), ), - MockIO::print( - "Permissioned candidates updated. The change will be effective in two main chain epochs.", - )]) -} - -fn update_permissioned_candidates_failed_io() -> MockIO { - MockIO::Group(vec![ - establish_pc_contracts_cli_config_io(), - MockIO::file_read("partner-chains-cli-chain-config.json"), - MockIO::run_command_with_result( - "./pc-contracts-cli update-permissioned-candidates --remove-all-candidates --network testnet --add-candidate 020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1:d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d:88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee --add-candidate 0390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27:8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48:d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69 --genesis-utxo 0000000000000000000000000000000000000000000000000000000000000000#0 --kupo-host localhost --kupo-port 1442 --ogmios-host localhost --ogmios-port 1337 --payment-signing-key-file payment.skey", - Err(anyhow::anyhow!("Something went wrong")) + prompt_with_default_and_save_to_existing_file( + CARDANO_PAYMENT_SIGNING_KEY_FILE, + Some("payment.skey"), + "payment.skey", ), + MockIO::file_read("payment.skey"), ]) } @@ -403,6 +422,37 @@ fn test_chain_config_content() -> serde_json::Value { }) } +fn initial_permissioned_candidates() -> Vec { + vec![ + sidechain_domain::PermissionedCandidateData { + sidechain_public_key: SidechainPublicKey( + hex!("020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1").to_vec(), + ), + aura_public_key: AuraPublicKey( + hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d").to_vec(), + ), + grandpa_public_key: GrandpaPublicKey( + hex!("88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee").to_vec(), + ), + }, + sidechain_domain::PermissionedCandidateData { + sidechain_public_key: SidechainPublicKey( + hex!("0390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27").to_vec(), + ), + aura_public_key: AuraPublicKey( + hex!("8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48").to_vec(), + ), + grandpa_public_key: GrandpaPublicKey( + hex!("d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69").to_vec(), + ), + }, + ] +} + +fn genesis_utxo() -> UtxoId { + UtxoId::new(hex!("0000000000000000000000000000000000000000000000000000000000000000"), 0) +} + fn chain_parameters_json() -> serde_json::Value { json!({ "genesis_utxo": "0000000000000000000000000000000000000000000000000000000000000000#0" From 1f02aea8eb6e25d1ec20e4e87402f72c4c88f2cd Mon Sep 17 00:00:00 2001 From: Nikolaos Dymitriadis Date: Wed, 11 Dec 2024 09:24:37 +0100 Subject: [PATCH 5/9] remove unused function --- .../src/setup_main_chain_state/tests.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs index 421059412..38f801509 100644 --- a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs +++ b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs @@ -286,17 +286,6 @@ fn upsert_permissioned_candidates_failed_io() -> MockIO { ]) } -fn establish_pc_contracts_cli_config_io() -> MockIO { - MockIO::Group(vec![ - establish_pc_contracts_cli_configuration_io(None, PcContractsCliResources::default()), - prompt_with_default_and_save_to_existing_file( - CARDANO_PAYMENT_SIGNING_KEY_FILE, - Some("payment.skey"), - "payment.skey", - ), - ]) -} - fn new_d_parameter() -> DParameter { DParameter::new(4, 7) } From 5f88aea29674cc4e61a55b3fe6df5022a9d1dfeb Mon Sep 17 00:00:00 2001 From: Nikolaos Dymitriadis Date: Wed, 11 Dec 2024 09:26:09 +0100 Subject: [PATCH 6/9] use different transaction hasshes --- .../src/setup_main_chain_state/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs index 38f801509..87e34a26f 100644 --- a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs +++ b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs @@ -47,13 +47,13 @@ fn no_ariadne_parameters_on_main_chain_do_updates() { UtxoId::default(), new_d_parameter(), payment_signing_key(), - Ok(Some(McTxHash::default())), + Ok(Some(McTxHash([1; 32]))), ) .with_upsert_permissioned_candidates( genesis_utxo(), &initial_permissioned_candidates(), payment_signing_key(), - Ok(Some(McTxHash::default())), + Ok(Some(McTxHash([2; 32]))), ); let mock_context = MockIOContext::new() .with_file(PC_CONTRACTS_CLI_PATH, "") @@ -105,13 +105,13 @@ fn ariadne_parameters_are_on_main_chain_do_update() { UtxoId::default(), new_d_parameter(), payment_signing_key(), - Ok(Some(McTxHash::default())), + Ok(Some(McTxHash([1; 32]))), ) .with_upsert_permissioned_candidates( genesis_utxo(), &initial_permissioned_candidates(), payment_signing_key(), - Ok(Some(McTxHash::default())), + Ok(Some(McTxHash([2; 32]))), ); let mock_context = MockIOContext::new() .with_file(PC_CONTRACTS_CLI_PATH, "") From f0e6dacedcbccb7d17a16a72f1fa3a4ea0ec3a76 Mon Sep 17 00:00:00 2001 From: Nikolaos Dymitriadis Date: Wed, 11 Dec 2024 09:31:00 +0100 Subject: [PATCH 7/9] clean up dependence on pc-contracts-cli --- .../src/setup_main_chain_state/mod.rs | 10 +------ .../src/setup_main_chain_state/tests.rs | 27 +++---------------- 2 files changed, 4 insertions(+), 33 deletions(-) diff --git a/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs b/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs index 40e3d194f..ddd89966b 100644 --- a/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs +++ b/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs @@ -1,8 +1,5 @@ use crate::config::config_fields::{CARDANO_PAYMENT_SIGNING_KEY_FILE, POSTGRES_CONNECTION_STRING}; -use crate::config::{ - config_fields, ChainConfig, ConfigFieldDefinition, CHAIN_CONFIG_FILE_PATH, - PC_CONTRACTS_CLI_PATH, -}; +use crate::config::{config_fields, ChainConfig, ConfigFieldDefinition, CHAIN_CONFIG_FILE_PATH}; use crate::io::IOContext; use crate::pc_contracts_cli_resources::prompt_ogmios_configuration; use crate::permissioned_candidates::{ParsedPermissionedCandidatesKeys, PermissionedCandidateKeys}; @@ -98,11 +95,6 @@ impl SetupMainChainStateCmd { context.print( "This wizard will set or update D-Parameter and Permissioned Candidates on the main chain. Setting either of these costs ADA!", ); - if !context.file_exists(PC_CONTRACTS_CLI_PATH) { - return Err(anyhow!( - "Partner Chains Smart Contracts executable file ({PC_CONTRACTS_CLI_PATH}) is missing", - )); - } let config_initial_authorities = initial_permissioned_candidates_from_chain_config(context)?; if let Some(ariadne_parameters) = get_ariadne_parameters(context, &chain_config)? { diff --git a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs index 87e34a26f..ec81750e7 100644 --- a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs +++ b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs @@ -1,10 +1,8 @@ +use crate::config::config_fields; use crate::config::config_fields::CARDANO_PAYMENT_SIGNING_KEY_FILE; -use crate::config::{config_fields, PC_CONTRACTS_CLI_PATH}; use crate::config::{CHAIN_CONFIG_FILE_PATH, RESOURCES_CONFIG_FILE_PATH}; -use crate::pc_contracts_cli_resources::tests::{ - establish_pc_contracts_cli_configuration_io, prompt_ogmios_configuration_io, -}; -use crate::pc_contracts_cli_resources::{default_ogmios_service_config, PcContractsCliResources}; +use crate::pc_contracts_cli_resources::default_ogmios_service_config; +use crate::pc_contracts_cli_resources::tests::prompt_ogmios_configuration_io; use crate::prepare_configuration::tests::{ prompt_and_save_to_existing_file, prompt_with_default_and_save_to_existing_file, }; @@ -22,7 +20,6 @@ use sp_core::offchain::Timestamp; #[test] fn no_ariadne_parameters_on_main_chain_no_updates() { let mock_context = MockIOContext::new() - .with_file(PC_CONTRACTS_CLI_PATH, "") .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) .with_json_file(RESOURCES_CONFIG_FILE_PATH, test_resources_config_content()) .with_expected_io(vec![ @@ -56,7 +53,6 @@ fn no_ariadne_parameters_on_main_chain_do_updates() { Ok(Some(McTxHash([2; 32]))), ); let mock_context = MockIOContext::new() - .with_file(PC_CONTRACTS_CLI_PATH, "") .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) .with_json_file(RESOURCES_CONFIG_FILE_PATH, test_resources_config_content()) .with_json_file("payment.skey", valid_payment_signing_key_content()) @@ -80,7 +76,6 @@ fn no_ariadne_parameters_on_main_chain_do_updates() { #[test] fn ariadne_parameters_are_on_main_chain_no_updates() { let mock_context = MockIOContext::new() - .with_file(PC_CONTRACTS_CLI_PATH, "") .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) .with_json_file(RESOURCES_CONFIG_FILE_PATH, test_resources_config_content()) .with_expected_io(vec![ @@ -114,7 +109,6 @@ fn ariadne_parameters_are_on_main_chain_do_update() { Ok(Some(McTxHash([2; 32]))), ); let mock_context = MockIOContext::new() - .with_file(PC_CONTRACTS_CLI_PATH, "") .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) .with_json_file(RESOURCES_CONFIG_FILE_PATH, test_resources_config_content()) .with_json_file("payment.skey", valid_payment_signing_key_content()) @@ -146,7 +140,6 @@ fn fails_if_update_permissioned_candidates_fail() { ); let mock_context = MockIOContext::new() .with_offchain_mocks(OffchainMocks::new_with_mock("http://localhost:1337", offchain_mock)) - .with_file(PC_CONTRACTS_CLI_PATH, "") .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) .with_json_file(RESOURCES_CONFIG_FILE_PATH, test_resources_config_content()) .with_expected_io(vec![ @@ -165,7 +158,6 @@ fn fails_if_update_permissioned_candidates_fail() { #[test] fn candidates_on_main_chain_are_same_as_in_config_no_updates() { let mock_context = MockIOContext::new() - .with_file(PC_CONTRACTS_CLI_PATH, "") .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) .with_json_file(RESOURCES_CONFIG_FILE_PATH, test_resources_config_content()) .with_expected_io(vec![ @@ -182,19 +174,6 @@ fn candidates_on_main_chain_are_same_as_in_config_no_updates() { result.expect("should succeed"); } -#[test] -fn should_return_error_message_if_pc_cli_missing() { - let mock_context = MockIOContext::new() - .with_json_file(CHAIN_CONFIG_FILE_PATH, test_chain_config_content()) - .with_expected_io(vec![read_chain_config_io(), print_info_io()]); - let result = SetupMainChainStateCmd.run(&mock_context); - let err = result.expect_err("should return error"); - assert_eq!( - err.to_string(), - "Partner Chains Smart Contracts executable file (./pc-contracts-cli) is missing" - ); -} - fn read_chain_config_io() -> MockIO { MockIO::file_read(CHAIN_CONFIG_FILE_PATH) } From 3a44c729ab43129e30278a674151db64ea5a1481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lech=20G=C5=82owiak?= Date: Wed, 18 Dec 2024 16:15:46 +0100 Subject: [PATCH 8/9] Cleanup unused kupo and pc-contracts-cli, query ogmios for network --- toolkit/partner-chains-cli/src/config.rs | 50 +------ .../partner-chains-cli/src/deregister/mod.rs | 6 +- .../src/deregister/tests.rs | 9 +- toolkit/partner-chains-cli/src/main.rs | 2 - .../partner-chains-cli/src/ogmios/config.rs | 82 ++++++++++++ toolkit/partner-chains-cli/src/ogmios/mod.rs | 115 ++++++++++++++++ .../src/pc_contracts_cli_resources.rs | 126 ------------------ .../prepare_cardano_params.rs | 117 ++-------------- .../prepare_chain_params.rs | 15 ++- .../prepare_main_chain_config.rs | 11 +- .../src/register/register1.rs | 96 ++++++------- .../src/register/register3.rs | 45 +++---- .../src/setup_main_chain_state/mod.rs | 2 +- .../src/setup_main_chain_state/tests.rs | 3 +- .../partner-chains-cli/src/smart_contracts.rs | 21 --- .../partner-chains-cli/src/start_node/mod.rs | 1 - 16 files changed, 287 insertions(+), 414 deletions(-) create mode 100644 toolkit/partner-chains-cli/src/ogmios/config.rs delete mode 100644 toolkit/partner-chains-cli/src/pc_contracts_cli_resources.rs delete mode 100644 toolkit/partner-chains-cli/src/smart_contracts.rs diff --git a/toolkit/partner-chains-cli/src/config.rs b/toolkit/partner-chains-cli/src/config.rs index 0eb4a6640..664b56841 100644 --- a/toolkit/partner-chains-cli/src/config.rs +++ b/toolkit/partner-chains-cli/src/config.rs @@ -1,13 +1,11 @@ use crate::config::config_fields::{ CARDANO_ACTIVE_SLOTS_COEFF, CARDANO_EPOCH_DURATION_MILLIS, CARDANO_FIRST_EPOCH_NUMBER, - CARDANO_FIRST_EPOCH_TIMESTAMP_MILLIS, CARDANO_FIRST_SLOT_NUMBER, CARDANO_NETWORK, - CARDANO_SECURITY_PARAMETER, + CARDANO_FIRST_EPOCH_TIMESTAMP_MILLIS, CARDANO_FIRST_SLOT_NUMBER, CARDANO_SECURITY_PARAMETER, }; use crate::io::IOContext; -use anyhow::anyhow; use clap::{arg, Parser}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use sidechain_domain::{NetworkType, UtxoId}; +use sidechain_domain::UtxoId; use sp_core::offchain::{Duration, Timestamp}; use std::fmt::{Display, Formatter}; use std::str::FromStr; @@ -275,12 +273,10 @@ pub struct CardanoParameters { pub first_slot_number: u64, pub epoch_duration_millis: u64, pub first_epoch_timestamp_millis: u64, - pub network: NetworkType, } impl CardanoParameters { pub fn save(&self, context: &impl IOContext) { - CARDANO_NETWORK.save_to_file(&self.network, context); CARDANO_SECURITY_PARAMETER.save_to_file(&self.security_parameter, context); CARDANO_ACTIVE_SLOTS_COEFF.save_to_file(&self.active_slots_coeff, context); CARDANO_FIRST_EPOCH_NUMBER.save_to_file(&self.first_epoch_number, context); @@ -299,7 +295,6 @@ impl CardanoParameters { epoch_duration_millis: CARDANO_EPOCH_DURATION_MILLIS.load_from_file(context)?, first_epoch_timestamp_millis: CARDANO_FIRST_EPOCH_TIMESTAMP_MILLIS .load_from_file(context)?, - network: CARDANO_NETWORK.load_from_file(context)?, }) } } @@ -353,7 +348,6 @@ pub const KEYS_FILE_PATH: &str = "partner-chains-public-keys.json"; pub const CHAIN_CONFIG_FILE_PATH: &str = "partner-chains-cli-chain-config.json"; pub const RESOURCES_CONFIG_FILE_PATH: &str = "partner-chains-cli-resources-config.json"; pub const CHAIN_SPEC_PATH: &str = "chain-spec.json"; -pub const PC_CONTRACTS_CLI_PATH: &str = "./pc-contracts-cli"; pub fn load_chain_config(context: &impl IOContext) -> anyhow::Result { if let Some(chain_config_file) = context.read_file(CHAIN_CONFIG_FILE_PATH) { @@ -364,12 +358,6 @@ pub fn load_chain_config(context: &impl IOContext) -> anyhow::Result anyhow::Result { - CARDANO_NETWORK.load_from_file(context).ok_or(anyhow!( - "Cardano network not configured. Please run prepare-main-chain-config command first." - )) -} - pub mod config_fields { use super::*; use sidechain_domain::UtxoId; @@ -446,15 +434,6 @@ pub mod config_fields { _marker: PhantomData, }; - pub const CARDANO_NETWORK: ConfigFieldDefinition<'static, NetworkType> = - ConfigFieldDefinition { - config_file: CHAIN_CONFIG_FILE_PATH, - path: &["cardano", "network"], - name: "cardano network", - default: None, - _marker: PhantomData, - }; - pub const GENESIS_UTXO: ConfigFieldDefinition<'static, UtxoId> = ConfigFieldDefinition { config_file: CHAIN_CONFIG_FILE_PATH, path: &["chain_parameters", "genesis_utxo"], @@ -491,31 +470,6 @@ pub mod config_fields { _marker: PhantomData, }; - pub const KUPO_PROTOCOL: ConfigFieldDefinition<'static, NetworkProtocol> = - ConfigFieldDefinition { - config_file: RESOURCES_CONFIG_FILE_PATH, - path: &["kupo", "protocol"], - name: "Kupo protocol (http/https)", - default: Some("http"), - _marker: PhantomData, - }; - - pub const KUPO_HOSTNAME: ConfigFieldDefinition<'static, String> = ConfigFieldDefinition { - config_file: RESOURCES_CONFIG_FILE_PATH, - path: &["kupo", "hostname"], - name: "Kupo hostname", - default: Some("localhost"), - _marker: PhantomData, - }; - - pub const KUPO_PORT: ConfigFieldDefinition<'static, u16> = ConfigFieldDefinition { - config_file: RESOURCES_CONFIG_FILE_PATH, - path: &["kupo", "port"], - name: "Kupo port", - default: Some("1442"), - _marker: PhantomData, - }; - pub const OGMIOS_PROTOCOL: ConfigFieldDefinition<'static, NetworkProtocol> = ConfigFieldDefinition { config_file: RESOURCES_CONFIG_FILE_PATH, diff --git a/toolkit/partner-chains-cli/src/deregister/mod.rs b/toolkit/partner-chains-cli/src/deregister/mod.rs index 95c319a09..76bb1c26f 100644 --- a/toolkit/partner-chains-cli/src/deregister/mod.rs +++ b/toolkit/partner-chains-cli/src/deregister/mod.rs @@ -7,7 +7,7 @@ use crate::config::config_fields::{ }; use crate::config::CHAIN_CONFIG_FILE_PATH; use crate::io::IOContext; -use crate::pc_contracts_cli_resources::establish_pc_contracts_cli_configuration; +use crate::ogmios::config::establish_ogmios_configuration; use crate::CmdRun; use anyhow::anyhow; use partner_chains_cardano_offchain::register::Deregister; @@ -30,8 +30,8 @@ impl CmdRun for DeregisterCmd { let cold_vkey_path = CARDANO_COLD_VERIFICATION_KEY_FILE.prompt_with_default_from_file_and_save(context); let stake_ownership_pub_key = get_mc_pubkey_from_file(&cold_vkey_path, context)?; - let pc_contracts_cli_resources = establish_pc_contracts_cli_configuration(context)?; - let offchain = context.offchain_impl(&pc_contracts_cli_resources.ogmios)?; + let ogmios_config = establish_ogmios_configuration(context)?; + let offchain = context.offchain_impl(&ogmios_config)?; let runtime = tokio::runtime::Runtime::new().map_err(|e| anyhow::anyhow!(e))?; runtime diff --git a/toolkit/partner-chains-cli/src/deregister/tests.rs b/toolkit/partner-chains-cli/src/deregister/tests.rs index 9441a45d1..87ace07c6 100644 --- a/toolkit/partner-chains-cli/src/deregister/tests.rs +++ b/toolkit/partner-chains-cli/src/deregister/tests.rs @@ -1,7 +1,8 @@ use crate::config::{CHAIN_CONFIG_FILE_PATH, RESOURCES_CONFIG_FILE_PATH}; use crate::deregister::DeregisterCmd; -use crate::pc_contracts_cli_resources::tests::establish_pc_contracts_cli_configuration_io; -use crate::pc_contracts_cli_resources::PcContractsCliResources; +use crate::ogmios::config::tests::{ + default_ogmios_service_config, establish_ogmios_configuration_io, +}; use crate::tests::{MockIO, MockIOContext, OffchainMock, OffchainMocks}; use crate::CmdRun; use hex_literal::hex; @@ -32,7 +33,7 @@ fn happy_path() { MockIO::file_read(CHAIN_CONFIG_FILE_PATH), print_info_io(), read_keys_io(), - establish_pc_contracts_cli_configuration_io(None, PcContractsCliResources::default()), + establish_ogmios_configuration_io(None, default_ogmios_service_config()), ]); let result = DeregisterCmd.run(&mock_context); assert!(result.is_ok()); @@ -56,7 +57,7 @@ fn errors_if_smart_contracts_dont_output_transaction_id() { MockIO::file_read(CHAIN_CONFIG_FILE_PATH), print_info_io(), read_keys_io(), - establish_pc_contracts_cli_configuration_io(None, PcContractsCliResources::default()), + establish_ogmios_configuration_io(None, default_ogmios_service_config()), ]); let result = DeregisterCmd.run(&mock_context); assert_eq!( diff --git a/toolkit/partner-chains-cli/src/main.rs b/toolkit/partner-chains-cli/src/main.rs index 0d8490a44..7480c54cb 100644 --- a/toolkit/partner-chains-cli/src/main.rs +++ b/toolkit/partner-chains-cli/src/main.rs @@ -10,13 +10,11 @@ pub mod io; pub mod keystore; pub(crate) mod main_chain_follower; pub(crate) mod ogmios; -pub(crate) mod pc_contracts_cli_resources; pub(crate) mod permissioned_candidates; mod prepare_configuration; pub mod register; pub(crate) mod select_utxo; mod setup_main_chain_state; -pub(crate) mod smart_contracts; pub mod start_node; #[cfg(test)] diff --git a/toolkit/partner-chains-cli/src/ogmios/config.rs b/toolkit/partner-chains-cli/src/ogmios/config.rs new file mode 100644 index 000000000..0fabaff25 --- /dev/null +++ b/toolkit/partner-chains-cli/src/ogmios/config.rs @@ -0,0 +1,82 @@ +use crate::config::config_fields::*; +use crate::config::ServiceConfig; +use crate::io::IOContext; + +pub(crate) const OGMIOS_REQUIRED: &str = + "Partner Chains Smart Contracts require access to Ogmios. Please provide its configuration."; + +pub(crate) fn establish_ogmios_configuration( + context: &C, +) -> anyhow::Result { + context.print(OGMIOS_REQUIRED); + prompt_ogmios_configuration(context) +} + +pub(crate) fn prompt_ogmios_configuration( + context: &C, +) -> anyhow::Result { + let ogmios_protocol = OGMIOS_PROTOCOL + .select_options_with_default_from_file_and_save(OGMIOS_PROTOCOL.name, context) + .map_err(anyhow::Error::msg)?; + let ogmios_hostname = OGMIOS_HOSTNAME.prompt_with_default_from_file_and_save(context); + let ogmios_port = OGMIOS_PORT.prompt_with_default_from_file_parse_and_save(context)?; + Ok(ServiceConfig { protocol: ogmios_protocol, hostname: ogmios_hostname, port: ogmios_port }) +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::config::NetworkProtocol; + use crate::prepare_configuration::tests::{ + prompt_multi_option_with_default_and_save_to_existing_file, + prompt_with_default_and_save_to_existing_file, + }; + use crate::tests::MockIO; + use std::str::FromStr; + + pub(crate) fn default_ogmios_service_config() -> ServiceConfig { + ServiceConfig { + protocol: OGMIOS_PROTOCOL + .default + .and_then(|p| NetworkProtocol::from_str(p).ok()) + .unwrap_or(NetworkProtocol::Http), + hostname: OGMIOS_HOSTNAME.default.unwrap_or("localhost").to_string(), + port: OGMIOS_PORT.default.unwrap_or("1337").parse().unwrap(), + } + } + + /// Assumption for this function is that resources config file exists, so tests context should have it. + pub(crate) fn establish_ogmios_configuration_io( + existing_config: Option, + config_to_set: ServiceConfig, + ) -> MockIO { + let default_config = existing_config.unwrap_or(default_ogmios_service_config()); + MockIO::Group(vec![ + MockIO::print(OGMIOS_REQUIRED), + prompt_ogmios_configuration_io(&default_config, &config_to_set), + ]) + } + + pub(crate) fn prompt_ogmios_configuration_io( + default_config: &ServiceConfig, + config_to_set: &ServiceConfig, + ) -> MockIO { + MockIO::Group(vec![ + prompt_multi_option_with_default_and_save_to_existing_file( + OGMIOS_PROTOCOL, + Some(&default_config.protocol.to_string()), + &config_to_set.protocol.to_string(), + ), + prompt_with_default_and_save_to_existing_file( + OGMIOS_HOSTNAME, + Some(&default_config.hostname), + &config_to_set.hostname, + ), + prompt_with_default_and_save_to_existing_file( + OGMIOS_PORT, + Some(&default_config.port.to_string()), + &config_to_set.port.to_string(), + ), + ]) + } +} diff --git a/toolkit/partner-chains-cli/src/ogmios/mod.rs b/toolkit/partner-chains-cli/src/ogmios/mod.rs index bc5959c46..864e4857a 100644 --- a/toolkit/partner-chains-cli/src/ogmios/mod.rs +++ b/toolkit/partner-chains-cli/src/ogmios/mod.rs @@ -1,3 +1,4 @@ +use crate::IOContext; use anyhow::anyhow; use jsonrpsee::http_client::HttpClient; use ogmios_client::{ @@ -5,6 +6,8 @@ use ogmios_client::{ }; use sidechain_domain::NetworkType; +pub(crate) mod config; + #[derive(Debug, Eq, PartialEq)] pub enum OgmiosRequest { QueryLedgerStateEraSummaries, @@ -139,3 +142,115 @@ impl TryFrom }) } } + +pub(crate) fn get_shelley_config( + addr: &str, + context: &C, +) -> anyhow::Result { + let response = context.ogmios_rpc(addr, OgmiosRequest::QueryNetworkShelleyGenesis)?; + match response { + OgmiosResponse::QueryNetworkShelleyGenesis(shelley_config) => Ok(shelley_config), + other => Err(anyhow::anyhow!(format!("Unexpected response from Ogmios when quering for shelley genesis configuration: {other:?}"))), + } +} + +#[cfg(test)] +pub(crate) mod test_values { + use crate::ogmios::EpochParameters; + + use super::ShelleyGenesisConfiguration; + use super::{EpochBoundary, EraSummary}; + use sidechain_domain::NetworkType; + + pub(crate) fn preprod_eras_summaries() -> Vec { + vec![ + EraSummary { + start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, + parameters: EpochParameters { epoch_length: 21600, slot_length_millis: 20000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 1728000, slot: 86400, epoch: 4 }, + parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 2160000, slot: 518400, epoch: 5 }, + parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 2592000, slot: 950400, epoch: 6 }, + parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 3024000, slot: 1382400, epoch: 7 }, + parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 5184000, slot: 3542400, epoch: 12 }, + parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 5184000, slot: 3542400, epoch: 12 }, + parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 70416000, slot: 68774400, epoch: 163 }, + parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, + }, + ] + } + + pub(crate) fn preprod_shelley_config() -> ShelleyGenesisConfiguration { + ShelleyGenesisConfiguration { + security_parameter: 2160, + active_slots_coefficient: 0.05, + epoch_length: 432000, + slot_length_millis: 1000, + start_time: 1654041600, + network: NetworkType::Testnet, + } + } + + pub(crate) fn preview_eras_summaries() -> Vec { + vec![ + EraSummary { + start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, + parameters: EpochParameters { epoch_length: 4320, slot_length_millis: 20000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, + parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, + parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, + parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, + parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 259200, slot: 259200, epoch: 3 }, + parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, + }, + EraSummary { + start: EpochBoundary { time_seconds: 55814400, slot: 55814400, epoch: 646 }, + parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, + }, + ] + } + + pub(crate) fn preview_shelley_config() -> ShelleyGenesisConfiguration { + ShelleyGenesisConfiguration { + security_parameter: 432, + active_slots_coefficient: 0.05, + epoch_length: 86400, + slot_length_millis: 1000, + start_time: 1666656000, + network: NetworkType::Testnet, + } + } +} diff --git a/toolkit/partner-chains-cli/src/pc_contracts_cli_resources.rs b/toolkit/partner-chains-cli/src/pc_contracts_cli_resources.rs deleted file mode 100644 index b8408a22d..000000000 --- a/toolkit/partner-chains-cli/src/pc_contracts_cli_resources.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::config::config_fields::*; -use crate::config::{NetworkProtocol, ServiceConfig}; -use crate::io::IOContext; -use std::str::FromStr; - -#[derive(Debug, Clone)] -pub(crate) struct PcContractsCliResources { - pub(crate) kupo: ServiceConfig, - pub(crate) ogmios: ServiceConfig, -} - -impl Default for PcContractsCliResources { - fn default() -> Self { - Self { - kupo: ServiceConfig { - protocol: KUPO_PROTOCOL - .default - .and_then(|p| NetworkProtocol::from_str(p).ok()) - .unwrap_or(NetworkProtocol::Http), - hostname: KUPO_HOSTNAME.default.unwrap_or("localhost").to_string(), - port: KUPO_PORT.default.unwrap_or("1442").parse().unwrap(), - }, - ogmios: default_ogmios_service_config(), - } - } -} - -pub(crate) fn default_ogmios_service_config() -> ServiceConfig { - ServiceConfig { - protocol: OGMIOS_PROTOCOL - .default - .and_then(|p| NetworkProtocol::from_str(p).ok()) - .unwrap_or(NetworkProtocol::Http), - hostname: OGMIOS_HOSTNAME.default.unwrap_or("localhost").to_string(), - port: OGMIOS_PORT.default.unwrap_or("1337").parse().unwrap(), - } -} - -pub(crate) const KUPO_AND_OGMIOS_REQUIRED: &str = "Partner Chains Smart Contracts require access to Kupo and Ogmios. Please provide their configuration."; - -pub(crate) fn establish_pc_contracts_cli_configuration( - context: &C, -) -> anyhow::Result { - context.print(KUPO_AND_OGMIOS_REQUIRED); - let kupo_protocol = KUPO_PROTOCOL - .select_options_with_default_from_file_and_save(KUPO_PROTOCOL.name, context) - .map_err(anyhow::Error::msg)?; - let kupo_hostname = KUPO_HOSTNAME.prompt_with_default_from_file_and_save(context); - let kupo_port = KUPO_PORT.prompt_with_default_from_file_parse_and_save(context)?; - let ogmios = prompt_ogmios_configuration(context)?; - Ok(PcContractsCliResources { - kupo: ServiceConfig { protocol: kupo_protocol, hostname: kupo_hostname, port: kupo_port }, - ogmios, - }) -} - -pub(crate) fn prompt_ogmios_configuration( - context: &C, -) -> anyhow::Result { - let ogmios_protocol = OGMIOS_PROTOCOL - .select_options_with_default_from_file_and_save(OGMIOS_PROTOCOL.name, context) - .map_err(anyhow::Error::msg)?; - let ogmios_hostname = OGMIOS_HOSTNAME.prompt_with_default_from_file_and_save(context); - let ogmios_port = OGMIOS_PORT.prompt_with_default_from_file_parse_and_save(context)?; - Ok(ServiceConfig { protocol: ogmios_protocol, hostname: ogmios_hostname, port: ogmios_port }) -} - -#[cfg(test)] -pub(crate) mod tests { - use crate::pc_contracts_cli_resources::*; - use crate::prepare_configuration::tests::{ - prompt_multi_option_with_default_and_save_to_existing_file, - prompt_with_default_and_save_to_existing_file, - }; - use crate::tests::MockIO; - - /// Assumption for this function is that resources config file exists, so tests context should have it. - pub(crate) fn establish_pc_contracts_cli_configuration_io( - existing_config: Option, - config_to_set: PcContractsCliResources, - ) -> MockIO { - let default_config = existing_config.unwrap_or_default(); - MockIO::Group(vec![ - MockIO::print(KUPO_AND_OGMIOS_REQUIRED), - prompt_multi_option_with_default_and_save_to_existing_file( - KUPO_PROTOCOL, - Some(&default_config.kupo.protocol.to_string()), - &config_to_set.kupo.protocol.to_string(), - ), - prompt_with_default_and_save_to_existing_file( - KUPO_HOSTNAME, - Some(&default_config.kupo.hostname), - &config_to_set.kupo.hostname, - ), - prompt_with_default_and_save_to_existing_file( - KUPO_PORT, - Some(&default_config.kupo.port.to_string()), - &config_to_set.kupo.port.to_string(), - ), - prompt_ogmios_configuration_io(&default_config.ogmios, &config_to_set.ogmios), - ]) - } - - pub(crate) fn prompt_ogmios_configuration_io( - default_config: &ServiceConfig, - config_to_set: &ServiceConfig, - ) -> MockIO { - MockIO::Group(vec![ - prompt_multi_option_with_default_and_save_to_existing_file( - OGMIOS_PROTOCOL, - Some(&default_config.protocol.to_string()), - &config_to_set.protocol.to_string(), - ), - prompt_with_default_and_save_to_existing_file( - OGMIOS_HOSTNAME, - Some(&default_config.hostname), - &config_to_set.hostname, - ), - prompt_with_default_and_save_to_existing_file( - OGMIOS_PORT, - Some(&default_config.port.to_string()), - &config_to_set.port.to_string(), - ), - ]) - } -} diff --git a/toolkit/partner-chains-cli/src/prepare_configuration/prepare_cardano_params.rs b/toolkit/partner-chains-cli/src/prepare_configuration/prepare_cardano_params.rs index 64101ce2b..196533d2b 100644 --- a/toolkit/partner-chains-cli/src/prepare_configuration/prepare_cardano_params.rs +++ b/toolkit/partner-chains-cli/src/prepare_configuration/prepare_cardano_params.rs @@ -1,6 +1,8 @@ use crate::config::{CardanoParameters, ServiceConfig}; use crate::io::IOContext; -use crate::ogmios::{EraSummary, OgmiosRequest, OgmiosResponse, ShelleyGenesisConfiguration}; +use crate::ogmios::{ + get_shelley_config, EraSummary, OgmiosRequest, OgmiosResponse, ShelleyGenesisConfiguration, +}; pub fn prepare_cardano_params( ogmios_config: &ServiceConfig, @@ -22,17 +24,6 @@ fn get_eras_summaries(addr: &str, context: &C) -> anyhow::Result( - addr: &str, - context: &C, -) -> anyhow::Result { - let response = context.ogmios_rpc(addr, OgmiosRequest::QueryNetworkShelleyGenesis)?; - match response { - OgmiosResponse::QueryNetworkShelleyGenesis(shelley_config) => Ok(shelley_config), - other => Err(anyhow::anyhow!(format!("Unexpected response from Ogmios when quering for shelley genesis configuration: {other:?}"))), - } -} - fn caradano_parameters( eras_summaries: Vec, shelley_config: ShelleyGenesisConfiguration, @@ -51,7 +42,6 @@ fn caradano_parameters( .checked_add(first_epoch_era.start.time_seconds) .and_then(|seconds| seconds.checked_mul(1000)) .ok_or_else(|| anyhow::anyhow!("First epoch timestamp overflow"))?, - network: shelley_config.network, }) } @@ -78,11 +68,14 @@ fn get_first_epoch_era(eras_summaries: Vec) -> Result Vec { - vec![ - EraSummary { - start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, - parameters: EpochParameters { epoch_length: 21600, slot_length_millis: 20000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 1728000, slot: 86400, epoch: 4 }, - parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 2160000, slot: 518400, epoch: 5 }, - parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 2592000, slot: 950400, epoch: 6 }, - parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 3024000, slot: 1382400, epoch: 7 }, - parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 5184000, slot: 3542400, epoch: 12 }, - parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 5184000, slot: 3542400, epoch: 12 }, - parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 70416000, slot: 68774400, epoch: 163 }, - parameters: EpochParameters { epoch_length: 432000, slot_length_millis: 1000 }, - }, - ] - } - - pub(crate) fn preprod_shelley_config() -> ShelleyGenesisConfiguration { - ShelleyGenesisConfiguration { - network: NetworkType::Testnet, - security_parameter: 2160, - active_slots_coefficient: 0.05, - epoch_length: 432000, - slot_length_millis: 1000, - start_time: 1654041600, - } - } - - pub(crate) fn preview_eras_summaries() -> Vec { - vec![ - EraSummary { - start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, - parameters: EpochParameters { epoch_length: 4320, slot_length_millis: 20000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, - parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, - parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, - parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 0, slot: 0, epoch: 0 }, - parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 259200, slot: 259200, epoch: 3 }, - parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, - }, - EraSummary { - start: EpochBoundary { time_seconds: 55814400, slot: 55814400, epoch: 646 }, - parameters: EpochParameters { epoch_length: 86400, slot_length_millis: 1000 }, - }, - ] - } - - pub(crate) fn preview_shelley_config() -> ShelleyGenesisConfiguration { - ShelleyGenesisConfiguration { - network: NetworkType::Testnet, - security_parameter: 432, - active_slots_coefficient: 0.05, - epoch_length: 86400, - slot_length_millis: 1000, - start_time: 1666656000, - } - } } diff --git a/toolkit/partner-chains-cli/src/prepare_configuration/prepare_chain_params.rs b/toolkit/partner-chains-cli/src/prepare_configuration/prepare_chain_params.rs index 6a7352623..a5027fac3 100644 --- a/toolkit/partner-chains-cli/src/prepare_configuration/prepare_chain_params.rs +++ b/toolkit/partner-chains-cli/src/prepare_configuration/prepare_chain_params.rs @@ -1,18 +1,18 @@ +use crate::cardano_key; use crate::config::config_fields::{self, GENESIS_UTXO}; use crate::config::{ConfigFieldDefinition, ServiceConfig}; use crate::io::IOContext; +use crate::ogmios::config::prompt_ogmios_configuration; +use crate::ogmios::get_shelley_config; use crate::select_utxo::{query_utxos, select_from_utxos}; -use crate::{cardano_key, pc_contracts_cli_resources}; use anyhow::anyhow; use partner_chains_cardano_offchain::csl::NetworkTypeExt; use serde::de::DeserializeOwned; use sidechain_domain::{NetworkType, UtxoId}; -use super::prepare_cardano_params::get_shelley_config; - pub fn prepare_chain_params(context: &C) -> anyhow::Result<(UtxoId, ServiceConfig)> { context.eprint(INTRO); - let ogmios_configuration = pc_contracts_cli_resources::prompt_ogmios_configuration(context)?; + let ogmios_configuration = prompt_ogmios_configuration(context)?; let shelley_config = get_shelley_config(&ogmios_configuration.to_string(), context)?; let address = derive_address(context, shelley_config.network)?; let utxo_query_result = query_utxos(context, &ogmios_configuration, &address)?; @@ -62,10 +62,11 @@ const CAUTION: &str = mod tests { use crate::config::config_fields::GENESIS_UTXO; use crate::config::RESOURCES_CONFIG_FILE_PATH; + use crate::ogmios::config::tests::{ + default_ogmios_service_config, prompt_ogmios_configuration_io, + }; + use crate::ogmios::test_values::preview_shelley_config; use crate::ogmios::{OgmiosRequest, OgmiosResponse}; - use crate::pc_contracts_cli_resources::default_ogmios_service_config; - use crate::pc_contracts_cli_resources::tests::prompt_ogmios_configuration_io; - use crate::prepare_configuration::prepare_cardano_params::tests::preview_shelley_config; use crate::prepare_configuration::prepare_chain_params::{ prepare_chain_params, CAUTION, INTRO, }; 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 b8d87f7b9..e677d9dea 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 @@ -119,16 +119,15 @@ After setting up the permissioned candidates, execute the 'create-chain-spec' co #[cfg(test)] mod tests { use super::*; - use crate::config::config_fields::{GENESIS_UTXO, KUPO_PROTOCOL, OGMIOS_PROTOCOL}; + use crate::config::config_fields::{GENESIS_UTXO, OGMIOS_PROTOCOL}; use crate::config::NetworkProtocol; use crate::ogmios::{OgmiosRequest, OgmiosResponse}; - use crate::prepare_configuration::prepare_cardano_params::tests::{ - preprod_eras_summaries, preprod_shelley_config, PREPROD_CARDANO_PARAMS, - }; + 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; @@ -161,7 +160,6 @@ mod tests { pub fn save_cardano_params() -> MockIO { MockIO::Group(vec![ - save_to_existing_file(CARDANO_NETWORK, &PREPROD_CARDANO_PARAMS.network.to_string()), save_to_existing_file( CARDANO_SECURITY_PARAMETER, &PREPROD_CARDANO_PARAMS.security_parameter.to_string(), @@ -305,7 +303,7 @@ mod tests { }), ) .with_json_file("payment.skey", payment_key_content()) - .with_json_file(KUPO_PROTOCOL.config_file, serde_json::json!({})) + .with_json_file(OGMIOS_PROTOCOL.config_file, serde_json::json!({})) .with_offchain_mocks(preprod_offchain_mocks()) .with_expected_io(vec![ MockIO::ogmios_request( @@ -409,7 +407,6 @@ mod tests { fn test_chain_config() -> Value { serde_json::json!({ "cardano": { - "network": "testnet", "security_parameter": PREPROD_CARDANO_PARAMS.security_parameter, "active_slots_coeff": PREPROD_CARDANO_PARAMS.active_slots_coeff, "first_epoch_number": PREPROD_CARDANO_PARAMS.first_epoch_number, diff --git a/toolkit/partner-chains-cli/src/register/register1.rs b/toolkit/partner-chains-cli/src/register/register1.rs index d60374a99..924a46a1e 100644 --- a/toolkit/partner-chains-cli/src/register/register1.rs +++ b/toolkit/partner-chains-cli/src/register/register1.rs @@ -7,6 +7,8 @@ use crate::{config::config_fields, *}; use anyhow::anyhow; use cli_commands::registration_signatures::RegisterValidatorMessage; use cli_commands::signing::sc_public_key_and_signature_for_datum; +use ogmios::config::prompt_ogmios_configuration; +use ogmios::get_shelley_config; use partner_chains_cardano_offchain::csl::NetworkTypeExt; use select_utxo::{query_utxos, select_from_utxos}; use serde::de::DeserializeOwned; @@ -22,10 +24,6 @@ impl CmdRun for Register1Cmd { fn run(&self, context: &C) -> anyhow::Result<()> { context.print("⚙️ Registering as a committee candidate (step 1/3)"); let genesis_utxo = load_chain_config_field(context, &config_fields::GENESIS_UTXO)?; - let cardano_network = config_fields::CARDANO_NETWORK.load_from_file(context).ok_or_else(|| { - context.eprint("⚠️ Cardano network is not specified in the chain configuration file `partner-chains-cli-chain-config.json`"); - anyhow!("failed to read cardano network") - })?; let node_data_base_path = config_fields::SUBSTRATE_NODE_DATA_BASE_PATH .load_from_file(context) @@ -40,9 +38,10 @@ impl CmdRun for Register1Cmd { })?; context.print("This wizard will query your UTXOs using address derived from the payment verification key and Ogmios service"); - let address = derive_address(context, cardano_network)?; - let ogmios_configuration = - pc_contracts_cli_resources::prompt_ogmios_configuration(context)?; + let ogmios_configuration = prompt_ogmios_configuration(context)?; + let shelley_genesis_config = + get_shelley_config(&format!("{ogmios_configuration}"), context)?; + let address = derive_address(context, shelley_genesis_config.network)?; let utxo_query_result = query_utxos(context, &ogmios_configuration, &address)?; if utxo_query_result.is_empty() { @@ -162,9 +161,11 @@ fn derive_address( mod tests { use super::*; use crate::tests::{MockIO, MockIOContext}; - use ogmios::OgmiosRequest; - use pc_contracts_cli_resources::default_ogmios_service_config; - use pc_contracts_cli_resources::tests::prompt_ogmios_configuration_io; + use ogmios::{ + config::tests::{default_ogmios_service_config, prompt_ogmios_configuration_io}, + test_values::preview_shelley_config, + OgmiosRequest, + }; use select_utxo::tests::{mock_7_valid_utxos_rows, mock_result_7_valid}; const PAYMENT_VKEY_PATH: &str = "payment.vkey"; @@ -261,35 +262,6 @@ mod tests { assert!(result.is_ok()); } - #[test] - fn fail_if_cardano_network_is_not_specified() { - let chain_config_without_cardano_network: serde_json::Value = serde_json::json!({ - "chain_parameters": { - "genesis_utxo": "0000000000000000000000000000000000000000000000000000000000000001#0", - }, - }); - - let mock_context = MockIOContext::new() - .with_json_file(CHAIN_CONFIG_PATH, chain_config_without_cardano_network) - .with_json_file(RESOURCE_CONFIG_PATH, resource_config_content()) - .with_json_file(KEYS_FILE_PATH, generated_keys_file_content()) - .with_expected_io( - vec![ - intro_msg_io(), - read_chain_config_io(), - vec![ - MockIO::eprint("⚠️ Cardano network is not specified in the chain configuration file `partner-chains-cli-chain-config.json`"), - ] - ] - .into_iter() - .flatten() - .collect::>(), - ); - - let result = Register1Cmd {}.run(&mock_context); - assert!(result.is_err()); - } - #[test] fn report_error_if_payment_file_is_invalid() { let mock_context = MockIOContext::new() @@ -329,11 +301,9 @@ mod tests { intro_msg_io(), read_chain_config_io(), read_resource_config_io(), + derive_address_io(), vec![ - address_and_utxo_msg_io(), - prompt_cardano_payment_verification_key_file_io(), - read_payment_verification_key_file_io(), - prompt_ogmios_configuration_io(&default_ogmios_service_config(), &default_ogmios_service_config()), + MockIO::print("⚙️ Querying UTXOs of addr_test1vqezxrh24ts0775hulcg3ejcwj7hns8792vnn8met6z9gwsxt87zy from Ogmios at http://localhost:1337..."), MockIO::ogmios_request( "http://localhost:1337", @@ -483,7 +453,6 @@ mod tests { fn read_chain_config_io() -> Vec { vec![ MockIO::file_read(CHAIN_CONFIG_PATH), // genesis utxo - MockIO::file_read(CHAIN_CONFIG_PATH), // cardano network ] } @@ -500,6 +469,14 @@ mod tests { ]) } + fn ogmios_network_request_io() -> MockIO { + MockIO::ogmios_request( + "http://localhost:1337", + OgmiosRequest::QueryNetworkShelleyGenesis, + Ok(ogmios::OgmiosResponse::QueryNetworkShelleyGenesis(preview_shelley_config())), + ) + } + fn prompt_cardano_payment_verification_key_file_io() -> MockIO { MockIO::Group(vec![ MockIO::file_read(RESOURCE_CONFIG_PATH), @@ -511,7 +488,15 @@ mod tests { MockIO::file_read(RESOURCE_CONFIG_PATH), MockIO::file_write_json( RESOURCE_CONFIG_PATH, - serde_json::json!({"substrate_node_base_path": "/path/to/data", "cardano_payment_verification_key_file": PAYMENT_VKEY_PATH}), + serde_json::json!({ + "substrate_node_base_path": "/path/to/data", + "cardano_payment_verification_key_file": PAYMENT_VKEY_PATH, + "ogmios": { + "hostname": "localhost", + "port": 1337, + "protocol": "http" + } + }), ), ]) } @@ -523,23 +508,22 @@ mod tests { fn derive_address_io() -> Vec { vec![ address_and_utxo_msg_io(), + prompt_ogmios_configuration_io( + &default_ogmios_service_config(), + &default_ogmios_service_config(), + ), + ogmios_network_request_io(), prompt_cardano_payment_verification_key_file_io(), read_payment_verification_key_file_io(), ] } fn query_utxos_io() -> Vec { - vec![ - prompt_ogmios_configuration_io( - &default_ogmios_service_config(), - &default_ogmios_service_config(), - ), - crate::select_utxo::tests::query_utxos_io( - "addr_test1vqezxrh24ts0775hulcg3ejcwj7hns8792vnn8met6z9gwsxt87zy", - "http://localhost:1337", - mock_result_7_valid(), - ), - ] + vec![crate::select_utxo::tests::query_utxos_io( + "addr_test1vqezxrh24ts0775hulcg3ejcwj7hns8792vnn8met6z9gwsxt87zy", + "http://localhost:1337", + mock_result_7_valid(), + )] } fn select_utxo_io() -> Vec { diff --git a/toolkit/partner-chains-cli/src/register/register3.rs b/toolkit/partner-chains-cli/src/register/register3.rs index ef1d68ecb..8aefd0692 100644 --- a/toolkit/partner-chains-cli/src/register/register3.rs +++ b/toolkit/partner-chains-cli/src/register/register3.rs @@ -4,7 +4,7 @@ use crate::config::config_fields; use crate::config::CHAIN_CONFIG_FILE_PATH; use crate::io::IOContext; use crate::main_chain_follower::set_main_chain_follower_env; -use crate::pc_contracts_cli_resources::establish_pc_contracts_cli_configuration; +use crate::ogmios::config::establish_ogmios_configuration; use crate::CmdRun; use clap::Parser; use partner_chains_cardano_offchain::register::Register; @@ -49,10 +49,9 @@ impl CmdRun for Register3Cmd { let cardano_payment_signing_key_path = context.prompt("Path to mainchain payment signing key file", Some("payment.skey")); - let pc_contracts_cli_resources = establish_pc_contracts_cli_configuration(context)?; let payment_signing_key = get_mc_pkey_from_file(&cardano_payment_signing_key_path, context)?; - + let ogmios_configuration = establish_ogmios_configuration(context)?; let candidate_registration = CandidateRegistration { stake_ownership: AdaBasedStaking { pub_key: self.spo_public_key.clone(), @@ -65,7 +64,7 @@ impl CmdRun for Register3Cmd { aura_pub_key: self.aura_pub_key.clone(), grandpa_pub_key: self.grandpa_pub_key.clone(), }; - let offchain = context.offchain_impl(&pc_contracts_cli_resources.ogmios)?; + let offchain = context.offchain_impl(&ogmios_configuration)?; let runtime = tokio::runtime::Runtime::new().map_err(|e| anyhow::anyhow!(e))?; runtime @@ -154,9 +153,7 @@ mod tests { config_fields::POSTGRES_CONNECTION_STRING, CHAIN_CONFIG_FILE_PATH, RESOURCES_CONFIG_FILE_PATH, }, - pc_contracts_cli_resources::{ - tests::establish_pc_contracts_cli_configuration_io, PcContractsCliResources, - }, + ogmios::config::tests::{default_ogmios_service_config, establish_ogmios_configuration_io}, tests::{MockIO, MockIOContext, OffchainMock, OffchainMocks}, }; use hex_literal::hex; @@ -185,11 +182,8 @@ mod tests { vec![ intro_msg_io(), prompt_mc_payment_key_path_io(), - vec![establish_pc_contracts_cli_configuration_io( - None, - PcContractsCliResources::default(), - )], - run_registration_io(), + read_payment_skey(), + get_ogmios_config(), prompt_for_registration_status_y(), show_registration_status_io(), ] @@ -222,11 +216,8 @@ mod tests { vec![ intro_msg_io(), prompt_mc_payment_key_path_io(), - vec![establish_pc_contracts_cli_configuration_io( - None, - PcContractsCliResources::default(), - )], - run_registration_fail_io(), + read_payment_skey(), + get_ogmios_config(), ] .into_iter() .flatten() @@ -257,11 +248,8 @@ mod tests { vec![ intro_msg_io(), prompt_mc_payment_key_path_io(), - vec![establish_pc_contracts_cli_configuration_io( - None, - PcContractsCliResources::default(), - )], - run_registration_io(), + read_payment_skey(), + get_ogmios_config(), prompt_for_registration_status_n(), ] .into_iter() @@ -290,6 +278,13 @@ mod tests { )] } + fn get_ogmios_config() -> Vec { + vec![establish_ogmios_configuration_io( + Some(default_ogmios_service_config()), + default_ogmios_service_config(), + )] + } + fn prompt_for_registration_status_y() -> Vec { vec![MockIO::prompt_yes_no("Show registration status?", true, true)] } @@ -326,11 +321,7 @@ mod tests { ] } - fn run_registration_io() -> Vec { - vec![MockIO::file_read("/path/to/payment.skey")] - } - - fn run_registration_fail_io() -> Vec { + fn read_payment_skey() -> Vec { vec![MockIO::file_read("/path/to/payment.skey")] } diff --git a/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs b/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs index ddd89966b..c2ceb9a8f 100644 --- a/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs +++ b/toolkit/partner-chains-cli/src/setup_main_chain_state/mod.rs @@ -1,7 +1,7 @@ use crate::config::config_fields::{CARDANO_PAYMENT_SIGNING_KEY_FILE, POSTGRES_CONNECTION_STRING}; use crate::config::{config_fields, ChainConfig, ConfigFieldDefinition, CHAIN_CONFIG_FILE_PATH}; use crate::io::IOContext; -use crate::pc_contracts_cli_resources::prompt_ogmios_configuration; +use crate::ogmios::config::prompt_ogmios_configuration; use crate::permissioned_candidates::{ParsedPermissionedCandidatesKeys, PermissionedCandidateKeys}; use crate::{cardano_key, CmdRun}; use anyhow::anyhow; diff --git a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs index ec81750e7..1db6b072b 100644 --- a/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs +++ b/toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs @@ -1,8 +1,7 @@ use crate::config::config_fields; use crate::config::config_fields::CARDANO_PAYMENT_SIGNING_KEY_FILE; use crate::config::{CHAIN_CONFIG_FILE_PATH, RESOURCES_CONFIG_FILE_PATH}; -use crate::pc_contracts_cli_resources::default_ogmios_service_config; -use crate::pc_contracts_cli_resources::tests::prompt_ogmios_configuration_io; +use crate::ogmios::config::tests::{default_ogmios_service_config, prompt_ogmios_configuration_io}; use crate::prepare_configuration::tests::{ prompt_and_save_to_existing_file, prompt_with_default_and_save_to_existing_file, }; diff --git a/toolkit/partner-chains-cli/src/smart_contracts.rs b/toolkit/partner-chains-cli/src/smart_contracts.rs deleted file mode 100644 index 3af2629b0..000000000 --- a/toolkit/partner-chains-cli/src/smart_contracts.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::pc_contracts_cli_resources::PcContractsCliResources; -use sidechain_domain::UtxoId; - -pub fn sidechain_params_arguments(genesis_utxo: UtxoId) -> String { - format!("--genesis-utxo {}", genesis_utxo) -} - -pub fn runtime_config_arguments( - runtime_config: &PcContractsCliResources, - payment_signing_key_path: &str, -) -> String { - format!("--kupo-host {} --kupo-port {} {} --ogmios-host {} --ogmios-port {} {} --payment-signing-key-file {}", - runtime_config.kupo.hostname, - runtime_config.kupo.port, - if runtime_config.kupo.protocol.is_secure() { "--kupo-secure" } else { "" }, - runtime_config.ogmios.hostname, - runtime_config.ogmios.port, - if runtime_config.ogmios.protocol.is_secure() { "--ogmios-secure" } else { "" }, - payment_signing_key_path - ) -} diff --git a/toolkit/partner-chains-cli/src/start_node/mod.rs b/toolkit/partner-chains-cli/src/start_node/mod.rs index 76817a878..f0fd13f3f 100644 --- a/toolkit/partner-chains-cli/src/start_node/mod.rs +++ b/toolkit/partner-chains-cli/src/start_node/mod.rs @@ -195,7 +195,6 @@ pub fn start_node( first_slot_number, epoch_duration_millis, first_epoch_timestamp_millis, - network: _network, }, bootnodes, }: StartNodeChainConfig, From b07ebe5b9c58cf39a1b3a83012372d61e4ff5920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lech=20G=C5=82owiak?= Date: Fri, 20 Dec 2024 09:50:54 +0100 Subject: [PATCH 9/9] fix: permissioned candidates + refactor common methods --- changelog.md | 2 +- toolkit/offchain/src/csl.rs | 32 +- toolkit/offchain/src/d_param/mod.rs | 114 ++---- toolkit/offchain/src/d_param/tests.rs | 40 +- toolkit/offchain/src/init_governance/mod.rs | 65 ++- .../offchain/src/permissioned_candidates.rs | 386 ++++++++---------- toolkit/offchain/src/plutus_script.rs | 5 +- toolkit/offchain/src/register.rs | 4 +- toolkit/offchain/src/reserve/init.rs | 13 +- toolkit/offchain/src/test_values.rs | 19 +- toolkit/offchain/tests/integration_tests.rs | 48 ++- 11 files changed, 334 insertions(+), 394 deletions(-) diff --git a/changelog.md b/changelog.md index 42d1f8ce8..8ad80353e 100644 --- a/changelog.md +++ b/changelog.md @@ -6,7 +6,7 @@ This changelog is based on [Keep A Changelog](https://keepachangelog.com/en/1.1. ## Changed -* `setup-main-chain-state` command now uses native Rust to insert the D-Parameter +* `setup-main-chain-state` command now uses native Rust to upsert the D-Parameter and upsert permissioned candidates ## Removed diff --git a/toolkit/offchain/src/csl.rs b/toolkit/offchain/src/csl.rs index bb2414a5c..73f78bd7e 100644 --- a/toolkit/offchain/src/csl.rs +++ b/toolkit/offchain/src/csl.rs @@ -321,7 +321,7 @@ pub(crate) trait TransactionBuilderExt { fn add_mint_one_script_token( &mut self, script: &PlutusScript, - ex_units: ExUnits, + ex_units: &ExUnits, ) -> Result<(), JsError>; /// Adds minting of 1 token (with empty asset name) for the given script using reference input @@ -329,7 +329,7 @@ pub(crate) trait TransactionBuilderExt { &mut self, script: &PlutusScript, ref_input: &TransactionInput, - ex_units: ExUnits, + ex_units: &ExUnits, ) -> Result<(), JsError>; /// Sets fields required by the most of partner-chains smart contract transactions. @@ -389,7 +389,7 @@ impl TransactionBuilderExt for TransactionBuilder { fn add_mint_one_script_token( &mut self, script: &PlutusScript, - ex_units: ExUnits, + ex_units: &ExUnits, ) -> Result<(), JsError> { let mut mint_builder = self.get_mint_builder().unwrap_or(MintBuilder::new()); @@ -400,7 +400,7 @@ impl TransactionBuilderExt for TransactionBuilder { &RedeemerTag::new_mint(), &0u32.into(), &PlutusData::new_empty_constr_plutus_data(&0u32.into()), - &ex_units, + ex_units, ), ); mint_builder.add_asset(&mint_witness, &empty_asset_name(), &Int::new_i32(1))?; @@ -412,7 +412,7 @@ impl TransactionBuilderExt for TransactionBuilder { &mut self, script: &PlutusScript, ref_input: &TransactionInput, - ex_units: ExUnits, + ex_units: &ExUnits, ) -> Result<(), JsError> { let mut mint_builder = self.get_mint_builder().unwrap_or(MintBuilder::new()); @@ -428,7 +428,7 @@ impl TransactionBuilderExt for TransactionBuilder { &RedeemerTag::new_mint(), &0u32.into(), &PlutusData::new_empty_constr_plutus_data(&0u32.into()), - &ex_units, + ex_units, ), ); mint_builder.add_asset(&mint_witness, &empty_asset_name(), &Int::new_i32(1))?; @@ -500,7 +500,7 @@ pub(crate) trait InputsBuilderExt: Sized { &mut self, utxo: &OgmiosUtxo, script: &PlutusScript, - ex_units: ExUnits, + ex_units: &ExUnits, ) -> Result<(), JsError>; /// Adds ogmios inputs to the tx inputs builder. @@ -515,7 +515,7 @@ impl InputsBuilderExt for TxInputsBuilder { &mut self, utxo: &OgmiosUtxo, script: &PlutusScript, - ex_units: ExUnits, + ex_units: &ExUnits, ) -> Result<(), JsError> { let input = utxo.to_csl_tx_input(); let amount = convert_value(&utxo.value)?; @@ -526,7 +526,7 @@ impl InputsBuilderExt for TxInputsBuilder { // CSL will set redeemer index for the index of script input after sorting transaction inputs &0u32.into(), &PlutusData::new_empty_constr_plutus_data(&0u32.into()), - &ex_units, + ex_units, ), ); self.add_plutus_script_input(&witness, &input, &amount); @@ -729,11 +729,12 @@ mod tests { #[cfg(test)] mod prop_tests { - use super::{get_builder_config, OgmiosUtxoExt, TransactionBuilderExt, TransactionContext}; + use super::{ + get_builder_config, zero_ex_units, OgmiosUtxoExt, TransactionBuilderExt, TransactionContext, + }; use crate::test_values::*; use cardano_serialization_lib::{ - BigNum, ExUnits, NetworkIdKind, Transaction, TransactionBuilder, TransactionInputs, - TransactionOutput, Value, + NetworkIdKind, Transaction, TransactionBuilder, TransactionInputs, TransactionOutput, Value, }; use ogmios_client::types::OgmiosValue; use ogmios_client::types::{OgmiosTx, OgmiosUtxo}; @@ -755,12 +756,7 @@ mod prop_tests { protocol_parameters: protocol_parameters(), }; let mut tx_builder = TransactionBuilder::new(&get_builder_config(&ctx).unwrap()); - tx_builder - .add_mint_one_script_token( - &test_policy(), - ExUnits::new(&BigNum::zero(), &BigNum::zero()), - ) - .unwrap(); + tx_builder.add_mint_one_script_token(&test_policy(), &zero_ex_units()).unwrap(); tx_builder .add_output_with_one_script_token( &test_validator(), diff --git a/toolkit/offchain/src/d_param/mod.rs b/toolkit/offchain/src/d_param/mod.rs index b42b44f97..cab66e6ee 100644 --- a/toolkit/offchain/src/d_param/mod.rs +++ b/toolkit/offchain/src/d_param/mod.rs @@ -5,18 +5,18 @@ //! `datum` field being `[num_permissioned_candidates, num_registered_candidates]`. use crate::csl::{ - get_builder_config, get_validator_budgets, InputsBuilderExt, ScriptExUnits, + get_builder_config, get_validator_budgets, zero_ex_units, InputsBuilderExt, ScriptExUnits, TransactionBuilderExt, TransactionContext, }; +use crate::init_governance::{self, GovernanceData}; use crate::plutus_script::PlutusScript; use anyhow::anyhow; use cardano_serialization_lib::{ ExUnits, JsError, PlutusData, ScriptHash, Transaction, TransactionBuilder, TxInputsBuilder, }; -use cardano_serialization_lib::{LanguageKind, TransactionHash, TransactionInput}; use ogmios_client::{ query_ledger_state::QueryLedgerState, query_network::QueryNetwork, transactions::Transactions, - types::OgmiosScript::Plutus, types::OgmiosUtxo, + types::OgmiosUtxo, }; use partner_chains_plutus_data::d_param::{d_parameter_to_plutus_data, DParamDatum}; use sidechain_domain::{DParameter, McTxHash, UtxoId}; @@ -116,24 +116,17 @@ async fn insert_d_param( ctx: TransactionContext, genesis_utxo: UtxoId, client: &C, -) -> anyhow::Result -where - C: Transactions, -{ - let zero_ex_units = ExUnits::new(&0u64.into(), &0u64.into()); - - let gov_utxo = crate::init_governance::get_governance_utxo(genesis_utxo, client) - .await - .map_err(|e| JsError::from_str(e.to_string().as_str()))?; +) -> anyhow::Result { + let gov_data = init_governance::get_governance_data(genesis_utxo, client).await?; let tx = mint_d_param_token_tx( validator, policy, d_parameter, + &gov_data, &ctx, - zero_ex_units.clone(), - zero_ex_units, - gov_utxo.clone(), + &zero_ex_units(), + &zero_ex_units(), )?; let evaluate_response = client.evaluate_transaction(&tx.to_bytes()).await.map_err(|e| { @@ -144,26 +137,27 @@ where ) })?; - let ogmios_gov_script = - gov_utxo.clone().script.expect("Gov UTXO should have a reference script"); - let gov_policy = PlutusScript::from_ogmios(ogmios_gov_script)?; let mint_keys = tx.body().mint().expect("insert D parameter transaction has two mints").keys(); let script_to_index: HashMap = vec![(mint_keys.get(0), 0), (mint_keys.get(1), 1)].into_iter().collect(); let mint_ex_units = get_validator_budgets(evaluate_response).mint_ex_units; - let policy_idx = script_to_index.get(&policy.csl_script_hash()).unwrap().clone(); - let gov_policy_idx = script_to_index.get(&gov_policy.csl_script_hash()).unwrap().clone(); - let policy_ex_units = mint_ex_units.get(policy_idx).expect("Ogmios response should have entry for d_param policy"); - let gov_policy_ex_units = mint_ex_units.get(gov_policy_idx).expect("Ogmios response should have entry for gov policy"); + let policy_idx = *script_to_index.get(&policy.csl_script_hash()).unwrap(); + let gov_policy_idx = *script_to_index.get(&gov_data.policy_script.csl_script_hash()).unwrap(); + let policy_ex_units = mint_ex_units + .get(policy_idx) + .expect("Evaluate transaction response should have entry for d_param policy"); + let gov_policy_ex_units = mint_ex_units + .get(gov_policy_idx) + .expect("Evaluate transaction response should have entry for governance policy"); let tx = mint_d_param_token_tx( validator, policy, d_parameter, + &gov_data, &ctx, - policy_ex_units.clone(), - gov_policy_ex_units.clone(), - gov_utxo, + policy_ex_units, + gov_policy_ex_units, )?; let signed_tx = ctx.sign(&tx).to_bytes(); let res = client.submit_transaction(&signed_tx).await.map_err(|e| { @@ -186,27 +180,22 @@ async fn update_d_param( ctx: TransactionContext, genesis_utxo: UtxoId, client: &C, -) -> anyhow::Result -where - C: Transactions, -{ +) -> anyhow::Result { let zero_ex_units = ScriptExUnits { - mint_ex_units: vec![ExUnits::new(&0u64.into(), &0u64.into())], - spend_ex_units: vec![ExUnits::new(&0u64.into(), &0u64.into())], + mint_ex_units: vec![zero_ex_units()], + spend_ex_units: vec![zero_ex_units()], }; - let gov_utxo = crate::init_governance::get_governance_utxo(genesis_utxo, client) - .await - .map_err(|e| JsError::from_str(e.to_string().as_str()))?; + let governance_data = init_governance::get_governance_data(genesis_utxo, client).await?; let tx = update_d_param_tx( validator, policy, d_parameter, current_utxo, + &governance_data, &ctx, zero_ex_units, - gov_utxo.clone(), )?; let evaluate_response = client.evaluate_transaction(&tx.to_bytes()).await.map_err(|e| { anyhow!( @@ -222,9 +211,9 @@ where policy, d_parameter, current_utxo, + &governance_data, &ctx, spend_ex_units, - gov_utxo.clone(), )?; let signed_tx = ctx.sign(&tx).to_bytes(); let res = client.submit_transaction(&signed_tx).await.map_err(|e| { @@ -243,13 +232,13 @@ fn mint_d_param_token_tx( validator: &PlutusScript, policy: &PlutusScript, d_parameter: &DParameter, + governance_data: &GovernanceData, ctx: &TransactionContext, - d_param_policy_ex_units: ExUnits, - gov_policy_ex_units: ExUnits, - gov_utxo: OgmiosUtxo, + d_param_policy_ex_units: &ExUnits, + gov_policy_ex_units: &ExUnits, ) -> Result { let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?); - // The essence of transaction: mint a governance token, then mint tokena D-Param and set output with it + // The essence of transaction: mint D-Param token and set output with it, mint a governance token. tx_builder.add_mint_one_script_token(policy, d_param_policy_ex_units)?; tx_builder.add_output_with_one_script_token( validator, @@ -258,23 +247,15 @@ fn mint_d_param_token_tx( ctx, )?; - let gov_policy = match gov_utxo.script { - Some(Plutus(ps)) => PlutusScript::from_cbor(&ps.cbor, LanguageKind::PlutusV2), - _ => return Err(JsError::from_str("Governance UTXO script is not PlutusScript")), - }; - - let gov_tx_input = TransactionInput::new( - &TransactionHash::from_bytes(gov_utxo.transaction.id.into())?, - gov_utxo.index.into(), - ); + let gov_tx_input = governance_data.utxo_id_as_tx_input(); tx_builder.add_mint_one_script_token_using_reference_script( - &gov_policy, + &governance_data.policy_script, &gov_tx_input, gov_policy_ex_units, )?; - tx_builder.add_script_reference_input(&gov_tx_input, gov_policy.bytes.len()); - tx_builder.add_required_signer(&ctx.payment_key_hash()); + tx_builder.add_script_reference_input(&gov_tx_input, governance_data.policy_script.bytes.len()); + //tx_builder.add_required_signer(&ctx.payment_key_hash()); tx_builder.balance_update_and_build(ctx) } @@ -283,12 +264,11 @@ fn update_d_param_tx( policy: &PlutusScript, d_parameter: &DParameter, script_utxo: &OgmiosUtxo, + governance_data: &GovernanceData, ctx: &TransactionContext, - mut ex_units: ScriptExUnits, - gov_utxo: OgmiosUtxo, + ex_units: ScriptExUnits, ) -> Result { - let config = crate::csl::get_builder_config(ctx)?; - let mut tx_builder = TransactionBuilder::new(&config); + let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?); let mut inputs = TxInputsBuilder::new(); inputs.add_script_utxo_input( @@ -296,8 +276,8 @@ fn update_d_param_tx( validator, ex_units .spend_ex_units - .pop() - .unwrap_or_else(|| panic!("Spend ex units not found")), + .first() + .ok_or_else(|| JsError::from_str("Spend ex units not found"))?, )?; tx_builder.set_inputs(&inputs); @@ -308,25 +288,17 @@ fn update_d_param_tx( ctx, )?; - let gov_policy = match gov_utxo.script { - Some(Plutus(ps)) => PlutusScript::from_cbor(&ps.cbor, LanguageKind::PlutusV2), - _ => return Err(JsError::from_str("Governance UTXO script is not PlutusScript")), - }; - - let gov_tx_input = TransactionInput::new( - &TransactionHash::from_bytes(gov_utxo.transaction.id.into())?, - gov_utxo.index.into(), - ); + let gov_tx_input = governance_data.utxo_id_as_tx_input(); tx_builder.add_mint_one_script_token_using_reference_script( - &gov_policy, + &governance_data.policy_script, &gov_tx_input, ex_units .mint_ex_units - .pop() - .unwrap_or_else(|| panic!("Mint ex units not found")), + .first() + .ok_or_else(|| JsError::from_str("MInt ex units not found"))?, )?; - tx_builder.add_script_reference_input(&gov_tx_input, gov_policy.bytes.len()); + tx_builder.add_script_reference_input(&gov_tx_input, governance_data.policy_script.bytes.len()); tx_builder.add_required_signer(&ctx.payment_key_hash()); tx_builder.balance_update_and_build(ctx) } diff --git a/toolkit/offchain/src/d_param/tests.rs b/toolkit/offchain/src/d_param/tests.rs index 62eaef03a..6d390c2e5 100644 --- a/toolkit/offchain/src/d_param/tests.rs +++ b/toolkit/offchain/src/d_param/tests.rs @@ -1,5 +1,6 @@ use super::{mint_d_param_token_tx, update_d_param_tx}; use crate::d_param::ScriptExUnits; +use crate::init_governance::GovernanceData; use crate::{ csl::{empty_asset_name, TransactionContext}, test_values::*, @@ -9,19 +10,18 @@ use cardano_serialization_lib::{ }; use hex_literal::hex; use ogmios_client::types::{Asset as OgmiosAsset, OgmiosTx, OgmiosUtxo, OgmiosValue}; -use ogmios_client::types::{OgmiosScript, PlutusScript}; use partner_chains_plutus_data::d_param::d_parameter_to_plutus_data; -use sidechain_domain::DParameter; +use sidechain_domain::{DParameter, UtxoId}; mod mint_tx { use super::*; use cardano_serialization_lib::Transaction; - fn ex_units() -> ExUnits { + fn policy_ex_units() -> ExUnits { ExUnits::new(&10000u32.into(), &200u32.into()) } - fn ex_units_2() -> ExUnits { + fn goveranance_ex_units() -> ExUnits { ExUnits::new(&20000u32.into(), &400u32.into()) } @@ -30,10 +30,10 @@ mod mint_tx { &test_validator(), &test_policy(), &input_d_param(), + &governance_data(), &test_tx_context(), - ex_units(), - ex_units_2(), - governance_utxo(), + &policy_ex_units(), + &goveranance_ex_units(), ) .expect("Test transaction should be constructed without error") } @@ -67,13 +67,13 @@ mod mint_tx { assert_eq!(redeemer.tag(), RedeemerTag::new_mint()); assert_eq!(redeemer.index(), 0u64.into()); assert_eq!(redeemer.data(), PlutusData::new_empty_constr_plutus_data(&0u64.into())); - assert_eq!(redeemer.ex_units(), ex_units_2()); + assert_eq!(redeemer.ex_units(), goveranance_ex_units()); let redeemer_2 = redeemers.get(1); assert_eq!(redeemer_2.tag(), RedeemerTag::new_mint()); assert_eq!(redeemer_2.index(), 1u64.into()); assert_eq!(redeemer_2.data(), PlutusData::new_empty_constr_plutus_data(&0u64.into())); - assert_eq!(redeemer_2.ex_units(), ex_units()); + assert_eq!(redeemer_2.ex_units(), policy_ex_units()); } #[test] @@ -204,9 +204,9 @@ mod update_d_parameter { &test_policy(), &input_d_param(), &script_utxo(), + &governance_data(), &test_tx_context(), ex_units(), - governance_utxo(), ) .unwrap() } @@ -322,24 +322,16 @@ fn test_tx_context() -> TransactionContext { } } -fn governance_script() -> PlutusScript { - PlutusScript { language: "PlutusV2".to_string(), cbor: vec![] } +fn governance_script() -> crate::plutus_script::PlutusScript { + crate::plutus_script::PlutusScript { language: LanguageKind::PlutusV2, bytes: vec![] } } + fn governance_script_hash() -> ScriptHash { - crate::plutus_script::PlutusScript::from_cbor(&governance_script().cbor, LanguageKind::PlutusV2) - .to_csl() - .hash() + governance_script().csl_script_hash() } -fn governance_utxo() -> OgmiosUtxo { - OgmiosUtxo { - transaction: OgmiosTx { id: [15; 32] }, - index: 0, - value: OgmiosValue::new_lovelace(1000000), - address: payment_addr().to_bech32(None).unwrap(), - script: Some(OgmiosScript::Plutus(governance_script())), - ..Default::default() - } +fn governance_data() -> GovernanceData { + GovernanceData { policy_script: governance_script(), utxo_id: UtxoId::new([15; 32], 0) } } fn lesser_payment_utxo() -> OgmiosUtxo { diff --git a/toolkit/offchain/src/init_governance/mod.rs b/toolkit/offchain/src/init_governance/mod.rs index 7a92a933b..07451816f 100644 --- a/toolkit/offchain/src/init_governance/mod.rs +++ b/toolkit/offchain/src/init_governance/mod.rs @@ -8,7 +8,6 @@ use crate::{ }; use anyhow::anyhow; use cardano_serialization_lib::*; -use ogmios_client::types::OgmiosScript; use ogmios_client::{ query_ledger_state::{QueryLedgerState, QueryUtxoByUtxoId}, query_network::QueryNetwork, @@ -129,18 +128,34 @@ pub async fn run_init_governance< Ok((genesis_utxo.to_domain(), result.transaction)) } -pub async fn get_governance_utxo( +pub(crate) async fn get_governance_utxo( genesis_utxo: UtxoId, client: &T, -) -> anyhow::Result { - let network = client.shelley_genesis_configuration().await?.network; +) -> Result { + let network = client + .shelley_genesis_configuration() + .await + .map_err(|e| { + JsError::from_str(&format!("Could not get Shelley Genesis Configuration: {}", e)) + })? + .network; let (_, version_oracle_policy, validator_address) = - scripts_data::version_scripts_and_address(genesis_utxo, network.to_csl())?; - - let utxos = client.query_utxos(&[validator_address]).await?; - - let governance_utxo = utxos + scripts_data::version_scripts_and_address(genesis_utxo, network.to_csl()).map_err(|e| { + JsError::from_str(&format!( + "Could not get Version Oracle Script Data for: {}, {}", + genesis_utxo, e + )) + })?; + + let utxos = client.query_utxos(&[validator_address.clone()]).await.map_err(|e| { + JsError::from_str(&format!( + "Could not query UTXOs Governance Validator at {}: {}", + validator_address, e + )) + })?; + + utxos .into_iter() .find(|utxo| { let correct_datum = utxo @@ -159,9 +174,7 @@ pub async fn get_governance_utxo( genesis_utxo: UtxoId, client: &T, -) -> anyhow::Result { +) -> Result { let utxo = get_governance_utxo(genesis_utxo, client).await?; + let policy_script = read_policy(&utxo)?; let utxo_id = utxo.to_domain(); - if let Some(OgmiosScript::Plutus(ps)) = utxo.script.clone() { - Ok(GovernanceData { - policy_script: plutus_script::PlutusScript::from_cbor(&ps.cbor, LanguageKind::PlutusV2), - utxo_id, - }) - } else { - Err(anyhow!("Programmatic Error: Governance UTXO script is not PlutusScript")) - } + Ok(GovernanceData { policy_script, utxo_id }) +} + +pub(crate) fn read_policy( + governance_utxo: &OgmiosUtxo, +) -> Result { + let script = governance_utxo + .script + .clone() + .ok_or_else(|| JsError::from_str("No 'script' in governance UTXO"))?; + plutus_script::PlutusScript::from_ogmios(script).map_err(|e| { + JsError::from_str(&format!( + "Cannot convert script from UTXO {}: {}", + governance_utxo.to_domain(), + e + )) + }) } diff --git a/toolkit/offchain/src/permissioned_candidates.rs b/toolkit/offchain/src/permissioned_candidates.rs index 346971704..cc006991c 100644 --- a/toolkit/offchain/src/permissioned_candidates.rs +++ b/toolkit/offchain/src/permissioned_candidates.rs @@ -6,14 +6,20 @@ //! in the `datum` field of it. Field should contain list of list, where each inner list is a triple of byte strings //! `[sidechain_public_key, aura_public_key, grandpa_publicKey]`. +use std::collections::HashMap; + use crate::csl::{ - get_builder_config, get_first_validator_budget, InputsBuilderExt, TransactionBuilderExt, - TransactionContext, + convert_value, empty_asset_name, get_builder_config, get_validator_budgets, zero_ex_units, + OgmiosUtxoExt, TransactionBuilderExt, TransactionContext, }; +use crate::init_governance::{self, GovernanceData}; use crate::plutus_script::PlutusScript; +use crate::scripts_data; use anyhow::anyhow; use cardano_serialization_lib::{ - ExUnits, JsError, PlutusData, Transaction, TransactionBuilder, TxInputsBuilder, + BigInt, ExUnits, Int, JsError, MintBuilder, MintWitness, PlutusData, PlutusScriptSource, + PlutusWitness, Redeemer, RedeemerTag, ScriptHash, Transaction, TransactionBuilder, + TxInputsBuilder, }; use ogmios_client::query_ledger_state::QueryLedgerState; use ogmios_client::query_network::QueryNetwork; @@ -53,7 +59,8 @@ pub async fn upsert_permissioned_candidates anyhow::Result> { let ctx = TransactionContext::for_payment_key(payment_signing_key, ogmios_client).await?; let (validator, policy) = - crate::scripts_data::permissioned_candidates_scripts(genesis_utxo, ctx.network)?; + scripts_data::permissioned_candidates_scripts(genesis_utxo, ctx.network)?; + let governance_data = init_governance::get_governance_data(genesis_utxo, ogmios_client).await?; let validator_address = validator.address_bech32(ctx.network)?; let validator_utxos = ogmios_client.query_utxos(&[validator_address]).await?; let mut candidates = candidates.to_owned(); @@ -76,6 +83,7 @@ pub async fn upsert_permissioned_candidates( validator: &PlutusScript, policy: &PlutusScript, candidates: &[PermissionedCandidateData], + governance_data: &GovernanceData, ctx: TransactionContext, client: &C, ) -> anyhow::Result where - C: Transactions, + C: Transactions + QueryLedgerState + QueryNetwork, { - let zero_ex_units = ExUnits::new(&0u64.into(), &0u64.into()); - let tx = - mint_permissioned_candidates_token_tx(validator, policy, candidates, &ctx, zero_ex_units)?; + let tx = mint_permissioned_candidates_token_tx( + validator, + policy, + candidates, + governance_data, + &ctx, + &zero_ex_units(), + &zero_ex_units(), + )?; + let evaluate_response = client.evaluate_transaction(&tx.to_bytes()).await.map_err(|e| { anyhow!( "Evaluate insert permissioned candidates transaction request failed: {}, bytes: {}", @@ -141,13 +158,29 @@ where hex::encode(tx.to_bytes()) ) })?; - let mint_witness_ex_units = get_first_validator_budget(evaluate_response)?; + + let mint_keys = tx.body().mint().expect("insert D parameter transaction has two mints").keys(); + let script_to_index: HashMap = + vec![(mint_keys.get(0), 0), (mint_keys.get(1), 1)].into_iter().collect(); + let mint_ex_units = get_validator_budgets(evaluate_response).mint_ex_units; + let policy_idx = *script_to_index.get(&policy.csl_script_hash()).unwrap(); + let gov_policy_idx = + *script_to_index.get(&governance_data.policy_script.csl_script_hash()).unwrap(); + let policy_ex_units = mint_ex_units + .get(policy_idx) + .expect("Evaluate transaction response should have entry for d_param policy"); + let gov_policy_ex_units = mint_ex_units + .get(gov_policy_idx) + .expect("Evaluate transaction response should have entry for governance policy"); + let tx = mint_permissioned_candidates_token_tx( validator, policy, candidates, + governance_data, &ctx, - mint_witness_ex_units, + policy_ex_units, + gov_policy_ex_units, )?; let signed_tx = ctx.sign(&tx).to_bytes(); let res = client.submit_transaction(&signed_tx).await.map_err(|e| { @@ -167,20 +200,22 @@ async fn update_permissioned_candidates( policy: &PlutusScript, candidates: &[PermissionedCandidateData], current_utxo: &OgmiosUtxo, + governance_data: &GovernanceData, ctx: TransactionContext, client: &C, ) -> anyhow::Result where - C: Transactions, + C: Transactions + QueryNetwork + QueryLedgerState, { - let zero_ex_units = ExUnits::new(&0u64.into(), &0u64.into()); let tx = update_permissioned_candidates_tx( validator, policy, candidates, current_utxo, + governance_data, &ctx, - zero_ex_units, + &zero_ex_units(), + &zero_ex_units(), )?; let evaluate_response = client.evaluate_transaction(&tx.to_bytes()).await.map_err(|e| { anyhow!( @@ -189,15 +224,24 @@ where hex::encode(tx.to_bytes()) ) })?; - let spend_ex_units = get_first_validator_budget(evaluate_response)?; + let spend_ex_units = get_validator_budgets(evaluate_response); + let permissioned_candidates_spend_ex_units = spend_ex_units + .spend_ex_units + .first() + .ok_or_else(|| JsError::from_str("Spend ex units for Permissioned Candidates Policy are missing in Evaluate Response"))?; + let governance_mint_ex_units = spend_ex_units.spend_ex_units.first().ok_or_else(|| { + JsError::from_str("Mint ex units for Governance Policy are missing in Evaluate Response") + })?; let tx = update_permissioned_candidates_tx( validator, policy, candidates, current_utxo, + governance_data, &ctx, - spend_ex_units, + permissioned_candidates_spend_ex_units, + governance_mint_ex_units, )?; let signed_tx = ctx.sign(&tx).to_bytes(); let res = client.submit_transaction(&signed_tx).await.map_err(|e| { @@ -212,16 +256,34 @@ where Ok(tx_id) } +/// Builds a transaction that mints a Permissioned Candidates token and also mint governance token fn mint_permissioned_candidates_token_tx( validator: &PlutusScript, policy: &PlutusScript, permissioned_candidates: &[PermissionedCandidateData], + governance_data: &init_governance::GovernanceData, ctx: &TransactionContext, - mint_witness_ex_units: ExUnits, + permissioned_candidates_ex_units: &ExUnits, + governance_ex_units: &ExUnits, ) -> Result { let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?); - // The essence of transaction: mint token and set output with it - tx_builder.add_mint_one_script_token(policy, mint_witness_ex_units)?; + // The essence of transaction: mint permissioned candidates token and set output with it, mint a governance token. + { + // Can use `add_mint_one_script_token` here because plutus data is different for unknown reason. There is an issue ETCM-9109 to explain it. + let mut mint_builder = MintBuilder::new(); + let validator_source = PlutusScriptSource::new(&policy.to_csl()); + let mint_witness = MintWitness::new_plutus_script( + &validator_source, + &Redeemer::new( + &RedeemerTag::new_mint(), + &0u32.into(), + &PlutusData::new_integer(&BigInt::zero()), + permissioned_candidates_ex_units, + ), + ); + mint_builder.add_asset(&mint_witness, &empty_asset_name(), &Int::new_i32(1))?; + tx_builder.set_mint_builder(&mint_builder); + } tx_builder.add_output_with_one_script_token( validator, policy, @@ -229,6 +291,15 @@ fn mint_permissioned_candidates_token_tx( ctx, )?; + let gov_tx_input = governance_data.utxo_id_as_tx_input(); + tx_builder.add_mint_one_script_token_using_reference_script( + &governance_data.policy_script, + &gov_tx_input, + governance_ex_units, + )?; + + tx_builder.add_script_reference_input(&gov_tx_input, governance_data.policy_script.bytes.len()); + //tx_builder.add_required_signer(&ctx.payment_key_hash()); tx_builder.balance_update_and_build(ctx) } @@ -237,15 +308,32 @@ fn update_permissioned_candidates_tx( policy: &PlutusScript, permissioned_candidates: &[PermissionedCandidateData], script_utxo: &OgmiosUtxo, + governance_data: &GovernanceData, ctx: &TransactionContext, - validator_redeemer_ex_units: ExUnits, + permissioned_candidates_ex_units: &ExUnits, + governance_ex_units: &ExUnits, ) -> Result { - let config = crate::csl::get_builder_config(ctx)?; - let mut tx_builder = TransactionBuilder::new(&config); + let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?); - let mut inputs = TxInputsBuilder::new(); - inputs.add_script_utxo_input(script_utxo, policy, validator_redeemer_ex_units)?; - tx_builder.set_inputs(&inputs); + // Cannot use `add_script_utxo_input` because of different redeemer plutus data, ETCM-9109 + // Also the index isn't really always 0. + { + let mut inputs = TxInputsBuilder::new(); + let input = script_utxo.to_csl_tx_input(); + let amount = convert_value(&script_utxo.value)?; + let witness = PlutusWitness::new_without_datum( + &validator.to_csl(), + &Redeemer::new( + &RedeemerTag::new_spend(), + // CSL will set redeemer index for the index of script input after sorting transaction inputs + &0u32.into(), + &PlutusData::new_integer(&BigInt::zero()), + permissioned_candidates_ex_units, + ), + ); + inputs.add_plutus_script_input(&witness, &input, &amount); + tx_builder.set_inputs(&inputs); + } tx_builder.add_output_with_one_script_token( validator, @@ -254,6 +342,15 @@ fn update_permissioned_candidates_tx( ctx, )?; + let gov_tx_input = governance_data.utxo_id_as_tx_input(); + tx_builder.add_mint_one_script_token_using_reference_script( + &governance_data.policy_script, + &gov_tx_input, + governance_ex_units, + )?; + + tx_builder.add_script_reference_input(&gov_tx_input, governance_data.policy_script.bytes.len()); + tx_builder.add_required_signer(&ctx.payment_key_hash()); tx_builder.balance_update_and_build(ctx) } @@ -262,40 +359,28 @@ mod tests { use super::{mint_permissioned_candidates_token_tx, update_permissioned_candidates_tx}; use crate::{ csl::{empty_asset_name, TransactionContext}, - ogmios_mock::MockOgmiosClient, - permissioned_candidates::upsert_permissioned_candidates, - scripts_data::get_scripts_data, + init_governance::GovernanceData, + plutus_script::PlutusScript, test_values::*, }; - use cardano_serialization_lib::{ - Address, ExUnits, Int, NetworkIdKind, PlutusData, RedeemerTag, - }; + use cardano_serialization_lib::{Address, ExUnits, Int, NetworkIdKind, PlutusData}; use hex_literal::hex; - use ogmios_client::{ - transactions::{ - OgmiosBudget, OgmiosEvaluateTransactionResponse, OgmiosValidatorIndex, - SubmitTransactionResponse, - }, - types::{Asset as OgmiosAsset, OgmiosTx, OgmiosUtxo, OgmiosValue}, - }; + use ogmios_client::types::{Asset as OgmiosAsset, OgmiosTx, OgmiosUtxo, OgmiosValue}; use partner_chains_plutus_data::permissioned_candidates::permissioned_candidates_to_plutus_data; use sidechain_domain::{ - AuraPublicKey, GrandpaPublicKey, McTxHash, PermissionedCandidateData, SidechainPublicKey, - UtxoId, + AuraPublicKey, GrandpaPublicKey, PermissionedCandidateData, SidechainPublicKey, UtxoId, }; - use std::str::FromStr; #[test] fn mint_permissioned_candiates_token_tx_regression_test() { - // We know the expected values were obtained with the correct code - let ex_units = ExUnits::new(&10000u32.into(), &200u32.into()); - let tx = mint_permissioned_candidates_token_tx( &test_validator(), &test_policy(), &input_candidates(), + &test_governance_data(), &test_tx_context(), - ex_units.clone(), + &permissioned_candidates_ex_units(), + &governance_ex_units(), ) .unwrap(); @@ -351,19 +436,7 @@ mod tests { .unwrap(), Int::new_i32(1) ); - // Redeemer is set - let redeemers = tx.witness_set().redeemers().unwrap(); - assert_eq!(redeemers.len(), 1); - let redeemer = redeemers.get(0); - assert_eq!(redeemer.tag(), RedeemerTag::new_mint()); - assert_eq!(redeemer.index(), 0u64.into()); - assert_eq!(redeemer.data(), PlutusData::new_empty_constr_plutus_data(&0u64.into())); - assert_eq!(redeemer.ex_units(), ex_units); - // script_data_hash check - assert_eq!( - tx.body().script_data_hash().unwrap().to_hex(), - "5b95e874a40a87b017ee7827a7dccf7331d2b647190eddcde7f0edaba4393662" - ); + // Collateral return must be set let collateral_return = body.collateral_return().unwrap(); assert_eq!(collateral_return.address(), payment_addr()); @@ -376,7 +449,6 @@ mod tests { #[test] fn update_permissioned_candidates_tx_regression_test() { - // We know the expected values were obtained with the correct code let script_utxo_lovelace = 1952430; let script_utxo = OgmiosUtxo { transaction: OgmiosTx { id: [15; 32] }, @@ -394,15 +466,15 @@ mod tests { ..Default::default() }; - let ex_units = ExUnits::new(&10000u32.into(), &200u32.into()); - let tx = update_permissioned_candidates_tx( &test_validator(), &test_policy(), &input_candidates(), &script_utxo, + &test_governance_data(), &test_tx_context(), - ex_units.clone(), + &permissioned_candidates_ex_units(), + &governance_ex_units(), ) .unwrap(); let body = tx.body(); @@ -447,22 +519,7 @@ mod tests { 1u64.into() ); assert_eq!(script_output.plutus_data().unwrap(), expected_plutus_data()); - // No token is minted in the transaction - assert!(body.mint().is_none()); - // Spend redeemer is set - let redeemers = tx.witness_set().redeemers().unwrap(); - assert_eq!(redeemers.len(), 1); - let redeemer = redeemers.get(0); - assert_eq!(redeemer.tag(), RedeemerTag::new_spend()); - // Index is 1 because the script input is the 2nd input, if it was the 3rd input it would be 2, etc. - assert_eq!(redeemer.index(), 1u64.into()); - assert_eq!(redeemer.data(), PlutusData::new_empty_constr_plutus_data(&0u64.into())); - assert_eq!(redeemer.ex_units(), ex_units); - // script_data_hash check - assert_eq!( - tx.body().script_data_hash().unwrap().to_hex(), - "1b80a34a767a594124163993ee6206fdfa83fc5cb22b267e70e9173fc24b663f" - ); + // Collateral return must be set let collateral_return = body.collateral_return().unwrap(); assert_eq!(collateral_return.address(), payment_addr()); @@ -473,6 +530,31 @@ mod tests { ); } + fn permissioned_candidates_ex_units() -> ExUnits { + ExUnits::new(&10000u32.into(), &200u32.into()) + } + fn governance_ex_units() -> ExUnits { + ExUnits::new(&99999u32.into(), &999u32.into()) + } + + fn test_goveranance_policy() -> PlutusScript { + PlutusScript { + bytes: hex!("88991122").into(), + language: cardano_serialization_lib::LanguageKind::PlutusV2, + } + } + + fn test_goveranance_utxo_id() -> UtxoId { + UtxoId::new([123u8; 32], 17) + } + + fn test_governance_data() -> GovernanceData { + GovernanceData { + policy_script: test_goveranance_policy(), + utxo_id: test_goveranance_utxo_id(), + } + } + fn test_tx_context() -> TransactionContext { TransactionContext { payment_key: payment_key(), @@ -532,157 +614,7 @@ mod tests { ] } - fn existing_candidates() -> Vec { - // Unordered for testing purposes - vec![ - PermissionedCandidateData { - sidechain_public_key: SidechainPublicKey( - hex!("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd") - .into(), - ), - aura_public_key: AuraPublicKey( - hex!("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").into(), - ), - grandpa_public_key: GrandpaPublicKey( - hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").into(), - ), - }, - PermissionedCandidateData { - sidechain_public_key: SidechainPublicKey( - hex!("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc") - .into(), - ), - aura_public_key: AuraPublicKey( - hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").into(), - ), - grandpa_public_key: GrandpaPublicKey( - hex!("0000000000000000000000000000000000000000000000000000000000000000").into(), - ), - }, - PermissionedCandidateData { - sidechain_public_key: SidechainPublicKey( - hex!("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") - .into(), - ), - aura_public_key: AuraPublicKey( - hex!("1111111111111111111111111111111111111111111111111111111111111111").into(), - ), - grandpa_public_key: GrandpaPublicKey( - hex!("2222222222222222222222222222222222222222222222222222222222222222").into(), - ), - }, - ] - } - fn expected_plutus_data() -> PlutusData { permissioned_candidates_to_plutus_data(&input_candidates()) } - - #[tokio::test] - async fn upsert_inserts_when_there_are_no_candidates_on_chain() { - let client = mock_client( - vec![OgmiosEvaluateTransactionResponse { - validator: OgmiosValidatorIndex { index: 0, purpose: "mint".into() }, - budget: OgmiosBudget { memory: 519278, cpu: 155707522 }, - }], - vec![], - ); - let tx = upsert_permissioned_candidates( - test_genesis_utxo(), - &input_candidates(), - payment_key().as_bytes().try_into().unwrap(), - &client, - ) - .await - .unwrap(); - assert_eq!(tx, Some(McTxHash(test_upsert_tx_hash()))) - } - - #[tokio::test] - async fn upsert_does_nothing_if_existing_candidates_are_equal_to_requested() { - let mut existing_candidates_in_different_order = existing_candidates(); - existing_candidates_in_different_order.reverse(); - let client = mock_client(vec![], vec![script_utxo(&existing_candidates())]); - - let tx = upsert_permissioned_candidates( - test_genesis_utxo(), - &existing_candidates_in_different_order, - payment_key().as_bytes().try_into().unwrap(), - &client, - ) - .await - .unwrap(); - assert_eq!(tx, None) - } - - #[tokio::test] - async fn upsert_updates_candidates_when_requested_are_different_to_existing() { - let client = mock_client( - vec![OgmiosEvaluateTransactionResponse { - validator: OgmiosValidatorIndex { index: 0, purpose: "spend".into() }, - budget: OgmiosBudget { memory: 519278, cpu: 155707522 }, - }], - vec![script_utxo(&existing_candidates())], - ); - let tx = upsert_permissioned_candidates( - test_genesis_utxo(), - &input_candidates(), - payment_key().as_bytes().try_into().unwrap(), - &client, - ) - .await - .unwrap(); - assert_eq!(tx, Some(McTxHash(test_upsert_tx_hash()))) - } - - // Creates an UTXO that has proper multi-asset and datum - fn script_utxo(permissioned_candidates: &Vec) -> OgmiosUtxo { - let plutus_data = permissioned_candidates_to_plutus_data(permissioned_candidates); - let policy = - crate::scripts_data::get_scripts_data(test_genesis_utxo(), NetworkIdKind::Testnet) - .unwrap() - .policy_ids - .permissioned_candidates; - - OgmiosUtxo { - transaction: OgmiosTx { id: [15; 32] }, - index: 0, - value: OgmiosValue { - lovelace: 10000000, - native_tokens: vec![(policy.0, vec![OgmiosAsset { name: vec![], amount: 1 }])] - .into_iter() - .collect(), - }, - address: get_scripts_data(test_genesis_utxo(), NetworkIdKind::Testnet) - .unwrap() - .addresses - .permissioned_candidates_validator, - datum: Some(ogmios_client::types::Datum { bytes: plutus_data.to_bytes() }), - ..Default::default() - } - } - - fn test_upsert_tx_hash() -> [u8; 32] { - hex!("aabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabb") - } - - fn test_genesis_utxo() -> UtxoId { - UtxoId::from_str("c389187c6cabf1cd2ca64cf8c76bf57288eb9c02ced6781935b810a1d0e7fbb4#1") - .unwrap() - } - - fn mock_client( - evaluate_response: Vec, - validator_utxos: Vec, - ) -> MockOgmiosClient { - MockOgmiosClient::new() - .with_evaluate_result(evaluate_response) - .with_submit_result(SubmitTransactionResponse { - transaction: test_upsert_tx_hash().into(), - }) - .with_utxos(vec![make_utxo(1u8, 0, 15000000, &payment_addr())]) - .with_utxos(validator_utxos) - .with_protocol_parameters(protocol_parameters()) - .with_shelley_config(shelley_config()) - } } diff --git a/toolkit/offchain/src/plutus_script.rs b/toolkit/offchain/src/plutus_script.rs index 2fc397989..7c012215b 100644 --- a/toolkit/offchain/src/plutus_script.rs +++ b/toolkit/offchain/src/plutus_script.rs @@ -26,7 +26,10 @@ impl PlutusScript { "plutus:v2" => LanguageKind::PlutusV2, "plutus:v3" => LanguageKind::PlutusV3, _ => { - return Err(anyhow!("Unsupported Plutus language version: {}", script.language)); + return Err(anyhow!( + "Unsupported Plutus language version: {}", + script.language + )); }, }; Ok(Self { bytes: script.cbor, language: language_kind }) diff --git a/toolkit/offchain/src/register.rs b/toolkit/offchain/src/register.rs index 2ae326161..c292c6c61 100644 --- a/toolkit/offchain/src/register.rs +++ b/toolkit/offchain/src/register.rs @@ -266,7 +266,7 @@ fn register_tx( inputs.add_script_utxo_input( own_registration_utxo, validator, - validator_redeemer_ex_units.clone(), + &validator_redeemer_ex_units, )?; } inputs.add_key_inputs(&[registration_utxo.clone()], &ctx.payment_key_hash())?; @@ -309,7 +309,7 @@ fn deregister_tx( inputs.add_script_utxo_input( own_registration_utxo, validator, - validator_redeemer_ex_units.clone(), + &validator_redeemer_ex_units.clone(), )?; } tx_builder.set_inputs(&inputs); diff --git a/toolkit/offchain/src/reserve/init.rs b/toolkit/offchain/src/reserve/init.rs index e8afb7f06..b0e81d6ad 100644 --- a/toolkit/offchain/src/reserve/init.rs +++ b/toolkit/offchain/src/reserve/init.rs @@ -101,7 +101,6 @@ impl ScriptData { } } -/// TODO: make it idempotent is the next step async fn initialize_script< T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId, A: AwaitTx, @@ -223,7 +222,7 @@ fn init_script_tx( tx_builder.add_mint_one_script_token_using_reference_script( &governance.policy_script, &gov_tx_input, - governance_script_cost, + &governance_script_cost, )?; tx_builder.add_script_reference_input(&gov_tx_input, governance.policy_script.bytes.len()); @@ -257,18 +256,16 @@ fn match_costs( vec![(mint_keys.get(0), 0), (mint_keys.get(1), 1)].into_iter().collect(); let mint_ex_units = get_validator_budgets(evaluate_response).mint_ex_units; if mint_ex_units.len() == 2 { - let version_policy_idx = script_to_index + let version_policy_idx = *script_to_index .get(version_oracle_policy) - .expect("Version Oracle Policy script is present in transaction mints") - .clone(); + .expect("Version Oracle Policy script is present in transaction mints"); let version_oracle_ex_units = mint_ex_units .get(version_policy_idx) .expect("mint_ex_units have two items") .clone(); - let gov_policy_idx = script_to_index + let gov_policy_idx = *script_to_index .get(governance_policy) - .expect("Governance Policy script is present in transaction mints") - .clone(); + .expect("Governance Policy script is present in transaction mints"); let governance_ex_units = mint_ex_units.get(gov_policy_idx).expect("mint_ex_units have two items").clone(); Ok((version_oracle_ex_units, governance_ex_units)) diff --git a/toolkit/offchain/src/test_values.rs b/toolkit/offchain/src/test_values.rs index 941e0af82..218c839b1 100644 --- a/toolkit/offchain/src/test_values.rs +++ b/toolkit/offchain/src/test_values.rs @@ -1,16 +1,13 @@ use crate::plutus_script::PlutusScript; use cardano_serialization_lib::{Address, LanguageKind, PlutusData, PrivateKey}; -use fraction::{Decimal, Fraction}; use hex_literal::hex; use ogmios_client::{ query_ledger_state::{ PlutusCostModels, ProtocolParametersResponse, ReferenceScriptsCosts, ScriptExecutionPrices, }, - query_network::ShelleyGenesisConfigurationResponse, - types::{OgmiosBytesSize, OgmiosTx, OgmiosUtxo, OgmiosValue, SlotLength}, + types::{OgmiosBytesSize, OgmiosTx, OgmiosUtxo, OgmiosValue}, }; -use sidechain_domain::{MainchainPublicKey, NetworkType}; -use time::OffsetDateTime; +use sidechain_domain::MainchainPublicKey; pub(crate) fn payment_key() -> PrivateKey { PrivateKey::from_normal_bytes(&hex!( @@ -66,18 +63,6 @@ pub(crate) fn protocol_parameters() -> ProtocolParametersResponse { } } -pub(crate) fn shelley_config() -> ShelleyGenesisConfigurationResponse { - ShelleyGenesisConfigurationResponse { - network_magic: 2, - network: NetworkType::Testnet, - start_time: OffsetDateTime::from_unix_timestamp(1666656000).unwrap(), - security_parameter: 432, - epoch_length: 86400, - active_slots_coefficient: Decimal::from_fraction(Fraction::new(1u64, 20u64)), - slot_length: SlotLength { milliseconds: 1000 }, - } -} - pub(crate) fn make_utxo(id_byte: u8, index: u16, lovelace: u64, addr: &Address) -> OgmiosUtxo { OgmiosUtxo { transaction: OgmiosTx { id: [id_byte; 32] }, diff --git a/toolkit/offchain/tests/integration_tests.rs b/toolkit/offchain/tests/integration_tests.rs index db0d01a26..80fdfa5a1 100644 --- a/toolkit/offchain/tests/integration_tests.rs +++ b/toolkit/offchain/tests/integration_tests.rs @@ -16,14 +16,14 @@ use ogmios_client::{ }; use partner_chains_cardano_offchain::{ await_tx::{AwaitTx, FixedDelayRetries}, - d_param, init_governance, + d_param, init_governance, permissioned_candidates, register::Register, reserve, }; use sidechain_domain::{ AdaBasedStaking, AuraPublicKey, CandidateRegistration, DParameter, GrandpaPublicKey, MainchainAddressHash, MainchainPrivateKey, MainchainPublicKey, MainchainSignature, McTxHash, - SidechainPublicKey, SidechainSignature, UtxoId, + PermissionedCandidateData, SidechainPublicKey, SidechainSignature, UtxoId, }; use std::time::Duration; use testcontainers::{clients::Cli, Container, GenericImage}; @@ -75,9 +75,20 @@ async fn upsert_d_param() { assert!(run_upsert_d_param(genesis_utxo, 1, 1, &client).await.is_some()) } +#[tokio::test] +async fn upsert_permissioned_candidates() { + let image = GenericImage::new(TEST_IMAGE, TEST_IMAGE_TAG); + let client = Cli::default(); + let container = client.run(image); + let client = initialize(&container).await; + let genesis_utxo = run_init_goveranance(&client).await; + assert!(run_upsert_permissioned_candidates(genesis_utxo, 77, &client).await.is_some()); + assert!(run_upsert_permissioned_candidates(genesis_utxo, 77, &client).await.is_none()); + assert!(run_upsert_permissioned_candidates(genesis_utxo, 231, &client).await.is_some()) +} + #[tokio::test] async fn init_reserve() { - let _ = env_logger::builder().is_test(true).try_init(); let image = GenericImage::new(TEST_IMAGE, TEST_IMAGE_TAG); let client = Cli::default(); let container = client.run(image); @@ -91,7 +102,6 @@ async fn init_reserve() { #[tokio::test] async fn register() { - let _ = env_logger::builder().is_test(true).try_init(); let image = GenericImage::new(TEST_IMAGE, TEST_IMAGE_TAG); let client = Cli::default(); let container = client.run(image); @@ -192,6 +202,36 @@ async fn run_upsert_d_param< tx_hash } +async fn run_upsert_permissioned_candidates< + T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId, +>( + genesis_utxo: UtxoId, + candidate: u8, + client: &T, +) -> Option { + let candidates = vec![PermissionedCandidateData { + sidechain_public_key: SidechainPublicKey([candidate; 33].to_vec()), + aura_public_key: AuraPublicKey([candidate; 32].to_vec()), + grandpa_public_key: GrandpaPublicKey([candidate; 32].to_vec()), + }]; + let tx_hash = permissioned_candidates::upsert_permissioned_candidates( + genesis_utxo, + &candidates, + GOVERNANCE_AUTHORITY_PAYMENT_KEY.0, + client, + ) + .await + .unwrap(); + match tx_hash { + Some(tx_hash) => FixedDelayRetries::new(Duration::from_millis(500), 100) + .await_tx_output(client, UtxoId::new(tx_hash.0, 0)) + .await + .unwrap(), + None => (), + }; + tx_hash +} + async fn run_init_reserve_management< T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId, >(