diff --git a/Cargo.lock b/Cargo.lock index d168691fd..0b74840d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6686,6 +6686,8 @@ dependencies = [ "cli-commands", "env_logger 0.11.3", "frame-support", + "log", + "log4rs", "parity-scale-codec", "partner-chains-smart-contracts-commands", "plutus", @@ -6723,8 +6725,6 @@ dependencies = [ "env_logger 0.11.3", "hex", "jsonrpsee", - "log", - "log4rs", "partner-chains-cardano-offchain", "serde", "serde_json", diff --git a/changelog.md b/changelog.md index 88ad97ae1..a10154ccb 100644 --- a/changelog.md +++ b/changelog.md @@ -10,8 +10,13 @@ This changelog is based on [Keep A Changelog](https://keepachangelog.com/en/1.1. ## Removed +* Separate binary partner-chains-smart-contracts-commands. + ## Fixed +* Crash of parnter-chain-node smart-contracts command. Logging is now set independently. +* Renamed of argument 'ogmios-host' to 'ogmios-url' in smart-contracts subcommands. + ## Added # v1.4.0 diff --git a/node/node/src/command/mod.rs b/node/node/src/command/mod.rs index a43e7fd60..208c0c8d0 100644 --- a/node/node/src/command/mod.rs +++ b/node/node/src/command/mod.rs @@ -66,6 +66,7 @@ pub fn run() -> sc_cli::Result<()> { match &cli.subcommand { Some(Subcommand::Key(cmd)) => cmd.run(&cli), Some(Subcommand::PartnerChains(cmd)) => { + partner_chains_node_commands::setup_log4rs()?; let make_dependencies = |config| { let components = service::new_partial(&config)?; Ok(( diff --git a/toolkit/cli/node-commands/Cargo.toml b/toolkit/cli/node-commands/Cargo.toml index 9488054aa..7a7a62b1a 100644 --- a/toolkit/cli/node-commands/Cargo.toml +++ b/toolkit/cli/node-commands/Cargo.toml @@ -10,6 +10,8 @@ clap = { workspace = true } cli-commands = { workspace = true } env_logger = { workspace = true } frame-support = { workspace = true } +log = { workspace = true } +log4rs = { workspace = true } parity-scale-codec = { workspace = true } partner-chains-smart-contracts-commands = { workspace = true } plutus = { workspace = true } diff --git a/toolkit/cli/node-commands/src/lib.rs b/toolkit/cli/node-commands/src/lib.rs index 027be9f21..a4cd06245 100644 --- a/toolkit/cli/node-commands/src/lib.rs +++ b/toolkit/cli/node-commands/src/lib.rs @@ -118,7 +118,6 @@ where SessionKeys: Decode + Send + Sync + 'static, CrossChainPublic: Decode + Encode + AsRef<[u8]> + Send + Sync + 'static, { - env_logger::init(); match cmd { PartnerChainsSubcommand::SidechainParams(cmd) => { let runner = cli.create_runner(&cmd)?; @@ -158,6 +157,28 @@ where } } +/// This sets logging in a very opinionated way. +/// Because Rust env_logger clashes with log4rs, this is exposed to be invoked by users of smart-contracts commands. +pub fn setup_log4rs() -> Result<(), Box> { + let stdout = log4rs::append::console::ConsoleAppender::builder().build(); + let ogmios_log = log4rs::append::file::FileAppender::builder().build("ogmios_client.log")?; + + let log_config = log4rs::config::Config::builder() + .appender(log4rs::config::Appender::builder().build("stdout", Box::new(stdout))) + .appender(log4rs::config::Appender::builder().build("ogmios-log", Box::new(ogmios_log))) + .logger( + log4rs::config::Logger::builder() + .appender("ogmios-log") + .additive(false) + .build("ogmios_client::jsonrpsee", log::LevelFilter::Debug), + ) + .build(log4rs::config::Root::builder().appender("stdout").build(log::LevelFilter::Info))?; + + log4rs::init_config(log_config)?; + + Ok(()) +} + async fn print_result(command_future: FIn) -> Result<(), sc_cli::Error> where FIn: Future>, diff --git a/toolkit/cli/smart-contracts-commands/Cargo.toml b/toolkit/cli/smart-contracts-commands/Cargo.toml index 18f073c75..314e1b439 100644 --- a/toolkit/cli/smart-contracts-commands/Cargo.toml +++ b/toolkit/cli/smart-contracts-commands/Cargo.toml @@ -14,6 +14,4 @@ jsonrpsee = { workspace = true, features = ["client-core", "http-client", "macro tokio = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -log = { workspace = true } -log4rs = { workspace = true } cardano-serialization-lib = { workspace = true } diff --git a/toolkit/cli/smart-contracts-commands/src/d_parameter.rs b/toolkit/cli/smart-contracts-commands/src/d_parameter.rs index 2605fc8a9..b0226a15a 100644 --- a/toolkit/cli/smart-contracts-commands/src/d_parameter.rs +++ b/toolkit/cli/smart-contracts-commands/src/d_parameter.rs @@ -26,7 +26,7 @@ impl UpsertDParameterCmd { num_permissioned_candidates: self.permissioned_candidates_count, num_registered_candidates: self.registered_candidates_count, }; - let client = HttpClient::builder().build(self.common_arguments.ogmios_host)?; + let client = HttpClient::builder().build(self.common_arguments.ogmios_url)?; upsert_d_param(self.genesis_utxo, &d_param, payment_key.0, &client).await?; diff --git a/toolkit/cli/smart-contracts-commands/src/get_scripts.rs b/toolkit/cli/smart-contracts-commands/src/get_scripts.rs index 7bd4216bc..dcdbc7108 100644 --- a/toolkit/cli/smart-contracts-commands/src/get_scripts.rs +++ b/toolkit/cli/smart-contracts-commands/src/get_scripts.rs @@ -6,13 +6,14 @@ use sidechain_domain::UtxoId; pub struct GetScripts { #[clap(flatten)] common_arguments: crate::CommonArguments, + /// Genesis UTXO that identifies the partner chain. #[arg(long, short = 'c')] genesis_utxo: UtxoId, } impl GetScripts { pub async fn execute(self) -> crate::CmdResult<()> { - let client = HttpClient::builder().build(self.common_arguments.ogmios_host)?; + let client = HttpClient::builder().build(self.common_arguments.ogmios_url)?; let scripts_data = get_scripts_data_with_ogmios(self.genesis_utxo, client).await?; let json = serde_json::to_string_pretty(&scripts_data)?; diff --git a/toolkit/cli/smart-contracts-commands/src/init_governance.rs b/toolkit/cli/smart-contracts-commands/src/init_governance.rs index 73ef72917..980189313 100644 --- a/toolkit/cli/smart-contracts-commands/src/init_governance.rs +++ b/toolkit/cli/smart-contracts-commands/src/init_governance.rs @@ -10,10 +10,13 @@ use crate::read_private_key_from_file; pub struct InitGovernanceCmd { #[clap(flatten)] common_arguments: crate::CommonArguments, + /// Governance authority hash to be set. #[arg(long, short = 'g')] governance_authority: MainchainAddressHash, + /// Path to the Cardano Payment Key file. #[arg(long, short = 'k')] payment_key_file: String, + /// Genesis UTXO of the new chain, it will be spent durning initialization. If not set, then one will be selected from UTXOs of the payment key. #[arg(long, short = 'c')] genesis_utxo: Option, } @@ -21,7 +24,7 @@ pub struct InitGovernanceCmd { impl InitGovernanceCmd { pub async fn execute(self) -> crate::CmdResult<()> { let payment_key = read_private_key_from_file(&self.payment_key_file)?; - let client = HttpClient::builder().build(self.common_arguments.ogmios_host)?; + let client = HttpClient::builder().build(self.common_arguments.ogmios_url)?; run_init_governance( self.governance_authority, diff --git a/toolkit/cli/smart-contracts-commands/src/lib.rs b/toolkit/cli/smart-contracts-commands/src/lib.rs index 9fffdddf3..0cd28074b 100644 --- a/toolkit/cli/smart-contracts-commands/src/lib.rs +++ b/toolkit/cli/smart-contracts-commands/src/lib.rs @@ -1,11 +1,7 @@ -use log4rs::{ - append::{console::ConsoleAppender, file::FileAppender}, - config::Appender, -}; use sidechain_domain::*; -pub mod get_scripts; pub mod d_parameter; +pub mod get_scripts; pub mod init_governance; pub mod register; @@ -26,7 +22,7 @@ pub enum SmartContractsCmd { #[command(author, version, about, long_about = None)] pub struct CommonArguments { #[arg(default_value = "http://localhost:1337", long, short = 'O')] - ogmios_host: String, + ogmios_url: String, } type CmdResult = Result>; @@ -42,8 +38,6 @@ impl SmartContractsCmd { } pub fn execute_blocking(self) -> CmdResult<()> { - setup_logging()?; - tokio::runtime::Runtime::new()?.block_on(self.execute()) } } @@ -64,26 +58,6 @@ pub(crate) fn read_private_key_from_file(path: &str) -> CmdResult Result<(), Box> { - let stdout = ConsoleAppender::builder().build(); - let ogmios_log = FileAppender::builder().build("ogmios_client.log")?; - - let log_config = log4rs::config::Config::builder() - .appender(Appender::builder().build("stdout", Box::new(stdout))) - .appender(Appender::builder().build("ogmios-log", Box::new(ogmios_log))) - .logger( - log4rs::config::Logger::builder() - .appender("ogmios-log") - .additive(false) - .build("ogmios_client::jsonrpsee", log::LevelFilter::Debug), - ) - .build(log4rs::config::Root::builder().appender("stdout").build(log::LevelFilter::Info))?; - - log4rs::init_config(log_config)?; - - Ok(()) -} - // Parses public keys in formatted as SIDECHAIN_KEY:AURA_KEY:GRANDPA_KEY pub(crate) fn parse_partnerchain_public_keys( partner_chain_public_keys: &str, diff --git a/toolkit/cli/smart-contracts-commands/src/main.rs b/toolkit/cli/smart-contracts-commands/src/main.rs deleted file mode 100644 index 9258f2fb6..000000000 --- a/toolkit/cli/smart-contracts-commands/src/main.rs +++ /dev/null @@ -1,17 +0,0 @@ -use clap::Parser; -use partner_chains_smart_contracts_commands::{setup_logging, SmartContractsCmd}; - -#[derive(Clone, Debug, clap::Parser)] -pub enum SmartContractsCmdStandalone { - #[clap(flatten)] - Inner(SmartContractsCmd), -} - -type CmdResult = Result>; - -#[tokio::main] -async fn main() -> CmdResult<()> { - setup_logging()?; - let SmartContractsCmdStandalone::Inner(cmd) = SmartContractsCmdStandalone::parse(); - cmd.execute().await -} diff --git a/toolkit/cli/smart-contracts-commands/src/register.rs b/toolkit/cli/smart-contracts-commands/src/register.rs index daeb73558..5da81faeb 100644 --- a/toolkit/cli/smart-contracts-commands/src/register.rs +++ b/toolkit/cli/smart-contracts-commands/src/register.rs @@ -35,7 +35,7 @@ pub struct RegisterCmd { impl RegisterCmd { pub async fn execute(self) -> crate::CmdResult<()> { let payment_key = read_private_key_from_file(&self.payment_key_file)?; - let client = HttpClient::builder().build(self.common_arguments.ogmios_host)?; + let client = HttpClient::builder().build(self.common_arguments.ogmios_url)?; let candidate_registration = CandidateRegistration { stake_ownership: AdaBasedStaking { pub_key: self.spo_public_key, diff --git a/toolkit/offchain/src/collateral_selection.rs b/toolkit/offchain/src/collateral_selection.rs deleted file mode 100644 index 8ce97215b..000000000 --- a/toolkit/offchain/src/collateral_selection.rs +++ /dev/null @@ -1,77 +0,0 @@ -use ogmios_client::types::*; - -pub const MIN_REQUIRED_COLLATERAL: u64 = 5_000_000; - -// Largest-First selection algorithm based on https://cips.cardano.org/cip/CIP-0002#largest-first -pub fn largest_first( - inputs_available: &[OgmiosUtxo], - amount_required: u64, - max_collateral_inputs: u32, - min_required_collateral: u64, -) -> Result, String> { - let amount_required = amount_required.max(min_required_collateral); - let amount_available: u64 = inputs_available.iter().map(|utxo| utxo.value.lovelace).sum(); - if amount_available < amount_required { - return Err(format!("The available amount of lovelace ({amount_available}) is less than the required collateral amount ({amount_required})")); - } - let mut inputs_available_sorted = inputs_available.iter().collect::>(); - inputs_available_sorted - .sort_by(|utxo_a, utxo_b| utxo_a.value.lovelace.cmp(&utxo_b.value.lovelace)); - - let mut inputs_selected = Vec::new(); - let mut sum = 0; - while sum < amount_required { - if let Some(utxo) = inputs_available_sorted.pop() { - sum += utxo.value.lovelace; - inputs_selected.push(utxo); - } - } - - if inputs_selected.len() as u32 > max_collateral_inputs { - return Err(format!("Could not find {amount_required} lovelace required for collateral in the {max_collateral_inputs} maximum allowed inputs")); - } - Ok(inputs_selected) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn fails_if_not_enough_utxos() { - assert_eq!( - largest_first(&utxos(), 111, 3, 10), - Err("The available amount of lovelace (110) is less than the required collateral amount (111)".to_string()) - ); - } - - #[test] - fn fails_if_too_many_selected() { - assert_eq!(largest_first(&utxos(), 109, 3, 10), Err("Could not find 109 lovelace required for collateral in the 3 maximum allowed inputs".to_string())); - } - - #[test] - fn succeeds() { - assert_eq!(largest_first(&utxos(), 55, 3, 10), Ok(vec![&mk_utxo(50), &mk_utxo(30)])); - } - - fn mk_utxo(value: u64) -> OgmiosUtxo { - OgmiosUtxo { - transaction: OgmiosTx { - id: hex_literal::hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - ), - }, - index: 0, - address: "address".to_string(), - value: OgmiosValue::new_lovelace(value), - datum: None, - datum_hash: None, - script: None, - } - } - - fn utxos() -> Vec { - vec![mk_utxo(10), mk_utxo(30), mk_utxo(20), mk_utxo(50)] - } -} diff --git a/toolkit/offchain/src/init_governance/mod.rs b/toolkit/offchain/src/init_governance/mod.rs index ede260827..739315358 100644 --- a/toolkit/offchain/src/init_governance/mod.rs +++ b/toolkit/offchain/src/init_governance/mod.rs @@ -1,3 +1,4 @@ +use crate::csl::OgmiosUtxoExt; use crate::scripts_data; use crate::{ await_tx::{AwaitTx, FixedDelayRetries}, @@ -21,6 +22,8 @@ mod tests; pub(crate) mod transaction; pub trait InitGovernance { + /// Initializes goveranance mechanism with the authority being `governance_authority`, + /// for the chain identified by `genesis_utxo_id`. #[allow(async_fn_in_trait)] async fn init_governance( &self, @@ -48,6 +51,7 @@ where FixedDelayRetries::two_minutes(), ) .await + .map(|(_, tx)| tx) .map_err(|e| OffchainError::InternalError(e.to_string())) } } @@ -61,7 +65,7 @@ pub async fn run_init_governance< genesis_utxo_id: Option, client: &T, await_tx: A, -) -> anyhow::Result { +) -> anyhow::Result<(UtxoId, OgmiosTx)> { let payment_key = PrivateKey::from_normal_bytes(&payment_key.0) .expect("MainchainPrivateKey is a valid PrivateKey"); @@ -116,7 +120,7 @@ pub async fn run_init_governance< raw_scripts::VERSION_ORACLE_POLICY, governance_authority, &tx_context, - genesis_utxo, + genesis_utxo.clone(), cost, )?; let signed_transaction = tx_context.sign(&unsigned_transaction); @@ -128,7 +132,7 @@ pub async fn run_init_governance< .await_tx_output(client, UtxoId { tx_hash: McTxHash(tx_id), index: UtxoIndex(0) }) .await?; - Ok(result.transaction) + Ok((genesis_utxo.to_domain(), result.transaction)) } pub async fn get_governance_utxo( diff --git a/toolkit/offchain/src/init_governance/tests.rs b/toolkit/offchain/src/init_governance/tests.rs index 08c84d7ee..21d9bb85c 100644 --- a/toolkit/offchain/src/init_governance/tests.rs +++ b/toolkit/offchain/src/init_governance/tests.rs @@ -166,17 +166,19 @@ async fn transaction_run() { }]) .with_submit_result(SubmitTransactionResponse { transaction }); - let result = run_init_governance( + let genesis_utxo = genesis_utxo().to_domain(); + let (result_genesis_utxo, result_tx) = run_init_governance( governance_authority(), payment_key_domain(), - Some(genesis_utxo().to_domain()), + Some(genesis_utxo), &mock_client, ImmediateSuccess, ) .await .expect("Should succeed"); - assert_eq!(result.id, transaction_id); + assert_eq!(result_tx.id, transaction_id); + assert_eq!(result_genesis_utxo, genesis_utxo); } fn genesis_utxo() -> OgmiosUtxo { diff --git a/toolkit/offchain/src/lib.rs b/toolkit/offchain/src/lib.rs index 0712216b8..2130da791 100644 --- a/toolkit/offchain/src/lib.rs +++ b/toolkit/offchain/src/lib.rs @@ -2,8 +2,6 @@ /// Primitives used for awaiting for tx being observed on the blockchain pub mod await_tx; -/// Collateral selection algorithm -pub mod collateral_selection; /// General purpose code for interacting with cardano-serialization-lib pub mod csl; /// Supports D-Parameter upsert diff --git a/toolkit/utils/ogmios-client/src/types.rs b/toolkit/utils/ogmios-client/src/types.rs index b762e15af..4dff2c790 100644 --- a/toolkit/utils/ogmios-client/src/types.rs +++ b/toolkit/utils/ogmios-client/src/types.rs @@ -1,7 +1,7 @@ //! Common types used in the Ogmios API. use serde::{Deserialize, Deserializer}; -use sidechain_domain::McTxHash; +use sidechain_domain::{McTxHash, UtxoId}; use std::collections::HashMap; use std::fmt::Debug; use std::str::FromStr; @@ -34,6 +34,12 @@ pub struct OgmiosUtxo { pub script: Option, } +impl OgmiosUtxo { + pub fn utxo_id(&self) -> UtxoId { + UtxoId::new(self.transaction.id, self.index) + } +} + impl core::fmt::Display for OgmiosUtxo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}#{}", hex::encode(self.transaction.id), self.index)