From 07f4baeaedaa8b5810b7b986f5c4c2ecba6d5291 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Fri, 12 Jan 2024 15:31:09 +0100 Subject: [PATCH 01/15] Added cw-multi-test 0.20.0 --- Cargo.toml | 6 ++---- packages/cw-orch-core/src/env.rs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02e81e6e0..dc16a6c99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,8 @@ repository = "https://github.com/AbstractSDK/cw-orchestrator" [workspace.dependencies] cw-utils = { version = "1.0.1" } cosmwasm-std = { version = "1.1" } -#cw-multi-test = { version = "0.16.4" } -cw-multi-test = { package = "abstract-cw-multi-test", version = "0.16.5", features = [ - "stargate", - "cosmwasm_1_3", +cw-multi-test = { package = "abstract-cw-multi-test", version = "0.20.0", features = [ + "cosmwasm_1_4", ] } cw20 = { git = "https://github.com/AbstractSDK/cw-plus.git" } cw20-base = { git = "https://github.com/AbstractSDK/cw-plus.git" } diff --git a/packages/cw-orch-core/src/env.rs b/packages/cw-orch-core/src/env.rs index b14ca13bf..0cb0f0d7b 100644 --- a/packages/cw-orch-core/src/env.rs +++ b/packages/cw-orch-core/src/env.rs @@ -3,7 +3,7 @@ //! To get the env variable parsed value, you can use //! ```rust,no_run //! use cw_orch_core::CwOrchEnvVars; -//! let env_variable = CwOrchEnvVars::load().unwrap().state_folder; +//! let env_variable = CwOrchEnvVars::load().unwrap().state_file; //! ``` use std::{env, path::PathBuf, str::FromStr}; From 1c6d09ba0962d6b913a99719a21e518263f1a4c5 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Fri, 12 Jan 2024 15:56:27 +0100 Subject: [PATCH 02/15] Removed secp, removed ibc-relayer-types --- cw-orch-daemon/Cargo.toml | 3 +-- cw-orch-daemon/src/channel.rs | 21 +++++++++------------ cw-orch-daemon/src/error.rs | 2 +- cw-orch-daemon/src/keys/private.rs | 2 +- cw-orch-daemon/src/keys/signature.rs | 8 ++++---- cw-orch-daemon/src/live_mock.rs | 5 ++++- cw-orch-daemon/src/sender.rs | 2 +- cw-orch-daemon/src/state.rs | 2 +- cw-orch-daemon/src/tx_broadcaster.rs | 2 +- cw-orch-daemon/src/tx_builder.rs | 2 +- cw-orch-daemon/tests/querier.rs | 2 +- 11 files changed, 25 insertions(+), 26 deletions(-) diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 11c7118a9..659b0f3f0 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -39,7 +39,6 @@ thiserror = { workspace = true } prost-types = { workspace = true } # Daemon deps sha256 = { workspace = true } -ibc-relayer-types = { workspace = true } prost = { version = "0.12.3" } bitcoin = { version = "0.30.0" } hex = { version = "0.4.3" } @@ -47,7 +46,6 @@ ripemd = { version = "0.1.3" } ibc-chain-registry = { workspace = true } tokio = { version = "1.4", features = ["full"] } tonic = { workspace = true, features = ["tls", "tls-roots"] } -secp256k1 = { version = "0.27.0", default-features = false } reqwest = { version = "0.11.9" } base64 = { version = "0.21.0" } hkd32 = { version = "0.7.0", features = ["bip39", "mnemonic", "bech32"] } @@ -70,6 +68,7 @@ async-recursion = "1.0.5" flate2 = { version = "1.0.26" } [dev-dependencies] +ibc-relayer-types = { workspace = true } cw-orch-daemon = { path = "." } uid = "0.1.7" env_logger = "0.10.0" diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/channel.rs index 6293199cd..d71e22a65 100644 --- a/cw-orch-daemon/src/channel.rs +++ b/cw-orch-daemon/src/channel.rs @@ -3,7 +3,6 @@ use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ }; use cw_orch_core::log::connectivity_target; use ibc_chain_registry::chain::Grpc; -use ibc_relayer_types::core::ics24_host::identifier::ChainId; use tonic::transport::{Channel, ClientTlsConfig}; use super::error::DaemonError; @@ -13,7 +12,7 @@ pub struct GrpcChannel {} impl GrpcChannel { /// Connect to any of the provided gRPC endpoints - pub async fn connect(grpc: &[Grpc], chain_id: &ChainId) -> Result { + pub async fn connect(grpc: &[Grpc], chain_id: &str) -> Result { let mut successful_connections = vec![]; for Grpc { address, .. } in grpc.iter() { @@ -67,16 +66,14 @@ impl GrpcChannel { .into_inner(); // local juno does not return a proper ChainId with epoch format - if ChainId::is_epoch_format(&node_info.default_node_info.as_ref().unwrap().network) { - // verify we are connected to the spected network - if node_info.default_node_info.as_ref().unwrap().network != chain_id.as_str() { - log::error!( - "Network mismatch: connection:{} != config:{}", - node_info.default_node_info.as_ref().unwrap().network, - chain_id.as_str() - ); - continue; - } + // verify we are connected to the expected network + if node_info.default_node_info.as_ref().unwrap().network != chain_id { + log::error!( + "Network mismatch: connection:{} != config:{}", + node_info.default_node_info.as_ref().unwrap().network, + chain_id + ); + continue; } // add endpoint to succesful connections diff --git a/cw-orch-daemon/src/error.rs b/cw-orch-daemon/src/error.rs index e284d97a2..fc1e163cd 100644 --- a/cw-orch-daemon/src/error.rs +++ b/cw-orch-daemon/src/error.rs @@ -19,7 +19,7 @@ pub enum DaemonError { #[error(transparent)] IOErr(#[from] ::std::io::Error), #[error(transparent)] - Secp256k1(#[from] ::secp256k1::Error), + Secp256k1(#[from] bitcoin::secp256k1::Error), #[error(transparent)] VarError(#[from] ::std::env::VarError), #[error(transparent)] diff --git a/cw-orch-daemon/src/keys/private.rs b/cw-orch-daemon/src/keys/private.rs index d0dcbac5c..e955198e6 100644 --- a/cw-orch-daemon/src/keys/private.rs +++ b/cw-orch-daemon/src/keys/private.rs @@ -4,6 +4,7 @@ use crate::DaemonError; #[cfg(feature = "eth")] use ::ethers_core::k256::ecdsa::SigningKey; use base64::Engine; +use bitcoin::secp256k1::{self, Secp256k1}; use bitcoin::{ bip32::{ExtendedPrivKey, IntoDerivationPath}, Network, @@ -13,7 +14,6 @@ use cw_orch_core::log::local_target; use hkd32::mnemonic::{Phrase, Seed}; use prost_types::Any; use rand_core::OsRng; -use secp256k1::Secp256k1; /// The Private key structure that is used to generate signatures and public keys /// WARNING: No Security Audit has been performed diff --git a/cw-orch-daemon/src/keys/signature.rs b/cw-orch-daemon/src/keys/signature.rs index c7ac3718e..4930c1251 100644 --- a/cw-orch-daemon/src/keys/signature.rs +++ b/cw-orch-daemon/src/keys/signature.rs @@ -1,10 +1,10 @@ use crate::DaemonError; use base64::engine::{general_purpose::STANDARD, Engine}; +use bitcoin::secp256k1::{Message, Secp256k1}; use ring::digest::SHA256; -use secp256k1::{Message, Secp256k1}; pub struct Signature {} impl Signature { - pub fn verify( + pub fn verify( secp: &Secp256k1, pub_key: &str, signature: &str, @@ -12,10 +12,10 @@ impl Signature { ) -> Result<(), DaemonError> { let public = STANDARD.decode(pub_key)?; let sig = STANDARD.decode(signature)?; - let pk = secp256k1::PublicKey::from_slice(public.as_slice())?; + let pk = bitcoin::secp256k1::PublicKey::from_slice(public.as_slice())?; let sha_result = ring::digest::digest(&SHA256, blob.as_bytes()); let message: Message = Message::from_slice(&sha_result.as_ref()[0..32])?; - let secp_sig = secp256k1::ecdsa::Signature::from_compact(sig.as_slice())?; + let secp_sig = bitcoin::secp256k1::ecdsa::Signature::from_compact(sig.as_slice())?; secp.verify_ecdsa(&message, &secp_sig, &pk)?; Ok(()) } diff --git a/cw-orch-daemon/src/live_mock.rs b/cw-orch-daemon/src/live_mock.rs index 3891ef51c..25410cd09 100644 --- a/cw-orch-daemon/src/live_mock.rs +++ b/cw-orch-daemon/src/live_mock.rs @@ -208,7 +208,10 @@ impl WasmMockQuerier { let rt = Runtime::new().unwrap(); let channel = rt - .block_on(GrpcChannel::connect(&chain.apis.grpc, &chain.chain_id)) + .block_on(GrpcChannel::connect( + &chain.apis.grpc, + chain.chain_id.as_str(), + )) .unwrap(); WasmMockQuerier { diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 9e8bb5a2c..c35107a28 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -33,7 +33,7 @@ use cosmrs::{ use cosmwasm_std::{coin, Addr, Coin}; use cw_orch_core::{log::local_target, CwOrchEnvVars}; -use secp256k1::{All, Context, Secp256k1, Signing}; +use bitcoin::secp256k1::{All, Context, Secp256k1, Signing}; use std::{convert::TryFrom, rc::Rc, str::FromStr}; use cosmos_modules::vesting::PeriodicVestingAccount; diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index b1ee4fa3c..d6d6928f4 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -46,7 +46,7 @@ impl DaemonState { // find working grpc channel let grpc_channel = - GrpcChannel::connect(&chain_data.apis.grpc, &chain_data.chain_id).await?; + GrpcChannel::connect(&chain_data.apis.grpc, chain_data.chain_id.as_str()).await?; // If the path is relative, we dis-ambiguate it and take the root at $HOME/$CW_ORCH_STATE_FOLDER let mut json_file_path = Self::state_file_path()?; diff --git a/cw-orch-daemon/src/tx_broadcaster.rs b/cw-orch-daemon/src/tx_broadcaster.rs index 6b8ca170d..5f6dd798b 100644 --- a/cw-orch-daemon/src/tx_broadcaster.rs +++ b/cw-orch-daemon/src/tx_broadcaster.rs @@ -1,8 +1,8 @@ use std::time::Duration; +use bitcoin::secp256k1::All; use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; use cw_orch_core::log::transaction_target; -use secp256k1::All; use crate::{ queriers::{DaemonQuerier, Node}, diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index 8b12bd4d7..e281660ec 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use bitcoin::secp256k1::All; use cosmrs::tx::{ModeInfo, SignMode}; use cosmrs::AccountId; use cosmrs::{ @@ -10,7 +11,6 @@ use cosmrs::{ }; use cw_orch_core::log::transaction_target; use cw_orch_core::CwOrchEnvVars; -use secp256k1::All; use super::{sender::Sender, DaemonError}; diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index 9d6edee3f..0720e0193 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -36,7 +36,7 @@ mod queriers { let chain: ChainId = ChainId::new(network.chain_id.to_owned(), 1); - let channel = GrpcChannel::connect(&grpcs, &chain).await; + let channel = GrpcChannel::connect(&grpcs, chain.as_str()).await; asserting!("channel connection is succesful") .that(&channel) From 9c3bfdc572092770a038f81da0f32c45b61d3f9d Mon Sep 17 00:00:00 2001 From: Kayanski Date: Fri, 12 Jan 2024 16:05:20 +0100 Subject: [PATCH 03/15] Removed ibc-relayer-types completely --- Cargo.toml | 1 - cw-orch-daemon/Cargo.toml | 1 - cw-orch-daemon/tests/querier.rs | 5 +---- cw-orch/Cargo.toml | 1 - 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dc16a6c99..38af0dd99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ prost = "0.11.9" # Test deps speculoos = "0.11.0" -ibc-relayer-types = { version = "0.25.1" } ibc-chain-registry = { version = "0.25.0" } # Logging diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 659b0f3f0..6900bdf34 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -68,7 +68,6 @@ async-recursion = "1.0.5" flate2 = { version = "1.0.26" } [dev-dependencies] -ibc-relayer-types = { workspace = true } cw-orch-daemon = { path = "." } uid = "0.1.7" env_logger = "0.10.0" diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index 0720e0193..fbd08390e 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -8,7 +8,6 @@ mod queriers { use cw_orch_daemon::GrpcChannel; use cw_orch_networks::networks; use ibc_chain_registry::chain::Grpc; - use ibc_relayer_types::core::ics24_host::identifier::ChainId; use mock_contract::InstantiateMsg; use speculoos::{asserting, result::ResultAssertions}; use std::str::FromStr; @@ -34,9 +33,7 @@ mod queriers { provider: None, }]; - let chain: ChainId = ChainId::new(network.chain_id.to_owned(), 1); - - let channel = GrpcChannel::connect(&grpcs, chain.as_str()).await; + let channel = GrpcChannel::connect(&grpcs, network.chain_id).await; asserting!("channel connection is succesful") .that(&channel) diff --git a/cw-orch/Cargo.toml b/cw-orch/Cargo.toml index 3c12295bd..0eba00918 100644 --- a/cw-orch/Cargo.toml +++ b/cw-orch/Cargo.toml @@ -110,5 +110,4 @@ mock-contract = { path = "../contracts/mock_contract", features = [ mock-contract-u64 = { path = "../contracts/mock_contract_u64", features = [ "interface", ] } -ibc-relayer-types = { workspace = true } serde_json = { workspace = true } From a56bb35d22234a1c4a8067f1df594d1d4c25b351 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Fri, 12 Jan 2024 16:24:59 +0100 Subject: [PATCH 04/15] =?UTF-8?q?updated=20prost=20types=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 6 +++--- cw-orch/Cargo.toml | 2 +- packages/cw-orch-traits/Cargo.toml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 38af0dd99..06fda462d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ cw20 = { git = "https://github.com/AbstractSDK/cw-plus.git" } cw20-base = { git = "https://github.com/AbstractSDK/cw-plus.git" } # Test Tube env deps -osmosis-test-tube = { version = "19.0.0" } +osmosis-test-tube = { version = "21.0.0" } anyhow = "1.0" serde = { version = "1.0.103", default-features = false, features = ["derive"] } @@ -33,8 +33,8 @@ thiserror = { version = "1.0.21" } sha256 = { version = "1" } serde_json = "1.0.79" tonic = { version = "0.10.2" } -prost-types = "0.11.9" -prost = "0.11.9" +prost-types = "0.12.3" +prost = "0.12.3" # Test deps speculoos = "0.11.0" diff --git a/cw-orch/Cargo.toml b/cw-orch/Cargo.toml index 0eba00918..37c80a1d3 100644 --- a/cw-orch/Cargo.toml +++ b/cw-orch/Cargo.toml @@ -85,7 +85,7 @@ tonic = { workspace = true, optional = true, features = ["tls", "tls-roots"] } # Test Tube env deps osmosis-test-tube = { workspace = true, optional = true } -osmosis-std = { version = "=0.19.1", optional = true } +osmosis-std = { version = "=0.21.0", optional = true } prost-types = { workspace = true, optional = true } prost = { workspace = true, optional = true } diff --git a/packages/cw-orch-traits/Cargo.toml b/packages/cw-orch-traits/Cargo.toml index e5fa78b0e..b0d816f1c 100644 --- a/packages/cw-orch-traits/Cargo.toml +++ b/packages/cw-orch-traits/Cargo.toml @@ -14,5 +14,5 @@ readme = "README.md" [dependencies] cw-orch-core = { workspace = true } # Prost and Prost-types were kept at 0.11.9 here, because osmosis-test-tube doesn't support prost above 0.11.9 -prost-types = { version = "0.11.9" } -prost = { version = "0.11.9" } +prost-types = { workspace = true } +prost = { workspace = true } From c7ca93c59f1244e50108b171e22b59fe6f67c622 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Fri, 12 Jan 2024 17:23:47 +0100 Subject: [PATCH 05/15] Start testing authz --- cw-orch-daemon/src/builder.rs | 18 +++++-- cw-orch-daemon/src/core.rs | 16 +++++-- cw-orch-daemon/src/sender.rs | 49 +++++++++++++++++-- cw-orch-daemon/src/sync/builder.rs | 8 ++++ cw-orch-daemon/src/sync/core.rs | 9 ++++ cw-orch-daemon/tests/authz.rs | 75 ++++++++++++++++++++++++++++++ 6 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 cw-orch-daemon/tests/authz.rs diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 984de2a9c..f016655fe 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,4 +1,4 @@ -use crate::{log::print_if_log_disabled, DaemonAsync, DaemonBuilder}; +use crate::{log::print_if_log_disabled, sender::SenderOptions, DaemonAsync, DaemonBuilder}; use std::rc::Rc; use ibc_chain_registry::chain::ChainData; @@ -28,6 +28,8 @@ pub struct DaemonAsyncBuilder { pub(crate) deployment_id: Option, /// Wallet mnemonic pub(crate) mnemonic: Option, + /// Authz capability + pub(crate) authz_granter: Option, } impl DaemonAsyncBuilder { @@ -53,6 +55,12 @@ impl DaemonAsyncBuilder { self } + /// Specifies wether authz should be used with this daemon + pub fn with_authz(&mut self, granter: impl ToString) -> &mut Self { + self.authz_granter = Some(granter.to_string()); + self + } + /// Build a daemon pub async fn build(&self) -> Result { let chain = self @@ -65,10 +73,13 @@ impl DaemonAsyncBuilder { .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); let state = Rc::new(DaemonState::new(chain, deployment_id, false).await?); // if mnemonic provided, use it. Else use env variables to retrieve mnemonic + let sender_options = SenderOptions { + authz_granter: self.authz_granter.clone(), + }; let sender = if let Some(mnemonic) = &self.mnemonic { - Sender::from_mnemonic(&state, mnemonic)? + Sender::from_mnemonic_with_options(&state, mnemonic, sender_options)? } else { - Sender::new(&state)? + Sender::new_with_options(&state, sender_options)? }; let daemon = DaemonAsync { state, @@ -85,6 +96,7 @@ impl From for DaemonAsyncBuilder { chain: value.chain, deployment_id: value.deployment_id, mnemonic: value.mnemonic, + authz_granter: value.authz_granter, } } } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 9951e129e..2e5f56723 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -98,6 +98,14 @@ impl DaemonAsync { self.sender.address().unwrap() } + fn get_message_sender_addr(&self) -> Result { + if let Some(sender) = &self.sender.options.authz_granter { + Ok(sender.clone()) + } else { + self.sender.pub_addr_str() + } + } + /// Execute a message on a contract. pub async fn execute( &self, @@ -106,7 +114,7 @@ impl DaemonAsync { contract_address: &Addr, ) -> Result { let exec_msg: MsgExecuteContract = MsgExecuteContract { - sender: self.sender.pub_addr()?, + sender: self.get_message_sender_addr()?.parse()?, contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&exec_msg)?, funds: parse_cw_coins(coins)?, @@ -132,7 +140,7 @@ impl DaemonAsync { code_id, label: Some(label.unwrap_or("instantiate_contract").to_string()), admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), - sender: sender.pub_addr()?, + sender: self.get_message_sender_addr()?.parse()?, msg: serde_json::to_vec(&init_msg)?, funds: parse_cw_coins(coins)?, }; @@ -169,7 +177,7 @@ impl DaemonAsync { contract_address: &Addr, ) -> Result { let exec_msg: MsgMigrateContract = MsgMigrateContract { - sender: self.sender.pub_addr()?, + sender: self.get_message_sender_addr()?.parse()?, contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&migrate_msg)?, code_id: new_code_id, @@ -243,7 +251,7 @@ impl DaemonAsync { e.write_all(&file_contents)?; let wasm_byte_code = e.finish()?; let store_msg = cosmrs::cosmwasm::MsgStoreCode { - sender: sender.pub_addr()?, + sender: self.get_message_sender_addr()?.parse()?, wasm_byte_code, instantiate_permission: None, }; diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 9e8bb5a2c..1fd5a4881 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -25,7 +25,7 @@ use crate::{core::parse_cw_coins, keys::private::PrivateKey}; use cosmrs::{ bank::MsgSend, crypto::secp256k1::SigningKey, - proto::traits::Message, + proto::{cosmos::authz::v1beta1::MsgExec, traits::Message}, tendermint::chain::Id, tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, AccountId, Any, @@ -48,14 +48,29 @@ pub type Wallet = Rc>; /// Signer of the transactions and helper for address derivation /// This is the main interface for simulating and signing transactions +#[derive(Clone)] pub struct Sender { pub private_key: PrivateKey, pub secp: Secp256k1, pub(crate) daemon_state: Rc, + pub(crate) options: SenderOptions, +} + +#[derive(Default, Clone)] +#[non_exhaustive] +pub struct SenderOptions { + pub authz_granter: Option, } impl Sender { pub fn new(daemon_state: &Rc) -> Result, DaemonError> { + Self::new_with_options(daemon_state, SenderOptions::default()) + } + + pub fn new_with_options( + daemon_state: &Rc, + options: SenderOptions, + ) -> Result, DaemonError> { let kind = ChainKind::from(daemon_state.chain_data.network_type.clone()); // NETWORK_MNEMONIC_GROUP let env_variable_name = kind.mnemonic_env_variable_name(); @@ -66,13 +81,22 @@ impl Sender { ) }); - Self::from_mnemonic(daemon_state, &mnemonic) + Self::from_mnemonic_with_options(daemon_state, &mnemonic, options) } - /// Construct a new Sender from a mnemonic + /// Construct a new Sender from a mnemonic with additional options pub fn from_mnemonic( daemon_state: &Rc, mnemonic: &str, + ) -> Result, DaemonError> { + Self::from_mnemonic_with_options(daemon_state, mnemonic, SenderOptions::default()) + } + + /// Construct a new Sender from a mnemonic with additional options + pub fn from_mnemonic_with_options( + daemon_state: &Rc, + mnemonic: &str, + options: SenderOptions, ) -> Result, DaemonError> { let secp = Secp256k1::new(); let p_key: PrivateKey = @@ -82,6 +106,7 @@ impl Sender { daemon_state: daemon_state.clone(), private_key: p_key, secp, + options, }; log::info!( target: &local_target(), @@ -92,6 +117,10 @@ impl Sender { Ok(sender) } + pub fn with_authz(&mut self, granter: impl Into) { + self.options.authz_granter = Some(granter.into()); + } + fn cosmos_private_key(&self) -> SigningKey { SigningKey::from_slice(&self.private_key.raw_key()).unwrap() } @@ -239,6 +268,20 @@ impl Sender { ) -> Result { let timeout_height = Node::new(self.channel()).block_height().await? + 10u64; + let msgs = if self.options.authz_granter.is_some() { + // We wrap authz messages + vec![Any { + type_url: "cosmos.authz.v1beta1.MsgExec".to_string(), + value: MsgExec { + grantee: self.pub_addr_str()?, + msgs, + } + .encode_to_vec(), + }] + } else { + msgs + }; + let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); let tx_builder = TxBuilder::new(tx_body); diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 2f7e87486..0a025cd81 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -24,6 +24,8 @@ pub struct DaemonBuilder { pub(crate) deployment_id: Option, /// Wallet mnemonic pub(crate) mnemonic: Option, + /// Authz capability + pub(crate) authz_granter: Option, } impl DaemonBuilder { @@ -64,6 +66,12 @@ impl DaemonBuilder { self } + /// Specifies wether authz should be used with this daemon + pub fn with_authz(&mut self, granter: impl ToString) -> &mut Self { + self.authz_granter = Some(granter.to_string()); + self + } + /// Build a Daemon pub fn build(&self) -> Result { let rt_handle = self diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 1d416d194..907fa8723 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -71,6 +71,15 @@ impl Daemon { pub fn wallet(&self) -> Wallet { self.daemon.sender.clone() } + + // Adds authz capability to the returned Daemon + pub fn with_authz(&self, granter: impl ToString) -> Self { + let mut new_daemon = self.clone(); + let mut new_sender = (*self.daemon.sender).clone(); + new_sender.with_authz(granter.to_string()); + new_daemon.daemon.sender = Rc::new(new_sender); + new_daemon + } } impl ChainState for Daemon { diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs new file mode 100644 index 000000000..6d6f04f9c --- /dev/null +++ b/cw-orch-daemon/tests/authz.rs @@ -0,0 +1,75 @@ +mod common; +#[cfg(feature = "node-tests")] +mod tests { + /* + DaemonAsync contract general tests + */ + + use cosmrs::proto::cosmos::{ + authz::v1beta1::{GenericAuthorization, MsgGrant, MsgGrantResponse}, + bank::v1beta1::MsgSend, + }; + use cosmwasm_std::Addr; + use cw_orch_core::{contract::interface_traits::*, environment::TxHandler}; + use cw_orch_daemon::Daemon; + use cw_orch_traits::Stargate; + use mock_contract::{InstantiateMsg, MigrateMsg, QueryMsg}; + use prost::Message; + use prost::Name; + use prost_types::Any; + + use speculoos::prelude::*; + + use crate::common::Id; + + pub const SECOND_MNEMONIC: &str ="salute trigger antenna west ignore own dance bounce battle soul girl scan test enroll luggage sorry distance traffic brand keen rich syrup wood repair"; + + #[test] + #[serial_test::serial] + fn authz() -> anyhow::Result<()> { + use cw_orch_networks::networks; + + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let daemon = Daemon::builder() + .chain(networks::LOCAL_JUNO) + .handle(runtime.handle()) + .build() + .unwrap(); + + let second_daemon = Daemon::builder() + .chain(networks::LOCAL_JUNO) + .handle(runtime.handle()) + .mnemonic(SECOND_MNEMONIC) + .build() + .unwrap(); + + let sender = daemon.sender().to_string(); + let grantee = second_daemon.sender().to_string(); + + // We start by granting authz to an account + + daemon.commit_any::( + vec![Any { + type_url: "cosmos.authz.v1beta1.MsgGrant".to_string(), + value: MsgGrant { + granter: sender, + grantee: grantee.clone(), + grant: Some(cosmrs::proto::cosmos::authz::v1beta1::Grant { + authorization: Some(cosmrs::Any { + type_url: "cosmos.authz.v1beta1.GenericAuthorization".to_string(), + value: GenericAuthorization { + msg: MsgSend::full_name(), + } + .encode_to_vec(), + }), + expiration: None, + }), + } + .encode_to_vec(), + }], + None, + )?; + Ok(()) + } +} From 4d02ed5ae9b74b8d643102c972ca7bdaff2374be Mon Sep 17 00:00:00 2001 From: Kayanski Date: Fri, 12 Jan 2024 17:57:30 +0100 Subject: [PATCH 06/15] Added authz test --- cw-orch-daemon/src/core.rs | 16 +++------- cw-orch-daemon/src/sender.rs | 12 ++++++-- cw-orch-daemon/tests/authz.rs | 58 +++++++++++++++++++++++++---------- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 2e5f56723..0ac070577 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -98,14 +98,6 @@ impl DaemonAsync { self.sender.address().unwrap() } - fn get_message_sender_addr(&self) -> Result { - if let Some(sender) = &self.sender.options.authz_granter { - Ok(sender.clone()) - } else { - self.sender.pub_addr_str() - } - } - /// Execute a message on a contract. pub async fn execute( &self, @@ -114,7 +106,7 @@ impl DaemonAsync { contract_address: &Addr, ) -> Result { let exec_msg: MsgExecuteContract = MsgExecuteContract { - sender: self.get_message_sender_addr()?.parse()?, + sender: self.sender.message_sender()?, contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&exec_msg)?, funds: parse_cw_coins(coins)?, @@ -140,7 +132,7 @@ impl DaemonAsync { code_id, label: Some(label.unwrap_or("instantiate_contract").to_string()), admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), - sender: self.get_message_sender_addr()?.parse()?, + sender: self.sender.message_sender()?, msg: serde_json::to_vec(&init_msg)?, funds: parse_cw_coins(coins)?, }; @@ -177,7 +169,7 @@ impl DaemonAsync { contract_address: &Addr, ) -> Result { let exec_msg: MsgMigrateContract = MsgMigrateContract { - sender: self.get_message_sender_addr()?.parse()?, + sender: self.sender.message_sender()?, contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&migrate_msg)?, code_id: new_code_id, @@ -251,7 +243,7 @@ impl DaemonAsync { e.write_all(&file_contents)?; let wasm_byte_code = e.finish()?; let store_msg = cosmrs::cosmwasm::MsgStoreCode { - sender: self.get_message_sender_addr()?.parse()?, + sender: self.sender.message_sender()?, wasm_byte_code, instantiate_permission: None, }; diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index f770252e5..b6b126772 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -144,13 +144,21 @@ impl Sender { Ok(self.pub_addr()?.to_string()) } + pub fn message_sender(&self) -> Result { + if let Some(sender) = &self.options.authz_granter { + Ok(sender.parse()?) + } else { + self.pub_addr() + } + } + pub async fn bank_send( &self, recipient: &str, coins: Vec, ) -> Result { let msg_send = MsgSend { - from_address: self.pub_addr()?, + from_address: self.message_sender()?, to_address: AccountId::from_str(recipient)?, amount: parse_cw_coins(&coins)?, }; @@ -271,7 +279,7 @@ impl Sender { let msgs = if self.options.authz_granter.is_some() { // We wrap authz messages vec![Any { - type_url: "cosmos.authz.v1beta1.MsgExec".to_string(), + type_url: "/cosmos.authz.v1beta1.MsgExec".to_string(), value: MsgExec { grantee: self.pub_addr_str()?, msgs, diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index 6d6f04f9c..4f3232605 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -9,18 +9,14 @@ mod tests { authz::v1beta1::{GenericAuthorization, MsgGrant, MsgGrantResponse}, bank::v1beta1::MsgSend, }; - use cosmwasm_std::Addr; - use cw_orch_core::{contract::interface_traits::*, environment::TxHandler}; + use cosmwasm_std::coins; + use cw_orch_core::environment::{BankQuerier, TxHandler}; use cw_orch_daemon::Daemon; + use cw_orch_networks::networks::LOCAL_JUNO; use cw_orch_traits::Stargate; - use mock_contract::{InstantiateMsg, MigrateMsg, QueryMsg}; use prost::Message; use prost::Name; - use prost_types::Any; - - use speculoos::prelude::*; - - use crate::common::Id; + use prost_types::{Any, Timestamp}; pub const SECOND_MNEMONIC: &str ="salute trigger antenna west ignore own dance bounce battle soul girl scan test enroll luggage sorry distance traffic brand keen rich syrup wood repair"; @@ -37,39 +33,69 @@ mod tests { .build() .unwrap(); + let sender = daemon.sender().to_string(); + let second_daemon = Daemon::builder() .chain(networks::LOCAL_JUNO) .handle(runtime.handle()) + .with_authz(sender.clone()) .mnemonic(SECOND_MNEMONIC) .build() .unwrap(); - let sender = daemon.sender().to_string(); let grantee = second_daemon.sender().to_string(); - // We start by granting authz to an account + let current_timestamp = daemon.block_info()?.time; - daemon.commit_any::( + // We start by granting authz to an account + daemon.commit_any::( vec![Any { - type_url: "cosmos.authz.v1beta1.MsgGrant".to_string(), + type_url: "/cosmos.authz.v1beta1.MsgGrant".to_string(), value: MsgGrant { - granter: sender, + granter: sender.clone(), grantee: grantee.clone(), grant: Some(cosmrs::proto::cosmos::authz::v1beta1::Grant { authorization: Some(cosmrs::Any { - type_url: "cosmos.authz.v1beta1.GenericAuthorization".to_string(), + type_url: "/cosmos.authz.v1beta1.GenericAuthorization".to_string(), value: GenericAuthorization { - msg: MsgSend::full_name(), + msg: MsgSend::type_url(), } .encode_to_vec(), }), - expiration: None, + expiration: Some(Timestamp { + seconds: (current_timestamp.seconds() + 3600) as i64, + nanos: 0, + }), }), } .encode_to_vec(), }], None, )?; + + // The we send some funds to the account + runtime.block_on( + daemon + .daemon + .sender + .bank_send(&grantee, coins(1_000_000, LOCAL_JUNO.gas_denom)), + )?; + + // And send a large amount of tokens on their behalf + runtime.block_on( + second_daemon + .daemon + .sender + .bank_send(&grantee, coins(5_000_000, LOCAL_JUNO.gas_denom)), + )?; + + // the balance of the grantee whould be 6_000_000 or close + + let grantee_balance = + daemon.balance(grantee.clone(), Some(LOCAL_JUNO.gas_denom.to_string()))?; + + assert_eq!(grantee_balance.first().unwrap().amount.u128(), 6_000_000); + Ok(()) } } From da502a03abe747b3b82ec749ba62b19c118166ac Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 17 Jan 2024 16:28:34 +0200 Subject: [PATCH 07/15] add authz querier --- cw-orch-daemon/src/lib.rs | 1 + cw-orch-daemon/src/queriers.rs | 2 + cw-orch-daemon/src/queriers/authz.rs | 79 ++++++++++++++++++++++++++++ cw-orch-daemon/tests/authz.rs | 40 ++++++++------ 4 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 cw-orch-daemon/src/queriers/authz.rs diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index 8c0209998..54d080894 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -31,6 +31,7 @@ pub(crate) mod cosmos_modules { pub use cosmrs::proto::{ cosmos::{ auth::v1beta1 as auth, + authz::v1beta1 as authz, bank::v1beta1 as bank, base::{abci::v1beta1 as abci, tendermint::v1beta1 as tendermint}, feegrant::v1beta1 as feegrant, diff --git a/cw-orch-daemon/src/queriers.rs b/cw-orch-daemon/src/queriers.rs index 67e3e5e99..df6be4f99 100644 --- a/cw-orch-daemon/src/queriers.rs +++ b/cw-orch-daemon/src/queriers.rs @@ -44,6 +44,7 @@ macro_rules! cosmos_query { }; } +mod authz; mod bank; mod cosmwasm; mod feegrant; @@ -52,6 +53,7 @@ mod ibc; mod node; mod staking; +pub use authz::Authz; pub use bank::{cosmrs_to_cosmwasm_coins, Bank}; pub use cosmwasm::CosmWasm; pub use feegrant::Feegrant; diff --git a/cw-orch-daemon/src/queriers/authz.rs b/cw-orch-daemon/src/queriers/authz.rs new file mode 100644 index 000000000..9af47937c --- /dev/null +++ b/cw-orch-daemon/src/queriers/authz.rs @@ -0,0 +1,79 @@ +use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; +use tonic::transport::Channel; + +use crate::{cosmos_modules, error::DaemonError}; + +use super::DaemonQuerier; + +/// Queries for Cosmos AuthZ Module +pub struct Authz { + channel: Channel, +} + +impl DaemonQuerier for Authz { + fn new(channel: Channel) -> Self { + Self { channel } + } +} + +impl Authz { + /// Query Authz Grants from grantee to granter + pub async fn grants( + &self, + granter: String, + grantee: String, + msg_type_url: String, + pagination: Option, + ) -> Result { + use cosmos_modules::authz::{query_client::QueryClient, QueryGrantsRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let grants = client + .grants(QueryGrantsRequest { + granter, + grantee, + msg_type_url, + pagination, + }) + .await? + .into_inner(); + Ok(grants) + } + + /// Query Authz Grants of grantee + pub async fn grantee_grants( + &self, + grantee: String, + pagination: Option, + ) -> Result + { + use cosmos_modules::authz::{query_client::QueryClient, QueryGranteeGrantsRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let grants = client + .grantee_grants(QueryGranteeGrantsRequest { + grantee, + pagination, + }) + .await? + .into_inner(); + Ok(grants) + } + + /// Query Authz Grants for granter + pub async fn granter_grants( + &self, + granter: String, + pagination: Option, + ) -> Result + { + use cosmos_modules::authz::{query_client::QueryClient, QueryGranterGrantsRequest}; + let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let grants = client + .granter_grants(QueryGranterGrantsRequest { + granter, + pagination, + }) + .await? + .into_inner(); + Ok(grants) + } +} diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index 4f3232605..d431344de 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -6,12 +6,12 @@ mod tests { */ use cosmrs::proto::cosmos::{ - authz::v1beta1::{GenericAuthorization, MsgGrant, MsgGrantResponse}, + authz::v1beta1::{GenericAuthorization, MsgGrant, MsgGrantResponse, QueryGrantsResponse}, bank::v1beta1::MsgSend, }; use cosmwasm_std::coins; use cw_orch_core::environment::{BankQuerier, TxHandler}; - use cw_orch_daemon::Daemon; + use cw_orch_daemon::{queriers::Authz, Daemon}; use cw_orch_networks::networks::LOCAL_JUNO; use cw_orch_traits::Stargate; use prost::Message; @@ -47,6 +47,19 @@ mod tests { let current_timestamp = daemon.block_info()?.time; + let grant = cosmrs::proto::cosmos::authz::v1beta1::Grant { + authorization: Some(cosmrs::Any { + type_url: "/cosmos.authz.v1beta1.GenericAuthorization".to_string(), + value: GenericAuthorization { + msg: MsgSend::type_url(), + } + .encode_to_vec(), + }), + expiration: Some(Timestamp { + seconds: (current_timestamp.seconds() + 3600) as i64, + nanos: 0, + }), + }; // We start by granting authz to an account daemon.commit_any::( vec![Any { @@ -54,25 +67,22 @@ mod tests { value: MsgGrant { granter: sender.clone(), grantee: grantee.clone(), - grant: Some(cosmrs::proto::cosmos::authz::v1beta1::Grant { - authorization: Some(cosmrs::Any { - type_url: "/cosmos.authz.v1beta1.GenericAuthorization".to_string(), - value: GenericAuthorization { - msg: MsgSend::type_url(), - } - .encode_to_vec(), - }), - expiration: Some(Timestamp { - seconds: (current_timestamp.seconds() + 3600) as i64, - nanos: 0, - }), - }), + grant: Some(grant.clone()), } .encode_to_vec(), }], None, )?; + // Check Query of the grant + let authz_querier: Authz = daemon.query_client(); + let grants: QueryGrantsResponse = runtime.handle().block_on(async { + authz_querier + .grants(sender.clone(), grantee.clone(), MsgSend::type_url(), None) + .await + })?; + assert_eq!(grants.grants, vec![grant]); + // The we send some funds to the account runtime.block_on( daemon From d23d0f7cc29bc7c6eeddd4df0727e95b32ad2c76 Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 17 Jan 2024 19:19:31 +0200 Subject: [PATCH 08/15] tx handler error with anyhow test --- .../src/environment/cosmwasm_environment.rs | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/packages/cw-orch-core/src/environment/cosmwasm_environment.rs b/packages/cw-orch-core/src/environment/cosmwasm_environment.rs index d984383e4..175a7a65b 100644 --- a/packages/cw-orch-core/src/environment/cosmwasm_environment.rs +++ b/packages/cw-orch-core/src/environment/cosmwasm_environment.rs @@ -123,3 +123,106 @@ pub trait BankQuerier: TxHandler { /// Query total supply in the bank for a denom fn supply_of(&self, denom: impl Into) -> Result::Error>; } + +// TODO: Perfect test candidate for `trybuild` +#[cfg(test)] +mod tests { + use cw_multi_test::AppResponse; + + use super::*; + + #[derive(Clone)] + struct MockHandler {} + + impl ChainState for MockHandler { + type Out = (); + + fn state(&self) -> Self::Out { + () + } + } + + impl TxHandler for MockHandler { + type Response = AppResponse; + + type Error = CwEnvError; + + type ContractSource = (); + + type Sender = (); + + fn sender(&self) -> Addr { + unimplemented!() + } + + fn set_sender(&mut self, _sender: Self::Sender) {} + + fn wait_blocks(&self, _amount: u64) -> Result<(), Self::Error> { + Ok(()) + } + + fn wait_seconds(&self, _secs: u64) -> Result<(), Self::Error> { + Ok(()) + } + + fn next_block(&self) -> Result<(), Self::Error> { + Ok(()) + } + + fn block_info(&self) -> Result { + unimplemented!() + } + + fn upload(&self, _contract_source: &impl Uploadable) -> Result { + unimplemented!() + } + + fn instantiate( + &self, + _code_id: u64, + _init_msg: &I, + _label: Option<&str>, + _admin: Option<&Addr>, + _coins: &[cosmwasm_std::Coin], + ) -> Result { + unimplemented!() + } + + fn execute( + &self, + _exec_msg: &E, + _coins: &[Coin], + _contract_address: &Addr, + ) -> Result { + unimplemented!() + } + + fn query( + &self, + _query_msg: &Q, + _contract_address: &Addr, + ) -> Result { + unimplemented!() + } + + fn migrate( + &self, + _migrate_msg: &M, + _new_code_id: u64, + _contract_address: &Addr, + ) -> Result { + unimplemented!() + } + } + + fn associated_error(t: T) -> anyhow::Result<()> { + t.wait_blocks(5)?; + Ok(()) + } + + #[test] + fn tx_handler_error_usable_on_anyhow() -> anyhow::Result<()> { + associated_error(MockHandler {})?; + Ok(()) + } +} From 5b7ebb29a1437b0d1920ebf708ddbc4863b7cea8 Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 17 Jan 2024 19:21:32 +0200 Subject: [PATCH 09/15] format --- cw-orch-daemon/src/log.rs | 2 +- .../cw-orch-core/src/environment/cosmwasm_environment.rs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cw-orch-daemon/src/log.rs b/cw-orch-daemon/src/log.rs index 9bbf5f942..cceb990b5 100644 --- a/cw-orch-daemon/src/log.rs +++ b/cw-orch-daemon/src/log.rs @@ -8,7 +8,7 @@ static LOGS_DISABLED: Once = Once::new(); // Prints a warning if log is disabled for the application pub fn print_if_log_disabled() -> Result<(), DaemonError> { - LOGS_DISABLED.call_once(|| { + LOGS_DISABLED.call_once(|| { // Here we check for logging capabilities. if !log::log_enabled!(log::Level::Info) && !CwOrchEnvVars::load().map(|env|env.disable_logs_message).unwrap_or(false){ println!( diff --git a/packages/cw-orch-core/src/environment/cosmwasm_environment.rs b/packages/cw-orch-core/src/environment/cosmwasm_environment.rs index 74bc4f1b7..71de4fa92 100644 --- a/packages/cw-orch-core/src/environment/cosmwasm_environment.rs +++ b/packages/cw-orch-core/src/environment/cosmwasm_environment.rs @@ -137,9 +137,7 @@ mod tests { impl ChainState for MockHandler { type Out = (); - fn state(&self) -> Self::Out { - () - } + fn state(&self) -> Self::Out {} } impl TxHandler for MockHandler { @@ -173,7 +171,10 @@ mod tests { unimplemented!() } - fn upload(&self, _contract_source: &impl Uploadable) -> Result { + fn upload( + &self, + _contract_source: &impl Uploadable, + ) -> Result { unimplemented!() } From 814066f8de81bd7ec7f5d84831c5135c9cf5cf8f Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 18 Jan 2024 11:39:45 +0100 Subject: [PATCH 10/15] Added authz builder method for options --- cw-orch-daemon/src/sender.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index b6b126772..87da26545 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -62,6 +62,13 @@ pub struct SenderOptions { pub authz_granter: Option, } +impl SenderOptions { + pub fn authz_granter(mut self, granter: &str) -> Self { + self.authz_granter = Some(granter.to_string()); + self + } +} + impl Sender { pub fn new(daemon_state: &Rc) -> Result, DaemonError> { Self::new_with_options(daemon_state, SenderOptions::default()) From 05e321b4960fdd1c2f8ba72393ad274a113e6a99 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 18 Jan 2024 11:52:51 +0100 Subject: [PATCH 11/15] Some nits --- cw-orch-daemon/src/builder.rs | 4 ++-- cw-orch-daemon/src/core.rs | 8 ++++---- cw-orch-daemon/src/sender.rs | 9 ++++++--- cw-orch-daemon/src/sync/builder.rs | 2 +- cw-orch-daemon/src/sync/core.rs | 4 ++-- cw-orch-daemon/tests/authz.rs | 13 +++++++++++-- 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index f016655fe..59197aa07 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -55,8 +55,8 @@ impl DaemonAsyncBuilder { self } - /// Specifies wether authz should be used with this daemon - pub fn with_authz(&mut self, granter: impl ToString) -> &mut Self { + /// Specifies whether authz should be used with this daemon + pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { self.authz_granter = Some(granter.to_string()); self } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 0ac070577..0b958abdd 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -106,7 +106,7 @@ impl DaemonAsync { contract_address: &Addr, ) -> Result { let exec_msg: MsgExecuteContract = MsgExecuteContract { - sender: self.sender.message_sender()?, + sender: self.sender.msg_sender()?, contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&exec_msg)?, funds: parse_cw_coins(coins)?, @@ -132,7 +132,7 @@ impl DaemonAsync { code_id, label: Some(label.unwrap_or("instantiate_contract").to_string()), admin: admin.map(|a| FromStr::from_str(a.as_str()).unwrap()), - sender: self.sender.message_sender()?, + sender: self.sender.msg_sender()?, msg: serde_json::to_vec(&init_msg)?, funds: parse_cw_coins(coins)?, }; @@ -169,7 +169,7 @@ impl DaemonAsync { contract_address: &Addr, ) -> Result { let exec_msg: MsgMigrateContract = MsgMigrateContract { - sender: self.sender.message_sender()?, + sender: self.sender.msg_sender()?, contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&migrate_msg)?, code_id: new_code_id, @@ -243,7 +243,7 @@ impl DaemonAsync { e.write_all(&file_contents)?; let wasm_byte_code = e.finish()?; let store_msg = cosmrs::cosmwasm::MsgStoreCode { - sender: self.sender.message_sender()?, + sender: self.sender.msg_sender()?, wasm_byte_code, instantiate_permission: None, }; diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 87da26545..7da3fa704 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -124,7 +124,7 @@ impl Sender { Ok(sender) } - pub fn with_authz(&mut self, granter: impl Into) { + pub fn authz_granter(&mut self, granter: impl Into) { self.options.authz_granter = Some(granter.into()); } @@ -151,7 +151,10 @@ impl Sender { Ok(self.pub_addr()?.to_string()) } - pub fn message_sender(&self) -> Result { + /// Returns the actual sender of every message sent. + /// If an authz granter is set, returns the authz granter + /// Else, returns the address associated with the current private key + pub fn msg_sender(&self) -> Result { if let Some(sender) = &self.options.authz_granter { Ok(sender.parse()?) } else { @@ -165,7 +168,7 @@ impl Sender { coins: Vec, ) -> Result { let msg_send = MsgSend { - from_address: self.message_sender()?, + from_address: self.msg_sender()?, to_address: AccountId::from_str(recipient)?, amount: parse_cw_coins(&coins)?, }; diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 0a025cd81..6f9b5be23 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -67,7 +67,7 @@ impl DaemonBuilder { } /// Specifies wether authz should be used with this daemon - pub fn with_authz(&mut self, granter: impl ToString) -> &mut Self { + pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { self.authz_granter = Some(granter.to_string()); self } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 907fa8723..dd01780e4 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -73,10 +73,10 @@ impl Daemon { } // Adds authz capability to the returned Daemon - pub fn with_authz(&self, granter: impl ToString) -> Self { + pub fn authz_granter(&self, granter: impl ToString) -> Self { let mut new_daemon = self.clone(); let mut new_sender = (*self.daemon.sender).clone(); - new_sender.with_authz(granter.to_string()); + new_sender.authz_granter(granter.to_string()); new_daemon.daemon.sender = Rc::new(new_sender); new_daemon } diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index d431344de..a2b75ade5 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -2,7 +2,7 @@ mod common; #[cfg(feature = "node-tests")] mod tests { /* - DaemonAsync contract general tests + Authz tests */ use cosmrs::proto::cosmos::{ @@ -38,7 +38,7 @@ mod tests { let second_daemon = Daemon::builder() .chain(networks::LOCAL_JUNO) .handle(runtime.handle()) - .with_authz(sender.clone()) + .authz_granter(sender.clone()) .mnemonic(SECOND_MNEMONIC) .build() .unwrap(); @@ -82,6 +82,15 @@ mod tests { .await })?; assert_eq!(grants.grants, vec![grant]); + // No grant gives out an error + runtime + .handle() + .block_on(async { + authz_querier + .grants(grantee.clone(), sender.clone(), MsgSend::type_url(), None) + .await + }) + .unwrap_err(); // The we send some funds to the account runtime.block_on( From 6e70d1fe82ee315b2086ed968fc96eafc3934a60 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 18 Jan 2024 12:06:32 +0100 Subject: [PATCH 12/15] Revert with_authz, added sender options setter --- cw-orch-daemon/src/sync/core.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index dd01780e4..97d535a55 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -3,6 +3,7 @@ use std::{fmt::Debug, rc::Rc, time::Duration}; use super::super::{sender::Wallet, DaemonAsync}; use crate::{ queriers::{cosmrs_to_cosmwasm_coins, Bank, DaemonQuerier, Node}, + sender::SenderOptions, CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, }; @@ -72,14 +73,23 @@ impl Daemon { self.daemon.sender.clone() } - // Adds authz capability to the returned Daemon - pub fn authz_granter(&self, granter: impl ToString) -> Self { + /// Adds authz capability to the returned Daemon + pub fn with_authz(&self, granter: impl ToString) -> Self { let mut new_daemon = self.clone(); let mut new_sender = (*self.daemon.sender).clone(); new_sender.authz_granter(granter.to_string()); new_daemon.daemon.sender = Rc::new(new_sender); new_daemon } + + /// Modifies all the sender options in one go + pub fn with_sender_options(&self, options: SenderOptions) -> Self { + let mut new_daemon = self.clone(); + let mut new_sender = (*self.daemon.sender).clone(); + new_sender.options = options; + new_daemon.daemon.sender = Rc::new(new_sender); + new_daemon + } } impl ChainState for Daemon { From 10b52a8326d81ddfddf0303bb8bacf7e76e49e90 Mon Sep 17 00:00:00 2001 From: Buckram Date: Thu, 18 Jan 2024 18:38:45 +0200 Subject: [PATCH 13/15] Check rest of authz queries --- cw-orch-daemon/tests/authz.rs | 54 ++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index a2b75ade5..1222dc5fe 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -6,7 +6,10 @@ mod tests { */ use cosmrs::proto::cosmos::{ - authz::v1beta1::{GenericAuthorization, MsgGrant, MsgGrantResponse, QueryGrantsResponse}, + authz::v1beta1::{ + GenericAuthorization, GrantAuthorization, MsgGrant, MsgGrantResponse, + QueryGranteeGrantsResponse, QueryGranterGrantsResponse, QueryGrantsResponse, + }, bank::v1beta1::MsgSend, }; use cosmwasm_std::coins; @@ -47,18 +50,20 @@ mod tests { let current_timestamp = daemon.block_info()?.time; + let authorization = cosmrs::Any { + type_url: "/cosmos.authz.v1beta1.GenericAuthorization".to_string(), + value: GenericAuthorization { + msg: MsgSend::type_url(), + } + .encode_to_vec(), + }; + let expiration = Timestamp { + seconds: (current_timestamp.seconds() + 3600) as i64, + nanos: 0, + }; let grant = cosmrs::proto::cosmos::authz::v1beta1::Grant { - authorization: Some(cosmrs::Any { - type_url: "/cosmos.authz.v1beta1.GenericAuthorization".to_string(), - value: GenericAuthorization { - msg: MsgSend::type_url(), - } - .encode_to_vec(), - }), - expiration: Some(Timestamp { - seconds: (current_timestamp.seconds() + 3600) as i64, - nanos: 0, - }), + authorization: Some(authorization.clone()), + expiration: Some(expiration.clone()), }; // We start by granting authz to an account daemon.commit_any::( @@ -74,7 +79,15 @@ mod tests { None, )?; - // Check Query of the grant + // Check Queries of the authz + let grant_authorization = GrantAuthorization { + granter: sender.clone(), + grantee: grantee.clone(), + authorization: Some(authorization.clone()), + expiration: Some(expiration.clone()), + }; + + // Grants let authz_querier: Authz = daemon.query_client(); let grants: QueryGrantsResponse = runtime.handle().block_on(async { authz_querier @@ -82,6 +95,19 @@ mod tests { .await })?; assert_eq!(grants.grants, vec![grant]); + + // Grantee grants + let grantee_grants: QueryGranteeGrantsResponse = runtime + .handle() + .block_on(async { authz_querier.grantee_grants(grantee.clone(), None).await })?; + assert_eq!(grantee_grants.grants, vec![grant_authorization.clone()]); + + // Granter grants + let granter_grants: QueryGranterGrantsResponse = runtime + .handle() + .block_on(async { authz_querier.granter_grants(sender.clone(), None).await })?; + assert_eq!(granter_grants.grants, vec![grant_authorization]); + // No grant gives out an error runtime .handle() @@ -92,6 +118,8 @@ mod tests { }) .unwrap_err(); + // Check use of grants + // The we send some funds to the account runtime.block_on( daemon From 52d29ae4fad6b0e99cd3606dca24d3d02f7ea6fe Mon Sep 17 00:00:00 2001 From: Kayanski Date: Fri, 19 Jan 2024 08:57:46 +0100 Subject: [PATCH 14/15] Added fee grant in daemon builder and interface --- cw-orch-daemon/src/builder.rs | 18 +++++++++++------- cw-orch-daemon/src/sender.rs | 18 +++++++++++++++++- cw-orch-daemon/src/sync/builder.rs | 14 ++++++++++---- cw-orch-daemon/src/sync/core.rs | 11 ++++++++++- cw-orch-daemon/src/tx_builder.rs | 13 ++++++++++--- packages/cw-orch-core/src/env.rs | 9 --------- 6 files changed, 58 insertions(+), 25 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 59197aa07..1031f4f2d 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -28,8 +28,8 @@ pub struct DaemonAsyncBuilder { pub(crate) deployment_id: Option, /// Wallet mnemonic pub(crate) mnemonic: Option, - /// Authz capability - pub(crate) authz_granter: Option, + /// Specify Daemon Sender Options + pub(crate) sender_options: SenderOptions, } impl DaemonAsyncBuilder { @@ -57,7 +57,13 @@ impl DaemonAsyncBuilder { /// Specifies whether authz should be used with this daemon pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { - self.authz_granter = Some(granter.to_string()); + self.sender_options.set_authz_granter(granter); + self + } + + /// Specifies whether a fee grant should be used with this daemon + pub fn fee_granter(&mut self, granter: impl ToString) -> &mut Self { + self.sender_options.set_fee_granter(granter); self } @@ -73,9 +79,7 @@ impl DaemonAsyncBuilder { .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); let state = Rc::new(DaemonState::new(chain, deployment_id, false).await?); // if mnemonic provided, use it. Else use env variables to retrieve mnemonic - let sender_options = SenderOptions { - authz_granter: self.authz_granter.clone(), - }; + let sender_options = self.sender_options.clone(); let sender = if let Some(mnemonic) = &self.mnemonic { Sender::from_mnemonic_with_options(&state, mnemonic, sender_options)? } else { @@ -96,7 +100,7 @@ impl From for DaemonAsyncBuilder { chain: value.chain, deployment_id: value.deployment_id, mnemonic: value.mnemonic, - authz_granter: value.authz_granter, + sender_options: value.sender_options, } } } diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 7da3fa704..037950168 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -60,13 +60,24 @@ pub struct Sender { #[non_exhaustive] pub struct SenderOptions { pub authz_granter: Option, + pub fee_granter: Option, } impl SenderOptions { - pub fn authz_granter(mut self, granter: &str) -> Self { + pub fn authz_granter(mut self, granter: impl ToString) -> Self { self.authz_granter = Some(granter.to_string()); self } + pub fn fee_granter(mut self, granter: impl ToString) -> Self { + self.fee_granter = Some(granter.to_string()); + self + } + pub fn set_authz_granter(&mut self, granter: impl ToString) { + self.authz_granter = Some(granter.to_string()); + } + pub fn set_fee_granter(&mut self, granter: impl ToString) { + self.fee_granter = Some(granter.to_string()); + } } impl Sender { @@ -128,6 +139,10 @@ impl Sender { self.options.authz_granter = Some(granter.into()); } + pub fn fee_granter(&mut self, granter: impl Into) { + self.options.fee_granter = Some(granter.into()); + } + fn cosmos_private_key(&self) -> SigningKey { SigningKey::from_slice(&self.private_key.raw_key()).unwrap() } @@ -216,6 +231,7 @@ impl Sender { 0u8, &self.daemon_state.chain_data.fees.fee_tokens[0].denom, 0, + self.options.clone(), )?; let auth_info = SignerInfo { diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 6f9b5be23..d150de025 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,6 +1,6 @@ use ibc_chain_registry::chain::ChainData; -use crate::DaemonAsyncBuilder; +use crate::{sender::SenderOptions, DaemonAsyncBuilder}; use super::{super::error::DaemonError, core::Daemon}; @@ -24,8 +24,8 @@ pub struct DaemonBuilder { pub(crate) deployment_id: Option, /// Wallet mnemonic pub(crate) mnemonic: Option, - /// Authz capability - pub(crate) authz_granter: Option, + /// Specify Daemon Sender Options + pub(crate) sender_options: SenderOptions, } impl DaemonBuilder { @@ -68,7 +68,13 @@ impl DaemonBuilder { /// Specifies wether authz should be used with this daemon pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { - self.authz_granter = Some(granter.to_string()); + self.sender_options.set_authz_granter(granter.to_string()); + self + } + + /// Specifies wether authz should be used with this daemon + pub fn fee_granter(&mut self, granter: impl ToString) -> &mut Self { + self.sender_options.set_fee_granter(granter.to_string()); self } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 97d535a55..1866bd867 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -74,7 +74,7 @@ impl Daemon { } /// Adds authz capability to the returned Daemon - pub fn with_authz(&self, granter: impl ToString) -> Self { + pub fn with_authz_granter(&self, granter: impl ToString) -> Self { let mut new_daemon = self.clone(); let mut new_sender = (*self.daemon.sender).clone(); new_sender.authz_granter(granter.to_string()); @@ -82,6 +82,15 @@ impl Daemon { new_daemon } + /// Adds authz capability to the returned Daemon + pub fn with_fee_granter(&self, granter: impl ToString) -> Self { + let mut new_daemon = self.clone(); + let mut new_sender = (*self.daemon.sender).clone(); + new_sender.fee_granter(granter.to_string()); + new_daemon.daemon.sender = Rc::new(new_sender); + new_daemon + } + /// Modifies all the sender options in one go pub fn with_sender_options(&self, options: SenderOptions) -> Self { let mut new_daemon = self.clone(); diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index e281660ec..db107b81d 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -10,7 +10,8 @@ use cosmrs::{ Any, Coin, }; use cw_orch_core::log::transaction_target; -use cw_orch_core::CwOrchEnvVars; + +use crate::sender::SenderOptions; use super::{sender::Sender, DaemonError}; @@ -65,10 +66,11 @@ impl TxBuilder { amount: impl Into, denom: &str, gas_limit: u64, + sender_options: SenderOptions, ) -> Result { let fee = Coin::new(amount.into(), denom).unwrap(); let mut fee = Fee::from_amount_and_gas(fee, gas_limit); - fee.granter = CwOrchEnvVars::load()? + fee.granter = sender_options .fee_granter .map(|g| AccountId::from_str(&g)) .transpose()?; @@ -132,7 +134,12 @@ impl TxBuilder { (fee_amount, gas_expected) }; - let fee = Self::build_fee(tx_fee, &wallet.get_fee_token(), gas_limit)?; + let fee = Self::build_fee( + tx_fee, + &wallet.get_fee_token(), + gas_limit, + wallet.options.clone(), + )?; log::debug!( target: &transaction_target(), diff --git a/packages/cw-orch-core/src/env.rs b/packages/cw-orch-core/src/env.rs index 0cb0f0d7b..d23021644 100644 --- a/packages/cw-orch-core/src/env.rs +++ b/packages/cw-orch-core/src/env.rs @@ -25,7 +25,6 @@ pub const DISABLE_WALLET_BALANCE_ASSERTION_ENV_NAME: &str = "CW_ORCH_DISABLE_WALLET_BALANCE_ASSERTION"; pub const DISABLE_MANUAL_INTERACTION_ENV_NAME: &str = "CW_ORCH_DISABLE_MANUAL_INTERACTION"; pub const DISABLE_ENABLE_LOGS_MESSAGE_ENV_NAME: &str = "CW_ORCH_DISABLE_ENABLE_LOGS_MESSAGE"; -pub const FEE_GRANTER_ENV_NAME: &str = "CW_ORCH_FEE_GRANTER"; pub const MAIN_MNEMONIC_ENV_NAME: &str = "MAIN_MNEMONIC"; pub const TEST_MNEMONIC_ENV_NAME: &str = "TEST_MNEMONIC"; pub const LOCAL_MNEMONIC_ENV_NAME: &str = "LOCAL_MNEMONIC"; @@ -101,10 +100,6 @@ pub struct CwOrchEnvVars { /// Disable the "Enable Logs" message /// It allows forcing cw-orch to not output anything pub disable_logs_message: bool, - /// Optional - string - /// Specify a fee granter for interacting with a chain - /// This allows interacting with the blockchain and make another address pay for your fees (if allowed) - pub fee_granter: Option, } /// Fetches the default state folder. @@ -136,7 +131,6 @@ impl Default for CwOrchEnvVars { disable_wallet_balance_assertion: false, disable_manual_interaction: false, disable_logs_message: false, - fee_granter: None, } } } @@ -185,9 +179,6 @@ impl CwOrchEnvVars { if let Ok(str_value) = env::var(LOCAL_MNEMONIC_ENV_NAME) { env_values.local_mnemonic = Some(str_value); } - if let Ok(str_value) = env::var(FEE_GRANTER_ENV_NAME) { - env_values.fee_granter = Some(str_value); - } Ok(env_values) } } From 1ab922a3f79422f028fae2ee3709f5097eb0cdc9 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 23 Jan 2024 15:01:41 +0100 Subject: [PATCH 15/15] Removed node-test default feature in cw-orch-daemon --- cw-orch-daemon/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 6900bdf34..c9db4e281 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -17,7 +17,7 @@ exclude = [".env"] all-features = true [features] -default = ["node-tests"] +default = [] # enable node-backed tests (ensure Docker is running) # run with `cargo test --jobs 1 --features node-tests` node-tests = []