From d6776da780df2b20cd0ba03b8d573941d5cf34cd Mon Sep 17 00:00:00 2001 From: Buckram Date: Mon, 12 Feb 2024 15:12:21 +0200 Subject: [PATCH 001/108] undefined behavior on simultaneous read or write --- cw-orch-daemon/tests/daemon_state.rs | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 cw-orch-daemon/tests/daemon_state.rs diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs new file mode 100644 index 000000000..38173f292 --- /dev/null +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -0,0 +1,42 @@ +use std::{thread, time::Duration}; + +use cw_orch_core::env::STATE_FILE_ENV_NAME; +use cw_orch_daemon::{ChainRegistryData, DaemonState}; +use cw_orch_networks::networks::JUNO_1; +use tokio::runtime::Runtime; + +#[test] +fn simultaneous_read() { + let runtime = Runtime::new().unwrap(); + + let chain_data: ChainRegistryData = JUNO_1.into(); + std::env::set_var(STATE_FILE_ENV_NAME, "./tests/test.json"); + + let daemon_state = runtime + .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) + .unwrap(); + daemon_state.set("test", "test", "test").unwrap(); + for _ in 0..25 { + let daemon_state = daemon_state.clone(); + std::thread::spawn(move || daemon_state.get("test")); + } + thread::sleep(Duration::from_millis(500)); +} + +#[test] +fn simultaneous_write() { + let runtime = Runtime::new().unwrap(); + + let chain_data: ChainRegistryData = JUNO_1.into(); + std::env::set_var(STATE_FILE_ENV_NAME, "./tests/test.json"); + + let daemon_state = runtime + .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) + .unwrap(); + + for i in 0..25 { + let daemon_state = daemon_state.clone(); + std::thread::spawn(move || daemon_state.set("test", &format!("test{i}"), format!("test-{i}"))); + } + thread::sleep(Duration::from_millis(500)); +} From 24950a3baadcab5f6dc9c9f4604efbc839ea2bbe Mon Sep 17 00:00:00 2001 From: Buckram Date: Mon, 12 Feb 2024 15:25:29 +0200 Subject: [PATCH 002/108] use handles --- cw-orch-daemon/tests/daemon_state.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 38173f292..9d907da63 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -1,5 +1,3 @@ -use std::{thread, time::Duration}; - use cw_orch_core::env::STATE_FILE_ENV_NAME; use cw_orch_daemon::{ChainRegistryData, DaemonState}; use cw_orch_networks::networks::JUNO_1; @@ -16,11 +14,17 @@ fn simultaneous_read() { .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) .unwrap(); daemon_state.set("test", "test", "test").unwrap(); + + let mut handles = vec![]; for _ in 0..25 { let daemon_state = daemon_state.clone(); - std::thread::spawn(move || daemon_state.get("test")); + let handle = std::thread::spawn(move || daemon_state.get("test").unwrap()); + handles.push(handle); + } + + for handle in handles { + handle.join().unwrap(); } - thread::sleep(Duration::from_millis(500)); } #[test] @@ -34,9 +38,17 @@ fn simultaneous_write() { .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) .unwrap(); + let mut handles = vec![]; for i in 0..25 { let daemon_state = daemon_state.clone(); - std::thread::spawn(move || daemon_state.set("test", &format!("test{i}"), format!("test-{i}"))); + let handle = std::thread::spawn(move || { + daemon_state + .set("test", &format!("test{i}"), format!("test-{i}")) + .unwrap(); + }); + handles.push(handle); + } + for handle in handles { + handle.join().unwrap(); } - thread::sleep(Duration::from_millis(500)); } From b40fcf54da956bec001a930cd36e22f739ef3103 Mon Sep 17 00:00:00 2001 From: Buckram Date: Mon, 12 Feb 2024 15:44:13 +0200 Subject: [PATCH 003/108] let it finish --- cw-orch-daemon/tests/daemon_state.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 9d907da63..8cc8b6aca 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -21,7 +21,6 @@ fn simultaneous_read() { let handle = std::thread::spawn(move || daemon_state.get("test").unwrap()); handles.push(handle); } - for handle in handles { handle.join().unwrap(); } @@ -48,7 +47,15 @@ fn simultaneous_write() { }); handles.push(handle); } + + let mut maybe_err = Ok(()); + // Finish all handles for handle in handles { - handle.join().unwrap(); + let result = handle.join(); + if result.is_err() { + maybe_err = result; + } } + // Error if at least one failed + maybe_err.unwrap() } From 22d5f3fa28b5164eb20cb2b357c97248750cc5b6 Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 13 Feb 2024 14:28:19 +0200 Subject: [PATCH 004/108] POC --- cw-orch-daemon/Cargo.toml | 4 ++ cw-orch-daemon/src/json_file.rs | 77 ++++++++++++++++++++++++++++- cw-orch-daemon/src/state.rs | 88 +++++++++++++++++++++++++++------ 3 files changed, 154 insertions(+), 15 deletions(-) diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 893dc7be2..b2465bfcd 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -67,6 +67,10 @@ async-recursion = "1.0.5" # Gzip flate2 = { version = "1.0.26" } +# Lock daemon +file-lock = { version = "2.1.10" } +once_cell = { version = "1.19.0" } + [dev-dependencies] cw-orch-daemon = { path = "." } uid = "0.1.7" diff --git a/cw-orch-daemon/src/json_file.rs b/cw-orch-daemon/src/json_file.rs index 4e231c4bf..b95664286 100644 --- a/cw-orch-daemon/src/json_file.rs +++ b/cw-orch-daemon/src/json_file.rs @@ -1,6 +1,81 @@ use crate::DaemonError; +use file_lock::{FileLock, FileOptions}; use serde_json::{from_reader, json, Value}; -use std::fs::{File, OpenOptions}; +use std::{ + fs::{File, OpenOptions}, + io::Seek, +}; + +pub struct JsonFileState { + lock: FileLock, + json: Value, +} + +impl JsonFileState { + /// Lock a new file + pub fn new(filename: &str) -> Self { + // open file pointer set read/write permissions to true + // create it if it does not exists + // dont truncate it + + let options = FileOptions::new() + .create(true) + .read(true) + .write(true) + .truncate(false); + + let lock: FileLock = FileLock::lock(filename, true, options).unwrap(); + + // return empty json object if file is empty + // return file content if not + let json: Value = if lock.file.metadata().unwrap().len().eq(&0) { + json!({}) + } else { + from_reader(&lock.file).unwrap() + }; + + JsonFileState { lock, json } + } + + /// Prepare json for further writes + pub fn prepare(&mut self, chain_id: &str, network_id: &str, deploy_id: &str) { + let json = &mut self.json; + // check and add network_id path if it's missing + if json.get(network_id).is_none() { + json[network_id] = json!({}); + } + + // add deployment_id to chain_id path + if json[network_id].get(chain_id).is_none() { + json[network_id][chain_id] = json!({ + deploy_id: {}, + "code_ids": {} + }); + } + } + + pub fn state(&self) -> Value { + self.json.clone() + } + + /// Get a value for read + // pub fn get(&self, chain_id: &str, network_id: &str) -> &Value { + // self.json[network_id].get(chain_id).unwrap() + // } + + /// Give a value to write + pub fn get_mut(&mut self, chain_id: &str, network_id: &str) -> &mut Value { + self.json[network_id].get_mut(chain_id).unwrap() + } +} + +// Write json when dropping +impl Drop for JsonFileState { + fn drop(&mut self) { + self.lock.file.rewind().unwrap(); + serde_json::to_writer_pretty(&self.lock.file, &self.json).unwrap(); + } +} pub fn write(filename: &String, chain_id: &String, network_id: &String, deploy_id: &String) { // open file pointer set read/write permissions to true diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index bd2fe6654..e5a098133 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -1,5 +1,5 @@ use super::error::DaemonError; -use crate::{channel::GrpcChannel, networks::ChainKind}; +use crate::{channel::GrpcChannel, json_file::JsonFileState, networks::ChainKind}; use cosmwasm_std::Addr; use cw_orch_core::{ @@ -9,17 +9,21 @@ use cw_orch_core::{ CwEnvError, CwOrchEnvVars, }; use ibc_chain_registry::chain::ChainData; +use once_cell::sync::Lazy; use serde::Serialize; use serde_json::{json, Value}; -use std::{collections::HashMap, fs::File, path::Path}; +use std::{collections::HashMap, path::Path, sync::Mutex}; use tonic::transport::Channel; +pub(crate) static GLOBAL_WRITE_STATE: Lazy>> = + Lazy::new(|| Mutex::new(HashMap::new())); + /// Stores the chain information and deployment state. /// Uses a simple JSON file to store the deployment information locally. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct DaemonState { /// this is passed via env var STATE_FILE - pub json_file_path: String, + json_file_path: String, /// Deployment identifier pub deployment_id: String, /// gRPC channel @@ -30,6 +34,24 @@ pub struct DaemonState { pub read_only: bool, } +impl Clone for DaemonState { + fn clone(&self) -> Self { + let new_self = Self { + json_file_path: self.json_file_path.clone(), + deployment_id: self.deployment_id.clone(), + grpc_channel: self.grpc_channel.clone(), + chain_data: self.chain_data.clone(), + read_only: self.read_only.clone(), + }; + // Increase DaemonStates count for this file + let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); + let (count, _) = lock.get_mut(&self.json_file_path).unwrap(); + *count += 1; + + new_self + } +} + impl DaemonState { /// Creates a new state from the given chain data and deployment id. /// Attempts to connect to any of the provided gRPC endpoints. @@ -85,7 +107,7 @@ impl DaemonState { // build daemon state let state = DaemonState { - json_file_path, + json_file_path: json_file_path.clone(), deployment_id, grpc_channel, chain_data, @@ -99,10 +121,23 @@ impl DaemonState { state.json_file_path ); - // write json state file - crate::json_file::write( - &state.json_file_path, - &state.chain_data.chain_id.to_string(), + let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); + // Lock file if first time write + let file_state = match lock.entry(json_file_path.clone()) { + // Increase count if already locked this file + std::collections::hash_map::Entry::Occupied(o) => { + let (count, lock) = o.into_mut(); + *count += 1; + lock + } + // Insert as 1 count of DaemonStates pointing to this file if it's first open + std::collections::hash_map::Entry::Vacant(v) => { + let (_, lock) = v.insert((1, JsonFileState::new(&json_file_path))); + lock + } + }; + file_state.prepare( + state.chain_data.chain_id.as_str(), &state.chain_data.chain_name, &state.deployment_id, ); @@ -148,7 +183,14 @@ impl DaemonState { } /// Get the state filepath and read it as json fn read_state(&self) -> Result { - crate::json_file::read(&self.json_file_path) + // Check if already open in write mode { + let lock = GLOBAL_WRITE_STATE.lock().unwrap(); + if let Some((_, j)) = lock.get(&self.json_file_path) { + Ok(j.state()) + } else { + // Or just read it from a file + crate::json_file::read(&self.json_file_path) + } } /// Retrieve a stateful value using the chainId and networkId @@ -168,16 +210,34 @@ impl DaemonState { return Err(DaemonError::StateReadOnly); } - let mut json = self.read_state()?; + let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); - json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key] - [contract_id] = json!(value); + let (_, file_state) = lock.get_mut(&self.json_file_path).unwrap(); + let val = file_state.get_mut( + self.chain_data.chain_id.as_str(), + &self.chain_data.chain_name, + ); + val[key][contract_id] = json!(value); - serde_json::to_writer_pretty(File::create(&self.json_file_path).unwrap(), &json)?; Ok(()) } } +impl Drop for DaemonState { + fn drop(&mut self) { + let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); + + // Decrease open count + let (count, _) = lock.get_mut(&self.json_file_path).unwrap(); + *count -= 1; + + // If we get to zero count - write to a file + if *count == 0 { + lock.remove(&self.json_file_path); + } + } +} + impl StateInterface for DaemonState { /// Read address for contract in deployment id from state file fn get_address(&self, contract_id: &str) -> Result { From d74338482eeb1f284c1102a99c4b94905fe30671 Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 13 Feb 2024 16:32:08 +0200 Subject: [PATCH 005/108] add force write --- cw-orch-daemon/src/json_file.rs | 9 +++++++-- cw-orch-daemon/src/state.rs | 8 ++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cw-orch-daemon/src/json_file.rs b/cw-orch-daemon/src/json_file.rs index b95664286..b903ecaaf 100644 --- a/cw-orch-daemon/src/json_file.rs +++ b/cw-orch-daemon/src/json_file.rs @@ -67,13 +67,18 @@ impl JsonFileState { pub fn get_mut(&mut self, chain_id: &str, network_id: &str) -> &mut Value { self.json[network_id].get_mut(chain_id).unwrap() } + + /// Force write to a file + pub fn force_write(&mut self) { + self.lock.file.rewind().unwrap(); + serde_json::to_writer_pretty(&self.lock.file, &self.json).unwrap(); + } } // Write json when dropping impl Drop for JsonFileState { fn drop(&mut self) { - self.lock.file.rewind().unwrap(); - serde_json::to_writer_pretty(&self.lock.file, &self.json).unwrap(); + self.force_write() } } diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index e5a098133..70c6b1392 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -221,6 +221,14 @@ impl DaemonState { Ok(()) } + + /// Forcefully write current json to a file + pub fn force_write(&self) -> Result<(), DaemonError> { + let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); + let (_, file_state) = lock.get_mut(&self.json_file_path).unwrap(); + file_state.force_write(); + Ok(()) + } } impl Drop for DaemonState { From 56da24fe9da991ef8683dfd3f2d7c2a522abc10b Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 14 Feb 2024 13:33:35 +0200 Subject: [PATCH 006/108] drop guard if not used --- cw-orch-daemon/src/state.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 70c6b1392..d54250f3d 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -188,6 +188,8 @@ impl DaemonState { if let Some((_, j)) = lock.get(&self.json_file_path) { Ok(j.state()) } else { + // drop guard if not found, since reading may take a while + drop(lock); // Or just read it from a file crate::json_file::read(&self.json_file_path) } From 0f301a6757fb5da9252fee6ce8e57df19964e381 Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 14 Feb 2024 13:39:48 +0200 Subject: [PATCH 007/108] should not be able to edit read-only --- cw-orch-daemon/src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index d54250f3d..6168645c0 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -31,7 +31,7 @@ pub struct DaemonState { /// Information about the chain pub chain_data: ChainData, /// Flag to set the daemon state readonly and not pollute the env file - pub read_only: bool, + read_only: bool, } impl Clone for DaemonState { From 31612327f85168d65f5cca1c349c0cdb7e6a9fe8 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Tue, 26 Mar 2024 16:34:04 +0200 Subject: [PATCH 008/108] create cw-orch service --- cw-orch-daemon/Cargo.toml | 4 +++- cw-orch-daemon/src/lib.rs | 1 + cw-orch-daemon/src/service.rs | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 cw-orch-daemon/src/service.rs diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 0c995c02b..e813ad4d4 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -57,7 +57,7 @@ chrono = { version = "0.4" } base16 = { version = "0.2.1" } ring = { version = "0.17.3" } dirs = "5.0.1" - +tower = { version = "0.4", features = ["retry", "reconnect"] } # Injective dependencies ethers-signers = { version = "2.0.7", optional = true } @@ -67,6 +67,8 @@ async-recursion = "1.0.5" # Gzip flate2 = { version = "1.0.26" } lazy_static = "1.4.0" +http = "0.2.11" +hyper = "0.14.27" [dev-dependencies] cw-orch-daemon = { path = "." } diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index fa2911e4b..bcfdafdde 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -20,6 +20,7 @@ mod log; pub mod queriers; pub mod tx_broadcaster; pub mod tx_builder; +pub mod service; pub use self::{builder::*, channel::*, core::*, error::*, state::*, sync::*, tx_resp::*}; pub use cw_orch_networks::chain_info::*; pub use cw_orch_networks::networks; diff --git a/cw-orch-daemon/src/service.rs b/cw-orch-daemon/src/service.rs new file mode 100644 index 000000000..5dae509d3 --- /dev/null +++ b/cw-orch-daemon/src/service.rs @@ -0,0 +1,36 @@ +use std::{sync::Arc, task::{Context, Poll}}; + +use tonic::{body::BoxBody, client::GrpcService, transport::{channel, Channel}, Request}; +use tower::{Service, ServiceBuilder}; + +use crate::DaemonState; + +struct DaemonChannel { + svs: Channel, +} + +impl DaemonChannel { + fn new(channel: Channel) -> Self { + Self { + svs: channel + } + } +} + +impl Service> for DaemonChannel { + type Response = http::Response; + type Error = tonic::transport::Error; + type Future = channel::ResponseFuture; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Service::poll_ready(&mut self.svs, cx) + } + + fn call(&mut self, request: http::Request) -> Self::Future { + Service::call(&mut self.svs, request) + } +} + +// Is automatically implemented by Tonic ! +// impl GrpcService for DaemonChannel { +// } \ No newline at end of file From 687c079e3fd60abb3c7b93efc4d02ba91cb842b5 Mon Sep 17 00:00:00 2001 From: Buckram Date: Fri, 19 Apr 2024 17:31:38 +0300 Subject: [PATCH 009/108] clean ups and more tests --- cw-orch-daemon/Cargo.toml | 3 ++ cw-orch-daemon/src/json_file.rs | 65 +++++----------------------- cw-orch-daemon/src/lib.rs | 2 +- cw-orch-daemon/src/state.rs | 29 +++++++++---- cw-orch-daemon/tests/daemon_state.rs | 36 +++++++++++++-- 5 files changed, 67 insertions(+), 68 deletions(-) diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 5365d9f17..8205de3c8 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -91,3 +91,6 @@ serial_test = "3.0.0" ethers-signers = { version = "2.0.7" } ethers-core = { version = "2.0.7" } tokio-test = "0.4.3" + +# File lock test +nix = { version = "0.28.0", features = ["process"] } diff --git a/cw-orch-daemon/src/json_file.rs b/cw-orch-daemon/src/json_file.rs index 519a3ec2f..a4eebd7b7 100644 --- a/cw-orch-daemon/src/json_file.rs +++ b/cw-orch-daemon/src/json_file.rs @@ -1,18 +1,18 @@ use crate::DaemonError; use file_lock::{FileLock, FileOptions}; use serde_json::{from_reader, json, Value}; -use std::{ - fs::{File, OpenOptions}, - io::Seek, -}; +use std::{fs::File, io::Seek}; +/// State file reader and writer +/// Mainly used by [`crate::Daemon`] and [`crate::DaemonAsync`], but could also be used for tests or custom edits of the state pub struct JsonFileState { lock: FileLock, json: Value, } impl JsonFileState { - /// Lock a new file + /// Lock a state files + /// Other process won't be able to lock it pub fn new(filename: &str) -> Self { // open file pointer set read/write permissions to true // create it if it does not exists @@ -24,7 +24,9 @@ impl JsonFileState { .write(true) .truncate(false); - let lock: FileLock = FileLock::lock(filename, true, options).unwrap(); + // Lock file, non blocking so it errors in case someone else already holding lock of it + let lock: FileLock = FileLock::lock(filename, false, options) + .unwrap_or_else(|_| panic!("Was not able to receive {filename} state lock")); // return empty json object if file is empty // return file content if not @@ -59,9 +61,9 @@ impl JsonFileState { } /// Get a value for read - // pub fn get(&self, chain_id: &str, network_id: &str) -> &Value { - // self.json[network_id].get(chain_id).unwrap() - // } + pub fn get(&self, chain_id: &str, network_id: &str) -> &Value { + &self.json[network_id][chain_id] + } /// Give a value to write pub fn get_mut(&mut self, chain_id: &str, network_id: &str) -> &mut Value { @@ -82,51 +84,6 @@ impl Drop for JsonFileState { } } -pub fn write(filename: &String, chain_id: &String, network_id: &String, deploy_id: &String) { - // open file pointer set read/write permissions to true - // create it if it does not exists - // dont truncate it - // Create the directory if they do not exist - let file_buf = PathBuf::from_str(filename).unwrap(); - if let Some(parent) = file_buf.parent() { - let _ = std::fs::create_dir_all(parent); - } - - let file = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .truncate(false) - .open(filename.clone()) - .unwrap(); - - // return empty json object if file is empty - // return file content if not - let mut json: Value = if file.metadata().unwrap().len().eq(&0) { - json!({}) - } else { - from_reader(file).unwrap() - }; - - // check and add network_id path if it's missing - if json.get(network_id).is_none() { - json[network_id] = json!({}); - } - - // add deployment_id to chain_id path - if json[network_id].get(chain_id).is_none() { - json[network_id][chain_id] = json!({ - deploy_id: {}, - "code_ids": {} - }); - } - - // write JSON data - // use File::create so we dont append data to the file - // but rather write all (because we have read the data before) - serde_json::to_writer_pretty(File::create(filename).unwrap(), &json).unwrap(); -} - pub fn read(filename: &String) -> Result { let file = File::open(filename).unwrap_or_else(|_| panic!("File should be present at {}", filename)); diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index fa2911e4b..60f289b7f 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -6,7 +6,7 @@ pub mod builder; pub mod channel; pub mod core; pub mod error; -pub(crate) mod json_file; +pub mod json_file; /// Proto types for different blockchains pub mod proto; pub mod sender; diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 6168645c0..0305253f8 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -34,6 +34,7 @@ pub struct DaemonState { read_only: bool, } +// On clone increase lock count so we track how many daemon states using the file impl Clone for DaemonState { fn clone(&self) -> Self { let new_self = Self { @@ -41,12 +42,15 @@ impl Clone for DaemonState { deployment_id: self.deployment_id.clone(), grpc_channel: self.grpc_channel.clone(), chain_data: self.chain_data.clone(), - read_only: self.read_only.clone(), + read_only: self.read_only, }; + // Increase DaemonStates count for this file - let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); - let (count, _) = lock.get_mut(&self.json_file_path).unwrap(); - *count += 1; + if !self.read_only { + let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); + let (count, _) = lock.get_mut(&self.json_file_path).unwrap(); + *count += 1; + } new_self } @@ -181,24 +185,30 @@ impl DaemonState { Ok(state_file_path) } - /// Get the state filepath and read it as json - fn read_state(&self) -> Result { + + /// Get the chain state as json + fn chain_state(&self) -> Result { // Check if already open in write mode { let lock = GLOBAL_WRITE_STATE.lock().unwrap(); if let Some((_, j)) = lock.get(&self.json_file_path) { - Ok(j.state()) + Ok(j.get( + &self.chain_data.chain_name, + self.chain_data.chain_id.as_str(), + ) + .clone()) } else { // drop guard if not found, since reading may take a while drop(lock); // Or just read it from a file crate::json_file::read(&self.json_file_path) + .map(|j| j[self.chain_data.chain_id.as_str()][&self.chain_data.chain_name].clone()) } } /// Retrieve a stateful value using the chainId and networkId pub fn get(&self, key: &str) -> Result { - let json = self.read_state()?; - Ok(json[&self.chain_data.chain_name][&self.chain_data.chain_id.to_string()][key].clone()) + let json = self.chain_state()?; + Ok(json[key].clone()) } /// Set a stateful value using the chainId and networkId @@ -233,6 +243,7 @@ impl DaemonState { } } +// Manual drop implementation to write state when no daemon states uses the file impl Drop for DaemonState { fn drop(&mut self) { let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 8cc8b6aca..6e0a27ffd 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -1,14 +1,16 @@ use cw_orch_core::env::STATE_FILE_ENV_NAME; -use cw_orch_daemon::{ChainRegistryData, DaemonState}; +use cw_orch_daemon::{json_file::JsonFileState, ChainRegistryData, DaemonState}; use cw_orch_networks::networks::JUNO_1; use tokio::runtime::Runtime; +const TEST_STATE_FILE: &str = "./tests/test.json"; + #[test] fn simultaneous_read() { let runtime = Runtime::new().unwrap(); let chain_data: ChainRegistryData = JUNO_1.into(); - std::env::set_var(STATE_FILE_ENV_NAME, "./tests/test.json"); + std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); let daemon_state = runtime .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) @@ -21,9 +23,17 @@ fn simultaneous_read() { let handle = std::thread::spawn(move || daemon_state.get("test").unwrap()); handles.push(handle); } + + let mut maybe_err = Ok(serde_json::Value::default()); + for handle in handles { - handle.join().unwrap(); + let result = handle.join(); + if result.is_err() { + maybe_err = result; + } } + // Error if at least one failed + let _ = maybe_err.unwrap(); } #[test] @@ -31,7 +41,7 @@ fn simultaneous_write() { let runtime = Runtime::new().unwrap(); let chain_data: ChainRegistryData = JUNO_1.into(); - std::env::set_var(STATE_FILE_ENV_NAME, "./tests/test.json"); + std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); let daemon_state = runtime .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) @@ -59,3 +69,21 @@ fn simultaneous_write() { // Error if at least one failed maybe_err.unwrap() } + +#[test] +#[should_panic] +fn panic_when_someone_else_holds_it() { + match unsafe { nix::unistd::fork() } { + Ok(nix::unistd::ForkResult::Child) => { + // Occur lock for file for 100 millis + let _state = JsonFileState::new(TEST_STATE_FILE); + std::thread::sleep(std::time::Duration::from_millis(100)); + } + Ok(nix::unistd::ForkResult::Parent { .. }) => { + // Wait a bit for child to occur lock and try to lock already locked file by child + std::thread::sleep(std::time::Duration::from_millis(50)); + let _state = JsonFileState::new(TEST_STATE_FILE); + } + Err(_) => (), + } +} From fa74aaa94da1a43e104076c6a7f582d2fd57f755 Mon Sep 17 00:00:00 2001 From: Buckram Date: Mon, 22 Apr 2024 11:39:07 +0300 Subject: [PATCH 010/108] update comment on Global Write State --- cw-orch-daemon/src/state.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 0305253f8..1035ecf38 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -15,6 +15,9 @@ use serde_json::{json, Value}; use std::{collections::HashMap, path::Path, sync::Mutex}; use tonic::transport::Channel; +/// Global state for writing json +/// Key: path to state file +/// Value: (DaemonState count that points to that state file, Json file) pub(crate) static GLOBAL_WRITE_STATE: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); From 7d4e19fa13baf0eb882b60207f0c7b318eaf8f39 Mon Sep 17 00:00:00 2001 From: Buckram Date: Mon, 22 Apr 2024 11:41:46 +0300 Subject: [PATCH 011/108] update comment for global write state --- cw-orch-daemon/src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 1035ecf38..e00a8ca12 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -15,7 +15,7 @@ use serde_json::{json, Value}; use std::{collections::HashMap, path::Path, sync::Mutex}; use tonic::transport::Channel; -/// Global state for writing json +/// Global state for sharing json files between daemons /// Key: path to state file /// Value: (DaemonState count that points to that state file, Json file) pub(crate) static GLOBAL_WRITE_STATE: Lazy>> = From a2d860c02ffa8de4a25cf131038e0ba6411e763c Mon Sep 17 00:00:00 2001 From: Buckram Date: Mon, 22 Apr 2024 12:38:43 +0300 Subject: [PATCH 012/108] remove var to fix tests --- cw-orch-daemon/src/state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index e00a8ca12..985839300 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -371,6 +371,7 @@ pub mod test { parent_and_relative_state_path ); + std::env::remove_var(STATE_FILE_ENV_NAME); Ok(()) } } From a6b881fbc8f94fbabf31fd7076261ff420259848 Mon Sep 17 00:00:00 2001 From: Buckram Date: Mon, 6 May 2024 14:13:32 +0300 Subject: [PATCH 013/108] Allow cloning locked state --- cw-orch-daemon/src/builder.rs | 6 +- cw-orch-daemon/src/core.rs | 19 +- cw-orch-daemon/src/error.rs | 6 +- .../src/{json_file.rs => json_lock.rs} | 9 +- cw-orch-daemon/src/lib.rs | 2 +- cw-orch-daemon/src/sender.rs | 36 ++-- cw-orch-daemon/src/state.rs | 184 +++++++----------- cw-orch-daemon/tests/daemon_state.rs | 6 +- .../cw-orch-core/src/environment/state.rs | 70 ++++--- 9 files changed, 169 insertions(+), 169 deletions(-) rename cw-orch-daemon/src/{json_file.rs => json_lock.rs} (95%) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index dcf922d21..84aa375f3 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -2,7 +2,7 @@ use crate::{ log::print_if_log_disabled, sender::SenderBuilder, sender::SenderOptions, DaemonAsync, DaemonBuilder, }; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use bitcoin::secp256k1::All; @@ -97,7 +97,9 @@ impl DaemonAsyncBuilder { .deployment_id .clone() .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); - let state = Arc::new(DaemonState::new(chain, deployment_id, false).await?); + let state = Arc::new(Mutex::new( + DaemonState::new(chain, deployment_id, false).await?, + )); // if mnemonic provided, use it. Else use env variables to retrieve mnemonic let sender_options = self.sender_options.clone(); diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index f4c665044..422aeb774 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -25,7 +25,7 @@ use std::{ fmt::Debug, io::Write, str::{from_utf8, FromStr}, - sync::Arc, + sync::{Arc, Mutex}, time::Duration, }; @@ -67,7 +67,7 @@ pub struct DaemonAsync { /// Sender to send transactions to the chain pub sender: Wallet, /// State of the daemon - pub state: Arc, + pub state: Arc>, } impl DaemonAsync { @@ -78,15 +78,17 @@ impl DaemonAsync { /// Get the channel configured for this DaemonAsync. pub fn channel(&self) -> Channel { - self.state.grpc_channel.clone() + // TODO: Channel shouldn't be inside state + let lock = self.state.lock().unwrap(); + lock.grpc_channel.clone() } } impl ChainState for DaemonAsync { - type Out = Arc; + type Out = Arc>; fn state(&self) -> Self::Out { - self.state.clone() + self.state } } @@ -101,10 +103,11 @@ impl DaemonAsync { /// Does not consume the original [`DaemonAsync`]. pub fn rebuild(&self) -> DaemonAsyncBuilder { let mut builder = Self::builder(); + let state = self.state().lock().unwrap(); builder - .chain(self.state().chain_data.clone()) + .chain(state.chain_data.clone()) .sender((*self.sender).clone()) - .deployment_id(&self.state().deployment_id); + .deployment_id(state.deployment_id.clone()); builder } @@ -281,7 +284,7 @@ impl DaemonAsync { _uploadable: &T, ) -> Result { let sender = &self.sender; - let wasm_path = ::wasm(&self.state.chain_data); + let wasm_path = ::wasm(&self.state.lock().unwrap().chain_data); log::debug!(target: &transaction_target(), "Uploading file at {:?}", wasm_path); diff --git a/cw-orch-daemon/src/error.rs b/cw-orch-daemon/src/error.rs index 063e16637..bc25d5dc1 100644 --- a/cw-orch-daemon/src/error.rs +++ b/cw-orch-daemon/src/error.rs @@ -116,12 +116,14 @@ pub enum DaemonError { InsufficientFee(String), #[error("Not enough balance, expected {expected}, found {current}")] NotEnoughBalance { expected: Coin, current: Coin }, - #[error("Can't set the daemon state, it's read-only")] - StateReadOnly, + #[error("Can't set the daemon state, it's read-only {0}")] + StateReadOnly(String), #[error("You need to pass a runtime to the querier object to do synchronous queries. Use daemon.querier instead")] QuerierNeedRuntime, #[error(transparent)] Instantiate2Error(#[from] Instantiate2AddressError), + #[error("State file {0} already locked, use another state file or clone daemon which holds the lock")] + StateAlreadyLocked(String), } impl DaemonError { diff --git a/cw-orch-daemon/src/json_file.rs b/cw-orch-daemon/src/json_lock.rs similarity index 95% rename from cw-orch-daemon/src/json_file.rs rename to cw-orch-daemon/src/json_lock.rs index a4eebd7b7..3b43992b8 100644 --- a/cw-orch-daemon/src/json_file.rs +++ b/cw-orch-daemon/src/json_lock.rs @@ -5,12 +5,13 @@ use std::{fs::File, io::Seek}; /// State file reader and writer /// Mainly used by [`crate::Daemon`] and [`crate::DaemonAsync`], but could also be used for tests or custom edits of the state -pub struct JsonFileState { +#[derive(Debug)] +pub struct JsonLockedState { lock: FileLock, json: Value, } -impl JsonFileState { +impl JsonLockedState { /// Lock a state files /// Other process won't be able to lock it pub fn new(filename: &str) -> Self { @@ -36,7 +37,7 @@ impl JsonFileState { from_reader(&lock.file).unwrap() }; - JsonFileState { lock, json } + JsonLockedState { lock, json } } /// Prepare json for further writes @@ -78,7 +79,7 @@ impl JsonFileState { } // Write json when dropping -impl Drop for JsonFileState { +impl Drop for JsonLockedState { fn drop(&mut self) { self.force_write() } diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index af53799e1..e26139b7c 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -6,7 +6,7 @@ pub mod builder; pub mod channel; pub mod core; pub mod error; -pub mod json_file; +pub mod json_lock; /// Proto types for different blockchains pub mod proto; pub mod sender; diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 951a5715f..ed9b78822 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -31,11 +31,18 @@ use cosmrs::{ AccountId, Any, }; use cosmwasm_std::{coin, Addr, Coin}; -use cw_orch_core::{environment::ChainKind, log::local_target, CoreEnvVars, CwEnvError}; +use cw_orch_core::{ + environment::{ChainInfoOwned, ChainKind}, + log::local_target, + CoreEnvVars, CwEnvError, +}; use crate::env::{LOCAL_MNEMONIC_ENV_NAME, MAIN_MNEMONIC_ENV_NAME, TEST_MNEMONIC_ENV_NAME}; use bitcoin::secp256k1::{All, Context, Secp256k1, Signing}; -use std::{str::FromStr, sync::Arc}; +use std::{ + str::FromStr, + sync::{Arc, Mutex}, +}; use cosmos_modules::vesting::PeriodicVestingAccount; use tonic::transport::Channel; @@ -60,7 +67,7 @@ pub type Wallet = Arc>; pub struct Sender { pub private_key: PrivateKey, pub secp: Secp256k1, - pub(crate) daemon_state: Arc, + pub(crate) chain_data: ChainInfoOwned, pub(crate) options: SenderOptions, } @@ -97,23 +104,28 @@ impl SenderOptions { } } +// TODO: Sender shouldn't need to use daemon state, chain_data should be enough impl Sender { - pub fn new(daemon_state: &Arc) -> Result, DaemonError> { + pub fn new(daemon_state: Arc>) -> Result, DaemonError> { Self::new_with_options(daemon_state, SenderOptions::default()) } pub fn new_with_options( - daemon_state: &Arc, + daemon_state: Arc>, options: SenderOptions, ) -> Result, DaemonError> { - let mnemonic = get_mnemonic_env(&daemon_state.chain_data.kind)?; + let lock_state = daemon_state.lock().unwrap(); + let mnemonic = get_mnemonic_env(&lock_state.chain_data.kind)?; + // We pass daemon_state inside from_mnemonic_with_options + // but we haven't dropped lock + drop(lock_state); Self::from_mnemonic_with_options(daemon_state, &mnemonic, options) } /// Construct a new Sender from a mnemonic with additional options pub fn from_mnemonic( - daemon_state: &Arc, + daemon_state: Arc>, mnemonic: &str, ) -> Result, DaemonError> { Self::from_mnemonic_with_options(daemon_state, mnemonic, SenderOptions::default()) @@ -121,20 +133,22 @@ impl Sender { /// Construct a new Sender from a mnemonic with additional options pub fn from_mnemonic_with_options( - daemon_state: &Arc, + daemon_state: Arc>, mnemonic: &str, options: SenderOptions, ) -> Result, DaemonError> { + let lock_state = daemon_state.lock().unwrap(); let secp = Secp256k1::new(); let p_key: PrivateKey = PrivateKey::from_words( &secp, mnemonic, 0, options.hd_index.unwrap_or(0), - daemon_state.chain_data.network_info.coin_type, + lock_state.chain_data.network_info.coin_type, )?; + let sender = Sender { - daemon_state: daemon_state.clone(), + chain_data: lock_state.chain_data, private_key: p_key, secp, options, @@ -142,7 +156,7 @@ impl Sender { log::info!( target: &local_target(), "Interacting with {} using address: {}", - daemon_state.chain_data.chain_id, + lock_state.chain_data.chain_id, sender.pub_addr_str()? ); Ok(sender) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 45d3439a1..198cee1d2 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -1,6 +1,6 @@ use super::error::DaemonError; use crate::env::{default_state_folder, DaemonEnvVars}; -use crate::{channel::GrpcChannel, json_file::JsonFileState, networks::ChainKind}; +use crate::{channel::GrpcChannel, json_lock::JsonLockedState, networks::ChainKind}; use cosmwasm_std::Addr; use cw_orch_core::environment::ChainInfoOwned; @@ -12,51 +12,38 @@ use cw_orch_core::{ use once_cell::sync::Lazy; use serde::Serialize; use serde_json::{json, Value}; +use std::collections::HashSet; use std::{collections::HashMap, path::Path, sync::Mutex}; use tonic::transport::Channel; -/// Global state for sharing json files between daemons -/// Key: path to state file -/// Value: (DaemonState count that points to that state file, Json file) -pub(crate) static GLOBAL_WRITE_STATE: Lazy>> = - Lazy::new(|| Mutex::new(HashMap::new())); +/// Global state to track which files are already open by other daemons from other threads +/// This is necessary because File lock will allow same process to lock file how many times as process wants +pub(crate) static LOCKED_FILES: Lazy>> = + Lazy::new(|| Mutex::new(HashSet::new())); /// Stores the chain information and deployment state. /// Uses a simple JSON file to store the deployment information locally. #[derive(Debug)] pub struct DaemonState { - /// this is passed via env var STATE_FILE - json_file_path: String, + json_state: DaemonStateFile, /// Deployment identifier pub deployment_id: String, + // TODO: Move grpc_channel and chain_data to DaemonAsync. No reason to have it here /// gRPC channel pub grpc_channel: Channel, /// Information about the chain pub chain_data: ChainInfoOwned, - /// Flag to set the daemon state readonly and not pollute the env file - read_only: bool, } -// On clone increase lock count so we track how many daemon states using the file -impl Clone for DaemonState { - fn clone(&self) -> Self { - let new_self = Self { - json_file_path: self.json_file_path.clone(), - deployment_id: self.deployment_id.clone(), - grpc_channel: self.grpc_channel.clone(), - chain_data: self.chain_data.clone(), - read_only: self.read_only, - }; - - // Increase DaemonStates count for this file - if !self.read_only { - let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); - let (count, _) = lock.get_mut(&self.json_file_path).unwrap(); - *count += 1; - } - - new_self - } +#[derive(Debug)] +enum DaemonStateFile { + ReadOnly { + /// this is passed via env var STATE_FILE + path: String, + }, + FullAccess { + json_file_state: JsonLockedState, + }, } impl DaemonState { @@ -98,46 +85,39 @@ impl DaemonState { json_file_path = format!("{folder}/{name}_local.json"); } - // build daemon state - let state = DaemonState { - json_file_path: json_file_path.clone(), - deployment_id, - grpc_channel, - chain_data, - read_only, - }; - - if !read_only { + let json_state = if read_only { + DaemonStateFile::ReadOnly { + path: json_file_path, + } + } else { log::info!( target: &local_target(), - "Writing daemon state JSON file: {:#?}", - state.json_file_path + "Writing daemon state JSON file: {json_file_path:#?}", ); - let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); - // Lock file if first time write - let file_state = match lock.entry(json_file_path.clone()) { - // Increase count if already locked this file - std::collections::hash_map::Entry::Occupied(o) => { - let (count, lock) = o.into_mut(); - *count += 1; - lock - } - // Insert as 1 count of DaemonStates pointing to this file if it's first open - std::collections::hash_map::Entry::Vacant(v) => { - let (_, lock) = v.insert((1, JsonFileState::new(&json_file_path))); - lock - } - }; - file_state.prepare( - &state.chain_data.chain_id, - &state.chain_data.network_info.chain_name, - &state.deployment_id, + let mut lock = LOCKED_FILES.lock().unwrap(); + if lock.contains(&json_file_path) { + return Err(DaemonError::StateAlreadyLocked(json_file_path)); + } + let mut json_file_state = JsonLockedState::new(&json_file_path); + // Insert and drop mutex lock asap + lock.insert(json_file_path); + drop(lock); + + json_file_state.prepare( + &chain_data.chain_id, + &chain_data.network_info.chain_name, + &deployment_id, ); - } + DaemonStateFile::FullAccess { json_file_state } + }; - // finish - Ok(state) + Ok(DaemonState { + json_state, + deployment_id, + grpc_channel, + chain_data, + }) } /// Returns the path of the file where the state of `cw-orchestrator` is stored. @@ -177,22 +157,20 @@ impl DaemonState { /// Get the chain state as json fn chain_state(&self) -> Result { - // Check if already open in write mode { - let lock = GLOBAL_WRITE_STATE.lock().unwrap(); - if let Some((_, j)) = lock.get(&self.json_file_path) { - Ok( - j.get(&self.chain_data.chain_id, self.chain_data.chain_id.as_str()) - .clone(), - ) - } else { - // drop guard if not found, since reading may take a while - drop(lock); - // Or just read it from a file - crate::json_file::read(&self.json_file_path).map(|j| { - j[self.chain_data.chain_id.as_str()][&self.chain_data.network_info.chain_name] - .clone() - }) - } + let state = match &self.json_state { + DaemonStateFile::ReadOnly { path } => { + let j = crate::json_lock::read(path)?; + + j[&self.chain_data.chain_id][&self.chain_data.network_info.chain_name] + } + DaemonStateFile::FullAccess { json_file_state } => json_file_state + .get( + &self.chain_data.chain_id, + &self.chain_data.network_info.chain_name, + ) + .clone(), + }; + Ok(state) } /// Retrieve a stateful value using the chainId and networkId @@ -203,20 +181,20 @@ impl DaemonState { /// Set a stateful value using the chainId and networkId pub fn set( - &self, + &mut self, key: &str, contract_id: &str, value: T, ) -> Result<(), DaemonError> { - if self.read_only { - return Err(DaemonError::StateReadOnly); - } - - let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); + let json_file_state = match &mut self.json_state { + DaemonStateFile::ReadOnly { path } => { + return Err(DaemonError::StateReadOnly(path.clone())) + } + DaemonStateFile::FullAccess { json_file_state } => json_file_state, + }; - let (_, file_state) = lock.get_mut(&self.json_file_path).unwrap(); - let val = file_state.get_mut( - self.chain_data.chain_id.as_str(), + let val = json_file_state.get_mut( + &self.chain_data.chain_id, &self.chain_data.network_info.chain_name, ); val[key][contract_id] = json!(value); @@ -225,30 +203,18 @@ impl DaemonState { } /// Forcefully write current json to a file - pub fn force_write(&self) -> Result<(), DaemonError> { - let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); - let (_, file_state) = lock.get_mut(&self.json_file_path).unwrap(); - file_state.force_write(); + pub fn force_write(&mut self) -> Result<(), DaemonError> { + let json_file_state = match &mut self.json_state { + DaemonStateFile::ReadOnly { path } => { + return Err(DaemonError::StateReadOnly(path.clone())) + } + DaemonStateFile::FullAccess { json_file_state } => json_file_state, + }; + json_file_state.force_write(); Ok(()) } } -// Manual drop implementation to write state when no daemon states uses the file -impl Drop for DaemonState { - fn drop(&mut self) { - let mut lock = GLOBAL_WRITE_STATE.lock().unwrap(); - - // Decrease open count - let (count, _) = lock.get_mut(&self.json_file_path).unwrap(); - *count -= 1; - - // If we get to zero count - write to a file - if *count == 0 { - lock.remove(&self.json_file_path); - } - } -} - impl StateInterface for DaemonState { /// Read address for contract in deployment id from state file fn get_address(&self, contract_id: &str) -> Result { diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 3915cca99..830cf1be8 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -1,4 +1,4 @@ -use cw_orch_daemon::{env::STATE_FILE_ENV_NAME, json_file::JsonFileState, DaemonState}; +use cw_orch_daemon::{env::STATE_FILE_ENV_NAME, json_lock::JsonLockedState, DaemonState}; use cw_orch_networks::networks::JUNO_1; use tokio::runtime::Runtime; @@ -75,13 +75,13 @@ fn panic_when_someone_else_holds_it() { match unsafe { nix::unistd::fork() } { Ok(nix::unistd::ForkResult::Child) => { // Occur lock for file for 100 millis - let _state = JsonFileState::new(TEST_STATE_FILE); + let _state = JsonLockedState::new(TEST_STATE_FILE); std::thread::sleep(std::time::Duration::from_millis(100)); } Ok(nix::unistd::ForkResult::Parent { .. }) => { // Wait a bit for child to occur lock and try to lock already locked file by child std::thread::sleep(std::time::Duration::from_millis(50)); - let _state = JsonFileState::new(TEST_STATE_FILE); + let _state = JsonLockedState::new(TEST_STATE_FILE); } Err(_) => (), } diff --git a/packages/cw-orch-core/src/environment/state.rs b/packages/cw-orch-core/src/environment/state.rs index e75db0ab8..f55fe67df 100644 --- a/packages/cw-orch-core/src/environment/state.rs +++ b/packages/cw-orch-core/src/environment/state.rs @@ -2,7 +2,12 @@ use crate::error::CwEnvError; use cosmwasm_std::Addr; -use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc}; +use std::{ + cell::RefCell, + collections::HashMap, + rc::Rc, + sync::{Arc, Mutex}, +}; /// State accessor trait. /// Indicates that the type has access to an underlying state. @@ -14,7 +19,7 @@ pub trait ChainState { } /// This Interface allows for managing the local state of a deployment on any CosmWasm-supported environment. -pub trait StateInterface: Clone { +pub trait StateInterface { /// Get the address of a contract using the specified contract id. fn get_address(&self, contract_id: &str) -> Result; @@ -60,54 +65,61 @@ impl StateInterface for Rc> { } } -impl StateInterface for Rc { - fn get_address(&self, contract_id: &str) -> Result { - (**self).get_address(contract_id) - } +// impl StateInterface for Rc { +// fn get_address(&self, contract_id: &str) -> Result { +// (**self).get_address(contract_id) +// } - fn set_address(&mut self, contract_id: &str, address: &Addr) { - (*Rc::make_mut(self)).set_address(contract_id, address) - } +// fn set_address(&mut self, contract_id: &str, address: &Addr) { +// (*Rc::make_mut(self)).set_address(contract_id, address) +// } - fn get_code_id(&self, contract_id: &str) -> Result { - (**self).get_code_id(contract_id) - } +// fn get_code_id(&self, contract_id: &str) -> Result { +// (**self).get_code_id(contract_id) +// } - fn set_code_id(&mut self, contract_id: &str, code_id: u64) { - (*Rc::make_mut(self)).set_code_id(contract_id, code_id) - } +// fn set_code_id(&mut self, contract_id: &str, code_id: u64) { +// (*Rc::make_mut(self)).set_code_id(contract_id, code_id) +// } - fn get_all_addresses(&self) -> Result, CwEnvError> { - (**self).get_all_addresses() - } +// fn get_all_addresses(&self) -> Result, CwEnvError> { +// (**self).get_all_addresses() +// } - fn get_all_code_ids(&self) -> Result, CwEnvError> { - (**self).get_all_code_ids() - } -} +// fn get_all_code_ids(&self) -> Result, CwEnvError> { +// (**self).get_all_code_ids() +// } +// } -impl StateInterface for Arc { +// TODO: error handling +impl StateInterface for Arc> { fn get_address(&self, contract_id: &str) -> Result { - (**self).get_address(contract_id) + let locked_state = self.lock().unwrap(); + locked_state.get_address(contract_id) } fn set_address(&mut self, contract_id: &str, address: &Addr) { - (*Arc::make_mut(self)).set_address(contract_id, address) + let mut locked_state = self.lock().unwrap(); + locked_state.set_address(contract_id, address) } fn get_code_id(&self, contract_id: &str) -> Result { - (**self).get_code_id(contract_id) + let locked_state = self.lock().unwrap(); + locked_state.get_code_id(contract_id) } fn set_code_id(&mut self, contract_id: &str, code_id: u64) { - (*Arc::make_mut(self)).set_code_id(contract_id, code_id) + let mut locked_state = self.lock().unwrap(); + locked_state.set_code_id(contract_id, code_id) } fn get_all_addresses(&self) -> Result, CwEnvError> { - (**self).get_all_addresses() + let locked_state = self.lock().unwrap(); + locked_state.get_all_addresses() } fn get_all_code_ids(&self) -> Result, CwEnvError> { - (**self).get_all_code_ids() + let locked_state = self.lock().unwrap(); + locked_state.get_all_code_ids() } } From eb239210b662892b76d0cfc790b52f22a4318baa Mon Sep 17 00:00:00 2001 From: Buckram Date: Mon, 6 May 2024 17:59:54 +0300 Subject: [PATCH 014/108] minimize mutex lock impact progress --- cw-orch-daemon/src/builder.rs | 47 +++++++--- cw-orch-daemon/src/core.rs | 44 ++++++---- cw-orch-daemon/src/queriers/cosmwasm.rs | 2 +- cw-orch-daemon/src/queriers/env.rs | 10 ++- cw-orch-daemon/src/sender.rs | 109 +++++++++++------------- cw-orch-daemon/src/state.rs | 54 ++++-------- cw-orch-daemon/src/sync/builder.rs | 11 ++- cw-orch-daemon/src/sync/core.rs | 22 +++-- cw-orch-daemon/src/tx_broadcaster.rs | 11 ++- cw-orch-daemon/src/tx_builder.rs | 19 +++-- cw-orch/src/osmosis_test_tube/core.rs | 2 +- 11 files changed, 182 insertions(+), 149 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 84aa375f3..3701d191f 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,13 +1,14 @@ use crate::{ - log::print_if_log_disabled, sender::SenderBuilder, sender::SenderOptions, DaemonAsync, - DaemonBuilder, + log::print_if_log_disabled, + sender::{SenderBuilder, SenderOptions}, + DaemonAsync, DaemonBuilder, GrpcChannel, }; use std::sync::{Arc, Mutex}; use bitcoin::secp256k1::All; use super::{error::DaemonError, sender::Sender, state::DaemonState}; -use cw_orch_core::environment::ChainInfoOwned; +use cw_orch_core::{environment::ChainInfoOwned, log::connectivity_target}; /// The default deployment id if none is provided pub const DEFAULT_DEPLOYMENT: &str = "default"; @@ -37,6 +38,9 @@ pub struct DaemonAsyncBuilder { pub(crate) sender: Option>, /// Specify Daemon Sender Options pub(crate) sender_options: SenderOptions, + + /* Rebuilder related options */ + pub(crate) state: Option>>, } impl DaemonAsyncBuilder { @@ -48,6 +52,7 @@ impl DaemonAsyncBuilder { /// Set the deployment id to use for the daemon interactions /// Defaults to `default` + /// This field is ignored for rebuilt daemon and deployment id of the original used instead pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { self.deployment_id = Some(deployment_id.into()); self @@ -89,7 +94,7 @@ impl DaemonAsyncBuilder { /// Build a daemon pub async fn build(&self) -> Result { - let chain = self + let chain_info = self .chain .clone() .ok_or(DaemonError::BuilderMissing("chain information".into()))?; @@ -97,27 +102,46 @@ impl DaemonAsyncBuilder { .deployment_id .clone() .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); - let state = Arc::new(Mutex::new( - DaemonState::new(chain, deployment_id, false).await?, - )); + + if chain_info.grpc_urls.is_empty() { + return Err(DaemonError::GRPCListIsEmpty); + } + + log::debug!(target: &connectivity_target(), "Found {} gRPC endpoints", chain_info.grpc_urls.len()); + + // find working grpc channel + let grpc_channel = + GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?; + + let state = match &self.state { + Some(state) => state.clone(), + None => Arc::new(Mutex::new( + DaemonState::new(chain_info.clone(), deployment_id, false).await?, + )), + }; // if mnemonic provided, use it. Else use env variables to retrieve mnemonic let sender_options = self.sender_options.clone(); let sender = match self.sender.clone() { Some(sender) => match sender { - SenderBuilder::Mnemonic(mnemonic) => { - Sender::from_mnemonic_with_options(&state, &mnemonic, sender_options)? - } + SenderBuilder::Mnemonic(mnemonic) => Sender::from_mnemonic_with_options( + chain_info.clone(), + &mnemonic, + sender_options, + )?, SenderBuilder::Sender(mut sender) => { sender.set_options(self.sender_options.clone()); sender } }, - None => Sender::new_with_options(&state, sender_options)?, + None => Sender::new_with_options(chain_info.clone(), sender_options)?, }; + let daemon = DaemonAsync { state, sender: Arc::new(sender), + chain_info, + grpc_channel, }; print_if_log_disabled()?; Ok(daemon) @@ -131,6 +155,7 @@ impl From for DaemonAsyncBuilder { deployment_id: value.deployment_id, sender_options: value.sender_options, sender: value.sender, + state: value.state, } } } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 422aeb774..d58804c12 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -14,7 +14,7 @@ use cosmrs::{ use cosmwasm_std::{Addr, Binary, Coin}; use cw_orch_core::{ contract::interface_traits::Uploadable, - environment::{ChainState, IndexResponse}, + environment::{ChainInfoOwned, ChainState, IndexResponse}, log::transaction_target, }; use flate2::{write, Compression}; @@ -68,6 +68,10 @@ pub struct DaemonAsync { pub sender: Wallet, /// State of the daemon pub state: Arc>, + /// gRPC channel + pub grpc_channel: Channel, + /// Information about the chain + pub chain_info: ChainInfoOwned, } impl DaemonAsync { @@ -78,9 +82,7 @@ impl DaemonAsync { /// Get the channel configured for this DaemonAsync. pub fn channel(&self) -> Channel { - // TODO: Channel shouldn't be inside state - let lock = self.state.lock().unwrap(); - lock.grpc_channel.clone() + self.grpc_channel.clone() } } @@ -88,7 +90,7 @@ impl ChainState for DaemonAsync { type Out = Arc>; fn state(&self) -> Self::Out { - self.state + Arc::clone(&self.state) } } @@ -102,12 +104,13 @@ impl DaemonAsync { /// Returns a new [`DaemonAsyncBuilder`] with the current configuration. /// Does not consume the original [`DaemonAsync`]. pub fn rebuild(&self) -> DaemonAsyncBuilder { - let mut builder = Self::builder(); - let state = self.state().lock().unwrap(); + let mut builder = DaemonAsyncBuilder { + state: Some(self.state()), + ..Default::default() + }; builder - .chain(state.chain_data.clone()) - .sender((*self.sender).clone()) - .deployment_id(state.deployment_id.clone()); + .chain(self.chain_info.clone()) + .sender((*self.sender).clone()); builder } @@ -124,7 +127,10 @@ impl DaemonAsync { msg: serde_json::to_vec(&exec_msg)?, funds: parse_cw_coins(coins)?, }; - let result = self.sender.commit_tx(vec![exec_msg], None).await?; + let result = self + .sender + .commit_tx(self.channel(), vec![exec_msg], None) + .await?; log::info!(target: &transaction_target(), "Execution done: {:?}", result.txhash); Ok(result) @@ -150,7 +156,9 @@ impl DaemonAsync { funds: parse_cw_coins(coins)?, }; - let result = sender.commit_tx(vec![init_msg], None).await?; + let result = sender + .commit_tx(self.channel(), vec![init_msg], None) + .await?; log::info!(target: &transaction_target(), "Instantiation done: {:?}", result.txhash); @@ -182,6 +190,7 @@ impl DaemonAsync { let result = sender .commit_tx_any( + self.channel(), vec![Any { type_url: "/cosmwasm.wasm.v1.MsgInstantiateContract2".to_string(), value: init_msg.encode_to_vec(), @@ -225,7 +234,10 @@ impl DaemonAsync { msg: serde_json::to_vec(&migrate_msg)?, code_id: new_code_id, }; - let result = self.sender.commit_tx(vec![exec_msg], None).await?; + let result = self + .sender + .commit_tx(self.channel(), vec![exec_msg], None) + .await?; Ok(result) } @@ -284,7 +296,7 @@ impl DaemonAsync { _uploadable: &T, ) -> Result { let sender = &self.sender; - let wasm_path = ::wasm(&self.state.lock().unwrap().chain_data); + let wasm_path = ::wasm(&self.chain_info); log::debug!(target: &transaction_target(), "Uploading file at {:?}", wasm_path); @@ -298,7 +310,9 @@ impl DaemonAsync { instantiate_permission: None, }; - let result = sender.commit_tx(vec![store_msg], None).await?; + let result = sender + .commit_tx(self.channel(), vec![store_msg], None) + .await?; log::info!(target: &transaction_target(), "Uploading done: {:?}", result.txhash); diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 8d998bb5d..4a3a51059 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -302,6 +302,6 @@ impl WasmQuerier for CosmWasm { &self, contract: &T, ) -> Result { - ::wasm(&contract.get_chain().daemon.state.chain_data).checksum() + ::wasm(&contract.get_chain().daemon.chain_info).checksum() } } diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index 2c72aa17f..d534f93ae 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -4,11 +4,13 @@ use crate::Daemon; impl EnvironmentQuerier for Daemon { fn env_info(&self) -> EnvironmentInfo { - let state = &self.daemon.sender.daemon_state; + let chain_info = &self.daemon.chain_info; + let locked_state = self.daemon.state.lock().unwrap(); + EnvironmentInfo { - chain_id: state.chain_data.chain_id.to_string(), - chain_name: state.chain_data.network_info.chain_name.to_string(), - deployment_id: state.deployment_id.clone(), + chain_id: chain_info.chain_id.to_string(), + chain_name: chain_info.network_info.chain_name.to_string(), + deployment_id: locked_state.deployment_id.clone(), } } } diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index ed9b78822..ea808cc27 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -12,7 +12,6 @@ use super::{ cosmos_modules::{self, auth::BaseAccount}, error::DaemonError, queriers::Node, - state::DaemonState, tx_builder::TxBuilder, tx_resp::CosmTxResponse, }; @@ -39,10 +38,7 @@ use cw_orch_core::{ use crate::env::{LOCAL_MNEMONIC_ENV_NAME, MAIN_MNEMONIC_ENV_NAME, TEST_MNEMONIC_ENV_NAME}; use bitcoin::secp256k1::{All, Context, Secp256k1, Signing}; -use std::{ - str::FromStr, - sync::{Arc, Mutex}, -}; +use std::{str::FromStr, sync::Arc}; use cosmos_modules::vesting::PeriodicVestingAccount; use tonic::transport::Channel; @@ -67,7 +63,7 @@ pub type Wallet = Arc>; pub struct Sender { pub private_key: PrivateKey, pub secp: Secp256k1, - pub(crate) chain_data: ChainInfoOwned, + pub(crate) chain_info: ChainInfoOwned, pub(crate) options: SenderOptions, } @@ -104,51 +100,45 @@ impl SenderOptions { } } -// TODO: Sender shouldn't need to use daemon state, chain_data should be enough impl Sender { - pub fn new(daemon_state: Arc>) -> Result, DaemonError> { - Self::new_with_options(daemon_state, SenderOptions::default()) + pub fn new(chain_info: ChainInfoOwned) -> Result, DaemonError> { + Self::new_with_options(chain_info, SenderOptions::default()) } pub fn new_with_options( - daemon_state: Arc>, + chain_info: ChainInfoOwned, options: SenderOptions, ) -> Result, DaemonError> { - let lock_state = daemon_state.lock().unwrap(); - let mnemonic = get_mnemonic_env(&lock_state.chain_data.kind)?; - // We pass daemon_state inside from_mnemonic_with_options - // but we haven't dropped lock - drop(lock_state); + let mnemonic = get_mnemonic_env(&chain_info.kind)?; - Self::from_mnemonic_with_options(daemon_state, &mnemonic, options) + Self::from_mnemonic_with_options(chain_info, &mnemonic, options) } /// Construct a new Sender from a mnemonic with additional options pub fn from_mnemonic( - daemon_state: Arc>, + chain_info: ChainInfoOwned, mnemonic: &str, ) -> Result, DaemonError> { - Self::from_mnemonic_with_options(daemon_state, mnemonic, SenderOptions::default()) + Self::from_mnemonic_with_options(chain_info, mnemonic, SenderOptions::default()) } /// Construct a new Sender from a mnemonic with additional options pub fn from_mnemonic_with_options( - daemon_state: Arc>, + chain_info: ChainInfoOwned, mnemonic: &str, options: SenderOptions, ) -> Result, DaemonError> { - let lock_state = daemon_state.lock().unwrap(); let secp = Secp256k1::new(); let p_key: PrivateKey = PrivateKey::from_words( &secp, mnemonic, 0, options.hd_index.unwrap_or(0), - lock_state.chain_data.network_info.coin_type, + chain_info.network_info.coin_type, )?; let sender = Sender { - chain_data: lock_state.chain_data, + chain_info: chain_info, private_key: p_key, secp, options, @@ -156,7 +146,7 @@ impl Sender { log::info!( target: &local_target(), "Interacting with {} using address: {}", - lock_state.chain_data.chain_id, + sender.chain_info.chain_id, sender.pub_addr_str()? ); Ok(sender) @@ -178,13 +168,9 @@ impl Sender { SigningKey::from_slice(&self.private_key.raw_key()).unwrap() } - pub fn channel(&self) -> Channel { - self.daemon_state.grpc_channel.clone() - } - pub fn pub_addr(&self) -> Result { Ok(AccountId::new( - &self.daemon_state.chain_data.network_info.pub_address_prefix, + &self.chain_info.network_info.pub_address_prefix, &self.private_key.public_key(&self.secp).raw_address.unwrap(), )?) } @@ -210,6 +196,7 @@ impl Sender { pub async fn bank_send( &self, + channel: Channel, recipient: &str, coins: Vec, ) -> Result { @@ -219,11 +206,12 @@ impl Sender { amount: parse_cw_coins(&coins)?, }; - self.commit_tx(vec![msg_send], Some("sending tokens")).await + self.commit_tx(channel, vec![msg_send], Some("sending tokens")) + .await } pub(crate) fn get_fee_token(&self) -> String { - self.daemon_state.chain_data.gas_denom.to_string() + self.chain_info.gas_denom.to_string() } /// Compute the gas fee from the expected gas in the transaction @@ -240,7 +228,7 @@ impl Sender { if let Some(min_gas) = DaemonEnvVars::min_gas() { gas_expected = (min_gas as f64).max(gas_expected); } - let fee_amount = gas_expected * (self.daemon_state.chain_data.gas_price + 0.00001); + let fee_amount = gas_expected * (self.chain_info.gas_price + 0.00001); Ok((gas_expected as u64, fee_amount as u128)) } @@ -248,16 +236,12 @@ impl Sender { /// Computes the gas needed for submitting a transaction pub async fn calculate_gas( &self, + channel: Channel, tx_body: &tx::Body, sequence: u64, account_number: u64, ) -> Result { - let fee = TxBuilder::build_fee( - 0u8, - &self.daemon_state.chain_data.gas_denom, - 0, - self.options.clone(), - )?; + let fee = TxBuilder::build_fee(0u8, &self.chain_info.gas_denom, 0, self.options.clone())?; let auth_info = SignerInfo { public_key: self.private_key.get_signer_public_key(&self.secp), @@ -269,13 +253,13 @@ impl Sender { let sign_doc = SignDoc::new( tx_body, &auth_info, - &Id::try_from(self.daemon_state.chain_data.chain_id.to_string())?, + &Id::try_from(self.chain_info.chain_id.to_string())?, account_number, )?; let tx_raw = self.sign(sign_doc)?; - Node::new_async(self.channel()) + Node::new_async(channel) ._simulate_tx(tx_raw.to_bytes()?) .await } @@ -284,14 +268,15 @@ impl Sender { /// Returns the gas needed as well as the fee needed for submitting a transaction pub async fn simulate( &self, + channel: Channel, msgs: Vec, memo: Option<&str>, ) -> Result<(u64, Coin), DaemonError> { - let timeout_height = Node::new_async(self.channel())._block_height().await? + 10u64; + let timeout_height = Node::new_async(channel.clone())._block_height().await? + 10u64; let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let tx_builder = TxBuilder::new(tx_body); + let tx_builder = TxBuilder::new(tx_body, channel.clone(), self.chain_info.chain_id.clone()); let gas_needed = tx_builder.simulate(self).await?; @@ -300,7 +285,7 @@ impl Sender { // During simulation, we also make sure the account has enough balance to submit the transaction // This is disabled by an env variable if DaemonEnvVars::wallet_balance_assertion() { - self.assert_wallet_balance(&expected_fee).await?; + self.assert_wallet_balance(channel, &expected_fee).await?; } Ok((gas_for_submission, expected_fee)) @@ -308,6 +293,7 @@ impl Sender { pub async fn commit_tx( &self, + channel: Channel, msgs: Vec, memo: Option<&str>, ) -> Result { @@ -317,15 +303,16 @@ impl Sender { .collect::, _>>() .unwrap(); - self.commit_tx_any(msgs, memo).await + self.commit_tx_any(channel, msgs, memo).await } pub async fn commit_tx_any( &self, + channel: Channel, msgs: Vec, memo: Option<&str>, ) -> Result { - let timeout_height = Node::new_async(self.channel())._block_height().await? + 10u64; + let timeout_height = Node::new_async(channel.clone())._block_height().await? + 10u64; let msgs = if self.options.authz_granter.is_some() { // We wrap authz messages @@ -343,7 +330,7 @@ impl Sender { let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let tx_builder = TxBuilder::new(tx_body); + let tx_builder = TxBuilder::new(tx_body, channel.clone(), self.chain_info.chain_id.clone()); // We retry broadcasting the tx, with the following strategies // 1. In case there is an `incorrect account sequence` error, we can retry as much as possible (doesn't cost anything to the user) @@ -352,10 +339,10 @@ impl Sender { let tx_response = TxBroadcaster::default() .add_strategy(insufficient_fee_strategy()) .add_strategy(account_sequence_strategy()) - .broadcast(tx_builder, self) + .broadcast(channel.clone(), tx_builder, self) .await?; - let resp = Node::new_async(self.channel()) + let resp = Node::new_async(channel) ._find_tx(tx_response.txhash) .await?; @@ -377,10 +364,10 @@ impl Sender { Ok(tx_raw) } - pub async fn base_account(&self) -> Result { + pub async fn base_account(&self, channel: Channel) -> Result { let addr = self.pub_addr().unwrap().to_string(); - let mut client = cosmos_modules::auth::query_client::QueryClient::new(self.channel()); + let mut client = cosmos_modules::auth::query_client::QueryClient::new(channel); let resp = client .account(cosmos_modules::auth::QueryAccountRequest { address: addr }) @@ -407,9 +394,10 @@ impl Sender { pub async fn broadcast_tx( &self, + channel: Channel, tx: Raw, ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new(self.channel()); + let mut client = cosmos_modules::tx::service_client::ServiceClient::new(channel); let commit = client .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { tx_bytes: tx.to_bytes()?, @@ -422,20 +410,25 @@ impl Sender { } /// Allows for checking wether the sender is able to broadcast a transaction that necessitates the provided `gas` - pub async fn has_enough_balance_for_gas(&self, gas: u64) -> Result<(), DaemonError> { + pub async fn has_enough_balance_for_gas( + &self, + channel: Channel, + + gas: u64, + ) -> Result<(), DaemonError> { let (_gas_expected, fee_amount) = self.get_fee_from_gas(gas)?; let fee_denom = self.get_fee_token(); - self.assert_wallet_balance(&coin(fee_amount, fee_denom)) + self.assert_wallet_balance(channel, &coin(fee_amount, fee_denom)) .await } /// Allows checking wether the sender has more funds than the provided `fee` argument #[async_recursion::async_recursion(?Send)] - async fn assert_wallet_balance(&self, fee: &Coin) -> Result<(), DaemonError> { - let chain_data = self.daemon_state.as_ref().chain_data.clone(); + async fn assert_wallet_balance(&self, channel: Channel, fee: &Coin) -> Result<(), DaemonError> { + let chain_info = self.chain_info.clone(); - let bank = Bank::new_async(self.daemon_state.grpc_channel.clone()); + let bank = Bank::new_async(channel.clone()); let balance = bank ._balance(self.address()?, Some(fee.denom.clone())) .await?[0] @@ -444,7 +437,7 @@ impl Sender { log::debug!( "Checking balance {} on chain {}, address {}. Expecting {}{}", balance.amount, - chain_data.chain_id, + chain_info.chain_id, self.address()?, fee, fee.denom @@ -461,7 +454,7 @@ impl Sender { "Not enough funds on chain {} at address {} to deploy the contract. Needed: {}{} but only have: {}. Press 'y' when the wallet balance has been increased to resume deployment", - self.daemon_state.chain_data.chain_id, + chain_info.chain_id, self.address()?, fee, fee.denom, @@ -473,7 +466,7 @@ impl Sender { std::io::stdin().read_line(&mut input)?; if input.to_lowercase().contains('y') { // We retry asserting the balance - self.assert_wallet_balance(fee).await + self.assert_wallet_balance(channel, fee).await } else { Err(DaemonError::NotEnoughBalance { expected: fee.clone(), diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 198cee1d2..ff09eeb10 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -1,20 +1,15 @@ use super::error::DaemonError; use crate::env::{default_state_folder, DaemonEnvVars}; -use crate::{channel::GrpcChannel, json_lock::JsonLockedState, networks::ChainKind}; +use crate::{json_lock::JsonLockedState, networks::ChainKind}; use cosmwasm_std::Addr; use cw_orch_core::environment::ChainInfoOwned; -use cw_orch_core::{ - environment::StateInterface, - log::{connectivity_target, local_target}, - CwEnvError, -}; +use cw_orch_core::{environment::StateInterface, log::local_target, CwEnvError}; use once_cell::sync::Lazy; use serde::Serialize; use serde_json::{json, Value}; use std::collections::HashSet; use std::{collections::HashMap, path::Path, sync::Mutex}; -use tonic::transport::Channel; /// Global state to track which files are already open by other daemons from other threads /// This is necessary because File lock will allow same process to lock file how many times as process wants @@ -28,11 +23,10 @@ pub struct DaemonState { json_state: DaemonStateFile, /// Deployment identifier pub deployment_id: String, - // TODO: Move grpc_channel and chain_data to DaemonAsync. No reason to have it here - /// gRPC channel - pub grpc_channel: Channel, - /// Information about the chain - pub chain_data: ChainInfoOwned, + /// Chain Id + pub chain_id: String, + /// Chain Name + pub chain_name: String, } #[derive(Debug)] @@ -54,15 +48,8 @@ impl DaemonState { deployment_id: String, read_only: bool, ) -> Result { - if chain_data.grpc_urls.is_empty() { - return Err(DaemonError::GRPCListIsEmpty); - } - - log::debug!(target: &connectivity_target(), "Found {} gRPC endpoints", chain_data.grpc_urls.len()); - - // find working grpc channel - let grpc_channel = - GrpcChannel::connect(&chain_data.grpc_urls, chain_data.chain_id.as_str()).await?; + let chain_id = chain_data.chain_id; + let chain_name = chain_data.network_info.chain_name; // 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()?; @@ -104,19 +91,15 @@ impl DaemonState { lock.insert(json_file_path); drop(lock); - json_file_state.prepare( - &chain_data.chain_id, - &chain_data.network_info.chain_name, - &deployment_id, - ); + json_file_state.prepare(&chain_id, &chain_name, &deployment_id); DaemonStateFile::FullAccess { json_file_state } }; Ok(DaemonState { json_state, deployment_id, - grpc_channel, - chain_data, + chain_id, + chain_name, }) } @@ -161,13 +144,10 @@ impl DaemonState { DaemonStateFile::ReadOnly { path } => { let j = crate::json_lock::read(path)?; - j[&self.chain_data.chain_id][&self.chain_data.network_info.chain_name] + j[&self.chain_id][&self.chain_name].clone() } DaemonStateFile::FullAccess { json_file_state } => json_file_state - .get( - &self.chain_data.chain_id, - &self.chain_data.network_info.chain_name, - ) + .get(&self.chain_id, &self.chain_name) .clone(), }; Ok(state) @@ -193,10 +173,7 @@ impl DaemonState { DaemonStateFile::FullAccess { json_file_state } => json_file_state, }; - let val = json_file_state.get_mut( - &self.chain_data.chain_id, - &self.chain_data.network_info.chain_name, - ); + let val = json_file_state.get_mut(&self.chain_id, &self.chain_name); val[key][contract_id] = json!(value); Ok(()) @@ -228,7 +205,8 @@ impl StateInterface for DaemonState { /// Set address for contract in deployment id in state file fn set_address(&mut self, contract_id: &str, address: &Addr) { - self.set(&self.deployment_id, contract_id, address.as_str()) + let deployment_id = self.deployment_id.clone(); + self.set(&deployment_id, contract_id, address.as_str()) .unwrap(); } diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 8dd39c1d0..9728da104 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,8 +1,10 @@ -use crate::RUNTIME; +use std::sync::{Arc, Mutex}; + use crate::{ sender::{Sender, SenderBuilder, SenderOptions}, DaemonAsyncBuilder, }; +use crate::{DaemonState, RUNTIME}; use bitcoin::secp256k1::All; use cw_orch_core::environment::ChainInfoOwned; @@ -35,6 +37,9 @@ pub struct DaemonBuilder { pub(crate) sender: Option>, /// Specify Daemon Sender Options pub(crate) sender_options: SenderOptions, + + /* Rebuilder related options */ + pub(crate) state: Option>>, } impl DaemonBuilder { @@ -175,9 +180,9 @@ mod test { .build() .unwrap(); - assert_eq!(daemon.daemon.state.chain_data.grpc_urls.len(), 1); + assert_eq!(daemon.daemon.chain_info.grpc_urls.len(), 1); assert_eq!( - daemon.daemon.state.chain_data.grpc_urls[0], + daemon.daemon.chain_info.grpc_urls[0], OSMOSIS_1.grpc_urls[0].to_string(), ); } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index bf8eb8b88..31784d462 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -1,4 +1,7 @@ -use std::{fmt::Debug, sync::Arc}; +use std::{ + fmt::Debug, + sync::{Arc, Mutex}, +}; use super::super::{sender::Wallet, DaemonAsync}; use crate::{ @@ -55,7 +58,7 @@ impl Daemon { /// Get the channel configured for this Daemon pub fn channel(&self) -> Channel { - self.daemon.state.grpc_channel.clone() + self.daemon.grpc_channel.clone() } /// Get the channel configured for this Daemon @@ -66,20 +69,22 @@ impl Daemon { /// Returns a new [`DaemonBuilder`] with the current configuration. /// Does not consume the original [`Daemon`]. pub fn rebuild(&self) -> DaemonBuilder { - let mut builder = Self::builder(); + let mut builder = DaemonBuilder { + state: Some(self.state()), + ..Default::default() + }; builder - .chain(self.state().chain_data.clone()) - .sender((*self.daemon.sender).clone()) - .deployment_id(&self.state().deployment_id); + .chain(self.daemon.chain_info.clone()) + .sender((*self.daemon.sender).clone()); builder } } impl ChainState for Daemon { - type Out = Arc; + type Out = Arc>; fn state(&self) -> Self::Out { - self.daemon.state.clone() + Arc::clone(&self.daemon.state) } } @@ -162,6 +167,7 @@ impl Stargate for Daemon { ) -> Result { self.rt_handle.block_on( self.wallet().commit_tx_any( + self.channel(), msgs.iter() .map(|msg| cosmrs::Any { type_url: msg.type_url.clone(), diff --git a/cw-orch-daemon/src/tx_broadcaster.rs b/cw-orch-daemon/src/tx_broadcaster.rs index a2c6f667b..1ef2c6ff5 100644 --- a/cw-orch-daemon/src/tx_broadcaster.rs +++ b/cw-orch-daemon/src/tx_broadcaster.rs @@ -3,6 +3,7 @@ use std::time::Duration; use bitcoin::secp256k1::All; use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; use cw_orch_core::log::transaction_target; +use tonic::transport::Channel; use crate::{queriers::Node, sender::Sender, CosmTxResponse, DaemonError, TxBuilder}; @@ -69,13 +70,14 @@ impl TxBroadcaster { // Thus we use a `while` loop structure here pub async fn broadcast( mut self, + channel: Channel, mut tx_builder: TxBuilder, wallet: &Sender, ) -> Result { let mut tx_retry = true; // We try and broadcast once - let mut tx_response = broadcast_helper(&mut tx_builder, wallet).await; + let mut tx_response = broadcast_helper(channel.clone(), &mut tx_builder, wallet).await; log::info!( target: &transaction_target(), "Awaiting TX inclusion in block..." @@ -93,7 +95,7 @@ impl TxBroadcaster { tx_retry = true; // We still await for the next block, to avoid spamming retry when an error occurs - let block_speed = Node::new_async(wallet.channel()) + let block_speed = Node::new_async(channel.clone()) ._average_block_speed(None) .await?; log::warn!( @@ -104,7 +106,7 @@ impl TxBroadcaster { ); tokio::time::sleep(Duration::from_secs(block_speed)).await; - tx_response = broadcast_helper(&mut tx_builder, wallet).await; + tx_response = broadcast_helper(channel.clone(), &mut tx_builder, wallet).await; continue; } } @@ -124,11 +126,12 @@ fn strategy_condition_met( } async fn broadcast_helper( + channel: Channel, tx_builder: &mut TxBuilder, wallet: &Sender, ) -> Result { let tx = tx_builder.build(wallet).await?; - let tx_response = wallet.broadcast_tx(tx).await?; + let tx_response = wallet.broadcast_tx(channel, tx).await?; log::debug!(target: &transaction_target(), "TX broadcast response: {:?}", tx_response); assert_broadcast_code_response(tx_response) diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index db107b81d..83ab68181 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -10,6 +10,7 @@ use cosmrs::{ Any, Coin, }; use cw_orch_core::log::transaction_target; +use tonic::transport::Channel; use crate::sender::SenderOptions; @@ -25,16 +26,22 @@ pub struct TxBuilder { pub(crate) gas_limit: Option, // if defined, use this sequence, else get it from the node pub(crate) sequence: Option, + // Channel over which tx is sent + pub(crate) channel: Channel, + /// `chain_id` is the unique identifier of the chain this transaction targets. + pub(crate) chain_id: String, } impl TxBuilder { /// Create a new TxBuilder with a given body. - pub fn new(body: Body) -> Self { + pub fn new(body: Body, channel: Channel, chain_id: String) -> Self { Self { body, fee_amount: None, gas_limit: None, sequence: None, + channel, + chain_id, } } /// Set a fixed fee amount for the tx @@ -84,13 +91,13 @@ impl TxBuilder { account_number, sequence, .. - } = wallet.base_account().await?; + } = wallet.base_account(self.channel.clone()).await?; // overwrite sequence if set (can be used for concurrent txs) let sequence = self.sequence.unwrap_or(sequence); wallet - .calculate_gas(&self.body, sequence, account_number) + .calculate_gas(self.channel.clone(), &self.body, sequence, account_number) .await } @@ -102,7 +109,7 @@ impl TxBuilder { account_number, sequence, .. - } = wallet.base_account().await?; + } = wallet.base_account(self.channel.clone()).await?; // overwrite sequence if set (can be used for concurrent txs) let sequence = self.sequence.unwrap_or(sequence); @@ -120,7 +127,7 @@ impl TxBuilder { (fee, gas_limit) } else { let sim_gas_used = wallet - .calculate_gas(&self.body, sequence, account_number) + .calculate_gas(self.channel.clone(), &self.body, sequence, account_number) .await?; log::debug!(target: &transaction_target(), "Simulated gas needed {:?}", sim_gas_used); @@ -159,7 +166,7 @@ impl TxBuilder { let sign_doc = SignDoc::new( &self.body, &auth_info, - &Id::try_from(wallet.daemon_state.chain_data.chain_id.to_string())?, + &Id::try_from(self.chain_id.clone())?, account_number, )?; wallet.sign(sign_doc).map_err(Into::into) diff --git a/cw-orch/src/osmosis_test_tube/core.rs b/cw-orch/src/osmosis_test_tube/core.rs index 892d21f0c..3b5365df4 100644 --- a/cw-orch/src/osmosis_test_tube/core.rs +++ b/cw-orch/src/osmosis_test_tube/core.rs @@ -208,7 +208,7 @@ impl ChainState for OsmosisTestTube { } // Execute on the test chain, returns test response type -impl TxHandler for OsmosisTestTube { +impl TxHandler for OsmosisTestTube { type Error = CwOrchError; type ContractSource = WasmPath; type Response = AppResponse; From ee5b4c27cf85174b1ed6a0d15ea5146f38a34e63 Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 7 May 2024 15:15:16 +0300 Subject: [PATCH 015/108] move channel to sender --- cw-orch-daemon/Cargo.toml | 2 +- .../examples/daemon-capabilities.rs | 2 +- cw-orch-daemon/src/builder.rs | 37 ++++- cw-orch-daemon/src/core.rs | 31 +--- cw-orch-daemon/src/json_lock.rs | 21 ++- cw-orch-daemon/src/queriers/cosmwasm.rs | 2 +- cw-orch-daemon/src/queriers/env.rs | 2 +- cw-orch-daemon/src/sender.rs | 71 +++++---- cw-orch-daemon/src/state.rs | 22 +-- cw-orch-daemon/src/sync/builder.rs | 28 +++- cw-orch-daemon/src/sync/core.rs | 5 +- cw-orch-daemon/src/tx_broadcaster.rs | 11 +- cw-orch-daemon/src/tx_builder.rs | 14 +- cw-orch-daemon/tests/daemon_state.rs | 142 +++++++++--------- 14 files changed, 208 insertions(+), 182 deletions(-) diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 758db5f18..840924106 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -84,7 +84,7 @@ duct = "0.13" mock-contract = { path = "../contracts/mock_contract", features = [ "interface", ] } -serial_test = "3.0.0" +serial_test = { version = "3.0.0" } # Ethereum deps ethers-signers = { version = "2.0.7" } diff --git a/cw-orch-daemon/examples/daemon-capabilities.rs b/cw-orch-daemon/examples/daemon-capabilities.rs index fb337557c..27cfa4810 100644 --- a/cw-orch-daemon/examples/daemon-capabilities.rs +++ b/cw-orch-daemon/examples/daemon-capabilities.rs @@ -16,7 +16,7 @@ pub fn main() -> anyhow::Result<()> { // We commit the tx (also resimulates the tx) // ANCHOR: send_tx let wallet = daemon.wallet(); - let rt = daemon.rt_handle; + let rt = daemon.rt_handle.clone(); rt.block_on(wallet.bank_send("", coins(345, "ujunox")))?; // ANCHOR_END: send_tx diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 3701d191f..3e7642833 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -31,6 +31,7 @@ pub struct DaemonAsyncBuilder { pub(crate) chain: Option, // # Optional pub(crate) deployment_id: Option, + pub(crate) state_path: Option, /* Sender related options */ /// Wallet sender @@ -52,7 +53,8 @@ impl DaemonAsyncBuilder { /// Set the deployment id to use for the daemon interactions /// Defaults to `default` - /// This field is ignored for rebuilt daemon and deployment id of the original used instead + /// + /// This field is ignored for rebuilt daemon and deployment id of the original daemon used instead pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { self.deployment_id = Some(deployment_id.into()); self @@ -92,6 +94,17 @@ impl DaemonAsyncBuilder { self } + /// Specifies path to the daemon state file + /// Defaults to env variable. + /// + /// Variable: STATE_FILE_ENV_NAME. + /// + /// This field is ignored for rebuilt daemon and path of the original daemon used instead + pub fn state_path(&mut self, path: impl ToString) -> &mut Self { + self.state_path = Some(path.to_string()); + self + } + /// Build a daemon pub async fn build(&self) -> Result { let chain_info = self @@ -115,9 +128,18 @@ impl DaemonAsyncBuilder { let state = match &self.state { Some(state) => state.clone(), - None => Arc::new(Mutex::new( - DaemonState::new(chain_info.clone(), deployment_id, false).await?, - )), + None => { + // If the path is relative, we dis-ambiguate it and take the root at $HOME/$CW_ORCH_STATE_FOLDER + let json_file_path = self + .state_path + .clone() + .unwrap_or(DaemonState::state_file_path()?); + + Arc::new(Mutex::new( + DaemonState::new(json_file_path, chain_info.clone(), deployment_id, false) + .await?, + )) + } }; // if mnemonic provided, use it. Else use env variables to retrieve mnemonic let sender_options = self.sender_options.clone(); @@ -126,22 +148,22 @@ impl DaemonAsyncBuilder { Some(sender) => match sender { SenderBuilder::Mnemonic(mnemonic) => Sender::from_mnemonic_with_options( chain_info.clone(), + grpc_channel, &mnemonic, sender_options, )?, SenderBuilder::Sender(mut sender) => { sender.set_options(self.sender_options.clone()); + sender.grpc_channel = grpc_channel; sender } }, - None => Sender::new_with_options(chain_info.clone(), sender_options)?, + None => Sender::new_with_options(chain_info.clone(), grpc_channel, sender_options)?, }; let daemon = DaemonAsync { state, sender: Arc::new(sender), - chain_info, - grpc_channel, }; print_if_log_disabled()?; Ok(daemon) @@ -156,6 +178,7 @@ impl From for DaemonAsyncBuilder { sender_options: value.sender_options, sender: value.sender, state: value.state, + state_path: value.state_path, } } } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index d58804c12..f93b34b81 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -14,7 +14,7 @@ use cosmrs::{ use cosmwasm_std::{Addr, Binary, Coin}; use cw_orch_core::{ contract::interface_traits::Uploadable, - environment::{ChainInfoOwned, ChainState, IndexResponse}, + environment::{ChainState, IndexResponse}, log::transaction_target, }; use flate2::{write, Compression}; @@ -68,10 +68,6 @@ pub struct DaemonAsync { pub sender: Wallet, /// State of the daemon pub state: Arc>, - /// gRPC channel - pub grpc_channel: Channel, - /// Information about the chain - pub chain_info: ChainInfoOwned, } impl DaemonAsync { @@ -82,7 +78,7 @@ impl DaemonAsync { /// Get the channel configured for this DaemonAsync. pub fn channel(&self) -> Channel { - self.grpc_channel.clone() + self.sender.grpc_channel.clone() } } @@ -109,7 +105,7 @@ impl DaemonAsync { ..Default::default() }; builder - .chain(self.chain_info.clone()) + .chain(self.sender.chain_info.clone()) .sender((*self.sender).clone()); builder } @@ -127,10 +123,7 @@ impl DaemonAsync { msg: serde_json::to_vec(&exec_msg)?, funds: parse_cw_coins(coins)?, }; - let result = self - .sender - .commit_tx(self.channel(), vec![exec_msg], None) - .await?; + let result = self.sender.commit_tx(vec![exec_msg], None).await?; log::info!(target: &transaction_target(), "Execution done: {:?}", result.txhash); Ok(result) @@ -156,9 +149,7 @@ impl DaemonAsync { funds: parse_cw_coins(coins)?, }; - let result = sender - .commit_tx(self.channel(), vec![init_msg], None) - .await?; + let result = sender.commit_tx(vec![init_msg], None).await?; log::info!(target: &transaction_target(), "Instantiation done: {:?}", result.txhash); @@ -190,7 +181,6 @@ impl DaemonAsync { let result = sender .commit_tx_any( - self.channel(), vec![Any { type_url: "/cosmwasm.wasm.v1.MsgInstantiateContract2".to_string(), value: init_msg.encode_to_vec(), @@ -234,10 +224,7 @@ impl DaemonAsync { msg: serde_json::to_vec(&migrate_msg)?, code_id: new_code_id, }; - let result = self - .sender - .commit_tx(self.channel(), vec![exec_msg], None) - .await?; + let result = self.sender.commit_tx(vec![exec_msg], None).await?; Ok(result) } @@ -296,7 +283,7 @@ impl DaemonAsync { _uploadable: &T, ) -> Result { let sender = &self.sender; - let wasm_path = ::wasm(&self.chain_info); + let wasm_path = ::wasm(&self.sender.chain_info); log::debug!(target: &transaction_target(), "Uploading file at {:?}", wasm_path); @@ -310,9 +297,7 @@ impl DaemonAsync { instantiate_permission: None, }; - let result = sender - .commit_tx(self.channel(), vec![store_msg], None) - .await?; + let result = sender.commit_tx(vec![store_msg], None).await?; log::info!(target: &transaction_target(), "Uploading done: {:?}", result.txhash); diff --git a/cw-orch-daemon/src/json_lock.rs b/cw-orch-daemon/src/json_lock.rs index 3b43992b8..9505f822f 100644 --- a/cw-orch-daemon/src/json_lock.rs +++ b/cw-orch-daemon/src/json_lock.rs @@ -9,15 +9,16 @@ use std::{fs::File, io::Seek}; pub struct JsonLockedState { lock: FileLock, json: Value, + path: String, } impl JsonLockedState { /// Lock a state files /// Other process won't be able to lock it - pub fn new(filename: &str) -> Self { + pub fn new(path: &str) -> Self { // open file pointer set read/write permissions to true // create it if it does not exists - // dont truncate it + // don't truncate it let options = FileOptions::new() .create(true) @@ -26,8 +27,8 @@ impl JsonLockedState { .truncate(false); // Lock file, non blocking so it errors in case someone else already holding lock of it - let lock: FileLock = FileLock::lock(filename, false, options) - .unwrap_or_else(|_| panic!("Was not able to receive {filename} state lock")); + let lock: FileLock = FileLock::lock(path, false, options) + .unwrap_or_else(|_| panic!("Was not able to receive {path} state lock")); // return empty json object if file is empty // return file content if not @@ -37,7 +38,13 @@ impl JsonLockedState { from_reader(&lock.file).unwrap() }; - JsonLockedState { lock, json } + let filename = path.to_owned(); + + JsonLockedState { + lock, + json, + path: filename, + } } /// Prepare json for further writes @@ -76,6 +83,10 @@ impl JsonLockedState { self.lock.file.rewind().unwrap(); serde_json::to_writer_pretty(&self.lock.file, &self.json).unwrap(); } + + pub fn path(&self) -> &str { + &self.path + } } // Write json when dropping diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 4a3a51059..2c93c3483 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -302,6 +302,6 @@ impl WasmQuerier for CosmWasm { &self, contract: &T, ) -> Result { - ::wasm(&contract.get_chain().daemon.chain_info).checksum() + ::wasm(&contract.get_chain().daemon.sender.chain_info).checksum() } } diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index d534f93ae..a95f1c6ce 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -4,7 +4,7 @@ use crate::Daemon; impl EnvironmentQuerier for Daemon { fn env_info(&self) -> EnvironmentInfo { - let chain_info = &self.daemon.chain_info; + let chain_info = &self.daemon.sender.chain_info; let locked_state = self.daemon.state.lock().unwrap(); EnvironmentInfo { diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index ea808cc27..c44308bb1 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -63,7 +63,10 @@ pub type Wallet = Arc>; pub struct Sender { pub private_key: PrivateKey, pub secp: Secp256k1, - pub(crate) chain_info: ChainInfoOwned, + /// gRPC channel + pub grpc_channel: Channel, + /// Information about the chain + pub chain_info: ChainInfoOwned, pub(crate) options: SenderOptions, } @@ -101,30 +104,37 @@ impl SenderOptions { } impl Sender { - pub fn new(chain_info: ChainInfoOwned) -> Result, DaemonError> { - Self::new_with_options(chain_info, SenderOptions::default()) + pub fn new(chain_info: ChainInfoOwned, channel: Channel) -> Result, DaemonError> { + Self::new_with_options(chain_info, channel, SenderOptions::default()) + } + + pub fn channel(&self) -> Channel { + self.grpc_channel.clone() } pub fn new_with_options( chain_info: ChainInfoOwned, + channel: Channel, options: SenderOptions, ) -> Result, DaemonError> { let mnemonic = get_mnemonic_env(&chain_info.kind)?; - Self::from_mnemonic_with_options(chain_info, &mnemonic, options) + Self::from_mnemonic_with_options(chain_info, channel, &mnemonic, options) } /// Construct a new Sender from a mnemonic with additional options pub fn from_mnemonic( chain_info: ChainInfoOwned, + channel: Channel, mnemonic: &str, ) -> Result, DaemonError> { - Self::from_mnemonic_with_options(chain_info, mnemonic, SenderOptions::default()) + Self::from_mnemonic_with_options(chain_info, channel, mnemonic, SenderOptions::default()) } /// Construct a new Sender from a mnemonic with additional options pub fn from_mnemonic_with_options( chain_info: ChainInfoOwned, + channel: Channel, mnemonic: &str, options: SenderOptions, ) -> Result, DaemonError> { @@ -138,7 +148,8 @@ impl Sender { )?; let sender = Sender { - chain_info: chain_info, + chain_info, + grpc_channel: channel, private_key: p_key, secp, options, @@ -196,7 +207,6 @@ impl Sender { pub async fn bank_send( &self, - channel: Channel, recipient: &str, coins: Vec, ) -> Result { @@ -206,8 +216,7 @@ impl Sender { amount: parse_cw_coins(&coins)?, }; - self.commit_tx(channel, vec![msg_send], Some("sending tokens")) - .await + self.commit_tx(vec![msg_send], Some("sending tokens")).await } pub(crate) fn get_fee_token(&self) -> String { @@ -236,7 +245,6 @@ impl Sender { /// Computes the gas needed for submitting a transaction pub async fn calculate_gas( &self, - channel: Channel, tx_body: &tx::Body, sequence: u64, account_number: u64, @@ -259,7 +267,7 @@ impl Sender { let tx_raw = self.sign(sign_doc)?; - Node::new_async(channel) + Node::new_async(self.channel()) ._simulate_tx(tx_raw.to_bytes()?) .await } @@ -268,15 +276,14 @@ impl Sender { /// Returns the gas needed as well as the fee needed for submitting a transaction pub async fn simulate( &self, - channel: Channel, msgs: Vec, memo: Option<&str>, ) -> Result<(u64, Coin), DaemonError> { - let timeout_height = Node::new_async(channel.clone())._block_height().await? + 10u64; + let timeout_height = Node::new_async(self.channel())._block_height().await? + 10u64; let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let tx_builder = TxBuilder::new(tx_body, channel.clone(), self.chain_info.chain_id.clone()); + let tx_builder = TxBuilder::new(tx_body, self.chain_info.chain_id.clone()); let gas_needed = tx_builder.simulate(self).await?; @@ -285,7 +292,7 @@ impl Sender { // During simulation, we also make sure the account has enough balance to submit the transaction // This is disabled by an env variable if DaemonEnvVars::wallet_balance_assertion() { - self.assert_wallet_balance(channel, &expected_fee).await?; + self.assert_wallet_balance(&expected_fee).await?; } Ok((gas_for_submission, expected_fee)) @@ -293,7 +300,6 @@ impl Sender { pub async fn commit_tx( &self, - channel: Channel, msgs: Vec, memo: Option<&str>, ) -> Result { @@ -303,16 +309,15 @@ impl Sender { .collect::, _>>() .unwrap(); - self.commit_tx_any(channel, msgs, memo).await + self.commit_tx_any(msgs, memo).await } pub async fn commit_tx_any( &self, - channel: Channel, msgs: Vec, memo: Option<&str>, ) -> Result { - let timeout_height = Node::new_async(channel.clone())._block_height().await? + 10u64; + let timeout_height = Node::new_async(self.channel())._block_height().await? + 10u64; let msgs = if self.options.authz_granter.is_some() { // We wrap authz messages @@ -330,7 +335,7 @@ impl Sender { let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let tx_builder = TxBuilder::new(tx_body, channel.clone(), self.chain_info.chain_id.clone()); + let tx_builder = TxBuilder::new(tx_body, self.chain_info.chain_id.clone()); // We retry broadcasting the tx, with the following strategies // 1. In case there is an `incorrect account sequence` error, we can retry as much as possible (doesn't cost anything to the user) @@ -339,10 +344,10 @@ impl Sender { let tx_response = TxBroadcaster::default() .add_strategy(insufficient_fee_strategy()) .add_strategy(account_sequence_strategy()) - .broadcast(channel.clone(), tx_builder, self) + .broadcast(tx_builder, self) .await?; - let resp = Node::new_async(channel) + let resp = Node::new_async(self.channel()) ._find_tx(tx_response.txhash) .await?; @@ -364,10 +369,10 @@ impl Sender { Ok(tx_raw) } - pub async fn base_account(&self, channel: Channel) -> Result { + pub async fn base_account(&self) -> Result { let addr = self.pub_addr().unwrap().to_string(); - let mut client = cosmos_modules::auth::query_client::QueryClient::new(channel); + let mut client = cosmos_modules::auth::query_client::QueryClient::new(self.channel()); let resp = client .account(cosmos_modules::auth::QueryAccountRequest { address: addr }) @@ -394,10 +399,9 @@ impl Sender { pub async fn broadcast_tx( &self, - channel: Channel, tx: Raw, ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new(channel); + let mut client = cosmos_modules::tx::service_client::ServiceClient::new(self.channel()); let commit = client .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { tx_bytes: tx.to_bytes()?, @@ -410,25 +414,20 @@ impl Sender { } /// Allows for checking wether the sender is able to broadcast a transaction that necessitates the provided `gas` - pub async fn has_enough_balance_for_gas( - &self, - channel: Channel, - - gas: u64, - ) -> Result<(), DaemonError> { + pub async fn has_enough_balance_for_gas(&self, gas: u64) -> Result<(), DaemonError> { let (_gas_expected, fee_amount) = self.get_fee_from_gas(gas)?; let fee_denom = self.get_fee_token(); - self.assert_wallet_balance(channel, &coin(fee_amount, fee_denom)) + self.assert_wallet_balance(&coin(fee_amount, fee_denom)) .await } /// Allows checking wether the sender has more funds than the provided `fee` argument #[async_recursion::async_recursion(?Send)] - async fn assert_wallet_balance(&self, channel: Channel, fee: &Coin) -> Result<(), DaemonError> { + async fn assert_wallet_balance(&self, fee: &Coin) -> Result<(), DaemonError> { let chain_info = self.chain_info.clone(); - let bank = Bank::new_async(channel.clone()); + let bank = Bank::new_async(self.channel()); let balance = bank ._balance(self.address()?, Some(fee.denom.clone())) .await?[0] @@ -466,7 +465,7 @@ impl Sender { std::io::stdin().read_line(&mut input)?; if input.to_lowercase().contains('y') { // We retry asserting the balance - self.assert_wallet_balance(channel, fee).await + self.assert_wallet_balance(fee).await } else { Err(DaemonError::NotEnoughBalance { expected: fee.clone(), diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index ff09eeb10..d2bddee66 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -29,21 +29,26 @@ pub struct DaemonState { pub chain_name: String, } +impl Drop for DaemonState { + fn drop(&mut self) { + if let DaemonStateFile::FullAccess { json_file_state } = &self.json_state { + let mut lock = LOCKED_FILES.lock().unwrap(); + lock.remove(json_file_state.path()); + } + } +} + #[derive(Debug)] enum DaemonStateFile { - ReadOnly { - /// this is passed via env var STATE_FILE - path: String, - }, - FullAccess { - json_file_state: JsonLockedState, - }, + ReadOnly { path: String }, + FullAccess { json_file_state: JsonLockedState }, } impl DaemonState { /// Creates a new state from the given chain data and deployment id. /// Attempts to connect to any of the provided gRPC endpoints. pub async fn new( + mut json_file_path: String, chain_data: ChainInfoOwned, deployment_id: String, read_only: bool, @@ -51,9 +56,6 @@ impl DaemonState { let chain_id = chain_data.chain_id; let chain_name = chain_data.network_info.chain_name; - // 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()?; - log::debug!(target: &local_target(), "Using state file : {}", json_file_path); // if the network we are connecting is a local kind, add it to the fn diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 9728da104..82fa303f4 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -31,6 +31,7 @@ pub struct DaemonBuilder { pub(crate) overwrite_grpc_url: Option, pub(crate) gas_denom: Option, pub(crate) gas_fee: Option, + pub(crate) state_path: Option, /* Sender Options */ /// Wallet sender @@ -51,6 +52,8 @@ impl DaemonBuilder { /// Set the deployment id to use for the Daemon interactions /// Defaults to `default` + /// + /// This field is ignored for rebuilt daemon and deployment id of the original daemon used instead pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { self.deployment_id = Some(deployment_id.into()); self @@ -121,6 +124,17 @@ impl DaemonBuilder { self } + /// Specifies path to the daemon state file + /// Defaults to env variable. + /// + /// Variable: STATE_FILE_ENV_NAME. + /// + /// This field is ignored for rebuilt daemon and path of the original daemon used instead + pub fn state_path(&mut self, path: impl ToString) -> &mut Self { + self.state_path = Some(path.to_string()); + self + } + /// Build a Daemon pub fn build(&self) -> Result { let rt_handle = self @@ -180,9 +194,9 @@ mod test { .build() .unwrap(); - assert_eq!(daemon.daemon.chain_info.grpc_urls.len(), 1); + assert_eq!(daemon.daemon.sender.chain_info.grpc_urls.len(), 1); assert_eq!( - daemon.daemon.chain_info.grpc_urls[0], + daemon.daemon.sender.chain_info.grpc_urls[0], OSMOSIS_1.grpc_urls[0].to_string(), ); } @@ -197,9 +211,9 @@ mod test { .gas(None, Some(fee_amount)) .build() .unwrap(); - println!("chain {:?}", daemon.daemon.state.chain_data); + println!("chain {:?}", daemon.daemon.sender.chain_info); - assert_eq!(daemon.daemon.state.chain_data.gas_price, fee_amount); + assert_eq!(daemon.daemon.sender.chain_info.gas_price, fee_amount); } #[test] @@ -213,7 +227,7 @@ mod test { .build() .unwrap(); - assert_eq!(daemon.daemon.state.chain_data.gas_denom, token.to_string()); + assert_eq!(daemon.daemon.sender.chain_info.gas_denom, token.to_string()); } #[test] @@ -228,8 +242,8 @@ mod test { .build() .unwrap(); - assert_eq!(daemon.daemon.state.chain_data.gas_denom, token.to_string()); + assert_eq!(daemon.daemon.sender.chain_info.gas_denom, token.to_string()); - assert_eq!(daemon.daemon.state.chain_data.gas_price, fee_amount); + assert_eq!(daemon.daemon.sender.chain_info.gas_price, fee_amount); } } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 31784d462..aa37baa53 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -58,7 +58,7 @@ impl Daemon { /// Get the channel configured for this Daemon pub fn channel(&self) -> Channel { - self.daemon.grpc_channel.clone() + self.daemon.sender.grpc_channel.clone() } /// Get the channel configured for this Daemon @@ -74,7 +74,7 @@ impl Daemon { ..Default::default() }; builder - .chain(self.daemon.chain_info.clone()) + .chain(self.daemon.sender.chain_info.clone()) .sender((*self.daemon.sender).clone()); builder } @@ -167,7 +167,6 @@ impl Stargate for Daemon { ) -> Result { self.rt_handle.block_on( self.wallet().commit_tx_any( - self.channel(), msgs.iter() .map(|msg| cosmrs::Any { type_url: msg.type_url.clone(), diff --git a/cw-orch-daemon/src/tx_broadcaster.rs b/cw-orch-daemon/src/tx_broadcaster.rs index 1ef2c6ff5..a2c6f667b 100644 --- a/cw-orch-daemon/src/tx_broadcaster.rs +++ b/cw-orch-daemon/src/tx_broadcaster.rs @@ -3,7 +3,6 @@ use std::time::Duration; use bitcoin::secp256k1::All; use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; use cw_orch_core::log::transaction_target; -use tonic::transport::Channel; use crate::{queriers::Node, sender::Sender, CosmTxResponse, DaemonError, TxBuilder}; @@ -70,14 +69,13 @@ impl TxBroadcaster { // Thus we use a `while` loop structure here pub async fn broadcast( mut self, - channel: Channel, mut tx_builder: TxBuilder, wallet: &Sender, ) -> Result { let mut tx_retry = true; // We try and broadcast once - let mut tx_response = broadcast_helper(channel.clone(), &mut tx_builder, wallet).await; + let mut tx_response = broadcast_helper(&mut tx_builder, wallet).await; log::info!( target: &transaction_target(), "Awaiting TX inclusion in block..." @@ -95,7 +93,7 @@ impl TxBroadcaster { tx_retry = true; // We still await for the next block, to avoid spamming retry when an error occurs - let block_speed = Node::new_async(channel.clone()) + let block_speed = Node::new_async(wallet.channel()) ._average_block_speed(None) .await?; log::warn!( @@ -106,7 +104,7 @@ impl TxBroadcaster { ); tokio::time::sleep(Duration::from_secs(block_speed)).await; - tx_response = broadcast_helper(channel.clone(), &mut tx_builder, wallet).await; + tx_response = broadcast_helper(&mut tx_builder, wallet).await; continue; } } @@ -126,12 +124,11 @@ fn strategy_condition_met( } async fn broadcast_helper( - channel: Channel, tx_builder: &mut TxBuilder, wallet: &Sender, ) -> Result { let tx = tx_builder.build(wallet).await?; - let tx_response = wallet.broadcast_tx(channel, tx).await?; + let tx_response = wallet.broadcast_tx(tx).await?; log::debug!(target: &transaction_target(), "TX broadcast response: {:?}", tx_response); assert_broadcast_code_response(tx_response) diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index 83ab68181..574311b9f 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -10,7 +10,6 @@ use cosmrs::{ Any, Coin, }; use cw_orch_core::log::transaction_target; -use tonic::transport::Channel; use crate::sender::SenderOptions; @@ -26,21 +25,18 @@ pub struct TxBuilder { pub(crate) gas_limit: Option, // if defined, use this sequence, else get it from the node pub(crate) sequence: Option, - // Channel over which tx is sent - pub(crate) channel: Channel, /// `chain_id` is the unique identifier of the chain this transaction targets. pub(crate) chain_id: String, } impl TxBuilder { /// Create a new TxBuilder with a given body. - pub fn new(body: Body, channel: Channel, chain_id: String) -> Self { + pub fn new(body: Body, chain_id: String) -> Self { Self { body, fee_amount: None, gas_limit: None, sequence: None, - channel, chain_id, } } @@ -91,13 +87,13 @@ impl TxBuilder { account_number, sequence, .. - } = wallet.base_account(self.channel.clone()).await?; + } = wallet.base_account().await?; // overwrite sequence if set (can be used for concurrent txs) let sequence = self.sequence.unwrap_or(sequence); wallet - .calculate_gas(self.channel.clone(), &self.body, sequence, account_number) + .calculate_gas(&self.body, sequence, account_number) .await } @@ -109,7 +105,7 @@ impl TxBuilder { account_number, sequence, .. - } = wallet.base_account(self.channel.clone()).await?; + } = wallet.base_account().await?; // overwrite sequence if set (can be used for concurrent txs) let sequence = self.sequence.unwrap_or(sequence); @@ -127,7 +123,7 @@ impl TxBuilder { (fee, gas_limit) } else { let sim_gas_used = wallet - .calculate_gas(self.channel.clone(), &self.body, sequence, account_number) + .calculate_gas(&self.body, sequence, account_number) .await?; log::debug!(target: &transaction_target(), "Simulated gas needed {:?}", sim_gas_used); diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 830cf1be8..9b0c3637c 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -4,85 +4,85 @@ use tokio::runtime::Runtime; const TEST_STATE_FILE: &str = "./tests/test.json"; -#[test] -fn simultaneous_read() { - let runtime = Runtime::new().unwrap(); +// #[test] +// fn simultaneous_read() { +// let runtime = Runtime::new().unwrap(); - let chain_data = JUNO_1.into(); - std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); +// let chain_data = JUNO_1.into(); +// std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - let daemon_state = runtime - .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) - .unwrap(); - daemon_state.set("test", "test", "test").unwrap(); +// let daemon_state = runtime +// .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) +// .unwrap(); +// daemon_state.set("test", "test", "test").unwrap(); - let mut handles = vec![]; - for _ in 0..25 { - let daemon_state = daemon_state.clone(); - let handle = std::thread::spawn(move || daemon_state.get("test").unwrap()); - handles.push(handle); - } +// let mut handles = vec![]; +// for _ in 0..25 { +// let daemon_state = daemon_state.clone(); +// let handle = std::thread::spawn(move || daemon_state.get("test").unwrap()); +// handles.push(handle); +// } - let mut maybe_err = Ok(serde_json::Value::default()); +// let mut maybe_err = Ok(serde_json::Value::default()); - for handle in handles { - let result = handle.join(); - if result.is_err() { - maybe_err = result; - } - } - // Error if at least one failed - let _ = maybe_err.unwrap(); -} +// for handle in handles { +// let result = handle.join(); +// if result.is_err() { +// maybe_err = result; +// } +// } +// // Error if at least one failed +// let _ = maybe_err.unwrap(); +// } -#[test] -fn simultaneous_write() { - let runtime = Runtime::new().unwrap(); +// #[test] +// fn simultaneous_write() { +// let runtime = Runtime::new().unwrap(); - let chain_data = JUNO_1.into(); - std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); +// let chain_data = JUNO_1.into(); +// std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - let daemon_state = runtime - .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) - .unwrap(); +// let daemon_state = runtime +// .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) +// .unwrap(); - let mut handles = vec![]; - for i in 0..25 { - let daemon_state = daemon_state.clone(); - let handle = std::thread::spawn(move || { - daemon_state - .set("test", &format!("test{i}"), format!("test-{i}")) - .unwrap(); - }); - handles.push(handle); - } +// let mut handles = vec![]; +// for i in 0..25 { +// let daemon_state = daemon_state.clone(); +// let handle = std::thread::spawn(move || { +// daemon_state +// .set("test", &format!("test{i}"), format!("test-{i}")) +// .unwrap(); +// }); +// handles.push(handle); +// } - let mut maybe_err = Ok(()); - // Finish all handles - for handle in handles { - let result = handle.join(); - if result.is_err() { - maybe_err = result; - } - } - // Error if at least one failed - maybe_err.unwrap() -} +// let mut maybe_err = Ok(()); +// // Finish all handles +// for handle in handles { +// let result = handle.join(); +// if result.is_err() { +// maybe_err = result; +// } +// } +// // Error if at least one failed +// maybe_err.unwrap() +// } -#[test] -#[should_panic] -fn panic_when_someone_else_holds_it() { - match unsafe { nix::unistd::fork() } { - Ok(nix::unistd::ForkResult::Child) => { - // Occur lock for file for 100 millis - let _state = JsonLockedState::new(TEST_STATE_FILE); - std::thread::sleep(std::time::Duration::from_millis(100)); - } - Ok(nix::unistd::ForkResult::Parent { .. }) => { - // Wait a bit for child to occur lock and try to lock already locked file by child - std::thread::sleep(std::time::Duration::from_millis(50)); - let _state = JsonLockedState::new(TEST_STATE_FILE); - } - Err(_) => (), - } -} +// #[test] +// #[should_panic] +// fn panic_when_someone_else_holds_it() { +// match unsafe { nix::unistd::fork() } { +// Ok(nix::unistd::ForkResult::Child) => { +// // Occur lock for file for 100 millis +// let _state = JsonLockedState::new(TEST_STATE_FILE); +// std::thread::sleep(std::time::Duration::from_millis(100)); +// } +// Ok(nix::unistd::ForkResult::Parent { .. }) => { +// // Wait a bit for child to occur lock and try to lock already locked file by child +// std::thread::sleep(std::time::Duration::from_millis(50)); +// let _state = JsonLockedState::new(TEST_STATE_FILE); +// } +// Err(_) => (), +// } +// } From 2c9b04009f711e3aaa93b5aaca149f64f6785123 Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 7 May 2024 16:01:58 +0300 Subject: [PATCH 016/108] add some tests --- cw-orch-daemon/src/builder.rs | 10 +- cw-orch-daemon/src/state.rs | 2 +- cw-orch-daemon/tests/daemon_state.rs | 247 ++++++++++++++++++--------- 3 files changed, 169 insertions(+), 90 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 3e7642833..ed37ec292 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -135,10 +135,12 @@ impl DaemonAsyncBuilder { .clone() .unwrap_or(DaemonState::state_file_path()?); - Arc::new(Mutex::new( - DaemonState::new(json_file_path, chain_info.clone(), deployment_id, false) - .await?, - )) + Arc::new(Mutex::new(DaemonState::new( + json_file_path, + chain_info.clone(), + deployment_id, + false, + )?)) } }; // if mnemonic provided, use it. Else use env variables to retrieve mnemonic diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index d2bddee66..afe56b26c 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -47,7 +47,7 @@ enum DaemonStateFile { impl DaemonState { /// Creates a new state from the given chain data and deployment id. /// Attempts to connect to any of the provided gRPC endpoints. - pub async fn new( + pub fn new( mut json_file_path: String, chain_data: ChainInfoOwned, deployment_id: String, diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 9b0c3637c..faf9a9f9f 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -1,88 +1,165 @@ -use cw_orch_daemon::{env::STATE_FILE_ENV_NAME, json_lock::JsonLockedState, DaemonState}; -use cw_orch_networks::networks::JUNO_1; -use tokio::runtime::Runtime; +use std::sync::Arc; +use cw_orch_core::environment::ChainState; +use cw_orch_daemon::{json_lock::JsonLockedState, networks::OSMOSIS_1, DaemonBuilder, DaemonError}; + +pub const DUMMY_MNEMONIC:&str = "chapter wrist alcohol shine angry noise mercy simple rebel recycle vehicle wrap morning giraffe lazy outdoor noise blood ginger sort reunion boss crowd dutch"; const TEST_STATE_FILE: &str = "./tests/test.json"; -// #[test] -// fn simultaneous_read() { -// let runtime = Runtime::new().unwrap(); - -// let chain_data = JUNO_1.into(); -// std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - -// let daemon_state = runtime -// .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) -// .unwrap(); -// daemon_state.set("test", "test", "test").unwrap(); - -// let mut handles = vec![]; -// for _ in 0..25 { -// let daemon_state = daemon_state.clone(); -// let handle = std::thread::spawn(move || daemon_state.get("test").unwrap()); -// handles.push(handle); -// } - -// let mut maybe_err = Ok(serde_json::Value::default()); - -// for handle in handles { -// let result = handle.join(); -// if result.is_err() { -// maybe_err = result; -// } -// } -// // Error if at least one failed -// let _ = maybe_err.unwrap(); -// } - -// #[test] -// fn simultaneous_write() { -// let runtime = Runtime::new().unwrap(); - -// let chain_data = JUNO_1.into(); -// std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - -// let daemon_state = runtime -// .block_on(DaemonState::new(chain_data, "test".to_owned(), false)) -// .unwrap(); - -// let mut handles = vec![]; -// for i in 0..25 { -// let daemon_state = daemon_state.clone(); -// let handle = std::thread::spawn(move || { -// daemon_state -// .set("test", &format!("test{i}"), format!("test-{i}")) -// .unwrap(); -// }); -// handles.push(handle); -// } - -// let mut maybe_err = Ok(()); -// // Finish all handles -// for handle in handles { -// let result = handle.join(); -// if result.is_err() { -// maybe_err = result; -// } -// } -// // Error if at least one failed -// maybe_err.unwrap() -// } - -// #[test] -// #[should_panic] -// fn panic_when_someone_else_holds_it() { -// match unsafe { nix::unistd::fork() } { -// Ok(nix::unistd::ForkResult::Child) => { -// // Occur lock for file for 100 millis -// let _state = JsonLockedState::new(TEST_STATE_FILE); -// std::thread::sleep(std::time::Duration::from_millis(100)); -// } -// Ok(nix::unistd::ForkResult::Parent { .. }) => { -// // Wait a bit for child to occur lock and try to lock already locked file by child -// std::thread::sleep(std::time::Duration::from_millis(50)); -// let _state = JsonLockedState::new(TEST_STATE_FILE); -// } -// Err(_) => (), -// } -// } +#[test] +#[serial_test::serial] +fn simultaneous_read() { + let daemon = DaemonBuilder::default() + .chain(OSMOSIS_1) + .mnemonic(DUMMY_MNEMONIC) + .state_path(TEST_STATE_FILE) + .build() + .unwrap(); + + // Write to state something, don't forget to drop lock to avoid deadlock + let mut daemon_state = daemon.daemon.state.lock().unwrap(); + daemon_state.set("test", "test", "test").unwrap(); + drop(daemon_state); + + let mut handles = vec![]; + for _ in 0..25 { + let daemon_state = daemon.state(); + let handle = std::thread::spawn(move || { + // Just make sure it outputs > 2 so we know state is shared + let strong_count = Arc::strong_count(&daemon_state); + dbg!(strong_count); + + let state_lock = daemon_state.lock().unwrap(); + state_lock.get("test").unwrap() + }); + handles.push(handle); + } + + let mut maybe_err = Ok(serde_json::Value::default()); + + for handle in handles { + let result = handle.join(); + if result.is_err() { + maybe_err = result; + } + } + // Error if at least one failed + let _ = maybe_err.unwrap(); +} + +#[test] +#[serial_test::serial] +fn simultaneous_write() { + let daemon = DaemonBuilder::default() + .chain(OSMOSIS_1) + .mnemonic(DUMMY_MNEMONIC) + .state_path(TEST_STATE_FILE) + .build() + .unwrap(); + + let mut handles = vec![]; + for i in 0..25 { + let daemon_state = daemon.state(); + let handle = std::thread::spawn(move || { + // Just make sure it outputs > 2 so we know state is shared + let strong_count = Arc::strong_count(&daemon_state); + dbg!(strong_count); + let mut state_lock = daemon_state.lock().unwrap(); + state_lock + .set("test", &format!("test{i}"), format!("test-{i}")) + .unwrap(); + }); + handles.push(handle); + } + + let mut maybe_err = Ok(()); + // Finish all handles + for handle in handles { + let result = handle.join(); + if result.is_err() { + maybe_err = result; + } + } + // Error if at least one failed + maybe_err.unwrap(); +} + +#[test] +#[serial_test::serial] +fn simultaneous_write_rebuilt() { + let daemon = DaemonBuilder::default() + .chain(OSMOSIS_1) + .mnemonic(DUMMY_MNEMONIC) + .state_path(TEST_STATE_FILE) + .build() + .unwrap(); + + let mut handles = vec![]; + // Note this one has lower iterations since it rebuild is pretty long process + for i in 0..10 { + let daemon = daemon.rebuild().build().unwrap(); + let daemon_state = daemon.state(); + let handle = std::thread::spawn(move || { + // Just make sure it outputs > 2 so we know state is shared + let strong_count = Arc::strong_count(&daemon_state); + dbg!(strong_count); + let mut state_lock = daemon_state.lock().unwrap(); + state_lock + .set("test", &format!("test{i}"), format!("test-{i}")) + .unwrap(); + }); + handles.push(handle); + } + + let mut maybe_err = Ok(()); + // Finish all handles + for handle in handles { + let result = handle.join(); + if result.is_err() { + maybe_err = result; + } + } + // Error if at least one failed + maybe_err.unwrap() +} + +#[test] +#[serial_test::serial] +#[should_panic] +fn panic_when_someone_else_json_file() { + match unsafe { nix::unistd::fork() } { + Ok(nix::unistd::ForkResult::Child) => { + // Occur lock for file for 100 millis + let _state = JsonLockedState::new(TEST_STATE_FILE); + std::thread::sleep(std::time::Duration::from_millis(100)); + } + Ok(nix::unistd::ForkResult::Parent { .. }) => { + // Wait a bit for child to occur lock and try to lock already locked file by child + std::thread::sleep(std::time::Duration::from_millis(50)); + let _state = JsonLockedState::new(TEST_STATE_FILE); + } + Err(_) => (), + } +} + +#[test] +#[serial_test::serial] +fn error_when_another_daemon_holds_it() { + let _daemon = DaemonBuilder::default() + .chain(OSMOSIS_1) + .mnemonic(DUMMY_MNEMONIC) + .state_path(TEST_STATE_FILE) + .build() + .unwrap(); + + let daemon_res = DaemonBuilder::default() + .chain(OSMOSIS_1) + .mnemonic(DUMMY_MNEMONIC) + .state_path(TEST_STATE_FILE) + .build(); + + assert!(matches!( + daemon_res, + Err(DaemonError::StateAlreadyLocked(_)) + )); +} From b8259e816050954c7e0c68890dc7cc0ccb9dac0a Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 7 May 2024 16:06:02 +0300 Subject: [PATCH 017/108] remove unused code --- .../cw-orch-core/src/environment/state.rs | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/packages/cw-orch-core/src/environment/state.rs b/packages/cw-orch-core/src/environment/state.rs index f55fe67df..b4ce304a1 100644 --- a/packages/cw-orch-core/src/environment/state.rs +++ b/packages/cw-orch-core/src/environment/state.rs @@ -65,32 +65,6 @@ impl StateInterface for Rc> { } } -// impl StateInterface for Rc { -// fn get_address(&self, contract_id: &str) -> Result { -// (**self).get_address(contract_id) -// } - -// fn set_address(&mut self, contract_id: &str, address: &Addr) { -// (*Rc::make_mut(self)).set_address(contract_id, address) -// } - -// fn get_code_id(&self, contract_id: &str) -> Result { -// (**self).get_code_id(contract_id) -// } - -// fn set_code_id(&mut self, contract_id: &str, code_id: u64) { -// (*Rc::make_mut(self)).set_code_id(contract_id, code_id) -// } - -// fn get_all_addresses(&self) -> Result, CwEnvError> { -// (**self).get_all_addresses() -// } - -// fn get_all_code_ids(&self) -> Result, CwEnvError> { -// (**self).get_all_code_ids() -// } -// } - // TODO: error handling impl StateInterface for Arc> { fn get_address(&self, contract_id: &str) -> Result { From fafd27ab9569ae45fa487d5cd0a799b2494d695b Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 7 May 2024 16:45:23 +0300 Subject: [PATCH 018/108] fix tests --- cw-orch-daemon/src/queriers/env.rs | 2 +- cw-orch-daemon/src/state.rs | 10 +++++++--- cw-orch-daemon/tests/authz.rs | 4 ++-- cw-orch-daemon/tests/daemon_helpers.rs | 6 ++++++ cw-orch-daemon/tests/daemon_state.rs | 2 +- cw-orch-daemon/tests/querier.rs | 3 +++ 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index a95f1c6ce..565a565b0 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -10,7 +10,7 @@ impl EnvironmentQuerier for Daemon { EnvironmentInfo { chain_id: chain_info.chain_id.to_string(), chain_name: chain_info.network_info.chain_name.to_string(), - deployment_id: locked_state.deployment_id.clone(), + deployment_id: locked_state.deployment_id(), } } } diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index afe56b26c..24643290b 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -22,11 +22,11 @@ pub(crate) static LOCKED_FILES: Lazy>> = pub struct DaemonState { json_state: DaemonStateFile, /// Deployment identifier - pub deployment_id: String, + deployment_id: String, /// Chain Id - pub chain_id: String, + chain_id: String, /// Chain Name - pub chain_name: String, + chain_name: String, } impl Drop for DaemonState { @@ -105,6 +105,10 @@ impl DaemonState { }) } + pub fn deployment_id(&self) -> String { + self.deployment_id.clone() + } + /// Returns the path of the file where the state of `cw-orchestrator` is stored. pub fn state_file_path() -> Result { // check if STATE_FILE en var is configured, default to state.json diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index f5e27a5f3..c2a662796 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -35,8 +35,8 @@ mod tests { let sender = daemon.sender().to_string(); - let second_daemon = Daemon::builder() - .chain(networks::LOCAL_JUNO) + let second_daemon = daemon + .rebuild() .authz_granter(sender.clone()) .mnemonic(SECOND_MNEMONIC) .build() diff --git a/cw-orch-daemon/tests/daemon_helpers.rs b/cw-orch-daemon/tests/daemon_helpers.rs index 7a2769323..bbd9a4bbc 100644 --- a/cw-orch-daemon/tests/daemon_helpers.rs +++ b/cw-orch-daemon/tests/daemon_helpers.rs @@ -85,6 +85,9 @@ mod tests { asserting!("code_id is ok") .that(&contract.code_id()) .is_ok(); + + // Don't write to the file to make it reproducible + std::mem::forget(daemon); } // #[test] @@ -162,5 +165,8 @@ mod tests { asserting!("that upload_if_needed returns None") .that(&contract.upload_if_needed().unwrap()) .is_none(); + + // Don't write to the file to make it reproducible + std::mem::forget(daemon); } } diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index faf9a9f9f..398792e0b 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -126,7 +126,7 @@ fn simultaneous_write_rebuilt() { #[test] #[serial_test::serial] #[should_panic] -fn panic_when_someone_else_json_file() { +fn panic_when_someone_holds_json_file() { match unsafe { nix::unistd::fork() } { Ok(nix::unistd::ForkResult::Child) => { // Occur lock for file for 100 millis diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index a5d94d813..cb04f411c 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -238,6 +238,9 @@ mod queriers { asserting!("contract info is ok") .that(&contract_info) .is_ok(); + + // Don't write to the file to make it reproducible + std::mem::forget(daemon); } fn parse_cw_coins(coins: &[cosmwasm_std::Coin]) -> Result, DaemonError> { From d4f8e46a25e220e8d4fe5ac3fb5dfc158473b1bf Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 7 May 2024 16:52:40 +0300 Subject: [PATCH 019/108] add error handling --- packages/cw-orch-core/src/environment/state.rs | 13 ++++++------- packages/cw-orch-core/src/error.rs | 2 ++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/cw-orch-core/src/environment/state.rs b/packages/cw-orch-core/src/environment/state.rs index b4ce304a1..0a1457b17 100644 --- a/packages/cw-orch-core/src/environment/state.rs +++ b/packages/cw-orch-core/src/environment/state.rs @@ -65,35 +65,34 @@ impl StateInterface for Rc> { } } -// TODO: error handling impl StateInterface for Arc> { fn get_address(&self, contract_id: &str) -> Result { - let locked_state = self.lock().unwrap(); + let locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {})?; locked_state.get_address(contract_id) } fn set_address(&mut self, contract_id: &str, address: &Addr) { - let mut locked_state = self.lock().unwrap(); + let mut locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {}).unwrap(); locked_state.set_address(contract_id, address) } fn get_code_id(&self, contract_id: &str) -> Result { - let locked_state = self.lock().unwrap(); + let locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {})?; locked_state.get_code_id(contract_id) } fn set_code_id(&mut self, contract_id: &str, code_id: u64) { - let mut locked_state = self.lock().unwrap(); + let mut locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {}).unwrap(); locked_state.set_code_id(contract_id, code_id) } fn get_all_addresses(&self) -> Result, CwEnvError> { - let locked_state = self.lock().unwrap(); + let locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {})?; locked_state.get_all_addresses() } fn get_all_code_ids(&self) -> Result, CwEnvError> { - let locked_state = self.lock().unwrap(); + let locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {})?; locked_state.get_all_code_ids() } } diff --git a/packages/cw-orch-core/src/error.rs b/packages/cw-orch-core/src/error.rs index 4890170f3..730963b3c 100644 --- a/packages/cw-orch-core/src/error.rs +++ b/packages/cw-orch-core/src/error.rs @@ -44,6 +44,8 @@ pub enum CwEnvError { StdErr(String), #[error("Environment variable not defined {0}")] EnvVarNotPresentNamed(String), + #[error("Poisoned lock: another state task failed inside")] + PoisonError {}, } impl CwEnvError { From d8d44ca06aedea0dc6ff0577e33ff102b282ebaa Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 7 May 2024 16:56:28 +0300 Subject: [PATCH 020/108] add drop tests --- cw-orch-daemon/tests/daemon_state.rs | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 398792e0b..018e8983d 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -5,6 +5,7 @@ use cw_orch_daemon::{json_lock::JsonLockedState, networks::OSMOSIS_1, DaemonBuil pub const DUMMY_MNEMONIC:&str = "chapter wrist alcohol shine angry noise mercy simple rebel recycle vehicle wrap morning giraffe lazy outdoor noise blood ginger sort reunion boss crowd dutch"; const TEST_STATE_FILE: &str = "./tests/test.json"; +const TEST2_STATE_FILE: &str = "./tests/test2.json"; #[test] #[serial_test::serial] @@ -163,3 +164,44 @@ fn error_when_another_daemon_holds_it() { Err(DaemonError::StateAlreadyLocked(_)) )); } + +#[test] +#[serial_test::serial] +fn does_not_error_when_previous_daemon_dropped_state() { + let daemon = DaemonBuilder::default() + .chain(OSMOSIS_1) + .mnemonic(DUMMY_MNEMONIC) + .state_path(TEST_STATE_FILE) + .build() + .unwrap(); + + drop(daemon); + + let daemon_res = DaemonBuilder::default() + .chain(OSMOSIS_1) + .mnemonic(DUMMY_MNEMONIC) + .state_path(TEST_STATE_FILE) + .build(); + + assert!(daemon_res.is_ok(),); +} + +#[test] +#[serial_test::serial] +fn does_not_error_when_using_different_files() { + let daemon = DaemonBuilder::default() + .chain(OSMOSIS_1) + .mnemonic(DUMMY_MNEMONIC) + .state_path(TEST_STATE_FILE) + .build() + .unwrap(); + + let daemon_res = DaemonBuilder::default() + .chain(OSMOSIS_1) + .mnemonic(DUMMY_MNEMONIC) + // Different file + .state_path(TEST2_STATE_FILE) + .build(); + + assert!(daemon_res.is_ok()); +} From 79a36b7c609d31be7f8f34551ad90360ebf4345b Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 7 May 2024 18:21:03 +0300 Subject: [PATCH 021/108] fix rest of the tests --- cw-orch-daemon/tests/daemon_helpers.rs | 6 ------ cw-orch-daemon/tests/daemon_state.rs | 2 +- cw-orch-daemon/tests/index.rs | 8 ++++---- cw-orch-daemon/tests/instantiate2.rs | 1 + cw-orch-daemon/tests/querier.rs | 3 --- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/cw-orch-daemon/tests/daemon_helpers.rs b/cw-orch-daemon/tests/daemon_helpers.rs index bbd9a4bbc..7a2769323 100644 --- a/cw-orch-daemon/tests/daemon_helpers.rs +++ b/cw-orch-daemon/tests/daemon_helpers.rs @@ -85,9 +85,6 @@ mod tests { asserting!("code_id is ok") .that(&contract.code_id()) .is_ok(); - - // Don't write to the file to make it reproducible - std::mem::forget(daemon); } // #[test] @@ -165,8 +162,5 @@ mod tests { asserting!("that upload_if_needed returns None") .that(&contract.upload_if_needed().unwrap()) .is_none(); - - // Don't write to the file to make it reproducible - std::mem::forget(daemon); } } diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 018e8983d..d1184c148 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -189,7 +189,7 @@ fn does_not_error_when_previous_daemon_dropped_state() { #[test] #[serial_test::serial] fn does_not_error_when_using_different_files() { - let daemon = DaemonBuilder::default() + let _daemon = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .state_path(TEST_STATE_FILE) diff --git a/cw-orch-daemon/tests/index.rs b/cw-orch-daemon/tests/index.rs index a468847d9..0aca3ab39 100644 --- a/cw-orch-daemon/tests/index.rs +++ b/cw-orch-daemon/tests/index.rs @@ -15,16 +15,16 @@ mod tests { .build() .unwrap(); + let daemon_sender = daemon.sender().to_string(); + // TODO: Should be using rebuild here, see ORC-127 + drop(daemon); let indexed_daemon = Daemon::builder() .chain(networks::LOCAL_JUNO) .hd_index(56) .build() .unwrap(); - assert_ne!( - daemon.sender().to_string(), - indexed_daemon.sender().to_string() - ); + assert_ne!(daemon_sender, indexed_daemon.sender().to_string()); Ok(()) } diff --git a/cw-orch-daemon/tests/instantiate2.rs b/cw-orch-daemon/tests/instantiate2.rs index 30c30a0e0..72c9acbc1 100644 --- a/cw-orch-daemon/tests/instantiate2.rs +++ b/cw-orch-daemon/tests/instantiate2.rs @@ -13,6 +13,7 @@ pub mod test { use mock_contract::MockContract; #[test] + #[serial_test::serial] fn instantiate2() -> anyhow::Result<()> { let app = Daemon::builder() .chain(networks::LOCAL_JUNO) diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index cb04f411c..a5d94d813 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -238,9 +238,6 @@ mod queriers { asserting!("contract info is ok") .that(&contract_info) .is_ok(); - - // Don't write to the file to make it reproducible - std::mem::forget(daemon); } fn parse_cw_coins(coins: &[cosmwasm_std::Coin]) -> Result, DaemonError> { From d84d515bdea665d49a3ae77bc228eece0c2717e3 Mon Sep 17 00:00:00 2001 From: Buckram Date: Thu, 9 May 2024 11:55:46 +0300 Subject: [PATCH 022/108] apply Robin review --- cw-orch-daemon/src/builder.rs | 11 ++---- cw-orch-daemon/src/core.rs | 7 ++-- cw-orch-daemon/src/queriers/env.rs | 15 ++++---- cw-orch-daemon/src/state.rs | 50 +++++++++++++++----------- cw-orch-daemon/src/sync/builder.rs | 4 +-- cw-orch-daemon/src/sync/core.rs | 9 ++--- cw-orch-daemon/tests/daemon_state.rs | 54 ++++++++++++++++------------ 7 files changed, 81 insertions(+), 69 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index ed37ec292..02c6c6782 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -3,7 +3,7 @@ use crate::{ sender::{SenderBuilder, SenderOptions}, DaemonAsync, DaemonBuilder, GrpcChannel, }; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use bitcoin::secp256k1::All; @@ -41,7 +41,7 @@ pub struct DaemonAsyncBuilder { pub(crate) sender_options: SenderOptions, /* Rebuilder related options */ - pub(crate) state: Option>>, + pub(crate) state: Option, } impl DaemonAsyncBuilder { @@ -135,12 +135,7 @@ impl DaemonAsyncBuilder { .clone() .unwrap_or(DaemonState::state_file_path()?); - Arc::new(Mutex::new(DaemonState::new( - json_file_path, - chain_info.clone(), - deployment_id, - false, - )?)) + DaemonState::new(json_file_path, chain_info.clone(), deployment_id, false)? } }; // if mnemonic provided, use it. Else use env variables to retrieve mnemonic diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index f93b34b81..c4423caff 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -25,7 +25,6 @@ use std::{ fmt::Debug, io::Write, str::{from_utf8, FromStr}, - sync::{Arc, Mutex}, time::Duration, }; @@ -67,7 +66,7 @@ pub struct DaemonAsync { /// Sender to send transactions to the chain pub sender: Wallet, /// State of the daemon - pub state: Arc>, + pub state: DaemonState, } impl DaemonAsync { @@ -83,10 +82,10 @@ impl DaemonAsync { } impl ChainState for DaemonAsync { - type Out = Arc>; + type Out = DaemonState; fn state(&self) -> Self::Out { - Arc::clone(&self.state) + self.state.clone() } } diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index 565a565b0..ef6c4195b 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -4,13 +4,16 @@ use crate::Daemon; impl EnvironmentQuerier for Daemon { fn env_info(&self) -> EnvironmentInfo { - let chain_info = &self.daemon.sender.chain_info; - let locked_state = self.daemon.state.lock().unwrap(); - EnvironmentInfo { - chain_id: chain_info.chain_id.to_string(), - chain_name: chain_info.network_info.chain_name.to_string(), - deployment_id: locked_state.deployment_id(), + chain_id: self.daemon.sender.chain_info.chain_id.clone(), + chain_name: self + .daemon + .sender + .chain_info + .network_info + .chain_name + .clone(), + deployment_id: self.daemon.state.deployment_id.clone(), } } } diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 24643290b..b8b6bc57c 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -8,8 +8,12 @@ use cw_orch_core::{environment::StateInterface, log::local_target, CwEnvError}; use once_cell::sync::Lazy; use serde::Serialize; use serde_json::{json, Value}; -use std::collections::HashSet; -use std::{collections::HashMap, path::Path, sync::Mutex}; +use std::sync::Arc; +use std::{ + collections::{HashMap, HashSet}, + path::Path, + sync::Mutex, +}; /// Global state to track which files are already open by other daemons from other threads /// This is necessary because File lock will allow same process to lock file how many times as process wants @@ -18,30 +22,35 @@ pub(crate) static LOCKED_FILES: Lazy>> = /// Stores the chain information and deployment state. /// Uses a simple JSON file to store the deployment information locally. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DaemonState { - json_state: DaemonStateFile, + pub json_state: DaemonStateFile, /// Deployment identifier - deployment_id: String, + pub deployment_id: String, /// Chain Id - chain_id: String, + pub chain_id: String, /// Chain Name - chain_name: String, + pub chain_name: String, } impl Drop for DaemonState { fn drop(&mut self) { if let DaemonStateFile::FullAccess { json_file_state } = &self.json_state { - let mut lock = LOCKED_FILES.lock().unwrap(); - lock.remove(json_file_state.path()); + let json_lock = json_file_state.lock().unwrap(); + let mut locked_files = LOCKED_FILES.lock().unwrap(); + locked_files.remove(json_lock.path()); } } } -#[derive(Debug)] -enum DaemonStateFile { - ReadOnly { path: String }, - FullAccess { json_file_state: JsonLockedState }, +#[derive(Debug, Clone)] +pub enum DaemonStateFile { + ReadOnly { + path: String, + }, + FullAccess { + json_file_state: Arc>, + }, } impl DaemonState { @@ -94,7 +103,9 @@ impl DaemonState { drop(lock); json_file_state.prepare(&chain_id, &chain_name, &deployment_id); - DaemonStateFile::FullAccess { json_file_state } + DaemonStateFile::FullAccess { + json_file_state: Arc::new(Mutex::new(json_file_state)), + } }; Ok(DaemonState { @@ -105,10 +116,6 @@ impl DaemonState { }) } - pub fn deployment_id(&self) -> String { - self.deployment_id.clone() - } - /// Returns the path of the file where the state of `cw-orchestrator` is stored. pub fn state_file_path() -> Result { // check if STATE_FILE en var is configured, default to state.json @@ -153,6 +160,8 @@ impl DaemonState { j[&self.chain_id][&self.chain_name].clone() } DaemonStateFile::FullAccess { json_file_state } => json_file_state + .lock() + .unwrap() .get(&self.chain_id, &self.chain_name) .clone(), }; @@ -179,7 +188,8 @@ impl DaemonState { DaemonStateFile::FullAccess { json_file_state } => json_file_state, }; - let val = json_file_state.get_mut(&self.chain_id, &self.chain_name); + let mut json_file_lock = json_file_state.lock().unwrap(); + let val = json_file_lock.get_mut(&self.chain_id, &self.chain_name); val[key][contract_id] = json!(value); Ok(()) @@ -193,7 +203,7 @@ impl DaemonState { } DaemonStateFile::FullAccess { json_file_state } => json_file_state, }; - json_file_state.force_write(); + json_file_state.lock().unwrap().force_write(); Ok(()) } } diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 82fa303f4..0f4001736 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,5 +1,3 @@ -use std::sync::{Arc, Mutex}; - use crate::{ sender::{Sender, SenderBuilder, SenderOptions}, DaemonAsyncBuilder, @@ -40,7 +38,7 @@ pub struct DaemonBuilder { pub(crate) sender_options: SenderOptions, /* Rebuilder related options */ - pub(crate) state: Option>>, + pub(crate) state: Option, } impl DaemonBuilder { diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index aa37baa53..75f91b103 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -1,7 +1,4 @@ -use std::{ - fmt::Debug, - sync::{Arc, Mutex}, -}; +use std::fmt::Debug; use super::super::{sender::Wallet, DaemonAsync}; use crate::{ @@ -81,10 +78,10 @@ impl Daemon { } impl ChainState for Daemon { - type Out = Arc>; + type Out = DaemonState; fn state(&self) -> Self::Out { - Arc::clone(&self.daemon.state) + self.daemon.state.clone() } } diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index d1184c148..1f5ba3036 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -1,7 +1,9 @@ use std::sync::Arc; use cw_orch_core::environment::ChainState; -use cw_orch_daemon::{json_lock::JsonLockedState, networks::OSMOSIS_1, DaemonBuilder, DaemonError}; +use cw_orch_daemon::{ + json_lock::JsonLockedState, networks::OSMOSIS_1, DaemonBuilder, DaemonError, DaemonStateFile, +}; pub const DUMMY_MNEMONIC:&str = "chapter wrist alcohol shine angry noise mercy simple rebel recycle vehicle wrap morning giraffe lazy outdoor noise blood ginger sort reunion boss crowd dutch"; const TEST_STATE_FILE: &str = "./tests/test.json"; @@ -17,8 +19,8 @@ fn simultaneous_read() { .build() .unwrap(); - // Write to state something, don't forget to drop lock to avoid deadlock - let mut daemon_state = daemon.daemon.state.lock().unwrap(); + // Write to state something + let mut daemon_state = daemon.state(); daemon_state.set("test", "test", "test").unwrap(); drop(daemon_state); @@ -26,12 +28,14 @@ fn simultaneous_read() { for _ in 0..25 { let daemon_state = daemon.state(); let handle = std::thread::spawn(move || { - // Just make sure it outputs > 2 so we know state is shared - let strong_count = Arc::strong_count(&daemon_state); - dbg!(strong_count); - - let state_lock = daemon_state.lock().unwrap(); - state_lock.get("test").unwrap() + if let DaemonStateFile::FullAccess { json_file_state } = &daemon_state.json_state { + // Just make sure it outputs > 2 so we know state is shared + let strong_count = Arc::strong_count(json_file_state); + dbg!(strong_count); + } else { + unreachable!("It's full access daemon"); + } + daemon_state.get("test").unwrap() }); handles.push(handle); } @@ -60,13 +64,16 @@ fn simultaneous_write() { let mut handles = vec![]; for i in 0..25 { - let daemon_state = daemon.state(); + let mut daemon_state = daemon.state(); let handle = std::thread::spawn(move || { - // Just make sure it outputs > 2 so we know state is shared - let strong_count = Arc::strong_count(&daemon_state); - dbg!(strong_count); - let mut state_lock = daemon_state.lock().unwrap(); - state_lock + if let DaemonStateFile::FullAccess { json_file_state } = &daemon_state.json_state { + // Just make sure it outputs > 2 so we know state is shared + let strong_count = Arc::strong_count(json_file_state); + dbg!(strong_count); + } else { + unreachable!("It's full access daemon"); + } + daemon_state .set("test", &format!("test{i}"), format!("test-{i}")) .unwrap(); }); @@ -96,16 +103,19 @@ fn simultaneous_write_rebuilt() { .unwrap(); let mut handles = vec![]; - // Note this one has lower iterations since it rebuild is pretty long process + // Note this one has lower iterations since rebuild is pretty long process for i in 0..10 { let daemon = daemon.rebuild().build().unwrap(); - let daemon_state = daemon.state(); + let mut daemon_state = daemon.state(); let handle = std::thread::spawn(move || { - // Just make sure it outputs > 2 so we know state is shared - let strong_count = Arc::strong_count(&daemon_state); - dbg!(strong_count); - let mut state_lock = daemon_state.lock().unwrap(); - state_lock + if let DaemonStateFile::FullAccess { json_file_state } = &daemon_state.json_state { + // Just make sure it outputs > 2 so we know state is shared + let strong_count = Arc::strong_count(json_file_state); + dbg!(strong_count); + } else { + unreachable!("It's full access daemon"); + } + daemon_state .set("test", &format!("test{i}"), format!("test-{i}")) .unwrap(); }); From e923332d10f16c5fa1bbcced23d0a56c40815202 Mon Sep 17 00:00:00 2001 From: Buckram Date: Thu, 9 May 2024 11:58:14 +0300 Subject: [PATCH 023/108] restore cw-orch-core --- .../cw-orch-core/src/environment/state.rs | 55 ++++++++++++------- packages/cw-orch-core/src/error.rs | 2 - 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/packages/cw-orch-core/src/environment/state.rs b/packages/cw-orch-core/src/environment/state.rs index 0a1457b17..e75db0ab8 100644 --- a/packages/cw-orch-core/src/environment/state.rs +++ b/packages/cw-orch-core/src/environment/state.rs @@ -2,12 +2,7 @@ use crate::error::CwEnvError; use cosmwasm_std::Addr; -use std::{ - cell::RefCell, - collections::HashMap, - rc::Rc, - sync::{Arc, Mutex}, -}; +use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc}; /// State accessor trait. /// Indicates that the type has access to an underlying state. @@ -19,7 +14,7 @@ pub trait ChainState { } /// This Interface allows for managing the local state of a deployment on any CosmWasm-supported environment. -pub trait StateInterface { +pub trait StateInterface: Clone { /// Get the address of a contract using the specified contract id. fn get_address(&self, contract_id: &str) -> Result; @@ -65,34 +60,54 @@ impl StateInterface for Rc> { } } -impl StateInterface for Arc> { +impl StateInterface for Rc { fn get_address(&self, contract_id: &str) -> Result { - let locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {})?; - locked_state.get_address(contract_id) + (**self).get_address(contract_id) } fn set_address(&mut self, contract_id: &str, address: &Addr) { - let mut locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {}).unwrap(); - locked_state.set_address(contract_id, address) + (*Rc::make_mut(self)).set_address(contract_id, address) } fn get_code_id(&self, contract_id: &str) -> Result { - let locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {})?; - locked_state.get_code_id(contract_id) + (**self).get_code_id(contract_id) } fn set_code_id(&mut self, contract_id: &str, code_id: u64) { - let mut locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {}).unwrap(); - locked_state.set_code_id(contract_id, code_id) + (*Rc::make_mut(self)).set_code_id(contract_id, code_id) } fn get_all_addresses(&self) -> Result, CwEnvError> { - let locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {})?; - locked_state.get_all_addresses() + (**self).get_all_addresses() } fn get_all_code_ids(&self) -> Result, CwEnvError> { - let locked_state = self.lock().map_err(|_| CwEnvError::PoisonError {})?; - locked_state.get_all_code_ids() + (**self).get_all_code_ids() + } +} + +impl StateInterface for Arc { + fn get_address(&self, contract_id: &str) -> Result { + (**self).get_address(contract_id) + } + + fn set_address(&mut self, contract_id: &str, address: &Addr) { + (*Arc::make_mut(self)).set_address(contract_id, address) + } + + fn get_code_id(&self, contract_id: &str) -> Result { + (**self).get_code_id(contract_id) + } + + fn set_code_id(&mut self, contract_id: &str, code_id: u64) { + (*Arc::make_mut(self)).set_code_id(contract_id, code_id) + } + + fn get_all_addresses(&self) -> Result, CwEnvError> { + (**self).get_all_addresses() + } + + fn get_all_code_ids(&self) -> Result, CwEnvError> { + (**self).get_all_code_ids() } } diff --git a/packages/cw-orch-core/src/error.rs b/packages/cw-orch-core/src/error.rs index 730963b3c..4890170f3 100644 --- a/packages/cw-orch-core/src/error.rs +++ b/packages/cw-orch-core/src/error.rs @@ -44,8 +44,6 @@ pub enum CwEnvError { StdErr(String), #[error("Environment variable not defined {0}")] EnvVarNotPresentNamed(String), - #[error("Poisoned lock: another state task failed inside")] - PoisonError {}, } impl CwEnvError { From 9cdce4f608311bd957d4dd51abb6b1a8eb6ee91f Mon Sep 17 00:00:00 2001 From: Buckram Date: Thu, 9 May 2024 11:59:00 +0300 Subject: [PATCH 024/108] restore tube --- cw-orch/src/osmosis_test_tube/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw-orch/src/osmosis_test_tube/core.rs b/cw-orch/src/osmosis_test_tube/core.rs index 3b5365df4..892d21f0c 100644 --- a/cw-orch/src/osmosis_test_tube/core.rs +++ b/cw-orch/src/osmosis_test_tube/core.rs @@ -208,7 +208,7 @@ impl ChainState for OsmosisTestTube { } // Execute on the test chain, returns test response type -impl TxHandler for OsmosisTestTube { +impl TxHandler for OsmosisTestTube { type Error = CwOrchError; type ContractSource = WasmPath; type Response = AppResponse; From 6a5c1ecb00c4c5d08e758a8d64ce6910cd567e9e Mon Sep 17 00:00:00 2001 From: Kayanski Date: Mon, 13 May 2024 08:20:00 +0000 Subject: [PATCH 025/108] Continued experimentation --- .../examples/daemon-capabilities.rs | 2 +- cw-orch-daemon/src/builder.rs | 7 +- cw-orch-daemon/src/core.rs | 14 +- cw-orch-daemon/src/lib.rs | 4 +- .../src/{sender.rs => senders/base_sender.rs} | 153 +++++++++--------- cw-orch-daemon/src/senders/mod.rs | 3 + cw-orch-daemon/src/senders/querier_trait.rs | 11 ++ cw-orch-daemon/src/senders/sender_trait.rs | 45 ++++++ cw-orch-daemon/src/sync/builder.rs | 2 +- cw-orch-daemon/src/sync/core.rs | 4 +- cw-orch-daemon/src/tx_broadcaster.rs | 6 +- cw-orch-daemon/src/tx_builder.rs | 4 +- cw-orch/src/daemon.rs | 1 - 13 files changed, 164 insertions(+), 92 deletions(-) rename cw-orch-daemon/src/{sender.rs => senders/base_sender.rs} (97%) create mode 100644 cw-orch-daemon/src/senders/mod.rs create mode 100644 cw-orch-daemon/src/senders/querier_trait.rs create mode 100644 cw-orch-daemon/src/senders/sender_trait.rs diff --git a/cw-orch-daemon/examples/daemon-capabilities.rs b/cw-orch-daemon/examples/daemon-capabilities.rs index fb337557c..9f889ff0f 100644 --- a/cw-orch-daemon/examples/daemon-capabilities.rs +++ b/cw-orch-daemon/examples/daemon-capabilities.rs @@ -3,9 +3,9 @@ use std::str::FromStr; use cosmrs::{tx::Msg, AccountId, Coin, Denom}; use cosmwasm_std::coins; // ANCHOR: full_counter_example +use cw_orch_daemon::senders::sender_trait::SenderTrait; use cw_orch_daemon::DaemonBuilder; use cw_orch_networks::networks; - const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; pub fn main() -> anyhow::Result<()> { std::env::set_var("LOCAL_MNEMONIC", LOCAL_MNEMONIC); diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index dcf922d21..6945fc623 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,12 +1,12 @@ use crate::{ - log::print_if_log_disabled, sender::SenderBuilder, sender::SenderOptions, DaemonAsync, - DaemonBuilder, + log::print_if_log_disabled, senders::base_sender::SenderBuilder, + senders::base_sender::SenderOptions, DaemonAsync, DaemonBuilder, }; use std::sync::Arc; use bitcoin::secp256k1::All; -use super::{error::DaemonError, sender::Sender, state::DaemonState}; +use super::{error::DaemonError, senders::base_sender::Sender, state::DaemonState}; use cw_orch_core::environment::ChainInfoOwned; /// The default deployment id if none is provided @@ -116,6 +116,7 @@ impl DaemonAsyncBuilder { let daemon = DaemonAsync { state, sender: Arc::new(sender), + querier: (), }; print_if_log_disabled()?; Ok(daemon) diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index f4c665044..db5468044 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -2,7 +2,7 @@ use crate::{queriers::CosmWasm, DaemonState}; use super::{ builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, queriers::Node, - sender::Wallet, tx_resp::CosmTxResponse, + senders::base_sender::Wallet, tx_resp::CosmTxResponse, }; use cosmrs::{ @@ -31,6 +31,8 @@ use std::{ use tonic::transport::Channel; +use crate::senders::sender_trait::SenderTrait; + #[derive(Clone)] /** Represents a blockchain node. @@ -63,9 +65,11 @@ use tonic::transport::Channel; If you do so, you WILL get account sequence errors and your transactions won't get broadcasted. Use a Mutex on top of this DaemonAsync to avoid such errors. */ -pub struct DaemonAsync { +pub struct DaemonAsync { /// Sender to send transactions to the chain - pub sender: Wallet, + pub sender: SenderTrait, + /// Querier associated with the Daemon object. It's used to query chain information + pub querier: QuerierTrait, /// State of the daemon pub state: Arc, } @@ -91,7 +95,7 @@ impl ChainState for DaemonAsync { } // Execute on the real chain, returns tx response. -impl DaemonAsync { +impl DaemonAsync { /// Get the sender address pub fn sender(&self) -> Addr { self.sender.address().unwrap() @@ -310,7 +314,7 @@ impl DaemonAsync { } /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender(&mut self, sender: &Wallet) { + pub fn set_sender(&mut self, sender: &Sender) { self.sender = sender.clone(); } } diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index 4e0071fca..685e4e71d 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -9,7 +9,7 @@ pub mod error; pub(crate) mod json_file; /// Proto types for different blockchains pub mod proto; -pub mod sender; +pub mod senders; pub mod state; pub mod sync; pub mod tx_resp; @@ -23,7 +23,7 @@ pub mod tx_broadcaster; pub mod tx_builder; pub use self::{builder::*, channel::*, core::*, error::*, state::*, sync::*, tx_resp::*}; pub use cw_orch_networks::networks; -pub use sender::Wallet; +pub use senders::base_sender::Wallet; pub use tx_builder::TxBuilder; pub(crate) mod cosmos_modules { diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/senders/base_sender.rs similarity index 97% rename from cw-orch-daemon/src/sender.rs rename to cw-orch-daemon/src/senders/base_sender.rs index 951a5715f..1361c42c2 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -8,7 +8,8 @@ use crate::{ }, }; -use super::{ +use crate::proto::injective::InjectiveEthAccount; +use crate::{ cosmos_modules::{self, auth::BaseAccount}, error::DaemonError, queriers::Node, @@ -16,7 +17,6 @@ use super::{ tx_builder::TxBuilder, tx_resp::CosmTxResponse, }; -use crate::proto::injective::InjectiveEthAccount; #[cfg(feature = "eth")] use crate::proto::injective::InjectiveSigner; @@ -40,6 +40,8 @@ use std::{str::FromStr, sync::Arc}; use cosmos_modules::vesting::PeriodicVestingAccount; use tonic::transport::Channel; +use super::sender_trait::SenderTrait; + const GAS_BUFFER: f64 = 1.3; const BUFFER_THRESHOLD: u64 = 200_000; const SMALL_GAS_BUFFER: f64 = 1.4; @@ -97,6 +99,80 @@ impl SenderOptions { } } +impl SenderTrait for Sender { + type Error = DaemonError; + + async fn commit_tx_any( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> Result { + let timeout_height = Node::new_async(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); + + // We retry broadcasting the tx, with the following strategies + // 1. In case there is an `incorrect account sequence` error, we can retry as much as possible (doesn't cost anything to the user) + // 2. In case there is an insufficient_fee error, we retry once (costs fee to the user everytime we submit this kind of tx) + // 3. In case there is an other error, we fail + let tx_response = TxBroadcaster::default() + .add_strategy(insufficient_fee_strategy()) + .add_strategy(account_sequence_strategy()) + .broadcast(tx_builder, self) + .await?; + + let resp = Node::new_async(self.channel()) + ._find_tx(tx_response.txhash) + .await?; + + assert_broadcast_code_cosm_response(resp) + } + + async fn broadcast_tx( + &self, + tx: Raw, + ) -> Result { + let mut client = cosmos_modules::tx::service_client::ServiceClient::new(self.channel()); + let commit = client + .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { + tx_bytes: tx.to_bytes()?, + mode: cosmos_modules::tx::BroadcastMode::Sync.into(), + }) + .await?; + + let commit = commit.into_inner().tx_response.unwrap(); + Ok(commit) + } + + fn address(&self) -> Result { + Ok(Addr::unchecked(self.pub_addr_str()?)) + } + + fn msg_sender(&self) -> Result { + if let Some(sender) = &self.options.authz_granter { + Ok(sender.parse()?) + } else { + self.pub_addr() + } + } +} + impl Sender { pub fn new(daemon_state: &Arc) -> Result, DaemonError> { Self::new_with_options(daemon_state, SenderOptions::default()) @@ -175,25 +251,10 @@ impl Sender { )?) } - pub fn address(&self) -> Result { - Ok(Addr::unchecked(self.pub_addr_str()?)) - } - pub fn pub_addr_str(&self) -> Result { Ok(self.pub_addr()?.to_string()) } - /// 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 { - self.pub_addr() - } - } - pub async fn bank_send( &self, recipient: &str, @@ -306,48 +367,6 @@ impl Sender { self.commit_tx_any(msgs, memo).await } - pub async fn commit_tx_any( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> Result { - let timeout_height = Node::new_async(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); - - // We retry broadcasting the tx, with the following strategies - // 1. In case there is an `incorrect account sequence` error, we can retry as much as possible (doesn't cost anything to the user) - // 2. In case there is an insufficient_fee error, we retry once (costs fee to the user everytime we submit this kind of tx) - // 3. In case there is an other error, we fail - let tx_response = TxBroadcaster::default() - .add_strategy(insufficient_fee_strategy()) - .add_strategy(account_sequence_strategy()) - .broadcast(tx_builder, self) - .await?; - - let resp = Node::new_async(self.channel()) - ._find_tx(tx_response.txhash) - .await?; - - assert_broadcast_code_cosm_response(resp) - } - pub fn sign(&self, sign_doc: SignDoc) -> Result { let tx_raw = if self.private_key.coin_type == ETHEREUM_COIN_TYPE { #[cfg(not(feature = "eth"))] @@ -391,22 +410,6 @@ impl Sender { Ok(acc) } - pub async fn broadcast_tx( - &self, - tx: Raw, - ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new(self.channel()); - let commit = client - .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { - tx_bytes: tx.to_bytes()?, - mode: cosmos_modules::tx::BroadcastMode::Sync.into(), - }) - .await?; - - let commit = commit.into_inner().tx_response.unwrap(); - Ok(commit) - } - /// Allows for checking wether the sender is able to broadcast a transaction that necessitates the provided `gas` pub async fn has_enough_balance_for_gas(&self, gas: u64) -> Result<(), DaemonError> { let (_gas_expected, fee_amount) = self.get_fee_from_gas(gas)?; diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs new file mode 100644 index 000000000..1e2d85ec8 --- /dev/null +++ b/cw-orch-daemon/src/senders/mod.rs @@ -0,0 +1,3 @@ +pub mod base_sender; +pub mod querier_trait; +pub mod sender_trait; diff --git a/cw-orch-daemon/src/senders/querier_trait.rs b/cw-orch-daemon/src/senders/querier_trait.rs new file mode 100644 index 000000000..7264c886f --- /dev/null +++ b/cw-orch-daemon/src/senders/querier_trait.rs @@ -0,0 +1,11 @@ +use tonic::transport::Channel; + +pub trait QuerierTrait { + fn channel(&self) -> Channel; +} + +impl QuerierTrait for () { + fn channel(&self) -> Channel { + todo!() + } +} diff --git a/cw-orch-daemon/src/senders/sender_trait.rs b/cw-orch-daemon/src/senders/sender_trait.rs new file mode 100644 index 000000000..c397bf019 --- /dev/null +++ b/cw-orch-daemon/src/senders/sender_trait.rs @@ -0,0 +1,45 @@ +use cosmrs::{ + tx::{Msg, Raw}, + AccountId, Any, +}; +use cosmwasm_std::Addr; + +use crate::{CosmTxResponse, DaemonError}; + +pub trait SenderTrait: Clone { + type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; + + // TODO: do we want to enforce sync on this function ? + fn address(&self) -> Result; + + // TODO: do we want to enforce sync on this function ? + /// 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 + fn msg_sender(&self) -> Result; + + async fn commit_tx( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> Result { + let msgs = msgs + .into_iter() + .map(Msg::into_any) + .collect::, _>>() + .unwrap(); + + self.commit_tx_any(msgs, memo).await + } + + async fn commit_tx_any( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> Result; + + async fn broadcast_tx( + &self, + tx: Raw, + ) -> Result; +} diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 8dd39c1d0..6e519f301 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,6 +1,6 @@ use crate::RUNTIME; use crate::{ - sender::{Sender, SenderBuilder, SenderOptions}, + senders::base_sender::{Sender, SenderBuilder, SenderOptions}, DaemonAsyncBuilder, }; use bitcoin::secp256k1::All; diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index bf8eb8b88..b5c9215d2 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -1,6 +1,6 @@ use std::{fmt::Debug, sync::Arc}; -use super::super::{sender::Wallet, DaemonAsync}; +use super::super::{senders::base_sender::Wallet, DaemonAsync}; use crate::{ queriers::{Bank, CosmWasm, Node}, CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, @@ -15,6 +15,8 @@ use serde::Serialize; use tokio::runtime::Handle; use tonic::transport::Channel; +use crate::senders::sender_trait::SenderTrait; + #[derive(Clone)] /** Represents a blockchain node. diff --git a/cw-orch-daemon/src/tx_broadcaster.rs b/cw-orch-daemon/src/tx_broadcaster.rs index a2c6f667b..41763ce0f 100644 --- a/cw-orch-daemon/src/tx_broadcaster.rs +++ b/cw-orch-daemon/src/tx_broadcaster.rs @@ -4,7 +4,11 @@ use bitcoin::secp256k1::All; use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; use cw_orch_core::log::transaction_target; -use crate::{queriers::Node, sender::Sender, CosmTxResponse, DaemonError, TxBuilder}; +use crate::{ + queriers::Node, + senders::{base_sender::Sender, sender_trait::SenderTrait}, + CosmTxResponse, DaemonError, TxBuilder, +}; pub type StrategyAction = fn(&mut TxBuilder, &Result) -> Result<(), DaemonError>; diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index db107b81d..e7b85da3f 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -11,9 +11,9 @@ use cosmrs::{ }; use cw_orch_core::log::transaction_target; -use crate::sender::SenderOptions; +use crate::senders::base_sender::SenderOptions; -use super::{sender::Sender, DaemonError}; +use super::{senders::base_sender::Sender, DaemonError}; /// Struct used to build a raw transaction and broadcast it with a sender. #[derive(Clone, Debug)] diff --git a/cw-orch/src/daemon.rs b/cw-orch/src/daemon.rs index 48b800ff9..fadd642cb 100644 --- a/cw-orch/src/daemon.rs +++ b/cw-orch/src/daemon.rs @@ -2,5 +2,4 @@ //! //! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. -pub use cw_orch_daemon::sender::Wallet; pub use cw_orch_daemon::*; From ee253353041d9913b6498b3c164720eb61257ec2 Mon Sep 17 00:00:00 2001 From: Mykhailo Donchenko <91957742+Buckram123@users.noreply.github.com> Date: Wed, 15 May 2024 12:05:25 +0300 Subject: [PATCH 026/108] Update cw-orch-daemon/src/state.rs Co-authored-by: CyberHoward <88450409+CyberHoward@users.noreply.github.com> --- cw-orch-daemon/src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index b8b6bc57c..e0caad810 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -27,7 +27,7 @@ pub struct DaemonState { pub json_state: DaemonStateFile, /// Deployment identifier pub deployment_id: String, - /// Chain Id + /// Chain Id, ex. "osmosis-1" pub chain_id: String, /// Chain Name pub chain_name: String, From 2370365b718556c244858b16989dc05899945c94 Mon Sep 17 00:00:00 2001 From: Mykhailo Donchenko <91957742+Buckram123@users.noreply.github.com> Date: Wed, 15 May 2024 12:05:41 +0300 Subject: [PATCH 027/108] Update cw-orch-daemon/src/state.rs Co-authored-by: CyberHoward <88450409+CyberHoward@users.noreply.github.com> --- cw-orch-daemon/src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index e0caad810..6f25a8c23 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -29,7 +29,7 @@ pub struct DaemonState { pub deployment_id: String, /// Chain Id, ex. "osmosis-1" pub chain_id: String, - /// Chain Name + /// Chain Name, ex. "osmosis" pub chain_name: String, } From cdb5ceeb3a4479c9d688dc0d0e84bbda722de636 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Mon, 20 May 2024 08:46:57 +0000 Subject: [PATCH 028/108] More traits --- cw-orch-daemon/src/builder.rs | 31 ++++++++---- cw-orch-daemon/src/core.rs | 31 ++++++++---- cw-orch-daemon/src/queriers/env.rs | 11 +++-- cw-orch-daemon/src/senders/base_sender.rs | 3 +- cw-orch-daemon/src/senders/querier_trait.rs | 4 +- cw-orch-daemon/src/senders/sender_trait.rs | 52 +++++++++++++++++---- cw-orch-daemon/src/sync/builder.rs | 14 ++++-- cw-orch-daemon/src/sync/core.rs | 35 +++++++++----- 8 files changed, 136 insertions(+), 45 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 6945fc623..0a23d7b5f 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,6 +1,11 @@ use crate::{ - log::print_if_log_disabled, senders::base_sender::SenderBuilder, - senders::base_sender::SenderOptions, DaemonAsync, DaemonBuilder, + log::print_if_log_disabled, + senders::{ + base_sender::{SenderBuilder, SenderOptions}, + querier_trait::QuerierTrait, + sender_trait::SenderTrait, + }, + DaemonAsync, DaemonBuilder, DaemonBuilderBase, Wallet, }; use std::sync::Arc; @@ -25,7 +30,7 @@ pub const DEFAULT_DEPLOYMENT: &str = "default"; /// .await.unwrap(); /// # }) /// ``` -pub struct DaemonAsyncBuilder { +pub struct DaemonAsyncBuilderBase { // # Required pub(crate) chain: Option, // # Optional @@ -34,12 +39,18 @@ pub struct DaemonAsyncBuilder { /* Sender related options */ /// Wallet sender /// Will be used in priority when set - pub(crate) sender: Option>, + pub(crate) sender: Option, /// Specify Daemon Sender Options pub(crate) sender_options: SenderOptions, + + pub(crate) querier_builder: QuerierGen::QuerierBuilder, } -impl DaemonAsyncBuilder { +pub type DaemonAsyncBuilder = DaemonAsyncBuilderBase; + +impl + DaemonAsyncBuilderBase +{ /// Set the chain the daemon will connect to pub fn chain(&mut self, chain: impl Into) -> &mut Self { self.chain = Some(chain.into()); @@ -123,13 +134,17 @@ impl DaemonAsyncBuilder { } } -impl From for DaemonAsyncBuilder { - fn from(value: DaemonBuilder) -> Self { - DaemonAsyncBuilder { +impl + From> + for DaemonAsyncBuilderBase +{ + fn from(value: DaemonBuilderBase) -> Self { + DaemonAsyncBuilderBase { chain: value.chain, deployment_id: value.deployment_id, sender_options: value.sender_options, sender: value.sender, + querier_builder: value.querier, } } } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index db5468044..cb16097b8 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -1,4 +1,8 @@ -use crate::{queriers::CosmWasm, DaemonState}; +use crate::{ + queriers::CosmWasm, + senders::{base_sender::Sender, querier_trait::QuerierTrait}, + DaemonAsyncBuilderBase, DaemonState, +}; use super::{ builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, queriers::Node, @@ -65,16 +69,18 @@ use crate::senders::sender_trait::SenderTrait; If you do so, you WILL get account sequence errors and your transactions won't get broadcasted. Use a Mutex on top of this DaemonAsync to avoid such errors. */ -pub struct DaemonAsync { +pub struct DaemonAsyncBase { /// Sender to send transactions to the chain - pub sender: SenderTrait, + pub sender: SenderGen, /// Querier associated with the Daemon object. It's used to query chain information - pub querier: QuerierTrait, + pub querier: QuerierGen, /// State of the daemon pub state: Arc, } -impl DaemonAsync { +pub type DaemonAsync = DaemonAsyncBase; + +impl DaemonAsyncBase { /// Get the daemon builder pub fn builder() -> DaemonAsyncBuilder { DaemonAsyncBuilder::default() @@ -95,7 +101,7 @@ impl ChainState for DaemonAsync { } // Execute on the real chain, returns tx response. -impl DaemonAsync { +impl DaemonAsyncBase { /// Get the sender address pub fn sender(&self) -> Addr { self.sender.address().unwrap() @@ -103,7 +109,7 @@ impl DaemonAsync { /// Returns a new [`DaemonAsyncBuilder`] with the current configuration. /// Does not consume the original [`DaemonAsync`]. - pub fn rebuild(&self) -> DaemonAsyncBuilder { + pub fn rebuild(&self) -> DaemonAsyncBuilderBase { let mut builder = Self::builder(); builder .chain(self.state().chain_data.clone()) @@ -314,8 +320,15 @@ impl DaemonAsync { } /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender(&mut self, sender: &Sender) { - self.sender = sender.clone(); + pub fn set_sender( + self, + sender: SenderGen2, + ) -> DaemonAsyncBase { + DaemonAsyncBase { + sender, + querier: self.querier, + state: self.state, + } } } diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index 2c72aa17f..a67ccff4f 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -1,10 +1,15 @@ use cw_orch_core::environment::{EnvironmentInfo, EnvironmentQuerier}; -use crate::Daemon; +use crate::{ + senders::{querier_trait::QuerierTrait, sender_trait::SenderTrait}, + DaemonBase, +}; -impl EnvironmentQuerier for Daemon { +impl EnvironmentQuerier + for DaemonBase +{ fn env_info(&self) -> EnvironmentInfo { - let state = &self.daemon.sender.daemon_state; + let state = &self.state()?; EnvironmentInfo { chain_id: state.chain_data.chain_id.to_string(), chain_name: state.chain_data.network_info.chain_name.to_string(), diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index 1361c42c2..c48c23e38 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -101,12 +101,13 @@ impl SenderOptions { impl SenderTrait for Sender { type Error = DaemonError; + type SenderBuilder = SenderBuilder; async fn commit_tx_any( &self, msgs: Vec, memo: Option<&str>, - ) -> Result { + ) -> Result { let timeout_height = Node::new_async(self.channel())._block_height().await? + 10u64; let msgs = if self.options.authz_granter.is_some() { diff --git a/cw-orch-daemon/src/senders/querier_trait.rs b/cw-orch-daemon/src/senders/querier_trait.rs index 7264c886f..b6da2eec3 100644 --- a/cw-orch-daemon/src/senders/querier_trait.rs +++ b/cw-orch-daemon/src/senders/querier_trait.rs @@ -1,10 +1,12 @@ use tonic::transport::Channel; -pub trait QuerierTrait { +pub trait QuerierTrait: Clone { + type QuerierBuilder; fn channel(&self) -> Channel; } impl QuerierTrait for () { + type QuerierBuilder = (); fn channel(&self) -> Channel { todo!() } diff --git a/cw-orch-daemon/src/senders/sender_trait.rs b/cw-orch-daemon/src/senders/sender_trait.rs index c397bf019..ef2f393b3 100644 --- a/cw-orch-daemon/src/senders/sender_trait.rs +++ b/cw-orch-daemon/src/senders/sender_trait.rs @@ -5,9 +5,13 @@ use cosmrs::{ use cosmwasm_std::Addr; use crate::{CosmTxResponse, DaemonError}; +use std::sync::Arc; -pub trait SenderTrait: Clone { +pub trait SenderTraitBase: SenderTrait where DaemonError: From{} + +pub trait SenderTraitBase: Clone { type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; + type SenderBuilder; // TODO: do we want to enforce sync on this function ? fn address(&self) -> Result; @@ -18,28 +22,60 @@ pub trait SenderTrait: Clone { /// Else, returns the address associated with the current private key fn msg_sender(&self) -> Result; - async fn commit_tx( + fn commit_tx( &self, msgs: Vec, memo: Option<&str>, - ) -> Result { + ) -> impl std::future::Future> + Send { let msgs = msgs .into_iter() .map(Msg::into_any) .collect::, _>>() .unwrap(); - self.commit_tx_any(msgs, memo).await + self.commit_tx_any(msgs, memo) + } + + fn commit_tx_any( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> impl std::future::Future> + Send; + + fn broadcast_tx( + &self, + tx: Raw, + ) -> impl std::future::Future< + Output = Result, + > + Send; +} + +impl SenderTrait for Arc { + type Error = T::Error; + type SenderBuilder = T::SenderBuilder; + + fn address(&self) -> Result { + (**self).address() } - async fn commit_tx_any( + fn msg_sender(&self) -> Result { + (**self).msg_sender() + } + + fn commit_tx_any( &self, msgs: Vec, memo: Option<&str>, - ) -> Result; + ) -> impl std::future::Future> + Send { + (**self).commit_tx_any(msgs, memo) + } - async fn broadcast_tx( + fn broadcast_tx( &self, tx: Raw, - ) -> Result; + ) -> impl std::future::Future< + Output = Result, + > + Send { + (**self).broadcast_tx(tx) + } } diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 6e519f301..80ffb53da 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,8 +1,10 @@ -use crate::RUNTIME; +use crate::senders::querier_trait::QuerierTrait; +use crate::senders::sender_trait::SenderTrait; use crate::{ senders::base_sender::{Sender, SenderBuilder, SenderOptions}, DaemonAsyncBuilder, }; +use crate::{Wallet, RUNTIME}; use bitcoin::secp256k1::All; use cw_orch_core::environment::ChainInfoOwned; @@ -20,7 +22,7 @@ use super::{super::error::DaemonError, core::Daemon}; /// .build() /// .unwrap(); /// ``` -pub struct DaemonBuilder { +pub struct DaemonBuilderBase { // # Required pub(crate) chain: Option, // # Optional @@ -32,12 +34,16 @@ pub struct DaemonBuilder { /* Sender Options */ /// Wallet sender - pub(crate) sender: Option>, + pub(crate) sender: Option, /// Specify Daemon Sender Options pub(crate) sender_options: SenderOptions, + + pub(crate) querier: Option, } -impl DaemonBuilder { +pub type DaemonBuilder = DaemonBuilderBase; + +impl DaemonBuilderBase { /// Set the chain the Daemon will connect to pub fn chain(&mut self, chain: impl Into) -> &mut Self { self.chain = Some(chain.into()); diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index b5c9215d2..8a338d480 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -1,9 +1,10 @@ use std::{fmt::Debug, sync::Arc}; -use super::super::{senders::base_sender::Wallet, DaemonAsync}; +use super::super::senders::base_sender::Wallet; use crate::{ queriers::{Bank, CosmWasm, Node}, - CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, + senders::querier_trait::QuerierTrait, + CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonBuilderBase, DaemonError, DaemonState, }; use cosmwasm_std::{Addr, Coin}; use cw_orch_core::{ @@ -43,13 +44,15 @@ use crate::senders::sender_trait::SenderTrait; Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. See [Querier](crate::queriers) for examples. */ -pub struct Daemon { - pub daemon: DaemonAsync, +pub struct DaemonBase { + pub daemon: DaemonAsyncBase, /// Runtime handle to execute async tasks pub rt_handle: Handle, } -impl Daemon { +pub type Daemon = DaemonBase; + +impl DaemonBase { /// Get the daemon builder pub fn builder() -> DaemonBuilder { DaemonBuilder::default() @@ -67,7 +70,7 @@ impl Daemon { /// Returns a new [`DaemonBuilder`] with the current configuration. /// Does not consume the original [`Daemon`]. - pub fn rebuild(&self) -> DaemonBuilder { + pub fn rebuild(&self) -> DaemonBuilderBase { let mut builder = Self::builder(); builder .chain(self.state().chain_data.clone()) @@ -77,7 +80,9 @@ impl Daemon { } } -impl ChainState for Daemon { +impl ChainState + for DaemonBase +{ type Out = Arc; fn state(&self) -> Self::Out { @@ -86,7 +91,9 @@ impl ChainState for Daemon { } // Execute on the real chain, returns tx response -impl TxHandler for Daemon { +impl TxHandler + for DaemonBase +{ type Response = CosmTxResponse; type Error = DaemonError; type ContractSource = WasmPath; @@ -156,7 +163,9 @@ impl TxHandler for Daemon { } } -impl Stargate for Daemon { +impl Stargate + for DaemonBase +{ fn commit_any( &self, msgs: Vec, @@ -176,7 +185,9 @@ impl Stargate for Daemon { } } -impl QueryHandler for Daemon { +impl QueryHandler + for DaemonBase +{ type Error = DaemonError; fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { @@ -198,7 +209,9 @@ impl QueryHandler for Daemon { } } -impl DefaultQueriers for Daemon { +impl DefaultQueriers + for DaemonBase +{ type Bank = Bank; type Wasm = CosmWasm; type Node = Node; From b569702a18e3582f27d0abef3f76922ad7289e75 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 21 May 2024 13:13:19 +0000 Subject: [PATCH 029/108] More changes, almost there --- cw-orch-daemon/src/builder.rs | 37 +++++----- cw-orch-daemon/src/core.rs | 76 ++++++++++--------- cw-orch-daemon/src/live_mock.rs | 6 +- cw-orch-daemon/src/queriers/bank.rs | 6 +- cw-orch-daemon/src/queriers/cosmwasm.rs | 36 ++++++--- cw-orch-daemon/src/queriers/env.rs | 13 +--- cw-orch-daemon/src/queriers/node.rs | 7 +- cw-orch-daemon/src/senders/mod.rs | 1 - cw-orch-daemon/src/senders/querier_trait.rs | 13 ---- cw-orch-daemon/src/senders/sender_trait.rs | 4 +- cw-orch-daemon/src/sync/builder.rs | 71 ++++++++++++++---- cw-orch-daemon/src/sync/core.rs | 81 +++++++++------------ 12 files changed, 190 insertions(+), 161 deletions(-) delete mode 100644 cw-orch-daemon/src/senders/querier_trait.rs diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 0a23d7b5f..6a6e8f397 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -2,10 +2,9 @@ use crate::{ log::print_if_log_disabled, senders::{ base_sender::{SenderBuilder, SenderOptions}, - querier_trait::QuerierTrait, sender_trait::SenderTrait, }, - DaemonAsync, DaemonBuilder, DaemonBuilderBase, Wallet, + DaemonAsync, DaemonBuilder, Wallet, }; use std::sync::Arc; @@ -17,7 +16,7 @@ use cw_orch_core::environment::ChainInfoOwned; /// The default deployment id if none is provided pub const DEFAULT_DEPLOYMENT: &str = "default"; -#[derive(Clone, Default)] +#[derive(Clone)] /// Create [`DaemonAsync`] through [`DaemonAsyncBuilder`] /// ## Example /// ```no_run @@ -30,7 +29,7 @@ pub const DEFAULT_DEPLOYMENT: &str = "default"; /// .await.unwrap(); /// # }) /// ``` -pub struct DaemonAsyncBuilderBase { +pub struct DaemonAsyncBuilderBase { // # Required pub(crate) chain: Option, // # Optional @@ -42,15 +41,22 @@ pub struct DaemonAsyncBuilderBase, /// Specify Daemon Sender Options pub(crate) sender_options: SenderOptions, - - pub(crate) querier_builder: QuerierGen::QuerierBuilder, } -pub type DaemonAsyncBuilder = DaemonAsyncBuilderBase; +pub type DaemonAsyncBuilder = DaemonAsyncBuilderBase; + +impl Default for DaemonAsyncBuilderBase { + fn default() -> Self { + Self { + chain: Default::default(), + deployment_id: Default::default(), + sender: Default::default(), + sender_options: Default::default(), + } + } +} -impl - DaemonAsyncBuilderBase -{ +impl DaemonAsyncBuilderBase { /// Set the chain the daemon will connect to pub fn chain(&mut self, chain: impl Into) -> &mut Self { self.chain = Some(chain.into()); @@ -127,24 +133,19 @@ impl let daemon = DaemonAsync { state, sender: Arc::new(sender), - querier: (), }; print_if_log_disabled()?; Ok(daemon) } } -impl - From> - for DaemonAsyncBuilderBase -{ - fn from(value: DaemonBuilderBase) -> Self { - DaemonAsyncBuilderBase { +impl From for DaemonAsyncBuilder { + fn from(value: DaemonBuilder) -> Self { + DaemonAsyncBuilder { chain: value.chain, deployment_id: value.deployment_id, sender_options: value.sender_options, sender: value.sender, - querier_builder: value.querier, } } } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index cb16097b8..107685aaf 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -1,8 +1,4 @@ -use crate::{ - queriers::CosmWasm, - senders::{base_sender::Sender, querier_trait::QuerierTrait}, - DaemonAsyncBuilderBase, DaemonState, -}; +use crate::{queriers::CosmWasm, DaemonState}; use super::{ builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, queriers::Node, @@ -69,18 +65,16 @@ use crate::senders::sender_trait::SenderTrait; If you do so, you WILL get account sequence errors and your transactions won't get broadcasted. Use a Mutex on top of this DaemonAsync to avoid such errors. */ -pub struct DaemonAsyncBase { +pub struct DaemonAsyncBase { /// Sender to send transactions to the chain pub sender: SenderGen, - /// Querier associated with the Daemon object. It's used to query chain information - pub querier: QuerierGen, /// State of the daemon pub state: Arc, } -pub type DaemonAsync = DaemonAsyncBase; +pub type DaemonAsync = DaemonAsyncBase; -impl DaemonAsyncBase { +impl DaemonAsyncBase { /// Get the daemon builder pub fn builder() -> DaemonAsyncBuilder { DaemonAsyncBuilder::default() @@ -101,22 +95,22 @@ impl ChainState for DaemonAsync { } // Execute on the real chain, returns tx response. -impl DaemonAsyncBase { +impl DaemonAsyncBase { /// Get the sender address pub fn sender(&self) -> Addr { self.sender.address().unwrap() } - /// Returns a new [`DaemonAsyncBuilder`] with the current configuration. - /// Does not consume the original [`DaemonAsync`]. - pub fn rebuild(&self) -> DaemonAsyncBuilderBase { - let mut builder = Self::builder(); - builder - .chain(self.state().chain_data.clone()) - .sender((*self.sender).clone()) - .deployment_id(&self.state().deployment_id); - builder - } + // /// Returns a new [`DaemonAsyncBuilder`] with the current configuration. + // /// Does not consume the original [`DaemonAsync`]. + // pub fn rebuild(&self) -> DaemonAsyncBuilderBase { + // let mut builder = Self::builder(); + // builder + // .chain(self.state().chain_data.clone()) + // .sender((*self.sender).clone()) + // .deployment_id(&self.state().deployment_id); + // builder + // } /// Execute a message on a contract. pub async fn execute( @@ -126,12 +120,16 @@ impl DaemonAsyncBase Result { let exec_msg: MsgExecuteContract = MsgExecuteContract { - sender: self.sender.msg_sender()?, + sender: self.sender.msg_sender().map_err(Into::into)?, contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&exec_msg)?, funds: parse_cw_coins(coins)?, }; - let result = self.sender.commit_tx(vec![exec_msg], None).await?; + let result = self + .sender + .commit_tx(vec![exec_msg], None) + .await + .map_err(Into::into)?; log::info!(target: &transaction_target(), "Execution done: {:?}", result.txhash); Ok(result) @@ -152,12 +150,15 @@ impl DaemonAsyncBase DaemonAsyncBase DaemonAsyncBase DaemonAsyncBase Result { let exec_msg: MsgMigrateContract = MsgMigrateContract { - sender: self.sender.msg_sender()?, + sender: self.sender.msg_sender().map_err(Into::into)?, contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&migrate_msg)?, code_id: new_code_id, }; - let result = self.sender.commit_tx(vec![exec_msg], None).await?; + let result = self + .sender + .commit_tx(vec![exec_msg], None) + .await + .map_err(Into::into)?; Ok(result) } @@ -300,19 +306,22 @@ impl DaemonAsyncBase::new_async(self.channel()); while wasm._code(code_id).await.is_err() { self.next_block().await?; } @@ -323,10 +332,9 @@ impl DaemonAsyncBase( self, sender: SenderGen2, - ) -> DaemonAsyncBase { + ) -> DaemonAsyncBase { DaemonAsyncBase { sender, - querier: self.querier, state: self.state, } } diff --git a/cw-orch-daemon/src/live_mock.rs b/cw-orch-daemon/src/live_mock.rs index e53d370a9..05d4a5993 100644 --- a/cw-orch-daemon/src/live_mock.rs +++ b/cw-orch-daemon/src/live_mock.rs @@ -4,6 +4,7 @@ use crate::queriers::Bank; use crate::queriers::CosmWasm; use crate::queriers::Staking; +use crate::Wallet; use crate::RUNTIME; use cosmwasm_std::testing::{MockApi, MockStorage}; use cosmwasm_std::Addr; @@ -81,10 +82,7 @@ impl WasmMockQuerier { let handle = RUNTIME.handle(); match &request { QueryRequest::Wasm(x) => { - let querier = CosmWasm { - channel: self.channel.clone(), - rt_handle: Some(handle.clone()), - }; + let querier = CosmWasm::::new_sync(self.channel.clone(), &handle); match x { WasmQuery::Smart { contract_addr, msg } => { // We forward the request to the cosmwasm querier diff --git a/cw-orch-daemon/src/queriers/bank.rs b/cw-orch-daemon/src/queriers/bank.rs index ba9106e59..fc6054c4c 100644 --- a/cw-orch-daemon/src/queriers/bank.rs +++ b/cw-orch-daemon/src/queriers/bank.rs @@ -1,4 +1,4 @@ -use crate::{cosmos_modules, error::DaemonError, Daemon}; +use crate::{cosmos_modules, error::DaemonError, senders::sender_trait::SenderTrait, DaemonBase}; use cosmrs::proto::cosmos::base::{query::v1beta1::PageRequest, v1beta1::Coin}; use cosmwasm_std::StdError; use cw_orch_core::environment::{BankQuerier, Querier, QuerierGetter}; @@ -13,7 +13,7 @@ pub struct Bank { } impl Bank { - pub fn new(daemon: &Daemon) -> Self { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), @@ -31,7 +31,7 @@ impl Querier for Bank { type Error = DaemonError; } -impl QuerierGetter for Daemon { +impl QuerierGetter for DaemonBase { fn querier(&self) -> Bank { Bank::new(self) } diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 8d998bb5d..e0942193a 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -1,6 +1,8 @@ -use std::str::FromStr; +use std::{marker::PhantomData, str::FromStr}; -use crate::{cosmos_modules, error::DaemonError, Daemon}; +use crate::{ + cosmos_modules, error::DaemonError, senders::sender_trait::SenderTrait, DaemonBase, Wallet, +}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmrs::AccountId; use cosmwasm_std::{ @@ -16,37 +18,47 @@ use tonic::transport::Channel; /// Querier for the CosmWasm SDK module /// All the async function are prefixed with `_` -pub struct CosmWasm { +pub struct CosmWasm { pub channel: Channel, pub rt_handle: Option, + _sender: PhantomData, } -impl CosmWasm { - pub fn new(daemon: &Daemon) -> Self { +impl CosmWasm { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), + _sender: PhantomData, } } pub fn new_async(channel: Channel) -> Self { Self { channel, rt_handle: None, + _sender: PhantomData, + } + } + pub fn new_sync(channel: Channel, handle: &Handle) -> Self { + Self { + channel, + rt_handle: Some(handle.clone()), + _sender: PhantomData, } } } -impl QuerierGetter for Daemon { - fn querier(&self) -> CosmWasm { +impl QuerierGetter> for DaemonBase { + fn querier(&self) -> CosmWasm { CosmWasm::new(self) } } -impl Querier for CosmWasm { +impl Querier for CosmWasm { type Error = DaemonError; } -impl CosmWasm { +impl CosmWasm { /// Query code_id by hash pub async fn _code_id_hash(&self, code_id: u64) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; @@ -200,8 +212,8 @@ impl CosmWasm { } } -impl WasmQuerier for CosmWasm { - type Chain = Daemon; +impl WasmQuerier for CosmWasm { + type Chain = DaemonBase; fn code_id_hash(&self, code_id: u64) -> Result { self.rt_handle .as_ref() @@ -297,7 +309,7 @@ impl WasmQuerier for CosmWasm { fn local_hash< T: cw_orch_core::contract::interface_traits::Uploadable - + cw_orch_core::contract::interface_traits::ContractInstance, + + cw_orch_core::contract::interface_traits::ContractInstance>, >( &self, contract: &T, diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index a67ccff4f..90fd9baa5 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -1,15 +1,10 @@ -use cw_orch_core::environment::{EnvironmentInfo, EnvironmentQuerier}; +use cw_orch_core::environment::{ChainState, EnvironmentInfo, EnvironmentQuerier}; -use crate::{ - senders::{querier_trait::QuerierTrait, sender_trait::SenderTrait}, - DaemonBase, -}; +use crate::{senders::sender_trait::SenderTrait, DaemonBase}; -impl EnvironmentQuerier - for DaemonBase -{ +impl EnvironmentQuerier for DaemonBase { fn env_info(&self) -> EnvironmentInfo { - let state = &self.state()?; + let state = &self.state(); EnvironmentInfo { chain_id: state.chain_data.chain_id.to_string(), chain_name: state.chain_data.network_info.chain_name.to_string(), diff --git a/cw-orch-daemon/src/queriers/node.rs b/cw-orch-daemon/src/queriers/node.rs index 3fa0e58ef..01ff10b75 100644 --- a/cw-orch-daemon/src/queriers/node.rs +++ b/cw-orch-daemon/src/queriers/node.rs @@ -1,7 +1,8 @@ use std::{cmp::min, time::Duration}; use crate::{ - cosmos_modules, env::DaemonEnvVars, error::DaemonError, tx_resp::CosmTxResponse, Daemon, + cosmos_modules, env::DaemonEnvVars, error::DaemonError, senders::sender_trait::SenderTrait, + tx_resp::CosmTxResponse, DaemonBase, }; use cosmrs::{ @@ -28,7 +29,7 @@ pub struct Node { } impl Node { - pub fn new(daemon: &Daemon) -> Self { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), @@ -42,7 +43,7 @@ impl Node { } } -impl QuerierGetter for Daemon { +impl QuerierGetter for DaemonBase { fn querier(&self) -> Node { Node::new(self) } diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index 1e2d85ec8..9f8ef40d3 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -1,3 +1,2 @@ pub mod base_sender; -pub mod querier_trait; pub mod sender_trait; diff --git a/cw-orch-daemon/src/senders/querier_trait.rs b/cw-orch-daemon/src/senders/querier_trait.rs deleted file mode 100644 index b6da2eec3..000000000 --- a/cw-orch-daemon/src/senders/querier_trait.rs +++ /dev/null @@ -1,13 +0,0 @@ -use tonic::transport::Channel; - -pub trait QuerierTrait: Clone { - type QuerierBuilder; - fn channel(&self) -> Channel; -} - -impl QuerierTrait for () { - type QuerierBuilder = (); - fn channel(&self) -> Channel { - todo!() - } -} diff --git a/cw-orch-daemon/src/senders/sender_trait.rs b/cw-orch-daemon/src/senders/sender_trait.rs index ef2f393b3..2608434d2 100644 --- a/cw-orch-daemon/src/senders/sender_trait.rs +++ b/cw-orch-daemon/src/senders/sender_trait.rs @@ -7,9 +7,7 @@ use cosmwasm_std::Addr; use crate::{CosmTxResponse, DaemonError}; use std::sync::Arc; -pub trait SenderTraitBase: SenderTrait where DaemonError: From{} - -pub trait SenderTraitBase: Clone { +pub trait SenderTrait: Clone { type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; type SenderBuilder; diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 80ffb53da..cba6cd28e 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,13 +1,14 @@ -use crate::senders::querier_trait::QuerierTrait; use crate::senders::sender_trait::SenderTrait; +use crate::RUNTIME; use crate::{ senders::base_sender::{Sender, SenderBuilder, SenderOptions}, DaemonAsyncBuilder, }; -use crate::{Wallet, RUNTIME}; use bitcoin::secp256k1::All; use cw_orch_core::environment::ChainInfoOwned; +use self::generic::DaemonBuilderBase; + use super::{super::error::DaemonError, core::Daemon}; #[derive(Clone, Default)] @@ -22,7 +23,7 @@ use super::{super::error::DaemonError, core::Daemon}; /// .build() /// .unwrap(); /// ``` -pub struct DaemonBuilderBase { +pub struct DaemonBuilder { // # Required pub(crate) chain: Option, // # Optional @@ -34,16 +35,12 @@ pub struct DaemonBuilderBase, + pub(crate) sender: Option>, /// Specify Daemon Sender Options pub(crate) sender_options: SenderOptions, - - pub(crate) querier: Option, } -pub type DaemonBuilder = DaemonBuilderBase; - -impl DaemonBuilderBase { +impl DaemonBuilder { /// Set the chain the Daemon will connect to pub fn chain(&mut self, chain: impl Into) -> &mut Self { self.chain = Some(chain.into()); @@ -122,6 +119,13 @@ impl DaemonBuilderBase( + &self, + sender: SenderGen, + ) -> DaemonBuilderBase { + DaemonBuilderBase::from_wallet_builder(self, sender) + } + /// Build a Daemon pub fn build(&self) -> Result { let rt_handle = self @@ -130,15 +134,9 @@ impl DaemonBuilderBase DaemonBuilderBase Option { + self.chain.clone().map(|mut chain| { + // Override gas fee + overwrite_fee(&mut chain, self.gas_denom.clone(), self.gas_fee); + // Override grpc_url + overwrite_grpc_url(&mut chain, self.overwrite_grpc_url.clone()); + chain + }) + } } fn overwrite_fee(chain: &mut ChainInfoOwned, denom: Option, amount: Option) { @@ -162,6 +170,37 @@ fn overwrite_grpc_url(chain: &mut ChainInfoOwned, grpc_url: Option) { } } +pub mod generic { + use cw_orch_core::environment::ChainInfoOwned; + + use crate::{senders::sender_trait::SenderTrait, DaemonBuilder}; + + #[derive(Clone)] + #[non_exhaustive] // To avoid building directly + /// TODO : doc comments + pub struct DaemonBuilderBase { + // # Required + pub(crate) chain: Option, + // # Optional + pub(crate) handle: Option, + pub(crate) deployment_id: Option, + + /* Sender Options */ + pub(crate) sender: SenderGen, + } + + impl DaemonBuilderBase { + pub fn from_wallet_builder(builder: &DaemonBuilder, sender: SenderGen) -> Self { + DaemonBuilderBase { + chain: builder.get_built_chain_object(), + handle: builder.handle, + deployment_id: builder.deployment_id, + sender, + } + } + } +} + #[cfg(test)] mod test { use cw_orch_networks::networks::OSMOSIS_1; diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 8a338d480..f7bc38445 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -3,8 +3,7 @@ use std::{fmt::Debug, sync::Arc}; use super::super::senders::base_sender::Wallet; use crate::{ queriers::{Bank, CosmWasm, Node}, - senders::querier_trait::QuerierTrait, - CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonBuilderBase, DaemonError, DaemonState, + CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonError, DaemonState, }; use cosmwasm_std::{Addr, Coin}; use cw_orch_core::{ @@ -44,15 +43,15 @@ use crate::senders::sender_trait::SenderTrait; Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. See [Querier](crate::queriers) for examples. */ -pub struct DaemonBase { - pub daemon: DaemonAsyncBase, +pub struct DaemonBase { + pub daemon: DaemonAsyncBase, /// Runtime handle to execute async tasks pub rt_handle: Handle, } -pub type Daemon = DaemonBase; +pub type Daemon = DaemonBase; -impl DaemonBase { +impl DaemonBase { /// Get the daemon builder pub fn builder() -> DaemonBuilder { DaemonBuilder::default() @@ -64,25 +63,23 @@ impl DaemonBase Wallet { + pub fn wallet(&self) -> SenderGen { self.daemon.sender.clone() } - /// Returns a new [`DaemonBuilder`] with the current configuration. - /// Does not consume the original [`Daemon`]. - pub fn rebuild(&self) -> DaemonBuilderBase { - let mut builder = Self::builder(); - builder - .chain(self.state().chain_data.clone()) - .sender((*self.daemon.sender).clone()) - .deployment_id(&self.state().deployment_id); - builder - } + // /// Returns a new [`DaemonBuilder`] with the current configuration. + // /// Does not consume the original [`Daemon`]. + // pub fn rebuild(&self) -> DaemonBuilderBase { + // let mut builder = Self::builder(); + // builder + // .chain(self.state().chain_data.clone()) + // .sender((*self.daemon.sender).clone()) + // .deployment_id(&self.state().deployment_id); + // builder + // } } -impl ChainState - for DaemonBase -{ +impl ChainState for DaemonBase { type Out = Arc; fn state(&self) -> Self::Out { @@ -91,13 +88,11 @@ impl ChainState } // Execute on the real chain, returns tx response -impl TxHandler - for DaemonBase -{ +impl TxHandler for DaemonBase { type Response = CosmTxResponse; type Error = DaemonError; type ContractSource = WasmPath; - type Sender = Wallet; + type Sender = SenderGen; fn sender(&self) -> Addr { self.daemon.sender.address().unwrap() @@ -163,31 +158,29 @@ impl TxHandler } } -impl Stargate - for DaemonBase -{ +impl Stargate for DaemonBase { fn commit_any( &self, msgs: Vec, memo: Option<&str>, ) -> Result { - self.rt_handle.block_on( - self.wallet().commit_tx_any( - msgs.iter() - .map(|msg| cosmrs::Any { - type_url: msg.type_url.clone(), - value: msg.value.clone(), - }) - .collect(), - memo, - ), - ) + self.rt_handle + .block_on( + self.wallet().commit_tx_any( + msgs.iter() + .map(|msg| cosmrs::Any { + type_url: msg.type_url.clone(), + value: msg.value.clone(), + }) + .collect(), + memo, + ), + ) + .map_err(Into::into) } } -impl QueryHandler - for DaemonBase -{ +impl QueryHandler for DaemonBase { type Error = DaemonError; fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { @@ -209,10 +202,8 @@ impl QueryHandler } } -impl DefaultQueriers - for DaemonBase -{ +impl DefaultQueriers for DaemonBase { type Bank = Bank; - type Wasm = CosmWasm; + type Wasm = CosmWasm; type Node = Node; } From baed9b1ca7de63135986344d975089f93d167856 Mon Sep 17 00:00:00 2001 From: Mykhailo Donchenko <91957742+Buckram123@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:13:36 +0300 Subject: [PATCH 030/108] Update outdated comment --- cw-orch-daemon/src/builder.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 02c6c6782..6633bfe5f 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -53,8 +53,6 @@ impl DaemonAsyncBuilder { /// Set the deployment id to use for the daemon interactions /// Defaults to `default` - /// - /// This field is ignored for rebuilt daemon and deployment id of the original daemon used instead pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { self.deployment_id = Some(deployment_id.into()); self From 85de796676fdc180211680a05c9255cc5b76b7af Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 4 Jun 2024 18:20:38 +0300 Subject: [PATCH 031/108] remove outdated comments --- cw-orch-daemon/src/builder.rs | 2 -- cw-orch-daemon/src/sync/builder.rs | 4 ---- 2 files changed, 6 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 6633bfe5f..2552df577 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -96,8 +96,6 @@ impl DaemonAsyncBuilder { /// Defaults to env variable. /// /// Variable: STATE_FILE_ENV_NAME. - /// - /// This field is ignored for rebuilt daemon and path of the original daemon used instead pub fn state_path(&mut self, path: impl ToString) -> &mut Self { self.state_path = Some(path.to_string()); self diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 0f4001736..467ae2979 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -50,8 +50,6 @@ impl DaemonBuilder { /// Set the deployment id to use for the Daemon interactions /// Defaults to `default` - /// - /// This field is ignored for rebuilt daemon and deployment id of the original daemon used instead pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { self.deployment_id = Some(deployment_id.into()); self @@ -126,8 +124,6 @@ impl DaemonBuilder { /// Defaults to env variable. /// /// Variable: STATE_FILE_ENV_NAME. - /// - /// This field is ignored for rebuilt daemon and path of the original daemon used instead pub fn state_path(&mut self, path: impl ToString) -> &mut Self { self.state_path = Some(path.to_string()); self From c20f68512de228a57a4b007a77994630fb307034 Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 4 Jun 2024 19:19:57 +0300 Subject: [PATCH 032/108] post-merge fixes --- .../examples/daemon-capabilities.rs | 2 +- cw-orch-daemon/src/core.rs | 2 +- cw-orch-daemon/src/json_lock.rs | 2 +- cw-orch-daemon/src/sender.rs | 13 +++-- cw-orch-daemon/src/state.rs | 49 +++++++++++-------- cw-orch-daemon/src/sync/core.rs | 2 +- cw-orch-daemon/tests/index.rs | 8 +-- packages/clone-testing/src/state.rs | 12 +++-- 8 files changed, 50 insertions(+), 40 deletions(-) diff --git a/cw-orch-daemon/examples/daemon-capabilities.rs b/cw-orch-daemon/examples/daemon-capabilities.rs index e960f43fe..4a40b2094 100644 --- a/cw-orch-daemon/examples/daemon-capabilities.rs +++ b/cw-orch-daemon/examples/daemon-capabilities.rs @@ -12,7 +12,7 @@ pub fn main() -> anyhow::Result<()> { std::env::set_var("LOCAL_MNEMONIC", LOCAL_MNEMONIC); let network = networks::LOCAL_JUNO; - let daemon = DaemonBuilder::default().chain(network).build()?; + let mut daemon = DaemonBuilder::default().chain(network).build()?; daemon.flush_state()?; diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index bac713846..3c9379ed8 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -82,7 +82,7 @@ impl DaemonAsync { /// Flushes all the state related to the current chain /// Only works on Local networks - pub fn flush_state(&self) -> Result<(), DaemonError> { + pub fn flush_state(&mut self) -> Result<(), DaemonError> { self.state.flush() } } diff --git a/cw-orch-daemon/src/json_lock.rs b/cw-orch-daemon/src/json_lock.rs index c6479c8fd..898475205 100644 --- a/cw-orch-daemon/src/json_lock.rs +++ b/cw-orch-daemon/src/json_lock.rs @@ -98,7 +98,7 @@ impl Drop for JsonLockedState { pub fn read(filename: &String) -> Result { let file = File::open(filename) - .map_err(|err| DaemonError::OpenFile(filename.to_string(), err.to_string())); + .map_err(|err| DaemonError::OpenFile(filename.to_string(), err.to_string()))?; let json: serde_json::Value = from_reader(file)?; Ok(json) } diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 7e559c254..681d70e52 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -165,7 +165,8 @@ impl Sender { /// Construct a new Sender from a raw key with additional options pub fn from_raw_key_with_options( - daemon_state: &Arc, + chain_info: ChainInfoOwned, + channel: Channel, raw_key: &[u8], options: SenderOptions, ) -> Result, DaemonError> { @@ -175,18 +176,19 @@ impl Sender { raw_key, 0, options.hd_index.unwrap_or(0), - daemon_state.chain_data.network_info.coin_type, + chain_info.network_info.coin_type, )?; let sender = Sender { - daemon_state: daemon_state.clone(), private_key: p_key, secp, options, + grpc_channel: channel, + chain_info, }; log::info!( target: &local_target(), "Interacting with {} using address: {}", - daemon_state.chain_data.chain_id, + sender.chain_info.chain_id, sender.pub_addr_str()? ); Ok(sender) @@ -204,7 +206,8 @@ impl Sender { if options.hd_index.is_some() { // Need to generate new sender as hd_index impacts private key let new_sender = Sender::from_raw_key_with_options( - &self.daemon_state, + self.chain_info.clone(), + self.channel(), &self.private_key.raw_key(), options, ) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 0c32c0fc3..fb82247ac 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -27,10 +27,8 @@ pub struct DaemonState { pub json_state: DaemonStateFile, /// Deployment identifier pub deployment_id: String, - /// Chain Id, ex. "osmosis-1" - pub chain_id: String, - /// Chain Name, ex. "osmosis" - pub chain_name: String, + /// Information about the chain + pub chain_data: ChainInfoOwned, } impl Drop for DaemonState { @@ -62,8 +60,8 @@ impl DaemonState { deployment_id: String, read_only: bool, ) -> Result { - let chain_id = chain_data.chain_id; - let chain_name = chain_data.network_info.chain_name; + let chain_id = &chain_data.chain_id; + let chain_name = &chain_data.network_info.chain_name; log::debug!(target: &local_target(), "Using state file : {}", json_file_path); @@ -102,7 +100,7 @@ impl DaemonState { lock.insert(json_file_path); drop(lock); - json_file_state.prepare(&chain_id, &chain_name, &deployment_id); + json_file_state.prepare(chain_id, chain_name, &deployment_id); DaemonStateFile::FullAccess { json_file_state: Arc::new(Mutex::new(json_file_state)), } @@ -111,8 +109,7 @@ impl DaemonState { Ok(DaemonState { json_state, deployment_id, - chain_id, - chain_name, + chain_data, }) } @@ -157,12 +154,15 @@ impl DaemonState { DaemonStateFile::ReadOnly { path } => { let j = crate::json_lock::read(path)?; - j[&self.chain_id][&self.chain_name].clone() + j[&self.chain_data.chain_id][&self.chain_data.network_info.chain_name].clone() } DaemonStateFile::FullAccess { json_file_state } => json_file_state .lock() .unwrap() - .get(&self.chain_id, &self.chain_name) + .get( + &self.chain_data.chain_id, + &self.chain_data.network_info.chain_name, + ) .clone(), }; Ok(state) @@ -189,7 +189,10 @@ impl DaemonState { }; let mut json_file_lock = json_file_state.lock().unwrap(); - let val = json_file_lock.get_mut(&self.chain_id, &self.chain_name); + let val = json_file_lock.get_mut( + &self.chain_data.chain_id, + &self.chain_data.network_info.chain_name, + ); val[key][contract_id] = json!(value); Ok(()) @@ -209,20 +212,24 @@ impl DaemonState { /// Flushes all the state related to the current chain /// Only works on Local networks - pub fn flush(&self) -> Result<(), DaemonError> { + pub fn flush(&mut self) -> Result<(), DaemonError> { if self.chain_data.kind != ChainKind::Local { panic!("Can only flush local chain state"); } - if self.read_only { - return Err(DaemonError::StateReadOnly); - } - - let mut json = self.read_state()?; + let json_file_state = match &mut self.json_state { + DaemonStateFile::ReadOnly { path } => { + return Err(DaemonError::StateReadOnly(path.clone())) + } + DaemonStateFile::FullAccess { json_file_state } => json_file_state, + }; - json[&self.chain_data.network_info.chain_name][&self.chain_data.chain_id.to_string()] = - json!({}); + let mut lock = json_file_state.lock().unwrap(); + let json = lock.get_mut( + &self.chain_data.chain_id, + &self.chain_data.network_info.chain_name, + ); - serde_json::to_writer_pretty(File::create(&self.json_file_path).unwrap(), &json)?; + *json = json!({}); Ok(()) } } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index e8e31400f..239807a7d 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -78,7 +78,7 @@ impl Daemon { /// Flushes all the state related to the current chain /// Only works on Local networks - pub fn flush_state(&self) -> Result<(), DaemonError> { + pub fn flush_state(&mut self) -> Result<(), DaemonError> { self.daemon.flush_state() } } diff --git a/cw-orch-daemon/tests/index.rs b/cw-orch-daemon/tests/index.rs index 0aca3ab39..fb5264977 100644 --- a/cw-orch-daemon/tests/index.rs +++ b/cw-orch-daemon/tests/index.rs @@ -16,13 +16,7 @@ mod tests { .unwrap(); let daemon_sender = daemon.sender().to_string(); - // TODO: Should be using rebuild here, see ORC-127 - drop(daemon); - let indexed_daemon = Daemon::builder() - .chain(networks::LOCAL_JUNO) - .hd_index(56) - .build() - .unwrap(); + let indexed_daemon = daemon.rebuild().hd_index(56).build().unwrap(); assert_ne!(daemon_sender, indexed_daemon.sender().to_string()); diff --git a/packages/clone-testing/src/state.rs b/packages/clone-testing/src/state.rs index 7eab6172b..ed8a284a1 100644 --- a/packages/clone-testing/src/state.rs +++ b/packages/clone-testing/src/state.rs @@ -21,13 +21,19 @@ pub struct MockState { impl MockState { /// Creates a new empty mock state + // TODO: rt unused + #[allow(unused)] pub fn new(rt: &Handle, chain: ChainInfoOwned, deployment_id: &str) -> Self { Self { addresses: HashMap::new(), code_ids: HashMap::new(), - daemon_state: rt - .block_on(DaemonState::new(chain, deployment_id.to_string(), true)) - .unwrap(), + daemon_state: DaemonState::new( + DaemonState::state_file_path().unwrap(), + chain, + deployment_id.to_string(), + true, + ) + .unwrap(), } } } From e3d489e5f37928264da82c29b3bf4d66cdc124ea Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 4 Jun 2024 19:44:10 +0300 Subject: [PATCH 033/108] update changelog --- CHANGELOG.md | 8 ++++++++ cw-orch-daemon/src/builder.rs | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c340d430..f4cf14d9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,14 @@ - EXCITING FEATURE : Added an item and a map query method to be able to query cw-storage-plus structure outside of contracts easily - Add `flush_state` method for Local Chain Daemons - cw-orch-interchain now errors on checking transactions for IBC packets if NO packets were found +- Add `state_path` method for `DaemonBuilder` and `DaemonAsyncBuilder` to easier manage different state files for different daemons +- `DaemonState` removed from `Sender` +- `Channel` moved from `DaemonState` to `Sender` +- `DaemonState` write-locks file unless it's read-only, meaning it will panic at initialization if other process holds lock of it +- `DaemonState` now can be safely used between threads and processes +- Two non-related Daemon's can't use same file for writing simultaneously (cloned or rebuilt are related Daemon) +- Writing to a file happens when all Daemon's that use same file dropped instead of hot writes +- `force_write` added to the `DaemonState` to allow force write of the state ### Breaking diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 2552df577..c674ed0c8 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -123,7 +123,12 @@ impl DaemonAsyncBuilder { GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?; let state = match &self.state { - Some(state) => state.clone(), + Some(state) => { + let mut state = state.clone(); + state.chain_data = chain_info.clone(); + state.deployment_id = deployment_id; + state + } None => { // If the path is relative, we dis-ambiguate it and take the root at $HOME/$CW_ORCH_STATE_FOLDER let json_file_path = self From 4d84f515ae24ee19ef208ab4a11554e3292a97ae Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 5 Jun 2024 18:09:20 +0300 Subject: [PATCH 034/108] Nicoco nits --- cw-orch-daemon/src/builder.rs | 9 +-------- cw-orch-daemon/src/channel.rs | 4 ++++ cw-orch-daemon/src/json_lock.rs | 4 ++-- cw-orch-daemon/src/sender.rs | 4 ++-- cw-orch-daemon/src/state.rs | 20 +++++++------------- cw-orch-daemon/src/tx_builder.rs | 7 ++----- 6 files changed, 18 insertions(+), 30 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index c674ed0c8..bce34af2a 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use bitcoin::secp256k1::All; use super::{error::DaemonError, sender::Sender, state::DaemonState}; -use cw_orch_core::{environment::ChainInfoOwned, log::connectivity_target}; +use cw_orch_core::environment::ChainInfoOwned; /// The default deployment id if none is provided pub const DEFAULT_DEPLOYMENT: &str = "default"; @@ -112,12 +112,6 @@ impl DaemonAsyncBuilder { .clone() .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); - if chain_info.grpc_urls.is_empty() { - return Err(DaemonError::GRPCListIsEmpty); - } - - log::debug!(target: &connectivity_target(), "Found {} gRPC endpoints", chain_info.grpc_urls.len()); - // find working grpc channel let grpc_channel = GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?; @@ -130,7 +124,6 @@ impl DaemonAsyncBuilder { state } None => { - // If the path is relative, we dis-ambiguate it and take the root at $HOME/$CW_ORCH_STATE_FOLDER let json_file_path = self .state_path .clone() diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/channel.rs index f96cfdfaa..791b99e29 100644 --- a/cw-orch-daemon/src/channel.rs +++ b/cw-orch-daemon/src/channel.rs @@ -12,6 +12,10 @@ pub struct GrpcChannel {} impl GrpcChannel { /// Connect to any of the provided gRPC endpoints pub async fn connect(grpc: &[String], chain_id: &str) -> Result { + if grpc.is_empty() { + return Err(DaemonError::GRPCListIsEmpty); + } + let mut successful_connections = vec![]; for address in grpc.iter() { diff --git a/cw-orch-daemon/src/json_lock.rs b/cw-orch-daemon/src/json_lock.rs index 898475205..c78e42000 100644 --- a/cw-orch-daemon/src/json_lock.rs +++ b/cw-orch-daemon/src/json_lock.rs @@ -69,12 +69,12 @@ impl JsonLockedState { } /// Get a value for read - pub fn get(&self, chain_id: &str, network_id: &str) -> &Value { + pub fn get(&self, network_id: &str, chain_id: &str) -> &Value { &self.json[network_id][chain_id] } /// Give a value to write - pub fn get_mut(&mut self, chain_id: &str, network_id: &str) -> &mut Value { + pub fn get_mut(&mut self, network_id: &str, chain_id: &str) -> &mut Value { self.json[network_id].get_mut(chain_id).unwrap() } diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 681d70e52..2cc790f9b 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -326,7 +326,7 @@ impl Sender { let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let tx_builder = TxBuilder::new(tx_body, self.chain_info.chain_id.clone()); + let tx_builder = TxBuilder::new(tx_body); let gas_needed = tx_builder.simulate(self).await?; @@ -378,7 +378,7 @@ impl Sender { let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); - let tx_builder = TxBuilder::new(tx_body, self.chain_info.chain_id.clone()); + let tx_builder = TxBuilder::new(tx_body); // We retry broadcasting the tx, with the following strategies // 1. In case there is an `incorrect account sequence` error, we can retry as much as possible (doesn't cost anything to the user) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index fb82247ac..1a3cba6ca 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -148,29 +148,23 @@ impl DaemonState { Ok(state_file_path) } - /// Get the chain state as json - fn chain_state(&self) -> Result { - let state = match &self.json_state { + /// Retrieve a stateful value using the chainId and networkId + pub fn get(&self, key: &str) -> Result { + let json = match &self.json_state { DaemonStateFile::ReadOnly { path } => { let j = crate::json_lock::read(path)?; - j[&self.chain_data.chain_id][&self.chain_data.network_info.chain_name].clone() + j[&self.chain_data.network_info.chain_name][&self.chain_data.chain_id].clone() } DaemonStateFile::FullAccess { json_file_state } => json_file_state .lock() .unwrap() .get( - &self.chain_data.chain_id, &self.chain_data.network_info.chain_name, + &self.chain_data.chain_id, ) .clone(), }; - Ok(state) - } - - /// Retrieve a stateful value using the chainId and networkId - pub fn get(&self, key: &str) -> Result { - let json = self.chain_state()?; Ok(json[key].clone()) } @@ -190,8 +184,8 @@ impl DaemonState { let mut json_file_lock = json_file_state.lock().unwrap(); let val = json_file_lock.get_mut( - &self.chain_data.chain_id, &self.chain_data.network_info.chain_name, + &self.chain_data.chain_id, ); val[key][contract_id] = json!(value); @@ -225,8 +219,8 @@ impl DaemonState { let mut lock = json_file_state.lock().unwrap(); let json = lock.get_mut( - &self.chain_data.chain_id, &self.chain_data.network_info.chain_name, + &self.chain_data.chain_id, ); *json = json!({}); diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index 574311b9f..140760c32 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -25,19 +25,16 @@ pub struct TxBuilder { pub(crate) gas_limit: Option, // if defined, use this sequence, else get it from the node pub(crate) sequence: Option, - /// `chain_id` is the unique identifier of the chain this transaction targets. - pub(crate) chain_id: String, } impl TxBuilder { /// Create a new TxBuilder with a given body. - pub fn new(body: Body, chain_id: String) -> Self { + pub fn new(body: Body) -> Self { Self { body, fee_amount: None, gas_limit: None, sequence: None, - chain_id, } } /// Set a fixed fee amount for the tx @@ -162,7 +159,7 @@ impl TxBuilder { let sign_doc = SignDoc::new( &self.body, &auth_info, - &Id::try_from(self.chain_id.clone())?, + &Id::try_from(wallet.chain_info.chain_id.to_string())?, account_number, )?; wallet.sign(sign_doc).map_err(Into::into) From 9d259ed85fca1555da802720e1a78d1498e9954f Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 5 Jun 2024 18:29:55 +0300 Subject: [PATCH 035/108] private `state_path` and fix grpc connection --- CHANGELOG.md | 1 - cw-orch-daemon/src/builder.rs | 16 ++++++++-------- cw-orch-daemon/src/sync/builder.rs | 3 ++- cw-orch-daemon/tests/daemon_state.rs | 21 ++++++++++----------- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4cf14d9c..49c896d16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,6 @@ - EXCITING FEATURE : Added an item and a map query method to be able to query cw-storage-plus structure outside of contracts easily - Add `flush_state` method for Local Chain Daemons - cw-orch-interchain now errors on checking transactions for IBC packets if NO packets were found -- Add `state_path` method for `DaemonBuilder` and `DaemonAsyncBuilder` to easier manage different state files for different daemons - `DaemonState` removed from `Sender` - `Channel` moved from `DaemonState` to `Sender` - `DaemonState` write-locks file unless it's read-only, meaning it will panic at initialization if other process holds lock of it diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index bce34af2a..75c871bd9 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -96,7 +96,8 @@ impl DaemonAsyncBuilder { /// Defaults to env variable. /// /// Variable: STATE_FILE_ENV_NAME. - pub fn state_path(&mut self, path: impl ToString) -> &mut Self { + #[allow(unused)] + pub(crate) fn state_path(&mut self, path: impl ToString) -> &mut Self { self.state_path = Some(path.to_string()); self } @@ -112,10 +113,6 @@ impl DaemonAsyncBuilder { .clone() .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); - // find working grpc channel - let grpc_channel = - GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?; - let state = match &self.state { Some(state) => { let mut state = state.clone(); @@ -139,17 +136,20 @@ impl DaemonAsyncBuilder { Some(sender) => match sender { SenderBuilder::Mnemonic(mnemonic) => Sender::from_mnemonic_with_options( chain_info.clone(), - grpc_channel, + GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?, &mnemonic, sender_options, )?, SenderBuilder::Sender(mut sender) => { sender.set_options(self.sender_options.clone()); - sender.grpc_channel = grpc_channel; sender } }, - None => Sender::new_with_options(chain_info.clone(), grpc_channel, sender_options)?, + None => Sender::new_with_options( + chain_info.clone(), + GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?, + sender_options, + )?, }; let daemon = DaemonAsync { diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index bafebd3fe..e10c2a1bd 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -124,7 +124,8 @@ impl DaemonBuilder { /// Defaults to env variable. /// /// Variable: STATE_FILE_ENV_NAME. - pub fn state_path(&mut self, path: impl ToString) -> &mut Self { + #[allow(unused)] + pub(crate) fn state_path(&mut self, path: impl ToString) -> &mut Self { self.state_path = Some(path.to_string()); self } diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 1f5ba3036..7fc1aee40 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -2,7 +2,8 @@ use std::sync::Arc; use cw_orch_core::environment::ChainState; use cw_orch_daemon::{ - json_lock::JsonLockedState, networks::OSMOSIS_1, DaemonBuilder, DaemonError, DaemonStateFile, + env::STATE_FILE_ENV_NAME, json_lock::JsonLockedState, networks::OSMOSIS_1, DaemonBuilder, + DaemonError, DaemonStateFile, }; pub const DUMMY_MNEMONIC:&str = "chapter wrist alcohol shine angry noise mercy simple rebel recycle vehicle wrap morning giraffe lazy outdoor noise blood ginger sort reunion boss crowd dutch"; @@ -12,10 +13,10 @@ const TEST2_STATE_FILE: &str = "./tests/test2.json"; #[test] #[serial_test::serial] fn simultaneous_read() { + std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); let daemon = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) - .state_path(TEST_STATE_FILE) .build() .unwrap(); @@ -55,10 +56,10 @@ fn simultaneous_read() { #[test] #[serial_test::serial] fn simultaneous_write() { + std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); let daemon = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) - .state_path(TEST_STATE_FILE) .build() .unwrap(); @@ -95,10 +96,10 @@ fn simultaneous_write() { #[test] #[serial_test::serial] fn simultaneous_write_rebuilt() { + std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); let daemon = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) - .state_path(TEST_STATE_FILE) .build() .unwrap(); @@ -156,17 +157,16 @@ fn panic_when_someone_holds_json_file() { #[test] #[serial_test::serial] fn error_when_another_daemon_holds_it() { + std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); let _daemon = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) - .state_path(TEST_STATE_FILE) .build() .unwrap(); let daemon_res = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) - .state_path(TEST_STATE_FILE) .build(); assert!(matches!( @@ -178,10 +178,10 @@ fn error_when_another_daemon_holds_it() { #[test] #[serial_test::serial] fn does_not_error_when_previous_daemon_dropped_state() { + std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); let daemon = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) - .state_path(TEST_STATE_FILE) .build() .unwrap(); @@ -190,7 +190,6 @@ fn does_not_error_when_previous_daemon_dropped_state() { let daemon_res = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) - .state_path(TEST_STATE_FILE) .build(); assert!(daemon_res.is_ok(),); @@ -199,18 +198,18 @@ fn does_not_error_when_previous_daemon_dropped_state() { #[test] #[serial_test::serial] fn does_not_error_when_using_different_files() { + std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); let _daemon = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) - .state_path(TEST_STATE_FILE) .build() .unwrap(); + // Different file + std::env::set_var(STATE_FILE_ENV_NAME, TEST2_STATE_FILE); let daemon_res = DaemonBuilder::default() .chain(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) - // Different file - .state_path(TEST2_STATE_FILE) .build(); assert!(daemon_res.is_ok()); From b9f54a1ea21b34ad289e1fa0520ec05ce0c5c120 Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 5 Jun 2024 18:46:12 +0300 Subject: [PATCH 036/108] need to unset state env --- cw-orch-daemon/src/channel.rs | 2 ++ cw-orch-daemon/tests/daemon_state.rs | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/channel.rs index 791b99e29..96cfb6837 100644 --- a/cw-orch-daemon/src/channel.rs +++ b/cw-orch-daemon/src/channel.rs @@ -102,6 +102,7 @@ mod tests { use speculoos::prelude::*; #[tokio::test] + #[serial_test::serial] async fn no_connection() { let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; let grpcs = &["https://127.0.0.1:99999"]; @@ -121,6 +122,7 @@ mod tests { } #[tokio::test] + #[serial_test::serial] async fn network_grpcs_list_is_empty() { let mut chain = cw_orch_daemon::networks::LOCAL_JUNO; let grpcs = &[]; diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 7fc1aee40..ada2addbc 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -51,6 +51,7 @@ fn simultaneous_read() { } // Error if at least one failed let _ = maybe_err.unwrap(); + std::env::remove_var(STATE_FILE_ENV_NAME); } #[test] @@ -91,6 +92,7 @@ fn simultaneous_write() { } // Error if at least one failed maybe_err.unwrap(); + std::env::remove_var(STATE_FILE_ENV_NAME); } #[test] @@ -132,7 +134,8 @@ fn simultaneous_write_rebuilt() { } } // Error if at least one failed - maybe_err.unwrap() + maybe_err.unwrap(); + std::env::remove_var(STATE_FILE_ENV_NAME); } #[test] @@ -173,6 +176,7 @@ fn error_when_another_daemon_holds_it() { daemon_res, Err(DaemonError::StateAlreadyLocked(_)) )); + std::env::remove_var(STATE_FILE_ENV_NAME); } #[test] @@ -192,7 +196,8 @@ fn does_not_error_when_previous_daemon_dropped_state() { .mnemonic(DUMMY_MNEMONIC) .build(); - assert!(daemon_res.is_ok(),); + assert!(daemon_res.is_ok()); + std::env::remove_var(STATE_FILE_ENV_NAME); } #[test] @@ -213,4 +218,5 @@ fn does_not_error_when_using_different_files() { .build(); assert!(daemon_res.is_ok()); + std::env::remove_var(STATE_FILE_ENV_NAME); } From eadc089e7d92db8cdbda7d13377eb05a7329eb4d Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 5 Jun 2024 20:03:08 +0300 Subject: [PATCH 037/108] fix write, for cases where new file smaller than original(for example flush) --- cw-orch-daemon/src/json_lock.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cw-orch-daemon/src/json_lock.rs b/cw-orch-daemon/src/json_lock.rs index c78e42000..c7451d106 100644 --- a/cw-orch-daemon/src/json_lock.rs +++ b/cw-orch-daemon/src/json_lock.rs @@ -80,6 +80,7 @@ impl JsonLockedState { /// Force write to a file pub fn force_write(&mut self) { + self.lock.file.set_len(0).unwrap(); self.lock.file.rewind().unwrap(); serde_json::to_writer_pretty(&self.lock.file, &self.json).unwrap(); } From 0ff395f839aec17c39472f9dfc9c3ea5efebf0f8 Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 5 Jun 2024 20:03:38 +0300 Subject: [PATCH 038/108] add flush where it's needed --- cw-orch-daemon/tests/daemon_helpers.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cw-orch-daemon/tests/daemon_helpers.rs b/cw-orch-daemon/tests/daemon_helpers.rs index 7a2769323..3f4bc6dc9 100644 --- a/cw-orch-daemon/tests/daemon_helpers.rs +++ b/cw-orch-daemon/tests/daemon_helpers.rs @@ -20,11 +20,13 @@ mod tests { fn helper_traits() { use cw_orch_networks::networks; - let daemon = Daemon::builder() + let mut daemon = Daemon::builder() .chain(networks::LOCAL_JUNO) .build() .unwrap(); + daemon.flush_state().unwrap(); + let sender = daemon.sender(); let contract = mock_contract::MockContract::new( From a795bbe8fd0a9edb744b67c87f454555ef1f4f87 Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 5 Jun 2024 20:03:46 +0300 Subject: [PATCH 039/108] ignore toxic test --- cw-orch-daemon/tests/daemon_state.rs | 39 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index ada2addbc..e0102a410 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -138,25 +138,6 @@ fn simultaneous_write_rebuilt() { std::env::remove_var(STATE_FILE_ENV_NAME); } -#[test] -#[serial_test::serial] -#[should_panic] -fn panic_when_someone_holds_json_file() { - match unsafe { nix::unistd::fork() } { - Ok(nix::unistd::ForkResult::Child) => { - // Occur lock for file for 100 millis - let _state = JsonLockedState::new(TEST_STATE_FILE); - std::thread::sleep(std::time::Duration::from_millis(100)); - } - Ok(nix::unistd::ForkResult::Parent { .. }) => { - // Wait a bit for child to occur lock and try to lock already locked file by child - std::thread::sleep(std::time::Duration::from_millis(50)); - let _state = JsonLockedState::new(TEST_STATE_FILE); - } - Err(_) => (), - } -} - #[test] #[serial_test::serial] fn error_when_another_daemon_holds_it() { @@ -220,3 +201,23 @@ fn does_not_error_when_using_different_files() { assert!(daemon_res.is_ok()); std::env::remove_var(STATE_FILE_ENV_NAME); } + +#[test] +#[serial_test::serial] +#[should_panic] +#[ignore = "Serial don't track forks for some reason, run it manually"] +fn panic_when_someone_holds_json_file() { + match unsafe { nix::unistd::fork() } { + Ok(nix::unistd::ForkResult::Child) => { + // Occur lock for file for 100 millis + let _state = JsonLockedState::new(TEST_STATE_FILE); + std::thread::sleep(std::time::Duration::from_millis(100)); + } + Ok(nix::unistd::ForkResult::Parent { .. }) => { + // Wait a bit for child to occur lock and try to lock already locked file by child + std::thread::sleep(std::time::Duration::from_millis(50)); + let _state = JsonLockedState::new(TEST_STATE_FILE); + } + Err(_) => (), + } +} From 499486a7f355e13c9339de0ed462f245a6b9a918 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 6 Jun 2024 08:39:03 +0000 Subject: [PATCH 040/108] Fixed everything, wallets are allowed --- cw-orch-daemon/src/builder.rs | 12 +- cw-orch-daemon/src/live_mock.rs | 2 +- cw-orch-daemon/src/queriers/cosmwasm.rs | 2 +- cw-orch-daemon/src/senders/base_sender.rs | 4 +- cw-orch-daemon/src/senders/sender_trait.rs | 46 ----- cw-orch-daemon/src/sync/builder.rs | 185 ++++++++++----------- cw-orch-daemon/src/sync/core.rs | 30 ++-- cw-orch-daemon/tests/querier.rs | 4 +- 8 files changed, 116 insertions(+), 169 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 546dacd4f..753b697f6 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,6 +1,6 @@ use crate::{ - log::print_if_log_disabled, senders::sender_trait::SenderTrait, DaemonAsyncBase, DaemonBuilder, - GrpcChannel, Wallet, + log::print_if_log_disabled, senders::sender_trait::SenderTrait, DaemonAsyncBase, + DaemonBuilderBase, GrpcChannel, Wallet, }; use super::{error::DaemonError, state::DaemonState}; @@ -172,9 +172,11 @@ impl DaemonAsyncBuilderBase { } } -impl From for DaemonAsyncBuilder { - fn from(value: DaemonBuilder) -> Self { - DaemonAsyncBuilder { +impl From> + for DaemonAsyncBuilderBase +{ + fn from(value: DaemonBuilderBase) -> Self { + DaemonAsyncBuilderBase { chain: value.chain, deployment_id: value.deployment_id, sender_options: value.sender_options, diff --git a/cw-orch-daemon/src/live_mock.rs b/cw-orch-daemon/src/live_mock.rs index 05d4a5993..2782050c4 100644 --- a/cw-orch-daemon/src/live_mock.rs +++ b/cw-orch-daemon/src/live_mock.rs @@ -82,7 +82,7 @@ impl WasmMockQuerier { let handle = RUNTIME.handle(); match &request { QueryRequest::Wasm(x) => { - let querier = CosmWasm::::new_sync(self.channel.clone(), &handle); + let querier = CosmWasm::::new_sync(self.channel.clone(), handle); match x { WasmQuery::Smart { contract_addr, msg } => { // We forward the request to the cosmwasm querier diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index f08c88e8d..60482fc46 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -18,7 +18,7 @@ use tonic::transport::Channel; /// Querier for the CosmWasm SDK module /// All the async function are prefixed with `_` -pub struct CosmWasm { +pub struct CosmWasm { pub channel: Channel, pub rt_handle: Option, _sender: PhantomData, diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index a2fad5e89..0c698c065 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -38,7 +38,7 @@ use cw_orch_core::{ use crate::env::{LOCAL_MNEMONIC_ENV_NAME, MAIN_MNEMONIC_ENV_NAME, TEST_MNEMONIC_ENV_NAME}; use bitcoin::secp256k1::{All, Context, Secp256k1, Signing}; -use std::{str::FromStr, sync::Arc}; +use std::str::FromStr; use cosmos_modules::vesting::PeriodicVestingAccount; use tonic::transport::Channel; @@ -65,7 +65,7 @@ const SMALL_GAS_BUFFER: f64 = 1.4; // } /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. -pub type Wallet = Arc>; +pub type Wallet = Sender; /// Signer of the transactions and helper for address derivation /// This is the main interface for simulating and signing transactions diff --git a/cw-orch-daemon/src/senders/sender_trait.rs b/cw-orch-daemon/src/senders/sender_trait.rs index 7f7071c5b..7bb75f515 100644 --- a/cw-orch-daemon/src/senders/sender_trait.rs +++ b/cw-orch-daemon/src/senders/sender_trait.rs @@ -4,7 +4,6 @@ use cw_orch_core::environment::ChainInfoOwned; use tonic::transport::Channel; use crate::{CosmTxResponse, DaemonError}; -use std::sync::Arc; pub trait SenderTrait: Clone { type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; @@ -51,48 +50,3 @@ pub trait SenderTrait: Clone { sender_options: Self::SenderOptions, ) -> Result; } - -impl SenderTrait for Arc { - type Error = T::Error; - type SenderOptions = T::SenderOptions; - - fn address(&self) -> Result { - (**self).address() - } - - fn msg_sender(&self) -> Result { - (**self).msg_sender() - } - - fn commit_tx_any( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> impl std::future::Future> + Send { - (**self).commit_tx_any(msgs, memo) - } - - fn chain_info(&self) -> &ChainInfoOwned { - (**self).chain_info() - } - - fn grpc_channel(&self) -> Channel { - (**self).grpc_channel() - } - - fn build( - chain_info: ChainInfoOwned, - grpc_channel: Channel, - sender_options: Self::SenderOptions, - ) -> Result { - Ok(Arc::new(T::build( - chain_info, - grpc_channel, - sender_options, - )?)) - } - - fn set_options(&mut self, options: Self::SenderOptions) { - (**self).set_options(options) - } -} diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index d8275e9bf..4b4bfb11c 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,13 +1,10 @@ use crate::senders::sender_trait::SenderTrait; -use crate::{senders::base_sender::SenderOptions, DaemonAsyncBuilder}; -use crate::{DaemonState, Wallet, RUNTIME}; +use crate::{DaemonAsyncBuilderBase, DaemonBase, DaemonState, Wallet, RUNTIME}; use cw_orch_core::environment::ChainInfoOwned; -use self::generic::DaemonBuilderBase; +use super::super::error::DaemonError; -use super::{super::error::DaemonError, core::Daemon}; - -#[derive(Clone, Default)] +#[derive(Clone)] /// Create [`Daemon`] through [`DaemonBuilder`] /// ## Example /// ```no_run @@ -19,7 +16,7 @@ use super::{super::error::DaemonError, core::Daemon}; /// .build() /// .unwrap(); /// ``` -pub struct DaemonBuilder { +pub struct DaemonBuilderBase { // # Required pub(crate) chain: Option, // # Optional @@ -30,64 +27,44 @@ pub struct DaemonBuilder { pub(crate) gas_fee: Option, pub(crate) state_path: Option, - pub(crate) sender: Option, + pub(crate) sender: Option, // /* Sender Options */ // /// Wallet sender // pub(crate) sender: SenderBuilder, /// Specify Daemon Sender Options - pub(crate) sender_options: SenderOptions, + pub(crate) sender_options: SenderGen::SenderOptions, /* Rebuilder related options */ pub(crate) state: Option, } -impl DaemonBuilder { - /// Set the chain the Daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self +impl Default for DaemonBuilderBase { + fn default() -> Self { + Self { + chain: Default::default(), + handle: Default::default(), + deployment_id: Default::default(), + overwrite_grpc_url: Default::default(), + gas_denom: Default::default(), + gas_fee: Default::default(), + state_path: Default::default(), + sender: Default::default(), + sender_options: Default::default(), + state: Default::default(), + } } +} - /// Set the deployment id to use for the Daemon interactions - /// Defaults to `default` - pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } +pub type DaemonBuilder = DaemonBuilderBase; - /// Set a custom tokio runtime handle to use for the Daemon - /// - /// ## Example - /// ```no_run - /// use cw_orch_daemon::Daemon; - /// use tokio::runtime::Runtime; - /// let rt = Runtime::new().unwrap(); - /// let Daemon = Daemon::builder() - /// .handle(rt.handle()) - /// // ... - /// .build() - /// .unwrap(); - /// ``` - pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { - self.handle = Some(handle.clone()); +impl DaemonBuilder { + /// Set the mnemonic to use with this chain. + pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { + self.sender_options.mnemonic = Some(mnemonic.to_string()); self } - //TODO - // /// Set the mnemonic to use with this chain. - // pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - // self.sender = Some(SenderBuilder::Mnemonic(mnemonic.to_string())); - // self - // } - - // /// Specifies a sender to use with this chain - // /// This will be used in priority when set on the builder - // pub fn sender(&mut self, wallet: Sender) -> &mut Self { - // self.sender = Some(SenderBuilder::Sender(wallet)); - // self - // } - /// Specifies wether authz should be used with this daemon pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { self.sender_options.set_authz_granter(granter.to_string()); @@ -106,12 +83,6 @@ impl DaemonBuilder { self } - /// Overwrites the grpc_url used to interact with the chain - pub fn grpc_url(&mut self, url: &str) -> &mut Self { - self.overwrite_grpc_url = Some(url.to_string()); - self - } - /// Overwrites the gas denom used for broadcasting transactions. /// Behavior : /// - If no gas denom is provided, the first gas denom specified in the `self.chain` is used @@ -121,13 +92,9 @@ impl DaemonBuilder { self.gas_fee = gas_fee.map(Into::into); self } +} - pub fn set_sender( - &self, - sender: SenderGen, - ) -> DaemonBuilderBase { - DaemonBuilderBase::from_wallet_builder(self, sender) - } +impl DaemonBuilderBase { /// Specifies path to the daemon state file /// Defaults to env variable. /// @@ -137,14 +104,71 @@ impl DaemonBuilder { self } + /// Set the chain the Daemon will connect to + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = Some(chain.into()); + self + } + + /// Set the deployment id to use for the Daemon interactions + /// Defaults to `default` + pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + + /// Set a custom tokio runtime handle to use for the Daemon + /// + /// ## Example + /// ```no_run + /// use cw_orch_daemon::Daemon; + /// use tokio::runtime::Runtime; + /// let rt = Runtime::new().unwrap(); + /// let Daemon = Daemon::builder() + /// .handle(rt.handle()) + /// // ... + /// .build() + /// .unwrap(); + /// ``` + pub fn handle(&mut self, handle: &tokio::runtime::Handle) -> &mut Self { + self.handle = Some(handle.clone()); + self + } + + /// Specifies a sender to use with this chain + /// This will be used in priority when set on the builder + pub fn sender( + &self, + wallet: OtherSenderGen, + ) -> DaemonBuilderBase { + DaemonBuilderBase { + chain: self.chain.clone(), + deployment_id: self.deployment_id.clone(), + state_path: self.state_path.clone(), + state: self.state.clone(), + sender: Some(wallet), + sender_options: OtherSenderGen::SenderOptions::default(), + handle: self.handle.clone(), + overwrite_grpc_url: self.overwrite_grpc_url.clone(), + gas_denom: self.gas_denom.clone(), + gas_fee: self.gas_fee, + } + } + + /// Overwrites the grpc_url used to interact with the chain + pub fn grpc_url(&mut self, url: &str) -> &mut Self { + self.overwrite_grpc_url = Some(url.to_string()); + self + } + /// Build a Daemon - pub fn build(&self) -> Result { + pub fn build(&self) -> Result, DaemonError> { let rt_handle = self .handle .clone() .unwrap_or_else(|| RUNTIME.handle().clone()); - let mut chain = self + let chain = self .get_built_chain_object() .ok_or(DaemonError::BuilderMissing("chain information".into()))?; @@ -152,9 +176,9 @@ impl DaemonBuilder { builder.chain = Some(chain); // build the underlying daemon - let daemon = rt_handle.block_on(DaemonAsyncBuilder::from(builder).build())?; + let daemon = rt_handle.block_on(DaemonAsyncBuilderBase::from(builder).build())?; - Ok(Daemon { rt_handle, daemon }) + Ok(DaemonBase { rt_handle, daemon }) } fn get_built_chain_object(&self) -> Option { @@ -181,37 +205,6 @@ fn overwrite_grpc_url(chain: &mut ChainInfoOwned, grpc_url: Option) { } } -pub mod generic { - use cw_orch_core::environment::ChainInfoOwned; - - use crate::{senders::sender_trait::SenderTrait, DaemonBuilder}; - - #[derive(Clone)] - #[non_exhaustive] // To avoid building directly - /// TODO : doc comments - pub struct DaemonBuilderBase { - // # Required - pub(crate) chain: Option, - // # Optional - pub(crate) handle: Option, - pub(crate) deployment_id: Option, - - /* Sender Options */ - pub(crate) sender: SenderGen, - } - - impl DaemonBuilderBase { - pub fn from_wallet_builder(builder: &DaemonBuilder, sender: SenderGen) -> Self { - DaemonBuilderBase { - chain: builder.get_built_chain_object(), - handle: builder.handle.clone(), - deployment_id: builder.deployment_id.clone(), - sender, - } - } - } -} - #[cfg(test)] mod test { use cw_orch_core::environment::TxHandler; diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 585fc161b..30283d3bf 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -2,8 +2,7 @@ use std::fmt::Debug; use super::super::senders::base_sender::Wallet; use crate::{ - queriers::{Bank, CosmWasm, Node}, - CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonError, DaemonState, + queriers::{Bank, CosmWasm, Node}, CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonBuilderBase, DaemonError, DaemonState }; use cosmwasm_std::{Addr, Coin}; use cw_orch_core::{ @@ -53,8 +52,8 @@ pub type Daemon = DaemonBase; impl DaemonBase { /// Get the daemon builder - pub fn builder() -> DaemonBuilder { - DaemonBuilder::default() + pub fn builder() -> DaemonBuilderBase { + DaemonBuilderBase::default() } /// Get the channel configured for this Daemon @@ -67,18 +66,17 @@ impl DaemonBase { self.daemon.sender.clone() } - // /// Returns a new [`DaemonBuilder`] with the current configuration. - // /// Does not consume the original [`Daemon`]. - // pub fn rebuild(&self) -> DaemonBuilderBase { - // let mut builder = DaemonBuilder { - // state: Some(self.state()), - // ..Default::default() - // }; - // builder - // .chain(self.daemon.sender.chain_info.clone()) - // .sender((*self.daemon.sender).clone()); - // builder - // } + /// Returns a new [`DaemonBuilder`] with the current configuration. + /// Does not consume the original [`Daemon`]. + pub fn rebuild(&self) -> DaemonBuilderBase { + let mut builder = DaemonBuilder { + state: Some(self.state()), + ..Default::default() + }; + builder + .chain(self.daemon.sender.chain_info().clone()) + .sender(self.daemon.sender.clone()) + } /// Flushes all the state related to the current chain /// Only works on Local networks diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index a5d94d813..8ced7a6b9 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -134,7 +134,7 @@ mod queriers { let rt = Runtime::new().unwrap(); let channel = rt.block_on(build_channel()); - let cw = CosmWasm::new_async(channel); + let cw = CosmWasm::::new_async(channel); let params = rt.block_on(cw._params()); asserting!("params is ok").that(¶ms).is_ok(); @@ -212,7 +212,7 @@ mod queriers { let rt = Runtime::new().unwrap(); let channel = rt.block_on(build_channel()); - let cosm_wasm = CosmWasm::new_async(channel); + let cosm_wasm = CosmWasm::::new_async(channel); let daemon = Daemon::builder() .chain(networks::LOCAL_JUNO) .build() From 1c9a7abd82536e2015daf9155073316db6956600 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 6 Jun 2024 09:13:32 +0000 Subject: [PATCH 041/108] Added multisender example --- contracts/counter/examples/multi-sender.rs | 53 ++++++++++++++++++ cw-orch-daemon/src/core.rs | 4 +- cw-orch-daemon/src/senders/multiple_sender.rs | 54 ++++++++++++++++--- 3 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 contracts/counter/examples/multi-sender.rs diff --git a/contracts/counter/examples/multi-sender.rs b/contracts/counter/examples/multi-sender.rs new file mode 100644 index 000000000..319c0fd93 --- /dev/null +++ b/contracts/counter/examples/multi-sender.rs @@ -0,0 +1,53 @@ +// ANCHOR: full_counter_example +use counter_contract::{ + msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, CounterQueryMsgFns, +}; +use cw_orch::{ + anyhow, + daemon::{senders::multiple_sender::MultipleSender, DaemonBuilderBase}, + prelude::*, +}; + +// From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 +const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; +pub fn main() -> anyhow::Result<()> { + std::env::set_var("LOCAL_MNEMONIC", LOCAL_MNEMONIC); + dotenv::dotenv().ok(); // Used to load the `.env` file if any + pretty_env_logger::init(); // Used to log contract and chain interactions + + let network = networks::LOCAL_JUNO; + let chain = DaemonBuilderBase::::default() + .chain(network) + .build()?; + + let counter = CounterContract::new(chain.clone()); + + counter.upload()?; + counter.instantiate(&InstantiateMsg { count: 0 }, None, None)?; + + counter.increment()?; + + // The count hasn't been incremented yet, we didn't broadcast the tx + let count = counter.get_count()?; + assert_eq!(count.count, 0); + + chain.rt_handle.block_on(chain.wallet().broadcast(None))?; + + let count = counter.get_count()?; + assert_eq!(count.count, 1); + + // Increment multiple times in the same transaction + counter.increment()?; + counter.increment()?; + counter.increment()?; + counter.increment()?; + counter.increment()?; + counter.increment()?; + + chain.rt_handle.block_on(chain.wallet().broadcast(None))?; + + let count = counter.get_count()?; + assert_eq!(count.count, 7); + + Ok(()) +} diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 8cf398f13..55a43e517 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -32,6 +32,8 @@ use tonic::transport::Channel; use crate::senders::sender_trait::SenderTrait; +pub const INSTANTIATE_2_TYPE_URL: &str = "/cosmwasm.wasm.v1.MsgInstantiateContract2"; + #[derive(Clone)] /** Represents a blockchain node. @@ -197,7 +199,7 @@ impl DaemonAsyncBase { let result = sender .commit_tx_any( vec![Any { - type_url: "/cosmwasm.wasm.v1.MsgInstantiateContract2".to_string(), + type_url: INSTANTIATE_2_TYPE_URL.to_string(), value: init_msg.encode_to_vec(), }], None, diff --git a/cw-orch-daemon/src/senders/multiple_sender.rs b/cw-orch-daemon/src/senders/multiple_sender.rs index f7586b0d9..1e7b6f53a 100644 --- a/cw-orch-daemon/src/senders/multiple_sender.rs +++ b/cw-orch-daemon/src/senders/multiple_sender.rs @@ -1,9 +1,12 @@ -use crate::Wallet; +use crate::{Wallet, INSTANTIATE_2_TYPE_URL}; use crate::{error::DaemonError, tx_resp::CosmTxResponse}; +use cosmrs::proto::cosmwasm::wasm::v1::{MsgInstantiateContract, MsgStoreCode}; use cosmrs::{AccountId, Any}; use cosmwasm_std::Addr; +use cw_orch_core::log::transaction_target; +use prost::Name; use std::sync::{Arc, Mutex}; @@ -13,6 +16,8 @@ use super::{base_sender::SenderOptions, sender_trait::SenderTrait}; /// This is the main interface for simulating and signing transactions #[derive(Clone)] pub struct MultipleSender { + /// Contains the different messages to broadcast + /// These are behind an Arc Mutex, because `commit_tx_any function` doesn't have access to a mutable reference to the object pub msgs: Arc>>, pub sender: Wallet, } @@ -24,12 +29,34 @@ impl SenderTrait for MultipleSender { async fn commit_tx_any( &self, msgs: Vec, - _memo: Option<&str>, + memo: Option<&str>, ) -> Result { - let mut msg_storage = self.msgs.lock().unwrap(); - msg_storage.extend(msgs); + // We check the type URLS. We can safely put them inside the lock if they DON'T correspond to the following: + // - Code Upload + // - Contract Instantiation (1 and 2) - Ok(CosmTxResponse::default()) + let broadcast_immediately_type_urls = [ + MsgStoreCode::type_url(), + MsgInstantiateContract::type_url(), + INSTANTIATE_2_TYPE_URL.to_string(), + ]; + + let broadcast_immediately = msgs + .iter() + .any(|msg| broadcast_immediately_type_urls.contains(&msg.type_url)); + + if broadcast_immediately { + self.sender.commit_tx_any(msgs, memo).await + } else { + log::info!( + target: &transaction_target(), + "Transaction not sent, use `DaemonBase::wallet().broadcast(), to broadcast the batched transactions", + ); + let mut msg_storage = self.msgs.lock().unwrap(); + msg_storage.extend(msgs); + + Ok(CosmTxResponse::default()) + } } fn address(&self) -> Result { @@ -65,8 +92,21 @@ impl SenderTrait for MultipleSender { } impl MultipleSender { - pub async fn flush(&self, memo: Option<&str>) -> Result { + pub async fn broadcast(&self, memo: Option<&str>) -> Result { let msgs = self.msgs.lock().unwrap().to_vec(); - self.sender.commit_tx_any(msgs, memo).await + log::info!( + target: &transaction_target(), + "[Broadcast] {} msgs in a single transaction", + msgs.len() + ); + let tx_result = self.sender.commit_tx_any(msgs, memo).await?; + log::info!( + target: &transaction_target(), + "[Broadcasted] Success", + ); + + let mut msgs_to_empty = self.msgs.lock().unwrap(); + *msgs_to_empty = vec![]; + Ok(tx_result) } } From 0c1a007a4321a644870c21893bfbfafee76c8167 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 6 Jun 2024 09:15:38 +0000 Subject: [PATCH 042/108] Added multidaemon type --- contracts/counter/examples/multi-sender.rs | 10 ++-------- cw-orch-daemon/src/senders/multiple_sender.rs | 4 +++- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/contracts/counter/examples/multi-sender.rs b/contracts/counter/examples/multi-sender.rs index 319c0fd93..25a650fa4 100644 --- a/contracts/counter/examples/multi-sender.rs +++ b/contracts/counter/examples/multi-sender.rs @@ -2,11 +2,7 @@ use counter_contract::{ msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, CounterQueryMsgFns, }; -use cw_orch::{ - anyhow, - daemon::{senders::multiple_sender::MultipleSender, DaemonBuilderBase}, - prelude::*, -}; +use cw_orch::{anyhow, daemon::senders::multiple_sender::MultiDaemon, prelude::*}; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; @@ -16,9 +12,7 @@ pub fn main() -> anyhow::Result<()> { pretty_env_logger::init(); // Used to log contract and chain interactions let network = networks::LOCAL_JUNO; - let chain = DaemonBuilderBase::::default() - .chain(network) - .build()?; + let chain = MultiDaemon::builder().chain(network).build()?; let counter = CounterContract::new(chain.clone()); diff --git a/cw-orch-daemon/src/senders/multiple_sender.rs b/cw-orch-daemon/src/senders/multiple_sender.rs index 1e7b6f53a..bf979d80e 100644 --- a/cw-orch-daemon/src/senders/multiple_sender.rs +++ b/cw-orch-daemon/src/senders/multiple_sender.rs @@ -1,4 +1,4 @@ -use crate::{Wallet, INSTANTIATE_2_TYPE_URL}; +use crate::{DaemonBase, Wallet, INSTANTIATE_2_TYPE_URL}; use crate::{error::DaemonError, tx_resp::CosmTxResponse}; @@ -12,6 +12,8 @@ use std::sync::{Arc, Mutex}; use super::{base_sender::SenderOptions, sender_trait::SenderTrait}; +pub type MultiDaemon = DaemonBase; + /// Signer of the transactions and helper for address derivation /// This is the main interface for simulating and signing transactions #[derive(Clone)] From 95a353be942f0b322a8bf95e531d6b0958658491 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 6 Jun 2024 09:54:00 +0000 Subject: [PATCH 043/108] Fix re-export --- cw-orch-daemon/src/senders/base_sender.rs | 15 --------------- cw-orch/src/daemon.rs | 2 +- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index 0c698c065..0a6a5e9fe 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -49,21 +49,6 @@ const GAS_BUFFER: f64 = 1.3; const BUFFER_THRESHOLD: u64 = 200_000; const SMALL_GAS_BUFFER: f64 = 1.4; -// /// This enum allows for choosing which sender type will be constructed in a DaemonBuilder -// #[derive(Clone)] -// pub enum SenderBuilder { -// Sender(SenderGen), -// DefaultSender(Sender), -// Mnemonic(String), -// None, -// } - -// impl Default for SenderBuilder { -// fn default() -> Self { -// Self::None -// } -// } - /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. pub type Wallet = Sender; diff --git a/cw-orch/src/daemon.rs b/cw-orch/src/daemon.rs index fadd642cb..bbca578b1 100644 --- a/cw-orch/src/daemon.rs +++ b/cw-orch/src/daemon.rs @@ -1,5 +1,5 @@ //! `Daemon` and `DaemonAsync` execution environments. //! //! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. - +pub use cw_orch_daemon::senders::base_sender::Wallet; pub use cw_orch_daemon::*; From eadf4af6da7e3a3d83db17dbb78e32bbe534129f Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 6 Jun 2024 09:57:23 +0000 Subject: [PATCH 044/108] fix doc tests --- packages/clone-testing/src/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clone-testing/src/core.rs b/packages/clone-testing/src/core.rs index 1cc445eea..41c8f6740 100644 --- a/packages/clone-testing/src/core.rs +++ b/packages/clone-testing/src/core.rs @@ -62,7 +62,7 @@ pub type CloneTestingApp = App; /// /// let rt = tokio::runtime::Runtime::new().unwrap(); /// let chain = cw_orch_daemon::networks::JUNO_1; -/// let mock: CloneTesting = CloneTesting::new_custom(&rt, chain.clone(), CustomState::new(&rt.handle(), chain.clone().into(), "mock")).unwrap(); +/// let mock: CloneTesting = CloneTesting::new_custom(&rt, chain.clone(), CustomState::new(chain.clone().into(), "mock")).unwrap(); /// ``` #[derive(Clone)] pub struct CloneTesting { From f9d96932be576a869c47f1d2d1169a64618a01cf Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 11 Jun 2024 10:49:00 +0000 Subject: [PATCH 045/108] Added manual sender --- cw-orch-daemon/src/senders/base_sender.rs | 11 +- cw-orch-daemon/src/senders/manual_sender.rs | 178 ++++++++++++++++++++ cw-orch-daemon/src/senders/mod.rs | 1 + cw-orch-daemon/src/tx_builder.rs | 9 +- 4 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 cw-orch-daemon/src/senders/manual_sender.rs diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index 0a6a5e9fe..c31d93db6 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -37,7 +37,7 @@ use cw_orch_core::{ }; use crate::env::{LOCAL_MNEMONIC_ENV_NAME, MAIN_MNEMONIC_ENV_NAME, TEST_MNEMONIC_ENV_NAME}; -use bitcoin::secp256k1::{All, Context, Secp256k1, Signing}; +use bitcoin::secp256k1::{All, Secp256k1, Signing}; use std::str::FromStr; use cosmos_modules::vesting::PeriodicVestingAccount; @@ -55,7 +55,7 @@ pub type Wallet = Sender; /// 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 struct Sender { pub private_key: PrivateKey, pub secp: Secp256k1, /// gRPC channel @@ -373,7 +373,12 @@ impl Sender { sequence: u64, account_number: u64, ) -> Result { - let fee = TxBuilder::build_fee(0u8, &self.chain_info.gas_denom, 0, self.options.clone())?; + let fee = TxBuilder::build_fee( + 0u8, + &self.chain_info.gas_denom, + 0, + self.options.fee_granter.clone(), + )?; let auth_info = SignerInfo { public_key: self.private_key.get_signer_public_key(&self.secp), diff --git a/cw-orch-daemon/src/senders/manual_sender.rs b/cw-orch-daemon/src/senders/manual_sender.rs new file mode 100644 index 000000000..ea42c4263 --- /dev/null +++ b/cw-orch-daemon/src/senders/manual_sender.rs @@ -0,0 +1,178 @@ +use crate::proto::injective::InjectiveEthAccount; +use crate::queriers::Node; +use crate::tx_broadcaster::assert_broadcast_code_cosm_response; +use crate::{cosmos_modules, DaemonBase, TxBuilder}; + +use crate::{error::DaemonError, tx_resp::CosmTxResponse}; + +use cosmrs::proto::cosmos; +use cosmrs::proto::cosmos::auth::v1beta1::BaseAccount; +use cosmrs::proto::cosmos::vesting::v1beta1::PeriodicVestingAccount; +use cosmrs::tendermint::chain::Id; +use cosmrs::tx::{ModeInfo, Raw, SignDoc, SignMode, SignerInfo}; +use cosmrs::{AccountId, Any}; +use cosmwasm_std::Addr; +use cw_orch_core::environment::ChainInfoOwned; +use prost::Message; +use tonic::transport::Channel; + +use std::io::{self, BufRead}; + +use super::sender_trait::SenderTrait; + +pub type ManualDaemon = DaemonBase; + +#[derive(Clone, Default)] +pub struct ManualSenderOptions { + pub sender_address: Option, +} + +/// Signer of the transactions and helper for address derivation +/// This is the main interface for simulating and signing transactions +#[derive(Clone)] +pub struct ManualSender { + pub sender: Addr, + pub chain_info: ChainInfoOwned, + pub grpc_channel: Channel, +} + +impl SenderTrait for ManualSender { + type Error = DaemonError; + type SenderOptions = ManualSenderOptions; + + async fn commit_tx_any( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> Result { + // We simulate + let gas_needed = self.simulate(msgs, memo).await?; + + log::info!("Here is the transaction to sign and broadcast: "); + log::info!("Gas needed: {}", gas_needed); + + log::info!("Enter the txhash to proceed"); + let stdin = io::stdin(); + let txhash = stdin.lock().lines().next().unwrap().unwrap(); + + // We print the any messages to broadcast + + // We wait for the txhash as input to be able to continue the execution + + let resp = Node::new_async(self.grpc_channel()) + ._find_tx(txhash) + .await?; + + assert_broadcast_code_cosm_response(resp) + } + + fn address(&self) -> Result { + Ok(self.sender.clone()) + } + + fn msg_sender(&self) -> Result { + self.sender.clone().to_string().parse().map_err(Into::into) + } + + fn chain_info(&self) -> &cw_orch_core::environment::ChainInfoOwned { + &self.chain_info + } + + fn grpc_channel(&self) -> tonic::transport::Channel { + self.grpc_channel.clone() + } + + fn build( + chain_info: cw_orch_core::environment::ChainInfoOwned, + grpc_channel: tonic::transport::Channel, + sender_options: Self::SenderOptions, + ) -> Result { + Ok(Self { + chain_info, + grpc_channel, + sender: Addr::unchecked( + sender_options + .sender_address + .expect("Manual sender needs an address"), + ), + }) + } + + fn set_options(&mut self, options: Self::SenderOptions) { + self.sender = Addr::unchecked( + options + .sender_address + .expect("Manual sender needs an address"), + ) + } +} + +impl ManualSender { + pub async fn simulate(&self, msgs: Vec, memo: Option<&str>) -> Result { + let timeout_height = Node::new_async(self.grpc_channel())._block_height().await? + 10u64; + + let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); + + let fee = TxBuilder::build_fee(0u8, &self.chain_info.gas_denom, 0, None)?; + + let BaseAccount { + account_number, + sequence, + pub_key, + .. + } = self.base_account().await?; + + let auth_info = SignerInfo { + public_key: pub_key.map(|key| key.try_into()).transpose()?, + mode_info: ModeInfo::single(SignMode::Direct), + sequence, + } + .auth_info(fee); + + let sign_doc = SignDoc::new( + &tx_body, + &auth_info, + &Id::try_from(self.chain_info.chain_id.to_string())?, + account_number, + )?; + + let tx_raw: Raw = cosmos::tx::v1beta1::TxRaw { + body_bytes: sign_doc.body_bytes, + auth_info_bytes: sign_doc.auth_info_bytes, + signatures: vec![vec![]], + } + .into(); + + Node::new_async(self.grpc_channel()) + ._simulate_tx(tx_raw.to_bytes()?) + .await + } + + async fn base_account(&self) -> Result { + let addr = self.address()?.to_string(); + + let mut client = cosmos_modules::auth::query_client::QueryClient::new(self.grpc_channel()); + + let resp = client + .account(cosmos_modules::auth::QueryAccountRequest { address: addr }) + .await? + .into_inner(); + + let account = resp.account.unwrap().value; + + let acc = if let Ok(acc) = BaseAccount::decode(account.as_ref()) { + acc + } else if let Ok(acc) = PeriodicVestingAccount::decode(account.as_ref()) { + // try vesting account, (used by Terra2) + acc.base_vesting_account.unwrap().base_account.unwrap() + } else if let Ok(acc) = InjectiveEthAccount::decode(account.as_ref()) { + acc.base_account.unwrap() + } else { + return Err(DaemonError::StdErr( + "Unknown account type returned from QueryAccountRequest".into(), + )); + }; + + Ok(acc) + } +} diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index 90e791044..1ed72e93d 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -1,3 +1,4 @@ pub mod base_sender; +pub mod manual_sender; pub mod multiple_sender; pub mod sender_trait; diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index f8b466e32..e978fb97f 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -66,14 +66,11 @@ impl TxBuilder { amount: impl Into, denom: &str, gas_limit: u64, - sender_options: SenderOptions, + fee_granter: Option, ) -> Result { let fee = Coin::new(amount.into(), denom).unwrap(); let mut fee = Fee::from_amount_and_gas(fee, gas_limit); - fee.granter = sender_options - .fee_granter - .map(|g| AccountId::from_str(&g)) - .transpose()?; + fee.granter = fee_granter.map(|g| AccountId::from_str(&g)).transpose()?; Ok(fee) } @@ -138,7 +135,7 @@ impl TxBuilder { tx_fee, &wallet.get_fee_token(), gas_limit, - wallet.options.clone(), + wallet.options.fee_granter.clone(), )?; log::debug!( From ec728dde3999faee98d9b628520e7238d8612b15 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 11 Jun 2024 11:04:18 +0000 Subject: [PATCH 046/108] Added manual sender example --- contracts/counter/examples/manual-sender.rs | 34 +++++++++++++++++++++ cw-orch-daemon/src/sync/builder.rs | 6 ++++ 2 files changed, 40 insertions(+) create mode 100644 contracts/counter/examples/manual-sender.rs diff --git a/contracts/counter/examples/manual-sender.rs b/contracts/counter/examples/manual-sender.rs new file mode 100644 index 000000000..710240755 --- /dev/null +++ b/contracts/counter/examples/manual-sender.rs @@ -0,0 +1,34 @@ +// ANCHOR: full_counter_example +use counter_contract::{msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns}; +use cw_orch::{ + anyhow, + daemon::senders::manual_sender::{ManualDaemon, ManualSenderOptions}, + prelude::*, +}; + +// THis is a test with a manual sender, to verify everything works, nothing is broadcasted + +// From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 +pub fn main() -> anyhow::Result<()> { + dotenv::dotenv().ok(); // Used to load the `.env` file if any + pretty_env_logger::init(); // Used to log contract and chain interactions + + let network = networks::JUNO_1; + let sender = "juno1xjf5xscdk08c5es2m7epmerrpqmkmc3n98650t"; + let chain = ManualDaemon::builder() + .options(ManualSenderOptions { + sender_address: Some(sender.to_string()), + }) + .chain(network) + .build()?; + + let counter = CounterContract::new(chain.clone()); + + // Example tx hash that succeed (correspond to a code upload tx) + // 58AA802705BEE4597A560FBC67F6C86400E66F5FCBD0F08AA37FB140BCD65B6D + // If not found, try to find the latests juno code uploaded (4380+) + // https://www.mintscan.io/juno/wasm/code/4380 + counter.upload()?; + + Ok(()) +} diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index d12f23536..39ff78205 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -136,6 +136,12 @@ impl DaemonBuilderBase { } } + /// Specifies sender builder options + pub fn options(&mut self, options: SenderGen::SenderOptions) -> &mut Self { + self.sender_options = options; + self + } + /// Overwrites the grpc_url used to interact with the chain pub fn grpc_url(&mut self, url: &str) -> &mut Self { self.overwrite_grpc_url = Some(url.to_string()); From 95b788a2940c8031fa96f7202e66a1916c8c9b9b Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 11 Jun 2024 11:04:44 +0000 Subject: [PATCH 047/108] formatting --- contracts/counter/examples/manual-sender.rs | 2 +- cw-orch-daemon/src/tx_builder.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/counter/examples/manual-sender.rs b/contracts/counter/examples/manual-sender.rs index 710240755..723e348ef 100644 --- a/contracts/counter/examples/manual-sender.rs +++ b/contracts/counter/examples/manual-sender.rs @@ -1,5 +1,5 @@ // ANCHOR: full_counter_example -use counter_contract::{msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns}; +use counter_contract::CounterContract; use cw_orch::{ anyhow, daemon::senders::manual_sender::{ManualDaemon, ManualSenderOptions}, diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index e978fb97f..2d0405d31 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -11,8 +11,6 @@ use cosmrs::{ }; use cw_orch_core::log::transaction_target; -use crate::senders::base_sender::SenderOptions; - use super::{senders::base_sender::Sender, DaemonError}; /// Struct used to build a raw transaction and broadcast it with a sender. From a346e5a7585c0848e26a4e4de3fcc730f74130d3 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 18 Jun 2024 16:37:20 +0000 Subject: [PATCH 048/108] formatting --- cw-orch-daemon/src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 5ea75ed36..d471c511c 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -112,7 +112,7 @@ impl DaemonAsyncBuilderBase { state: self.state.clone(), sender: Some(wallet), sender_options: OtherSenderGen::SenderOptions::default(), - write_on_change: self.write_on_change.clone(), + write_on_change: self.write_on_change, } } From c847ab8f307dd50816fcd8ad30abb77d5a2f5ba0 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 18 Jun 2024 17:30:34 +0000 Subject: [PATCH 049/108] Using mnemonic inside options --- cw-orch-daemon/src/senders/base_sender.rs | 11 ++++++++++- cw-orch-daemon/tests/authz.rs | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index c31d93db6..819b20be5 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -166,7 +166,16 @@ impl SenderTrait for Sender { } fn set_options(&mut self, options: Self::SenderOptions) { - if options.hd_index.is_some() { + if let Some(mnemonic) = options.mnemonic.clone() { + let new_sender = Sender::from_mnemonic_with_options( + self.chain_info.clone(), + self.channel(), + &mnemonic, + options, + ) + .unwrap(); + *self = new_sender; + } else if options.hd_index.is_some() { // Need to generate new sender as hd_index impacts private key let new_sender = Sender::from_raw_key_with_options( self.chain_info.clone(), diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index c2a662796..c32da47d6 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -63,6 +63,8 @@ mod tests { authorization: Some(authorization.clone()), expiration: Some(expiration.clone()), }; + + println!("{:?}-{:?}", sender, grantee); // We start by granting authz to an account daemon.commit_any::( vec![Any { From 51c9b8c3326e036957ea4a948360cf4486eca666 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 18 Jun 2024 17:37:21 +0000 Subject: [PATCH 050/108] Added mrsv for async traits --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 4b80e62d8..befa655d7 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -79,7 +79,7 @@ jobs: # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability strategy: matrix: - msrv: [1.73.0] # dep/feat syntax fixed with co-exist dep + msrv: [1.75.0] # dep/feat syntax fixed with co-exist dep name: ubuntu / ${{ matrix.msrv }} steps: - uses: actions/checkout@v4 From 079ee2a1b9c89a3f26d088d60dc5af1b13174d92 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Wed, 19 Jun 2024 09:56:58 +0000 Subject: [PATCH 051/108] Removed one packet assertion --- packages/interchain/interchain-core/Cargo.toml | 2 +- packages/interchain/interchain-core/src/env.rs | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/interchain/interchain-core/Cargo.toml b/packages/interchain/interchain-core/Cargo.toml index e2bcf6e3d..a7d04c99b 100644 --- a/packages/interchain/interchain-core/Cargo.toml +++ b/packages/interchain/interchain-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-orch-interchain-core" -version = "0.3.2" +version = "0.3.3" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/packages/interchain/interchain-core/src/env.rs b/packages/interchain/interchain-core/src/env.rs index 00cdfbf18..6e160c322 100644 --- a/packages/interchain/interchain-core/src/env.rs +++ b/packages/interchain/interchain-core/src/env.rs @@ -237,11 +237,6 @@ pub trait InterchainEnv { ) -> Result, InterchainError> { let tx_result = self.wait_ibc(chain_id, tx_response).map_err(Into::into)?; - ensure!( - !tx_result.get_success_packets()?.is_empty(), - InterchainError::NoPacketsFound {} - ); - tx_result.into_result()?; Ok(tx_result) From e1f1f64fb363b779447bdf330085949a5c1e4470 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Wed, 19 Jun 2024 09:59:28 +0000 Subject: [PATCH 052/108] Nit --- packages/interchain/interchain-core/src/env.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interchain/interchain-core/src/env.rs b/packages/interchain/interchain-core/src/env.rs index 6e160c322..22084ea48 100644 --- a/packages/interchain/interchain-core/src/env.rs +++ b/packages/interchain/interchain-core/src/env.rs @@ -1,6 +1,6 @@ //! This module contains the trait definition for an interchain analysis environment -use cosmwasm_std::{ensure, Binary, IbcOrder}; +use cosmwasm_std::{Binary, IbcOrder}; use cw_orch_core::{ contract::interface_traits::ContractInstance, environment::{CwEnv, IndexResponse, TxHandler}, From 23b0d7bc76b78bb720d0edee55109a129b458ff3 Mon Sep 17 00:00:00 2001 From: Kayanski <44806566+Kayanski@users.noreply.github.com> Date: Tue, 25 Jun 2024 15:52:59 +0200 Subject: [PATCH 053/108] Update contracts/counter/examples/manual-sender.rs Co-authored-by: Mykhailo Donchenko <91957742+Buckram123@users.noreply.github.com> --- contracts/counter/examples/manual-sender.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/counter/examples/manual-sender.rs b/contracts/counter/examples/manual-sender.rs index 723e348ef..bbc88120b 100644 --- a/contracts/counter/examples/manual-sender.rs +++ b/contracts/counter/examples/manual-sender.rs @@ -6,7 +6,7 @@ use cw_orch::{ prelude::*, }; -// THis is a test with a manual sender, to verify everything works, nothing is broadcasted +// This is a test with a manual sender, to verify everything works, nothing is broadcasted // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 pub fn main() -> anyhow::Result<()> { From ba1f2655ed11d913c5c966030979f075b5183cfa Mon Sep 17 00:00:00 2001 From: Kayanski <44806566+Kayanski@users.noreply.github.com> Date: Tue, 25 Jun 2024 15:53:17 +0200 Subject: [PATCH 054/108] Update cw-orch-daemon/src/builder.rs Co-authored-by: Mykhailo Donchenko <91957742+Buckram123@users.noreply.github.com> --- cw-orch-daemon/src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 41640b2d3..55241d2c3 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -51,7 +51,7 @@ impl Default for DaemonAsyncBuilderBase { sender: Default::default(), state_path: Default::default(), state: Default::default(), - write_on_change: None, + write_on_change: Default::default(), } } } From b8899cb7569dbaca916fb3f2824341b38e260b30 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 25 Jun 2024 14:25:07 +0000 Subject: [PATCH 055/108] Nits --- .../counter/examples/{multi-sender.rs => batch-sender.rs} | 4 ++-- .../src/senders/{multiple_sender.rs => batch_sender.rs} | 8 ++++---- cw-orch-daemon/src/senders/mod.rs | 2 +- cw-orch-daemon/tests/authz.rs | 1 - 4 files changed, 7 insertions(+), 8 deletions(-) rename contracts/counter/examples/{multi-sender.rs => batch-sender.rs} (91%) rename cw-orch-daemon/src/senders/{multiple_sender.rs => batch_sender.rs} (96%) diff --git a/contracts/counter/examples/multi-sender.rs b/contracts/counter/examples/batch-sender.rs similarity index 91% rename from contracts/counter/examples/multi-sender.rs rename to contracts/counter/examples/batch-sender.rs index 25a650fa4..2e5909eee 100644 --- a/contracts/counter/examples/multi-sender.rs +++ b/contracts/counter/examples/batch-sender.rs @@ -2,7 +2,7 @@ use counter_contract::{ msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, CounterQueryMsgFns, }; -use cw_orch::{anyhow, daemon::senders::multiple_sender::MultiDaemon, prelude::*}; +use cw_orch::{anyhow, daemon::senders::batch_sender::BatchDaemon, prelude::*}; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; @@ -12,7 +12,7 @@ pub fn main() -> anyhow::Result<()> { pretty_env_logger::init(); // Used to log contract and chain interactions let network = networks::LOCAL_JUNO; - let chain = MultiDaemon::builder().chain(network).build()?; + let chain = BatchDaemon::builder().chain(network).build()?; let counter = CounterContract::new(chain.clone()); diff --git a/cw-orch-daemon/src/senders/multiple_sender.rs b/cw-orch-daemon/src/senders/batch_sender.rs similarity index 96% rename from cw-orch-daemon/src/senders/multiple_sender.rs rename to cw-orch-daemon/src/senders/batch_sender.rs index 7c34a5d0b..9a3e3e479 100644 --- a/cw-orch-daemon/src/senders/multiple_sender.rs +++ b/cw-orch-daemon/src/senders/batch_sender.rs @@ -12,19 +12,19 @@ use std::sync::{Arc, Mutex}; use super::{base_sender::SenderOptions, sender_trait::SenderTrait}; -pub type MultiDaemon = DaemonBase; +pub type BatchDaemon = DaemonBase; /// Signer of the transactions and helper for address derivation /// This is the main interface for simulating and signing transactions #[derive(Clone)] -pub struct MultipleSender { +pub struct BatchSender { /// Contains the different messages to broadcast /// These are behind an Arc Mutex, because `commit_tx_any function` doesn't have access to a mutable reference to the object pub msgs: Arc>>, pub sender: Wallet, } -impl SenderTrait for MultipleSender { +impl SenderTrait for BatchSender { type Error = DaemonError; type SenderOptions = SenderOptions; @@ -93,7 +93,7 @@ impl SenderTrait for MultipleSender { } } -impl MultipleSender { +impl BatchSender { pub async fn broadcast(&self, memo: Option<&str>) -> Result { let msgs = self.msgs.lock().unwrap().to_vec(); log::info!( diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index 1ed72e93d..a97489474 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -1,4 +1,4 @@ pub mod base_sender; +pub mod batch_sender; pub mod manual_sender; -pub mod multiple_sender; pub mod sender_trait; diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index c32da47d6..930d694c4 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -64,7 +64,6 @@ mod tests { expiration: Some(expiration.clone()), }; - println!("{:?}-{:?}", sender, grantee); // We start by granting authz to an account daemon.commit_any::( vec![Any { From c654a80b643a7e89d6fcefb75391806732cd9b5b Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 25 Jun 2024 14:38:40 +0000 Subject: [PATCH 056/108] Nits --- cw-orch-daemon/src/builder.rs | 28 ++++++++++----------- cw-orch-daemon/src/core.rs | 18 ++++++------- cw-orch-daemon/src/queriers/bank.rs | 4 +-- cw-orch-daemon/src/queriers/cosmwasm.rs | 22 ++++++++-------- cw-orch-daemon/src/queriers/env.rs | 2 +- cw-orch-daemon/src/queriers/node.rs | 4 +-- cw-orch-daemon/src/senders/base_sender.rs | 14 +++++------ cw-orch-daemon/src/senders/manual_sender.rs | 24 ++++++++++-------- cw-orch-daemon/src/senders/sender_trait.rs | 2 -- cw-orch-daemon/src/sync/builder.rs | 22 ++++++++-------- cw-orch-daemon/src/sync/core.rs | 26 +++++++++---------- cw-orch-daemon/src/tx_broadcaster.rs | 7 +++--- cw-orch-daemon/src/tx_builder.rs | 9 ++++--- 13 files changed, 90 insertions(+), 92 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 55241d2c3..c73e14ea2 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -21,7 +21,7 @@ pub const DEFAULT_DEPLOYMENT: &str = "default"; /// .await.unwrap(); /// # }) /// ``` -pub struct DaemonAsyncBuilderBase { +pub struct DaemonAsyncBuilderBase { // # Required pub(crate) chain: Option, // # Optional @@ -34,15 +34,15 @@ pub struct DaemonAsyncBuilderBase { // Sender options // # Optional indicated sender - pub(crate) sender: Option, + pub(crate) sender: Option, /// Specify Daemon Sender Options - pub(crate) sender_options: SenderGen::SenderOptions, + pub(crate) sender_options: Sender::SenderOptions, } pub type DaemonAsyncBuilder = DaemonAsyncBuilderBase; -impl Default for DaemonAsyncBuilderBase { +impl Default for DaemonAsyncBuilderBase { fn default() -> Self { Self { chain: Default::default(), @@ -85,7 +85,7 @@ impl DaemonAsyncBuilder { } } -impl DaemonAsyncBuilderBase { +impl DaemonAsyncBuilderBase { /// Set the chain the daemon will connect to pub fn chain(&mut self, chain: impl Into) -> &mut Self { self.chain = Some(chain.into()); @@ -101,17 +101,17 @@ impl DaemonAsyncBuilderBase { /// Specifies a sender to use with this chain /// This will be used in priority when set on the builder - pub fn sender( + pub fn sender( &self, - wallet: OtherSenderGen, - ) -> DaemonAsyncBuilderBase { + wallet: NewSender, + ) -> DaemonAsyncBuilderBase { DaemonAsyncBuilderBase { chain: self.chain.clone(), deployment_id: self.deployment_id.clone(), state_path: self.state_path.clone(), state: self.state.clone(), sender: Some(wallet), - sender_options: OtherSenderGen::SenderOptions::default(), + sender_options: NewSender::SenderOptions::default(), write_on_change: self.write_on_change, } } @@ -143,7 +143,7 @@ impl DaemonAsyncBuilderBase { } /// Build a daemon - pub async fn build(&self) -> Result, DaemonError> { + pub async fn build(&self) -> Result, DaemonError> { let chain_info = self .chain .clone() @@ -199,7 +199,7 @@ impl DaemonAsyncBuilderBase { } else { let chain_id = chain_info.chain_id.clone(); let grpc_urls = chain_info.grpc_urls.clone(); - SenderGen::build( + Sender::build( chain_info, GrpcChannel::connect(&grpc_urls, &chain_id).await?, self.sender_options.clone(), @@ -214,10 +214,8 @@ impl DaemonAsyncBuilderBase { } } -impl From> - for DaemonAsyncBuilderBase -{ - fn from(value: DaemonBuilderBase) -> Self { +impl From> for DaemonAsyncBuilderBase { + fn from(value: DaemonBuilderBase) -> Self { DaemonAsyncBuilderBase { chain: value.chain, deployment_id: value.deployment_id, diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 73d37a873..66a51ccc3 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -66,16 +66,16 @@ pub const INSTANTIATE_2_TYPE_URL: &str = "/cosmwasm.wasm.v1.MsgInstantiateContra If you do so, you WILL get account sequence errors and your transactions won't get broadcasted. Use a Mutex on top of this DaemonAsync to avoid such errors. */ -pub struct DaemonAsyncBase { +pub struct DaemonAsyncBase { /// Sender to send transactions to the chain - pub sender: SenderGen, + pub sender: Sender, /// State of the daemon pub state: DaemonState, } pub type DaemonAsync = DaemonAsyncBase; -impl DaemonAsyncBase { +impl DaemonAsyncBase { /// Get the daemon builder pub fn builder() -> DaemonAsyncBuilder { DaemonAsyncBuilder::default() @@ -93,7 +93,7 @@ impl DaemonAsyncBase { } } -impl ChainState for DaemonAsyncBase { +impl ChainState for DaemonAsyncBase { type Out = DaemonState; fn state(&self) -> Self::Out { @@ -102,7 +102,7 @@ impl ChainState for DaemonAsyncBase { } // Execute on the real chain, returns tx response. -impl DaemonAsyncBase { +impl DaemonAsyncBase { /// Get the sender address pub fn sender(&self) -> Addr { self.sender.address().unwrap() @@ -110,7 +110,7 @@ impl DaemonAsyncBase { /// Returns a new [`DaemonAsyncBuilder`] with the current configuration. /// Does not consume the original [`DaemonAsync`]. - pub fn rebuild(&self) -> DaemonAsyncBuilderBase { + pub fn rebuild(&self) -> DaemonAsyncBuilderBase { let mut builder = DaemonAsyncBuilder { state: Some(self.state()), ..Default::default() @@ -337,10 +337,10 @@ impl DaemonAsyncBase { } /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender( + pub fn set_sender( self, - sender: SenderGen2, - ) -> DaemonAsyncBase { + sender: NewSender, + ) -> DaemonAsyncBase { DaemonAsyncBase { sender, state: self.state, diff --git a/cw-orch-daemon/src/queriers/bank.rs b/cw-orch-daemon/src/queriers/bank.rs index 50e63d9c4..08c3a6b52 100644 --- a/cw-orch-daemon/src/queriers/bank.rs +++ b/cw-orch-daemon/src/queriers/bank.rs @@ -13,7 +13,7 @@ pub struct Bank { } impl Bank { - pub fn new(daemon: &DaemonBase) -> Self { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), @@ -31,7 +31,7 @@ impl Querier for Bank { type Error = DaemonError; } -impl QuerierGetter for DaemonBase { +impl QuerierGetter for DaemonBase { fn querier(&self) -> Bank { Bank::new(self) } diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 8e48e5575..0b0688d13 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -18,16 +18,16 @@ use tonic::transport::Channel; /// Querier for the CosmWasm SDK module /// All the async function are prefixed with `_` -pub struct CosmWasmBase { +pub struct CosmWasmBase { pub channel: Channel, pub rt_handle: Option, - _sender: PhantomData, + _sender: PhantomData, } pub type CosmWasm = CosmWasmBase; -impl CosmWasmBase { - pub fn new(daemon: &DaemonBase) -> Self { +impl CosmWasmBase { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), @@ -50,17 +50,17 @@ impl CosmWasmBase { } } -impl QuerierGetter> for DaemonBase { - fn querier(&self) -> CosmWasmBase { +impl QuerierGetter> for DaemonBase { + fn querier(&self) -> CosmWasmBase { CosmWasmBase::new(self) } } -impl Querier for CosmWasmBase { +impl Querier for CosmWasmBase { type Error = DaemonError; } -impl CosmWasmBase { +impl CosmWasmBase { /// Query code_id by hash pub async fn _code_id_hash(&self, code_id: u64) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; @@ -232,8 +232,8 @@ impl CosmWasmBase { } } -impl WasmQuerier for CosmWasmBase { - type Chain = DaemonBase; +impl WasmQuerier for CosmWasmBase { + type Chain = DaemonBase; fn code_id_hash(&self, code_id: u64) -> Result { self.rt_handle .as_ref() @@ -304,7 +304,7 @@ impl WasmQuerier for CosmWasmBase { fn local_hash< T: cw_orch_core::contract::interface_traits::Uploadable - + cw_orch_core::contract::interface_traits::ContractInstance>, + + cw_orch_core::contract::interface_traits::ContractInstance>, >( &self, contract: &T, diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index b05f795db..420998b15 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -2,7 +2,7 @@ use cw_orch_core::environment::{EnvironmentInfo, EnvironmentQuerier}; use crate::{senders::sender_trait::SenderTrait, DaemonBase}; -impl EnvironmentQuerier for DaemonBase { +impl EnvironmentQuerier for DaemonBase { fn env_info(&self) -> EnvironmentInfo { EnvironmentInfo { chain_id: self.daemon.sender.chain_info().chain_id.clone(), diff --git a/cw-orch-daemon/src/queriers/node.rs b/cw-orch-daemon/src/queriers/node.rs index a20855e53..74787c355 100644 --- a/cw-orch-daemon/src/queriers/node.rs +++ b/cw-orch-daemon/src/queriers/node.rs @@ -29,7 +29,7 @@ pub struct Node { } impl Node { - pub fn new(daemon: &DaemonBase) -> Self { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), @@ -43,7 +43,7 @@ impl Node { } } -impl QuerierGetter for DaemonBase { +impl QuerierGetter for DaemonBase { fn querier(&self) -> Node { Node::new(self) } diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index 819b20be5..c4495b3cd 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -99,7 +99,7 @@ impl SenderOptions { } } -impl SenderTrait for Sender { +impl SenderTrait for Wallet { type Error = DaemonError; type SenderOptions = SenderOptions; @@ -208,8 +208,8 @@ impl SenderTrait for Sender { } } -impl Sender { - pub fn new(chain_info: ChainInfoOwned, channel: Channel) -> Result, DaemonError> { +impl Wallet { + pub fn new(chain_info: ChainInfoOwned, channel: Channel) -> Result { Self::new_with_options(chain_info, channel, SenderOptions::default()) } @@ -221,7 +221,7 @@ impl Sender { chain_info: ChainInfoOwned, channel: Channel, options: SenderOptions, - ) -> Result, DaemonError> { + ) -> Result { let mnemonic = get_mnemonic_env(&chain_info.kind)?; Self::from_mnemonic_with_options(chain_info, channel, &mnemonic, options) @@ -232,7 +232,7 @@ impl Sender { chain_info: ChainInfoOwned, channel: Channel, mnemonic: &str, - ) -> Result, DaemonError> { + ) -> Result { Self::from_mnemonic_with_options(chain_info, channel, mnemonic, SenderOptions::default()) } @@ -242,7 +242,7 @@ impl Sender { channel: Channel, mnemonic: &str, options: SenderOptions, - ) -> Result, DaemonError> { + ) -> Result { let secp = Secp256k1::new(); let p_key: PrivateKey = PrivateKey::from_words( &secp, @@ -274,7 +274,7 @@ impl Sender { channel: Channel, raw_key: &[u8], options: SenderOptions, - ) -> Result, DaemonError> { + ) -> Result { let secp = Secp256k1::new(); let p_key: PrivateKey = PrivateKey::from_raw_key( &secp, diff --git a/cw-orch-daemon/src/senders/manual_sender.rs b/cw-orch-daemon/src/senders/manual_sender.rs index ea42c4263..e4ec2443d 100644 --- a/cw-orch-daemon/src/senders/manual_sender.rs +++ b/cw-orch-daemon/src/senders/manual_sender.rs @@ -16,7 +16,7 @@ use cw_orch_core::environment::ChainInfoOwned; use prost::Message; use tonic::transport::Channel; -use std::io::{self, BufRead}; +use std::io::{self, Write}; use super::sender_trait::SenderTrait; @@ -45,22 +45,24 @@ impl SenderTrait for ManualSender { msgs: Vec, memo: Option<&str>, ) -> Result { + // We print the any messages to broadcast + println!("Here is the transaction to sign and broadcast: "); + println!("{:?}", msgs); // We simulate let gas_needed = self.simulate(msgs, memo).await?; - - log::info!("Here is the transaction to sign and broadcast: "); - log::info!("Gas needed: {}", gas_needed); - - log::info!("Enter the txhash to proceed"); - let stdin = io::stdin(); - let txhash = stdin.lock().lines().next().unwrap().unwrap(); - - // We print the any messages to broadcast + println!("Gas needed: {}", gas_needed); // We wait for the txhash as input to be able to continue the execution + println!("Enter the txhash to proceed"); + let mut txhash = String::new(); + io::stdout().flush().unwrap(); // Ensure the prompt is displayed + io::stdin() + .read_line(&mut txhash) + .expect("Failed to read line"); + let txhash = txhash.trim_end(); let resp = Node::new_async(self.grpc_channel()) - ._find_tx(txhash) + ._find_tx(txhash.to_string()) .await?; assert_broadcast_code_cosm_response(resp) diff --git a/cw-orch-daemon/src/senders/sender_trait.rs b/cw-orch-daemon/src/senders/sender_trait.rs index 7bb75f515..6fc921062 100644 --- a/cw-orch-daemon/src/senders/sender_trait.rs +++ b/cw-orch-daemon/src/senders/sender_trait.rs @@ -9,14 +9,12 @@ pub trait SenderTrait: Clone { type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; type SenderOptions: Default + Clone; - // TODO: do we want to enforce sync on this function ? fn address(&self) -> Result; fn chain_info(&self) -> &ChainInfoOwned; fn grpc_channel(&self) -> Channel; - // TODO: do we want to enforce sync on this function ? /// 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 diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 8c0eab81a..88edba663 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -16,7 +16,7 @@ use super::super::error::DaemonError; /// .build() /// .unwrap(); /// ``` -pub struct DaemonBuilderBase { +pub struct DaemonBuilderBase { // # Required pub(crate) chain: Option, // # Optional @@ -30,14 +30,14 @@ pub struct DaemonBuilderBase { pub(crate) state: Option, pub(crate) write_on_change: Option, - pub(crate) sender: Option, + pub(crate) sender: Option, // /* Sender Options */ /// Specify Daemon Sender Options - pub(crate) sender_options: SenderGen::SenderOptions, + pub(crate) sender_options: Sender::SenderOptions, } -impl Default for DaemonBuilderBase { +impl Default for DaemonBuilderBase { fn default() -> Self { Self { chain: Default::default(), @@ -83,7 +83,7 @@ impl DaemonBuilder { } } -impl DaemonBuilderBase { +impl DaemonBuilderBase { /// Set the chain the Daemon will connect to pub fn chain(&mut self, chain: impl Into) -> &mut Self { self.chain = Some(chain.into()); @@ -117,17 +117,17 @@ impl DaemonBuilderBase { /// Specifies a sender to use with this chain /// This will be used in priority when set on the builder - pub fn sender( + pub fn sender( &self, - wallet: OtherSenderGen, - ) -> DaemonBuilderBase { + wallet: OtherSender, + ) -> DaemonBuilderBase { DaemonBuilderBase { chain: self.chain.clone(), deployment_id: self.deployment_id.clone(), state_path: self.state_path.clone(), state: self.state.clone(), sender: Some(wallet), - sender_options: OtherSenderGen::SenderOptions::default(), + sender_options: OtherSender::SenderOptions::default(), handle: self.handle.clone(), overwrite_grpc_url: self.overwrite_grpc_url.clone(), gas_denom: self.gas_denom.clone(), @@ -137,7 +137,7 @@ impl DaemonBuilderBase { } /// Specifies sender builder options - pub fn options(&mut self, options: SenderGen::SenderOptions) -> &mut Self { + pub fn options(&mut self, options: Sender::SenderOptions) -> &mut Self { self.sender_options = options; self } @@ -185,7 +185,7 @@ impl DaemonBuilderBase { } /// Build a Daemon - pub fn build(&self) -> Result, DaemonError> { + pub fn build(&self) -> Result, DaemonError> { let rt_handle = self .handle .clone() diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 5afcd8d00..8234d1e92 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -43,17 +43,17 @@ use crate::senders::sender_trait::SenderTrait; Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. See [Querier](crate::queriers) for examples. */ -pub struct DaemonBase { - pub daemon: DaemonAsyncBase, +pub struct DaemonBase { + pub daemon: DaemonAsyncBase, /// Runtime handle to execute async tasks pub rt_handle: Handle, } pub type Daemon = DaemonBase; -impl DaemonBase { +impl DaemonBase { /// Get the daemon builder - pub fn builder() -> DaemonBuilderBase { + pub fn builder() -> DaemonBuilderBase { DaemonBuilderBase::default() } @@ -63,13 +63,13 @@ impl DaemonBase { } /// Get the channel configured for this Daemon - pub fn wallet(&self) -> SenderGen { + pub fn wallet(&self) -> Sender { self.daemon.sender.clone() } /// Returns a new [`DaemonBuilder`] with the current configuration. /// Does not consume the original [`Daemon`]. - pub fn rebuild(&self) -> DaemonBuilderBase { + pub fn rebuild(&self) -> DaemonBuilderBase { let mut builder = DaemonBuilder { state: Some(self.state()), ..Default::default() @@ -86,7 +86,7 @@ impl DaemonBase { } } -impl ChainState for DaemonBase { +impl ChainState for DaemonBase { type Out = DaemonState; fn state(&self) -> Self::Out { @@ -95,11 +95,11 @@ impl ChainState for DaemonBase { } // Execute on the real chain, returns tx response -impl TxHandler for DaemonBase { +impl TxHandler for DaemonBase { type Response = CosmTxResponse; type Error = DaemonError; type ContractSource = WasmPath; - type Sender = SenderGen; + type Sender = Sender; fn sender(&self) -> Addr { self.daemon.sender.address().unwrap() @@ -165,7 +165,7 @@ impl TxHandler for DaemonBase { } } -impl Stargate for DaemonBase { +impl Stargate for DaemonBase { fn commit_any( &self, msgs: Vec, @@ -187,7 +187,7 @@ impl Stargate for DaemonBase { } } -impl QueryHandler for DaemonBase { +impl QueryHandler for DaemonBase { type Error = DaemonError; fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { @@ -209,8 +209,8 @@ impl QueryHandler for DaemonBase { } } -impl DefaultQueriers for DaemonBase { +impl DefaultQueriers for DaemonBase { type Bank = Bank; - type Wasm = CosmWasmBase; + type Wasm = CosmWasmBase; type Node = Node; } diff --git a/cw-orch-daemon/src/tx_broadcaster.rs b/cw-orch-daemon/src/tx_broadcaster.rs index 4e2b8180a..04eb9c900 100644 --- a/cw-orch-daemon/src/tx_broadcaster.rs +++ b/cw-orch-daemon/src/tx_broadcaster.rs @@ -1,8 +1,7 @@ -use bitcoin::secp256k1::All; use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; use cw_orch_core::log::transaction_target; -use crate::{queriers::Node, senders::base_sender::Sender, CosmTxResponse, DaemonError, TxBuilder}; +use crate::{queriers::Node, CosmTxResponse, DaemonError, TxBuilder, Wallet}; pub type StrategyAction = fn(&mut TxBuilder, &Result) -> Result<(), DaemonError>; @@ -68,7 +67,7 @@ impl TxBroadcaster { pub async fn broadcast( mut self, mut tx_builder: TxBuilder, - wallet: &Sender, + wallet: &Wallet, ) -> Result { let mut tx_retry = true; @@ -123,7 +122,7 @@ fn strategy_condition_met( async fn broadcast_helper( tx_builder: &mut TxBuilder, - wallet: &Sender, + wallet: &Wallet, ) -> Result { let tx = tx_builder.build(wallet).await?; let tx_response = wallet.broadcast_tx(tx).await?; diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index 2d0405d31..cbbeaf635 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -1,6 +1,5 @@ use std::str::FromStr; -use bitcoin::secp256k1::All; use cosmrs::tx::{ModeInfo, SignMode}; use cosmrs::AccountId; use cosmrs::{ @@ -11,7 +10,9 @@ use cosmrs::{ }; use cw_orch_core::log::transaction_target; -use super::{senders::base_sender::Sender, DaemonError}; +use crate::Wallet; + +use super::DaemonError; /// Struct used to build a raw transaction and broadcast it with a sender. #[derive(Clone, Debug)] @@ -73,7 +74,7 @@ impl TxBuilder { } /// Simulates the transaction and returns the necessary gas fee returned by the simulation on a node - pub async fn simulate(&self, wallet: &Sender) -> Result { + pub async fn simulate(&self, wallet: &Wallet) -> Result { // get the account number of the wallet let BaseAccount { account_number, @@ -91,7 +92,7 @@ impl TxBuilder { /// Builds the raw tx with a given body and fee and signs it. /// Sets the TxBuilder's gas limit to its simulated amount for later use. - pub async fn build(&mut self, wallet: &Sender) -> Result { + pub async fn build(&mut self, wallet: &Wallet) -> Result { // get the account number of the wallet let BaseAccount { account_number, From ca87a14aa6d70be110bec0656cdb2035815e1160 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 27 Jun 2024 14:16:24 +0000 Subject: [PATCH 057/108] Moved manual sender, added querier-daemon --- contracts/counter/examples/manual-sender.rs | 34 ---------- cw-orch-daemon/Cargo.toml | 4 ++ .../examples/batch-sender.rs | 0 .../senders => examples}/manual_sender.rs | 51 ++++++++++++--- cw-orch-daemon/examples/querier-daemon.rs | 20 ++++++ cw-orch-daemon/src/senders/mod.rs | 2 +- cw-orch-daemon/src/senders/no_sender.rs | 63 +++++++++++++++++++ cw-orch-daemon/src/tx_broadcaster.rs | 2 +- cw-orch-daemon/src/tx_builder.rs | 2 +- 9 files changed, 132 insertions(+), 46 deletions(-) delete mode 100644 contracts/counter/examples/manual-sender.rs rename {contracts/counter => cw-orch-daemon}/examples/batch-sender.rs (100%) rename cw-orch-daemon/{src/senders => examples}/manual_sender.rs (75%) create mode 100644 cw-orch-daemon/examples/querier-daemon.rs create mode 100644 cw-orch-daemon/src/senders/no_sender.rs diff --git a/contracts/counter/examples/manual-sender.rs b/contracts/counter/examples/manual-sender.rs deleted file mode 100644 index bbc88120b..000000000 --- a/contracts/counter/examples/manual-sender.rs +++ /dev/null @@ -1,34 +0,0 @@ -// ANCHOR: full_counter_example -use counter_contract::CounterContract; -use cw_orch::{ - anyhow, - daemon::senders::manual_sender::{ManualDaemon, ManualSenderOptions}, - prelude::*, -}; - -// This is a test with a manual sender, to verify everything works, nothing is broadcasted - -// From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 -pub fn main() -> anyhow::Result<()> { - dotenv::dotenv().ok(); // Used to load the `.env` file if any - pretty_env_logger::init(); // Used to log contract and chain interactions - - let network = networks::JUNO_1; - let sender = "juno1xjf5xscdk08c5es2m7epmerrpqmkmc3n98650t"; - let chain = ManualDaemon::builder() - .options(ManualSenderOptions { - sender_address: Some(sender.to_string()), - }) - .chain(network) - .build()?; - - let counter = CounterContract::new(chain.clone()); - - // Example tx hash that succeed (correspond to a code upload tx) - // 58AA802705BEE4597A560FBC67F6C86400E66F5FCBD0F08AA37FB140BCD65B6D - // If not found, try to find the latests juno code uploaded (4380+) - // https://www.mintscan.io/juno/wasm/code/4380 - counter.upload()?; - - Ok(()) -} diff --git a/cw-orch-daemon/Cargo.toml b/cw-orch-daemon/Cargo.toml index 4333aa3ec..124541744 100644 --- a/cw-orch-daemon/Cargo.toml +++ b/cw-orch-daemon/Cargo.toml @@ -74,6 +74,7 @@ regex = "1.10.4" [dev-dependencies] cw-orch-daemon = { path = "." } +cw-orch = { path = "../cw-orch", features = ["daemon"] } uid = "0.1.7" env_logger = "0.11.2" cw20 = { version = "1" } @@ -94,3 +95,6 @@ tokio-test = "0.4.3" # File lock test nix = { version = "0.28.0", features = ["process"] } +counter-contract = { path = "../contracts/counter" } +dotenv = "0.15.0" +pretty_env_logger = "0.5.0" diff --git a/contracts/counter/examples/batch-sender.rs b/cw-orch-daemon/examples/batch-sender.rs similarity index 100% rename from contracts/counter/examples/batch-sender.rs rename to cw-orch-daemon/examples/batch-sender.rs diff --git a/cw-orch-daemon/src/senders/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs similarity index 75% rename from cw-orch-daemon/src/senders/manual_sender.rs rename to cw-orch-daemon/examples/manual_sender.rs index e4ec2443d..02ab66052 100644 --- a/cw-orch-daemon/src/senders/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -1,9 +1,11 @@ -use crate::proto::injective::InjectiveEthAccount; -use crate::queriers::Node; -use crate::tx_broadcaster::assert_broadcast_code_cosm_response; -use crate::{cosmos_modules, DaemonBase, TxBuilder}; +// This file illustrates an example for custom sender inside Daemon -use crate::{error::DaemonError, tx_resp::CosmTxResponse}; +use cw_orch_daemon::proto::injective::InjectiveEthAccount; +use cw_orch_daemon::queriers::Node; +use cw_orch_daemon::tx_broadcaster::assert_broadcast_code_cosm_response; +use cw_orch_daemon::{DaemonBase, TxBuilder}; + +use cw_orch_daemon::{error::DaemonError, tx_resp::CosmTxResponse}; use cosmrs::proto::cosmos; use cosmrs::proto::cosmos::auth::v1beta1::BaseAccount; @@ -12,13 +14,42 @@ use cosmrs::tendermint::chain::Id; use cosmrs::tx::{ModeInfo, Raw, SignDoc, SignMode, SignerInfo}; use cosmrs::{AccountId, Any}; use cosmwasm_std::Addr; +use cw_orch::prelude::*; use cw_orch_core::environment::ChainInfoOwned; use prost::Message; +use std::io::{self, Write}; use tonic::transport::Channel; -use std::io::{self, Write}; +// ANCHOR: full_counter_example +use counter_contract::CounterContract; + +// This is a test with a manual sender, to verify everything works, nothing is broadcasted + +pub fn main() -> anyhow::Result<()> { + dotenv::dotenv().ok(); // Used to load the `.env` file if any + pretty_env_logger::init(); // Used to log contract and chain interactions + + let network = cw_orch_networks::networks::JUNO_1; + let sender = "juno1xjf5xscdk08c5es2m7epmerrpqmkmc3n98650t"; + let chain = ManualDaemon::builder() + .options(ManualSenderOptions { + sender_address: Some(sender.to_string()), + }) + .chain(network) + .build()?; + + let counter = CounterContract::new(chain.clone()); + + // Example tx hash that succeed (correspond to a code upload tx) + // 58AA802705BEE4597A560FBC67F6C86400E66F5FCBD0F08AA37FB140BCD65B6D + // If not found, try to find the latests juno code uploaded (4380+) + // https://www.mintscan.io/juno/wasm/code/4380 + counter.upload()?; + + Ok(()) +} -use super::sender_trait::SenderTrait; +use cw_orch_daemon::senders::sender_trait::SenderTrait; pub type ManualDaemon = DaemonBase; @@ -153,10 +184,12 @@ impl ManualSender { async fn base_account(&self) -> Result { let addr = self.address()?.to_string(); - let mut client = cosmos_modules::auth::query_client::QueryClient::new(self.grpc_channel()); + let mut client = cosmrs::proto::cosmos::auth::v1beta1::query_client::QueryClient::new( + self.grpc_channel(), + ); let resp = client - .account(cosmos_modules::auth::QueryAccountRequest { address: addr }) + .account(cosmrs::proto::cosmos::auth::v1beta1::QueryAccountRequest { address: addr }) .await? .into_inner(); diff --git a/cw-orch-daemon/examples/querier-daemon.rs b/cw-orch-daemon/examples/querier-daemon.rs new file mode 100644 index 000000000..44f120da8 --- /dev/null +++ b/cw-orch-daemon/examples/querier-daemon.rs @@ -0,0 +1,20 @@ +// ANCHOR: full_counter_example + +use cw_orch::{anyhow, prelude::*}; +use cw_orch_daemon::senders::no_sender::QuerierDaemon; + +// From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L1 +pub const LOCAL_JUNO_SENDER: &str = "juno16g2rahf5846rxzp3fwlswy08fz8ccuwk03k57y"; + +pub fn main() -> anyhow::Result<()> { + pretty_env_logger::init(); // Used to log contract and chain interactions + + let network = networks::LOCAL_JUNO; + // There is no need to register a mnemonic to use this daemon querier + let chain = QuerierDaemon::builder().chain(network).build()?; + + let balances = chain.bank_querier().balance(LOCAL_JUNO_SENDER, None)?; + assert!(!balances.is_empty()); + + Ok(()) +} diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index a97489474..475d8f6c6 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -1,4 +1,4 @@ pub mod base_sender; pub mod batch_sender; -pub mod manual_sender; +pub mod no_sender; pub mod sender_trait; diff --git a/cw-orch-daemon/src/senders/no_sender.rs b/cw-orch-daemon/src/senders/no_sender.rs new file mode 100644 index 000000000..8e04c3dc0 --- /dev/null +++ b/cw-orch-daemon/src/senders/no_sender.rs @@ -0,0 +1,63 @@ +use crate::{error::DaemonError, tx_resp::CosmTxResponse, DaemonBase}; + +use cosmrs::{AccountId, Any}; +use cosmwasm_std::Addr; +use cw_orch_core::environment::ChainInfoOwned; + +use tonic::transport::Channel; + +use super::sender_trait::SenderTrait; + +pub type QuerierDaemon = DaemonBase; + +/// Signer of the transactions and helper for address derivation +/// This is the main interface for simulating and signing transactions +#[derive(Clone)] +pub struct NoSender { + /// gRPC channel + pub grpc_channel: Channel, + /// Information about the chain + pub chain_info: ChainInfoOwned, +} + +impl SenderTrait for NoSender { + type Error = DaemonError; + type SenderOptions = (); + + async fn commit_tx_any( + &self, + _msgs: Vec, + _memo: Option<&str>, + ) -> Result { + unimplemented!("You used the DaemonQuerier, which can't send transactions"); + } + + fn address(&self) -> Result { + unimplemented!("You used the DaemonQuerier, which doesn't have an associated address"); + } + + fn msg_sender(&self) -> Result { + unimplemented!("You used the DaemonQuerier, which doesn't have an associated msg sender"); + } + + fn chain_info(&self) -> &ChainInfoOwned { + &self.chain_info + } + + fn grpc_channel(&self) -> Channel { + self.grpc_channel.clone() + } + + fn set_options(&mut self, _options: Self::SenderOptions) {} + + fn build( + chain_info: ChainInfoOwned, + grpc_channel: Channel, + _sender_options: Self::SenderOptions, + ) -> Result { + Ok(NoSender { + grpc_channel, + chain_info, + }) + } +} diff --git a/cw-orch-daemon/src/tx_broadcaster.rs b/cw-orch-daemon/src/tx_broadcaster.rs index 04eb9c900..9632862f9 100644 --- a/cw-orch-daemon/src/tx_broadcaster.rs +++ b/cw-orch-daemon/src/tx_broadcaster.rs @@ -148,7 +148,7 @@ pub(crate) fn assert_broadcast_code_response( } /// Tx Responses with a non 0 code, should also error with the raw loq -pub(crate) fn assert_broadcast_code_cosm_response( +pub fn assert_broadcast_code_cosm_response( tx_response: CosmTxResponse, ) -> Result { // if tx result != 0 then the tx failed, so we return an error diff --git a/cw-orch-daemon/src/tx_builder.rs b/cw-orch-daemon/src/tx_builder.rs index cbbeaf635..4aeee68a7 100644 --- a/cw-orch-daemon/src/tx_builder.rs +++ b/cw-orch-daemon/src/tx_builder.rs @@ -61,7 +61,7 @@ impl TxBuilder { ) } - pub(crate) fn build_fee( + pub fn build_fee( amount: impl Into, denom: &str, gas_limit: u64, From 6b49536511f9fad93115a727f5cace6853906ef7 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 28 Jun 2024 14:51:26 +0200 Subject: [PATCH 058/108] get retry layer working --- cw-orch-daemon/src/queriers/cosmwasm.rs | 19 +++++++++++- cw-orch-daemon/src/service.rs | 41 ++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 70c89b6ba..cefc35b09 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -1,5 +1,7 @@ use std::str::FromStr; +use std::time::Duration; +use crate::service::{DaemonChannel, MyRetryPolicy}; use crate::{cosmos_modules, error::DaemonError, Daemon}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmrs::AccountId; @@ -14,6 +16,8 @@ use cw_orch_core::{ }; use tokio::runtime::Handle; use tonic::transport::Channel; +use tower::retry::RetryLayer; +use tower::{Service, ServiceBuilder}; /// Querier for the CosmWasm SDK module /// All the async function are prefixed with `_` @@ -160,7 +164,20 @@ impl CosmWasm { pagination: Option, ) -> Result, DaemonError> { use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let retry_policy = MyRetryPolicy { + max_retries: 3, // Maximum number of retries + backoff: Duration::from_secs(1), // Backoff duration + }; + + let retry_layer = RetryLayer::new(retry_policy); + + + let service = ServiceBuilder::new() + .layer(retry_layer) + .service(DaemonChannel::new(self.channel.clone())); + + let mut client = QueryClient::new(service); + let request = QueryCodesRequest { pagination }; let response = client.codes(request).await?.into_inner().code_infos; diff --git a/cw-orch-daemon/src/service.rs b/cw-orch-daemon/src/service.rs index 5dae509d3..96065e3b9 100644 --- a/cw-orch-daemon/src/service.rs +++ b/cw-orch-daemon/src/service.rs @@ -1,16 +1,17 @@ -use std::{sync::Arc, task::{Context, Poll}}; +use std::{future::{ready, Ready}, sync::Arc, task::{Context, Poll}, time::Duration}; use tonic::{body::BoxBody, client::GrpcService, transport::{channel, Channel}, Request}; -use tower::{Service, ServiceBuilder}; +use tower::{retry::{Policy, RetryLayer}, Service, ServiceBuilder}; use crate::DaemonState; -struct DaemonChannel { +#[derive(Clone)] +pub struct DaemonChannel { svs: Channel, } impl DaemonChannel { - fn new(channel: Channel) -> Self { + pub fn new(channel: Channel) -> Self { Self { svs: channel } @@ -33,4 +34,34 @@ impl Service> for DaemonChannel { // Is automatically implemented by Tonic ! // impl GrpcService for DaemonChannel { -// } \ No newline at end of file +// } + + +/// Retry policy that retries on all errors up to a maximum number of retries. +#[derive(Debug, Clone)] +pub struct MyRetryPolicy { + pub max_retries: usize, + pub backoff: Duration, +} + +impl Policy, http::Response, E> for MyRetryPolicy +where + E: Into>, +{ + type Future = Ready; + + fn retry(&self, _req: &http::Request, result: Result<&http::Response, &E>) -> Option { + if self.max_retries > 0 && result.is_err() { + Some(ready(MyRetryPolicy { + max_retries: self.max_retries - 1, + backoff: self.backoff, + })) + } else { + None + } + } + + fn clone_request(&self, req: &http::Request) -> Option> { + None + } +} \ No newline at end of file From 17b040e8a86cbede351b0a490a2de76fd7166cdd Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 28 Jun 2024 14:56:51 +0200 Subject: [PATCH 059/108] format --- cw-orch-daemon/src/queriers/cosmwasm.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index cefc35b09..a8de85200 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -164,14 +164,13 @@ impl CosmWasm { pagination: Option, ) -> Result, DaemonError> { use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; + let retry_policy = MyRetryPolicy { max_retries: 3, // Maximum number of retries backoff: Duration::from_secs(1), // Backoff duration }; - let retry_layer = RetryLayer::new(retry_policy); - let service = ServiceBuilder::new() .layer(retry_layer) .service(DaemonChannel::new(self.channel.clone())); From 0acd362e1d740ee50a93535fc44993508270ffc9 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 28 Jun 2024 16:28:59 +0200 Subject: [PATCH 060/108] play around further with service traits --- cw-orch-daemon/src/lib.rs | 2 +- cw-orch-daemon/src/queriers/cosmwasm.rs | 32 +++++++++--- cw-orch-daemon/src/service.rs | 69 +++++++++++++++++++++---- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index 71a012621..ded6e2e3c 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -19,9 +19,9 @@ pub mod keys; pub mod live_mock; mod log; pub mod queriers; +pub mod service; pub mod tx_broadcaster; pub mod tx_builder; -pub mod service; pub use self::{builder::*, channel::*, core::*, error::*, state::*, sync::*, tx_resp::*}; pub use cw_orch_networks::networks; pub use sender::Wallet; diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index a8de85200..52febcfba 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use std::time::Duration; -use crate::service::{DaemonChannel, MyRetryPolicy}; +use crate::service::{DaemonChannel, DaemonChannelFactory, MyRetryPolicy}; use crate::{cosmos_modules, error::DaemonError, Daemon}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmrs::AccountId; @@ -15,9 +15,10 @@ use cw_orch_core::{ environment::{Querier, QuerierGetter, WasmQuerier}, }; use tokio::runtime::Handle; -use tonic::transport::Channel; +use tonic::transport::{Channel, Endpoint}; +use tower::reconnect::Reconnect; use tower::retry::RetryLayer; -use tower::{Service, ServiceBuilder}; +use tower::{MakeService as _, Service, ServiceBuilder}; /// Querier for the CosmWasm SDK module /// All the async function are prefixed with `_` @@ -164,17 +165,32 @@ impl CosmWasm { pagination: Option, ) -> Result, DaemonError> { use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; - + + let endpoint = Endpoint::from_static("http://localhost:50051"); + + let cc = DaemonChannel::new(self.channel.clone()); + + // Create the Reconnect layer with the factory let retry_policy = MyRetryPolicy { - max_retries: 3, // Maximum number of retries + max_retries: 3, // Maximum number of retries backoff: Duration::from_secs(1), // Backoff duration }; let retry_layer = RetryLayer::new(retry_policy); + let reconnect_service: Reconnect = + Reconnect::new::<_, _>(DaemonChannelFactory{},"http://localhost:50051".into()); + + // Build your service stack let service = ServiceBuilder::new() - .layer(retry_layer) - .service(DaemonChannel::new(self.channel.clone())); - + .layer(reconnect_service) + // .layer(retry_layer) + .service_fn(|cc| DaemonChannelFactory{}.make_service(cc)); + + let service = ServiceBuilder::new() + .layer(reconnect_service) + .layer(retry_layer) + .service_fn(|cc| async { Ok(cc.clone()) }); + let mut client = QueryClient::new(service); let request = QueryCodesRequest { pagination }; diff --git a/cw-orch-daemon/src/service.rs b/cw-orch-daemon/src/service.rs index 96065e3b9..1f44c7dfa 100644 --- a/cw-orch-daemon/src/service.rs +++ b/cw-orch-daemon/src/service.rs @@ -1,20 +1,32 @@ -use std::{future::{ready, Ready}, sync::Arc, task::{Context, Poll}, time::Duration}; +use std::{ + error::Error, + future::{ready, Future, Ready}, + pin::Pin, + sync::Arc, + task::{Context, Poll}, + time::Duration, +}; -use tonic::{body::BoxBody, client::GrpcService, transport::{channel, Channel}, Request}; -use tower::{retry::{Policy, RetryLayer}, Service, ServiceBuilder}; +use tonic::{ + body::BoxBody, + client::GrpcService, + transport::{channel, Channel}, + Request, +}; +use tower::{ + retry::{Policy, RetryLayer}, Layer, MakeService, Service, ServiceBuilder +}; use crate::DaemonState; #[derive(Clone)] pub struct DaemonChannel { - svs: Channel, + pub (crate) svs: Channel, } impl DaemonChannel { pub fn new(channel: Channel) -> Self { - Self { - svs: channel - } + Self { svs: channel } } } @@ -32,10 +44,43 @@ impl Service> for DaemonChannel { } } -// Is automatically implemented by Tonic ! +// Is automatically implemented by Tonic ! // impl GrpcService for DaemonChannel { // } +pub struct DaemonChannelFactory {} + +impl Layer for DaemonChannel { + type Service = DaemonChannel; + + fn layer(&self, inner: Channel) -> Self::Service { + DaemonChannel::new(inner) + } +} + +impl Service for DaemonChannelFactory { + type Response = DaemonChannel; + type Error = Box; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, request: String) -> Self::Future { + let endpoint = request; + let fut = async move { + let channel = Channel::from_shared(endpoint) + .expect("valid URI") + .connect() + .await + .map_err(|e| e.into()) + .map(|channel| DaemonChannel::new(channel)); + channel + }; + Box::pin(fut) + } +} /// Retry policy that retries on all errors up to a maximum number of retries. #[derive(Debug, Clone)] @@ -50,7 +95,11 @@ where { type Future = Ready; - fn retry(&self, _req: &http::Request, result: Result<&http::Response, &E>) -> Option { + fn retry( + &self, + _req: &http::Request, + result: Result<&http::Response, &E>, + ) -> Option { if self.max_retries > 0 && result.is_err() { Some(ready(MyRetryPolicy { max_retries: self.max_retries - 1, @@ -64,4 +113,4 @@ where fn clone_request(&self, req: &http::Request) -> Option> { None } -} \ No newline at end of file +} From 97f67d6614e9794299ddc3ca9eea64c673182a17 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 28 Jun 2024 17:03:20 +0200 Subject: [PATCH 061/108] compile --- cw-orch-daemon/src/queriers/cosmwasm.rs | 13 ++++--------- cw-orch-daemon/src/service.rs | 8 -------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 52febcfba..919fc9051 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -166,8 +166,6 @@ impl CosmWasm { ) -> Result, DaemonError> { use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; - let endpoint = Endpoint::from_static("http://localhost:50051"); - let cc = DaemonChannel::new(self.channel.clone()); // Create the Reconnect layer with the factory @@ -178,18 +176,15 @@ impl CosmWasm { let retry_layer = RetryLayer::new(retry_policy); let reconnect_service: Reconnect = - Reconnect::new::<_, _>(DaemonChannelFactory{},"http://localhost:50051".into()); + Reconnect::new::(DaemonChannelFactory{},"http://localhost:50051".into()); // Build your service stack let service = ServiceBuilder::new() - .layer(reconnect_service) - // .layer(retry_layer) - .service_fn(|cc| DaemonChannelFactory{}.make_service(cc)); + .service(reconnect_service); - let service = ServiceBuilder::new() - .layer(reconnect_service) + let retry = ServiceBuilder::new() .layer(retry_layer) - .service_fn(|cc| async { Ok(cc.clone()) }); + .service(cc); let mut client = QueryClient::new(service); diff --git a/cw-orch-daemon/src/service.rs b/cw-orch-daemon/src/service.rs index 1f44c7dfa..26f7d2469 100644 --- a/cw-orch-daemon/src/service.rs +++ b/cw-orch-daemon/src/service.rs @@ -50,14 +50,6 @@ impl Service> for DaemonChannel { pub struct DaemonChannelFactory {} -impl Layer for DaemonChannel { - type Service = DaemonChannel; - - fn layer(&self, inner: Channel) -> Self::Service { - DaemonChannel::new(inner) - } -} - impl Service for DaemonChannelFactory { type Response = DaemonChannel; type Error = Box; From 1a18cf1b230852122fe102bc6995eeca16922b68 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 28 Jun 2024 17:21:13 +0200 Subject: [PATCH 062/108] formatting --- cw-orch-daemon/src/queriers/cosmwasm.rs | 18 ++------------- cw-orch-daemon/src/service.rs | 29 +++++++++++++++---------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 919fc9051..bc4b5f10d 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -166,25 +166,11 @@ impl CosmWasm { ) -> Result, DaemonError> { use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; - let cc = DaemonChannel::new(self.channel.clone()); - - // Create the Reconnect layer with the factory - let retry_policy = MyRetryPolicy { - max_retries: 3, // Maximum number of retries - backoff: Duration::from_secs(1), // Backoff duration - }; - let retry_layer = RetryLayer::new(retry_policy); - let reconnect_service: Reconnect = - Reconnect::new::(DaemonChannelFactory{},"http://localhost:50051".into()); + Reconnect::new::(DaemonChannelFactory {}, self.channel); // Build your service stack - let service = ServiceBuilder::new() - .service(reconnect_service); - - let retry = ServiceBuilder::new() - .layer(retry_layer) - .service(cc); + let service = ServiceBuilder::new().service(reconnect_service); let mut client = QueryClient::new(service); diff --git a/cw-orch-daemon/src/service.rs b/cw-orch-daemon/src/service.rs index 26f7d2469..c916994e5 100644 --- a/cw-orch-daemon/src/service.rs +++ b/cw-orch-daemon/src/service.rs @@ -14,14 +14,15 @@ use tonic::{ Request, }; use tower::{ - retry::{Policy, RetryLayer}, Layer, MakeService, Service, ServiceBuilder + retry::{Policy, Retry, RetryLayer}, + Layer, MakeService, Service, ServiceBuilder, }; use crate::DaemonState; #[derive(Clone)] pub struct DaemonChannel { - pub (crate) svs: Channel, + pub(crate) svs: Channel, } impl DaemonChannel { @@ -50,8 +51,9 @@ impl Service> for DaemonChannel { pub struct DaemonChannelFactory {} -impl Service for DaemonChannelFactory { - type Response = DaemonChannel; +// TODO: take different type than channel +impl Service for DaemonChannelFactory { + type Response = Retry; type Error = Box; type Future = Pin> + Send>>; @@ -59,16 +61,19 @@ impl Service for DaemonChannelFactory { Poll::Ready(Ok(())) } - fn call(&mut self, request: String) -> Self::Future { + fn call(&mut self, request: Channel) -> Self::Future { let endpoint = request; let fut = async move { - let channel = Channel::from_shared(endpoint) - .expect("valid URI") - .connect() - .await - .map_err(|e| e.into()) - .map(|channel| DaemonChannel::new(channel)); - channel + // Create the Reconnect layer with the factory + let retry_policy = MyRetryPolicy { + max_retries: 3, // Maximum number of retries + backoff: Duration::from_secs(1), // Backoff duration + }; + let retry_layer = RetryLayer::new(retry_policy); + let a = ServiceBuilder::new() + .layer(retry_layer) + .service(DaemonChannel::new(request)); + Ok(a) }; Box::pin(fut) } From e520ce21092e999ba831287511afad339c41b512 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 28 Jun 2024 17:21:42 +0200 Subject: [PATCH 063/108] fix compile --- cw-orch-daemon/src/queriers/cosmwasm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index bc4b5f10d..7d98d4c9a 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -166,8 +166,8 @@ impl CosmWasm { ) -> Result, DaemonError> { use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; - let reconnect_service: Reconnect = - Reconnect::new::(DaemonChannelFactory {}, self.channel); + let reconnect_service: Reconnect = + Reconnect::new::(DaemonChannelFactory {}, self.channel.clone()); // Build your service stack let service = ServiceBuilder::new().service(reconnect_service); From bedebe13285ebce9cc0c50b43f1edb89657265c1 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 28 Jun 2024 17:23:19 +0200 Subject: [PATCH 064/108] fix compile --- cw-orch-daemon/src/service.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cw-orch-daemon/src/service.rs b/cw-orch-daemon/src/service.rs index c916994e5..23b7c1090 100644 --- a/cw-orch-daemon/src/service.rs +++ b/cw-orch-daemon/src/service.rs @@ -57,12 +57,11 @@ impl Service for DaemonChannelFactory { type Error = Box; type Future = Pin> + Send>>; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, request: Channel) -> Self::Future { - let endpoint = request; let fut = async move { // Create the Reconnect layer with the factory let retry_policy = MyRetryPolicy { From a55185115c6e215093ad10c07ef176f59727ba64 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Mon, 1 Jul 2024 11:04:31 +0200 Subject: [PATCH 065/108] impl service --- cw-orch-daemon/src/builder.rs | 2 - cw-orch-daemon/src/core.rs | 8 ++-- cw-orch-daemon/src/live_mock.rs | 2 +- cw-orch-daemon/src/queriers/authz.rs | 18 ++++---- cw-orch-daemon/src/queriers/bank.rs | 16 +++---- cw-orch-daemon/src/queriers/cosmwasm.rs | 45 +++++++++++-------- cw-orch-daemon/src/queriers/feegrant.rs | 16 +++---- cw-orch-daemon/src/queriers/gov.rs | 16 +++---- cw-orch-daemon/src/queriers/ibc.rs | 16 +++---- cw-orch-daemon/src/queriers/node.rs | 18 ++++---- cw-orch-daemon/src/queriers/staking.rs | 16 +++---- cw-orch-daemon/src/sender.rs | 49 +++++++++----------- cw-orch-daemon/src/service.rs | 60 ++++++++++++++----------- cw-orch-daemon/src/state.rs | 3 ++ cw-orch-daemon/src/sync/core.rs | 9 ++-- 15 files changed, 151 insertions(+), 143 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 650c94370..e1f31ab9f 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -173,7 +173,6 @@ impl DaemonAsyncBuilder { Some(sender) => match sender { SenderBuilder::Mnemonic(mnemonic) => Sender::from_mnemonic_with_options( chain_info.clone(), - GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?, &mnemonic, sender_options, )?, @@ -184,7 +183,6 @@ impl DaemonAsyncBuilder { }, None => Sender::new_with_options( chain_info.clone(), - GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?, sender_options, )?, }; diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 3f6851c12..7de458461 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -1,4 +1,4 @@ -use crate::{queriers::CosmWasm, DaemonState}; +use crate::{queriers::CosmWasm, service::DaemonService, DaemonState}; use super::{ builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, queriers::Node, @@ -75,9 +75,9 @@ impl DaemonAsync { DaemonAsyncBuilder::default() } - /// Get the channel configured for this DaemonAsync. - pub fn channel(&self) -> Channel { - self.sender.grpc_channel.clone() + /// Get the communication service configured for this Daemon. + pub async fn service(&self) -> Result { + self.state.service().await } /// Flushes all the state related to the current chain diff --git a/cw-orch-daemon/src/live_mock.rs b/cw-orch-daemon/src/live_mock.rs index e53d370a9..3f4740fe8 100644 --- a/cw-orch-daemon/src/live_mock.rs +++ b/cw-orch-daemon/src/live_mock.rs @@ -82,7 +82,7 @@ impl WasmMockQuerier { match &request { QueryRequest::Wasm(x) => { let querier = CosmWasm { - channel: self.channel.clone(), + service: self.channel.clone(), rt_handle: Some(handle.clone()), }; match x { diff --git a/cw-orch-daemon/src/queriers/authz.rs b/cw-orch-daemon/src/queriers/authz.rs index 2b55ecb75..74621b311 100644 --- a/cw-orch-daemon/src/queriers/authz.rs +++ b/cw-orch-daemon/src/queriers/authz.rs @@ -1,4 +1,4 @@ -use crate::{cosmos_modules, error::DaemonError, Daemon}; +use crate::{cosmos_modules, error::DaemonError, service::DaemonService, Daemon}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cw_orch_core::environment::{Querier, QuerierGetter}; use tokio::runtime::Handle; @@ -7,21 +7,21 @@ use tonic::transport::Channel; /// Queries for Cosmos AuthZ Module /// All the async function are prefixed with `_` pub struct Authz { - pub channel: Channel, + pub service: DaemonService, pub rt_handle: Option, } impl Authz { - pub fn new(daemon: &Daemon) -> Self { - Self { - channel: daemon.channel(), - rt_handle: Some(daemon.rt_handle.clone()), - } + pub fn new(daemon: &Daemon) -> Result { + Ok(Self { + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } - pub fn new_async(channel: Channel) -> Self { + pub fn new_async(service: DaemonService) -> Self { Self { - channel, + service, rt_handle: None, } } diff --git a/cw-orch-daemon/src/queriers/bank.rs b/cw-orch-daemon/src/queriers/bank.rs index d9c8579c9..1f7d83429 100644 --- a/cw-orch-daemon/src/queriers/bank.rs +++ b/cw-orch-daemon/src/queriers/bank.rs @@ -8,20 +8,20 @@ use tonic::transport::Channel; /// Queries for Cosmos Bank Module /// All the async function are prefixed with `_` pub struct Bank { - pub channel: Channel, + pub service: DaemonService, pub rt_handle: Option, } impl Bank { - pub fn new(daemon: &Daemon) -> Self { - Self { - channel: daemon.channel(), - rt_handle: Some(daemon.rt_handle.clone()), - } + pub fn new(daemon: &Daemon) -> Result { + Ok(Self { + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } - pub fn new_async(channel: Channel) -> Self { + pub fn new_async(service: DaemonService) -> Self { Self { - channel, + service, rt_handle: None, } } diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 7d98d4c9a..a9ca5881e 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use std::time::Duration; -use crate::service::{DaemonChannel, DaemonChannelFactory, MyRetryPolicy}; +use crate::service::{DaemonChannel, DaemonChannelFactory, DaemonService, MyRetryPolicy}; use crate::{cosmos_modules, error::DaemonError, Daemon}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmrs::AccountId; @@ -23,20 +23,20 @@ use tower::{MakeService as _, Service, ServiceBuilder}; /// Querier for the CosmWasm SDK module /// All the async function are prefixed with `_` pub struct CosmWasm { - pub channel: Channel, + pub service: DaemonService, pub rt_handle: Option, } impl CosmWasm { pub fn new(daemon: &Daemon) -> Self { Self { - channel: daemon.channel(), + service: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), } } - pub fn new_async(channel: Channel) -> Self { + pub fn new_async(service: DaemonService) -> Self { Self { - channel, + service: channel, rt_handle: None, } } @@ -54,9 +54,11 @@ impl Querier for CosmWasm { impl CosmWasm { /// Query code_id by hash - pub async fn _code_id_hash(&self, code_id: u64) -> Result { + pub async fn _code_id_hash(&mut self, code_id: u64) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + + let mut client = QueryClient::new(&mut self.service); + let request = QueryCodeRequest { code_id }; let resp = client.code(request).await?.into_inner(); let contract_hash = resp.code_info.unwrap().data_hash; @@ -69,7 +71,7 @@ impl CosmWasm { address: impl Into, ) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryContractInfoRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let mut client: QueryClient = QueryClient::new(self.service.clone()); let request = QueryContractInfoRequest { address: address.into(), }; @@ -99,7 +101,7 @@ impl CosmWasm { pagination: Option, ) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryContractHistoryRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let mut client: QueryClient = QueryClient::new(self.service.clone()); let request = QueryContractHistoryRequest { address: address.into(), pagination, @@ -114,7 +116,7 @@ impl CosmWasm { query_data: Vec, ) -> Result, DaemonError> { use cosmos_modules::cosmwasm::{query_client::*, QuerySmartContractStateRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let mut client: QueryClient = QueryClient::new(self.service.clone()); let request = QuerySmartContractStateRequest { address: address.into(), query_data, @@ -133,7 +135,7 @@ impl CosmWasm { pagination: Option, ) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryAllContractStateRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let mut client: QueryClient = QueryClient::new(self.service.clone()); let request = QueryAllContractStateRequest { address: address.into(), pagination, @@ -144,7 +146,7 @@ impl CosmWasm { /// Query code pub async fn _code(&self, code_id: u64) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let mut client: QueryClient = QueryClient::new(self.service.clone()); let request = QueryCodeRequest { code_id }; let response = client.code(request).await?.into_inner().code_info.unwrap(); @@ -154,7 +156,7 @@ impl CosmWasm { /// Query code bytes pub async fn _code_data(&self, code_id: u64) -> Result, DaemonError> { use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let mut client: QueryClient = QueryClient::new(self.service.clone()); let request = QueryCodeRequest { code_id }; Ok(client.code(request).await?.into_inner().data) } @@ -167,7 +169,7 @@ impl CosmWasm { use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest}; let reconnect_service: Reconnect = - Reconnect::new::(DaemonChannelFactory {}, self.channel.clone()); + Reconnect::new::(DaemonChannelFactory {}, self.service.clone()); // Build your service stack let service = ServiceBuilder::new().service(reconnect_service); @@ -188,7 +190,7 @@ impl CosmWasm { &self, ) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let mut client: QueryClient = QueryClient::new(self.service.clone()); let request = QueryPinnedCodesRequest { pagination: None }; Ok(client.pinned_codes(request).await?.into_inner()) } @@ -199,7 +201,7 @@ impl CosmWasm { code_id: u64, ) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryContractsByCodeRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let mut client: QueryClient = QueryClient::new(self.service.clone()); let request = QueryContractsByCodeRequest { code_id, pagination: None, @@ -214,7 +216,7 @@ impl CosmWasm { query_data: Vec, ) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryRawContractStateRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + let mut client: QueryClient = QueryClient::new(self.service.clone()); let request = QueryRawContractStateRequest { address: address.into(), query_data, @@ -227,7 +229,14 @@ impl CosmWasm { &self, ) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest}; - let mut client: QueryClient = QueryClient::new(self.channel.clone()); + + let reconnect_service: Reconnect = + Reconnect::new::(DaemonChannelFactory {}, self.service.clone()); + + // Build your service stack + let service = ServiceBuilder::new().service(reconnect_service); + + let mut client = QueryClient::new(service); Ok(client.params(QueryParamsRequest {}).await?.into_inner()) } } diff --git a/cw-orch-daemon/src/queriers/feegrant.rs b/cw-orch-daemon/src/queriers/feegrant.rs index 5937075d4..a0f456c02 100644 --- a/cw-orch-daemon/src/queriers/feegrant.rs +++ b/cw-orch-daemon/src/queriers/feegrant.rs @@ -7,21 +7,21 @@ use tonic::transport::Channel; /// Querier for the Cosmos Gov module /// All the async function are prefixed with `_` pub struct FeeGrant { - pub channel: Channel, + pub service: DaemonService, pub rt_handle: Option, } impl FeeGrant { - pub fn new(daemon: &Daemon) -> Self { - Self { - channel: daemon.channel(), - rt_handle: Some(daemon.rt_handle.clone()), - } + pub fn new(daemon: &Daemon) -> Result { + Ok(Self { + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } - pub fn new_async(channel: Channel) -> Self { + pub fn new_async(service: DaemonService) -> Self { Self { - channel, + service, rt_handle: None, } } diff --git a/cw-orch-daemon/src/queriers/gov.rs b/cw-orch-daemon/src/queriers/gov.rs index 1fd4a5197..aca009dac 100644 --- a/cw-orch-daemon/src/queriers/gov.rs +++ b/cw-orch-daemon/src/queriers/gov.rs @@ -7,21 +7,21 @@ use tonic::transport::Channel; /// Querier for the Cosmos Gov module /// All the async function are prefixed with `_` pub struct Gov { - pub channel: Channel, + pub service: DaemonService, pub rt_handle: Option, } impl Gov { - pub fn new(daemon: &Daemon) -> Self { - Self { - channel: daemon.channel(), - rt_handle: Some(daemon.rt_handle.clone()), - } + pub fn new(daemon: &Daemon) -> Result { + Ok(Self { + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } - pub fn new_async(channel: Channel) -> Self { + pub fn new_async(service: DaemonService) -> Self { Self { - channel, + service, rt_handle: None, } } diff --git a/cw-orch-daemon/src/queriers/ibc.rs b/cw-orch-daemon/src/queriers/ibc.rs index 986ee5ff7..6d27c600f 100644 --- a/cw-orch-daemon/src/queriers/ibc.rs +++ b/cw-orch-daemon/src/queriers/ibc.rs @@ -17,21 +17,21 @@ use tonic::transport::Channel; /// Querier for the Cosmos IBC module /// All the async function are prefixed with `_` pub struct Ibc { - pub channel: Channel, + pub service: DaemonService, pub rt_handle: Option, } impl Ibc { - pub fn new(daemon: &Daemon) -> Self { - Self { - channel: daemon.channel(), - rt_handle: Some(daemon.rt_handle.clone()), - } + pub fn new(daemon: &Daemon) -> Result { + Ok(Self { + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } - pub fn new_async(channel: Channel) -> Self { + pub fn new_async(service: DaemonService) -> Self { Self { - channel, + service, rt_handle: None, } } diff --git a/cw-orch-daemon/src/queriers/node.rs b/cw-orch-daemon/src/queriers/node.rs index 83365a842..de8272963 100644 --- a/cw-orch-daemon/src/queriers/node.rs +++ b/cw-orch-daemon/src/queriers/node.rs @@ -1,7 +1,7 @@ use std::{cmp::min, time::Duration}; use crate::{ - cosmos_modules, env::DaemonEnvVars, error::DaemonError, tx_resp::CosmTxResponse, Daemon, + cosmos_modules, env::DaemonEnvVars, error::DaemonError, service::DaemonService, tx_resp::CosmTxResponse, Daemon }; use cosmrs::{ @@ -23,20 +23,20 @@ use tonic::transport::Channel; /// Supports queries for block and tx information /// All the async function are prefixed with `_` pub struct Node { - pub channel: Channel, + pub service: DaemonService, pub rt_handle: Option, } impl Node { - pub fn new(daemon: &Daemon) -> Self { - Self { - channel: daemon.channel(), - rt_handle: Some(daemon.rt_handle.clone()), - } + pub fn new(daemon: &Daemon) -> Result { + Ok(Self { + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } - pub fn new_async(channel: Channel) -> Self { + pub fn new_async(service: DaemonService) -> Self { Self { - channel, + service, rt_handle: None, } } diff --git a/cw-orch-daemon/src/queriers/staking.rs b/cw-orch-daemon/src/queriers/staking.rs index 63d8b8860..0b2d9b137 100644 --- a/cw-orch-daemon/src/queriers/staking.rs +++ b/cw-orch-daemon/src/queriers/staking.rs @@ -12,21 +12,21 @@ use super::bank::cosmrs_to_cosmwasm_coin; /// Querier for the Cosmos Staking module /// All the async function are prefixed with `_` pub struct Staking { - pub channel: Channel, + pub service: DaemonService, pub rt_handle: Option, } impl Staking { - pub fn new(daemon: &Daemon) -> Self { - Self { - channel: daemon.channel(), - rt_handle: Some(daemon.rt_handle.clone()), - } + pub fn new(daemon: &Daemon) -> Result { + Ok(Self { + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } - pub fn new_async(channel: Channel) -> Self { + pub fn new_async(service: DaemonService) -> Self { Self { - channel, + service, rt_handle: None, } } diff --git a/cw-orch-daemon/src/sender.rs b/cw-orch-daemon/src/sender.rs index 2cc790f9b..a5974a79f 100644 --- a/cw-orch-daemon/src/sender.rs +++ b/cw-orch-daemon/src/sender.rs @@ -1,11 +1,8 @@ use crate::{ - env::DaemonEnvVars, - proto::injective::ETHEREUM_COIN_TYPE, - queriers::Bank, - tx_broadcaster::{ + env::DaemonEnvVars, proto::injective::ETHEREUM_COIN_TYPE, queriers::Bank, service::{DaemonChannel, DaemonChannelFactory, DaemonService, DaemonServiceCreation}, tx_broadcaster::{ account_sequence_strategy, assert_broadcast_code_cosm_response, insufficient_fee_strategy, TxBroadcaster, - }, + }, GrpcChannel }; use super::{ @@ -63,8 +60,6 @@ pub type Wallet = Arc>; pub struct Sender { pub private_key: PrivateKey, pub secp: Secp256k1, - /// gRPC channel - pub grpc_channel: Channel, /// Information about the chain pub chain_info: ChainInfoOwned, pub(crate) options: SenderOptions, @@ -104,37 +99,30 @@ impl SenderOptions { } impl Sender { - pub fn new(chain_info: ChainInfoOwned, channel: Channel) -> Result, DaemonError> { - Self::new_with_options(chain_info, channel, SenderOptions::default()) - } - - pub fn channel(&self) -> Channel { - self.grpc_channel.clone() + pub fn new(chain_info: ChainInfoOwned) -> Result, DaemonError> { + Self::new_with_options(chain_info, SenderOptions::default()) } pub fn new_with_options( chain_info: ChainInfoOwned, - channel: Channel, options: SenderOptions, ) -> Result, DaemonError> { let mnemonic = get_mnemonic_env(&chain_info.kind)?; - Self::from_mnemonic_with_options(chain_info, channel, &mnemonic, options) + Self::from_mnemonic_with_options(chain_info, &mnemonic, options) } /// Construct a new Sender from a mnemonic with additional options pub fn from_mnemonic( chain_info: ChainInfoOwned, - channel: Channel, mnemonic: &str, ) -> Result, DaemonError> { - Self::from_mnemonic_with_options(chain_info, channel, mnemonic, SenderOptions::default()) + Self::from_mnemonic_with_options(chain_info, mnemonic, SenderOptions::default()) } /// Construct a new Sender from a mnemonic with additional options pub fn from_mnemonic_with_options( chain_info: ChainInfoOwned, - channel: Channel, mnemonic: &str, options: SenderOptions, ) -> Result, DaemonError> { @@ -149,7 +137,6 @@ impl Sender { let sender = Sender { chain_info, - grpc_channel: channel, private_key: p_key, secp, options, @@ -166,7 +153,6 @@ impl Sender { /// Construct a new Sender from a raw key with additional options pub fn from_raw_key_with_options( chain_info: ChainInfoOwned, - channel: Channel, raw_key: &[u8], options: SenderOptions, ) -> Result, DaemonError> { @@ -182,7 +168,6 @@ impl Sender { private_key: p_key, secp, options, - grpc_channel: channel, chain_info, }; log::info!( @@ -207,7 +192,6 @@ impl Sender { // Need to generate new sender as hd_index impacts private key let new_sender = Sender::from_raw_key_with_options( self.chain_info.clone(), - self.channel(), &self.private_key.raw_key(), options, ) @@ -310,7 +294,7 @@ impl Sender { let tx_raw = self.sign(sign_doc)?; - Node::new_async(self.channel()) + Node::new_async(self.channel()?) ._simulate_tx(tx_raw.to_bytes()?) .await } @@ -322,7 +306,7 @@ impl Sender { msgs: Vec, memo: Option<&str>, ) -> Result<(u64, Coin), DaemonError> { - let timeout_height = Node::new_async(self.channel())._block_height().await? + 10u64; + let timeout_height = Node::new_async(self.channel()?)._block_height().await? + 10u64; let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); @@ -360,7 +344,7 @@ impl Sender { msgs: Vec, memo: Option<&str>, ) -> Result { - let timeout_height = Node::new_async(self.channel())._block_height().await? + 10u64; + let timeout_height = Node::new_async(self.channel()?)._block_height().await? + 10u64; let msgs = if self.options.authz_granter.is_some() { // We wrap authz messages @@ -390,7 +374,7 @@ impl Sender { .broadcast(tx_builder, self) .await?; - let resp = Node::new_async(self.channel()) + let resp = Node::new_async(self.channel()?) ._find_tx(tx_response.txhash) .await?; @@ -415,7 +399,7 @@ impl Sender { pub async fn base_account(&self) -> Result { let addr = self.pub_addr().unwrap().to_string(); - let mut client = cosmos_modules::auth::query_client::QueryClient::new(self.channel()); + let mut client = cosmos_modules::auth::query_client::QueryClient::new(self.channel()?); let resp = client .account(cosmos_modules::auth::QueryAccountRequest { address: addr }) @@ -444,7 +428,7 @@ impl Sender { &self, tx: Raw, ) -> Result { - let mut client = cosmos_modules::tx::service_client::ServiceClient::new(self.channel()); + let mut client = cosmos_modules::tx::service_client::ServiceClient::new(self.channel()?); let commit = client .broadcast_tx(cosmos_modules::tx::BroadcastTxRequest { tx_bytes: tx.to_bytes()?, @@ -470,7 +454,7 @@ impl Sender { async fn assert_wallet_balance(&self, fee: &Coin) -> Result<(), DaemonError> { let chain_info = self.chain_info.clone(); - let bank = Bank::new_async(self.channel()); + let bank = Bank::new_async(self.channel()?); let balance = bank ._balance(self.address()?, Some(fee.denom.clone())) .await?[0] @@ -524,6 +508,13 @@ impl Sender { } } +impl DaemonServiceCreation for Sender { + async fn channel(&self) -> Result { + let channel = GrpcChannel::connect(&self.chain_info.grpc_urls, &self.chain_info.chain_id).await?; + Ok(DaemonService::new::(DaemonChannelFactory {}, channel)) + } +} + fn get_mnemonic_env(chain_kind: &ChainKind) -> Result { match chain_kind { ChainKind::Local => DaemonEnvVars::local_mnemonic(), diff --git a/cw-orch-daemon/src/service.rs b/cw-orch-daemon/src/service.rs index 23b7c1090..112b5714e 100644 --- a/cw-orch-daemon/src/service.rs +++ b/cw-orch-daemon/src/service.rs @@ -1,33 +1,54 @@ use std::{ - error::Error, - future::{ready, Future, Ready}, - pin::Pin, - sync::Arc, - task::{Context, Poll}, - time::Duration, + cell::RefCell, error::Error, future::{ready, Future, Ready}, pin::Pin, rc::Rc, sync::Arc, task::{Context, Poll}, time::Duration }; +use cw_orch_core::environment::ChainInfoOwned; use tonic::{ body::BoxBody, client::GrpcService, - transport::{channel, Channel}, + transport::{channel, Channel, Endpoint}, Request, }; use tower::{ - retry::{Policy, Retry, RetryLayer}, - Layer, MakeService, Service, ServiceBuilder, + reconnect::Reconnect, retry::{Policy, Retry, RetryLayer}, Layer, MakeService, Service, ServiceBuilder, ServiceExt }; -use crate::DaemonState; +use crate::{DaemonError, GrpcChannel}; + +/// Daemon Service is a wrapper around the [`Reconnect`] layer that can be shared within a thread. +/// This allows our services to scale between different threads while still being error-tolerant. +/// A signing daemon will have two DaemonService instances, one for transactions and one for queries. +pub type DaemonService = Rc>>; + +pub trait DaemonServiceCreation { + /// Create a new `Rc>>` service for interacting with chain. + async fn new_service(&self, chain_info: &ChainInfoOwned) -> Result { + let channel = GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?; + Ok(DaemonService::new(RefCell::new(Reconnect::new::(DaemonChannelFactory {}, channel)))); + } + + /// Get a new service for interacting with the chain. + async fn channel(&self) -> Result<&mut Reconnect, DaemonError>; +} #[derive(Clone)] pub struct DaemonChannel { - pub(crate) svs: Channel, + // Service that retries on failures + pub(crate) svs: Retry, } impl DaemonChannel { pub fn new(channel: Channel) -> Self { - Self { svs: channel } + // Create the Reconnect layer with the factory + let retry_policy = MyRetryPolicy { + max_retries: 3, // Maximum number of retries + backoff: Duration::from_secs(1), // Backoff duration + }; + let retry_layer = RetryLayer::new(retry_policy); + let a: Retry = ServiceBuilder::new() + .layer(retry_layer) + .service(channel); + Self { svs: a } } } @@ -45,10 +66,6 @@ impl Service> for DaemonChannel { } } -// Is automatically implemented by Tonic ! -// impl GrpcService for DaemonChannel { -// } - pub struct DaemonChannelFactory {} // TODO: take different type than channel @@ -63,16 +80,7 @@ impl Service for DaemonChannelFactory { fn call(&mut self, request: Channel) -> Self::Future { let fut = async move { - // Create the Reconnect layer with the factory - let retry_policy = MyRetryPolicy { - max_retries: 3, // Maximum number of retries - backoff: Duration::from_secs(1), // Backoff duration - }; - let retry_layer = RetryLayer::new(retry_policy); - let a = ServiceBuilder::new() - .layer(retry_layer) - .service(DaemonChannel::new(request)); - Ok(a) + Ok(DaemonChannel::new(request)) }; Box::pin(fut) } diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index c3105d4e6..4df0bc4e7 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -1,5 +1,7 @@ use super::error::DaemonError; use crate::env::{default_state_folder, DaemonEnvVars}; +use crate::service::{DaemonChannel, DaemonChannelFactory, DaemonService, DaemonServiceCreation}; +use crate::GrpcChannel; use crate::{json_lock::JsonLockedState, networks::ChainKind}; use cosmwasm_std::Addr; @@ -8,6 +10,7 @@ use cw_orch_core::{environment::StateInterface, log::local_target, CwEnvError}; use once_cell::sync::Lazy; use serde::Serialize; use serde_json::{json, Value}; +use tonic::transport::Channel; use std::sync::Arc; use std::{ collections::{HashMap, HashSet}, diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 239807a7d..a7a830227 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -2,8 +2,7 @@ use std::fmt::Debug; use super::super::{sender::Wallet, DaemonAsync}; use crate::{ - queriers::{Bank, CosmWasm, Node}, - CosmTxResponse, DaemonBuilder, DaemonError, DaemonState, + queriers::{Bank, CosmWasm, Node}, service::DaemonService, CosmTxResponse, DaemonBuilder, DaemonError, DaemonState }; use cosmwasm_std::{Addr, Coin}; use cw_orch_core::{ @@ -53,9 +52,9 @@ impl Daemon { DaemonBuilder::default() } - /// Get the channel configured for this Daemon - pub fn channel(&self) -> Channel { - self.daemon.sender.grpc_channel.clone() + /// Get the service to interact with a daemon + pub fn service(&self) -> Result { + self.rt_handle.block_on(self.daemon.service()) } /// Get the channel configured for this Daemon From d970e184f594bc22bc60f666d57ea37bc9447453 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Mon, 1 Jul 2024 11:38:33 +0200 Subject: [PATCH 066/108] nit renames --- .../examples/daemon-capabilities.rs | 2 +- cw-orch-daemon/examples/manual_sender.rs | 4 +- cw-orch-daemon/examples/querier-daemon.rs | 4 +- cw-orch-daemon/src/builder.rs | 12 ++-- cw-orch-daemon/src/core.rs | 12 ++-- cw-orch-daemon/src/queriers/bank.rs | 6 +- cw-orch-daemon/src/queriers/cosmwasm.rs | 12 ++-- cw-orch-daemon/src/queriers/env.rs | 4 +- cw-orch-daemon/src/queriers/node.rs | 6 +- cw-orch-daemon/src/senders/base_sender.rs | 4 +- cw-orch-daemon/src/senders/batch_sender.rs | 4 +- cw-orch-daemon/src/senders/mod.rs | 3 +- cw-orch-daemon/src/senders/no_sender.rs | 9 +-- cw-orch-daemon/src/senders/query.rs | 60 +++++++++++++++++++ .../src/senders/{sender_trait.rs => tx.rs} | 27 ++++++--- cw-orch-daemon/src/sync/builder.rs | 10 ++-- cw-orch-daemon/src/sync/core.rs | 16 ++--- 17 files changed, 133 insertions(+), 62 deletions(-) create mode 100644 cw-orch-daemon/src/senders/query.rs rename cw-orch-daemon/src/senders/{sender_trait.rs => tx.rs} (75%) diff --git a/cw-orch-daemon/examples/daemon-capabilities.rs b/cw-orch-daemon/examples/daemon-capabilities.rs index b28b407a7..23e474ce4 100644 --- a/cw-orch-daemon/examples/daemon-capabilities.rs +++ b/cw-orch-daemon/examples/daemon-capabilities.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use cosmrs::{tx::Msg, AccountId, Coin, Denom}; use cosmwasm_std::coins; // ANCHOR: full_counter_example -use cw_orch_daemon::senders::sender_trait::SenderTrait; +use cw_orch_daemon::senders::tx::TxSender; use cw_orch_daemon::DaemonBuilder; use cw_orch_networks::networks; diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index 02ab66052..5ed63208b 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -49,7 +49,7 @@ pub fn main() -> anyhow::Result<()> { Ok(()) } -use cw_orch_daemon::senders::sender_trait::SenderTrait; +use cw_orch_daemon::senders::tx::TxSender; pub type ManualDaemon = DaemonBase; @@ -67,7 +67,7 @@ pub struct ManualSender { pub grpc_channel: Channel, } -impl SenderTrait for ManualSender { +impl TxSender for ManualSender { type Error = DaemonError; type SenderOptions = ManualSenderOptions; diff --git a/cw-orch-daemon/examples/querier-daemon.rs b/cw-orch-daemon/examples/querier-daemon.rs index 44f120da8..d80b7b2f4 100644 --- a/cw-orch-daemon/examples/querier-daemon.rs +++ b/cw-orch-daemon/examples/querier-daemon.rs @@ -1,7 +1,7 @@ // ANCHOR: full_counter_example use cw_orch::{anyhow, prelude::*}; -use cw_orch_daemon::senders::no_sender::QuerierDaemon; +use cw_orch_daemon::senders::no_sender::QueryOnlyDaemon; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L1 pub const LOCAL_JUNO_SENDER: &str = "juno16g2rahf5846rxzp3fwlswy08fz8ccuwk03k57y"; @@ -11,7 +11,7 @@ pub fn main() -> anyhow::Result<()> { let network = networks::LOCAL_JUNO; // There is no need to register a mnemonic to use this daemon querier - let chain = QuerierDaemon::builder().chain(network).build()?; + let chain = QueryOnlyDaemon::builder().chain(network).build()?; let balances = chain.bank_querier().balance(LOCAL_JUNO_SENDER, None)?; assert!(!balances.is_empty()); diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index c73e14ea2..9e41b859b 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,5 +1,5 @@ use crate::{ - log::print_if_log_disabled, senders::sender_trait::SenderTrait, DaemonAsyncBase, + log::print_if_log_disabled, senders::tx::TxSender, DaemonAsyncBase, DaemonBuilderBase, DaemonStateFile, GrpcChannel, Wallet, }; @@ -21,7 +21,7 @@ pub const DEFAULT_DEPLOYMENT: &str = "default"; /// .await.unwrap(); /// # }) /// ``` -pub struct DaemonAsyncBuilderBase { +pub struct DaemonAsyncBuilderBase { // # Required pub(crate) chain: Option, // # Optional @@ -42,7 +42,7 @@ pub struct DaemonAsyncBuilderBase { pub type DaemonAsyncBuilder = DaemonAsyncBuilderBase; -impl Default for DaemonAsyncBuilderBase { +impl Default for DaemonAsyncBuilderBase { fn default() -> Self { Self { chain: Default::default(), @@ -85,7 +85,7 @@ impl DaemonAsyncBuilder { } } -impl DaemonAsyncBuilderBase { +impl DaemonAsyncBuilderBase { /// Set the chain the daemon will connect to pub fn chain(&mut self, chain: impl Into) -> &mut Self { self.chain = Some(chain.into()); @@ -101,7 +101,7 @@ impl DaemonAsyncBuilderBase { /// Specifies a sender to use with this chain /// This will be used in priority when set on the builder - pub fn sender( + pub fn sender( &self, wallet: NewSender, ) -> DaemonAsyncBuilderBase { @@ -214,7 +214,7 @@ impl DaemonAsyncBuilderBase { } } -impl From> for DaemonAsyncBuilderBase { +impl From> for DaemonAsyncBuilderBase { fn from(value: DaemonBuilderBase) -> Self { DaemonAsyncBuilderBase { chain: value.chain, diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index b0202adf7..34a5da48c 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -30,7 +30,7 @@ use std::{ use tonic::transport::Channel; -use crate::senders::sender_trait::SenderTrait; +use crate::senders::tx::TxSender; pub const INSTANTIATE_2_TYPE_URL: &str = "/cosmwasm.wasm.v1.MsgInstantiateContract2"; @@ -66,7 +66,7 @@ pub const INSTANTIATE_2_TYPE_URL: &str = "/cosmwasm.wasm.v1.MsgInstantiateContra If you do so, you WILL get account sequence errors and your transactions won't get broadcasted. Use a Mutex on top of this DaemonAsync to avoid such errors. */ -pub struct DaemonAsyncBase { +pub struct DaemonAsyncBase { /// Sender to send transactions to the chain pub sender: Sender, /// State of the daemon @@ -75,7 +75,7 @@ pub struct DaemonAsyncBase { pub type DaemonAsync = DaemonAsyncBase; -impl DaemonAsyncBase { +impl DaemonAsyncBase { /// Get the daemon builder pub fn builder() -> DaemonAsyncBuilder { DaemonAsyncBuilder::default() @@ -93,7 +93,7 @@ impl DaemonAsyncBase { } } -impl ChainState for DaemonAsyncBase { +impl ChainState for DaemonAsyncBase { type Out = DaemonState; fn state(&self) -> Self::Out { @@ -102,7 +102,7 @@ impl ChainState for DaemonAsyncBase { } // Execute on the real chain, returns tx response. -impl DaemonAsyncBase { +impl DaemonAsyncBase { /// Get the sender address pub fn sender(&self) -> Addr { self.sender.address().unwrap() @@ -337,7 +337,7 @@ impl DaemonAsyncBase { } /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender( + pub fn set_sender( self, sender: NewSender, ) -> DaemonAsyncBase { diff --git a/cw-orch-daemon/src/queriers/bank.rs b/cw-orch-daemon/src/queriers/bank.rs index 08c3a6b52..85ed8e0c4 100644 --- a/cw-orch-daemon/src/queriers/bank.rs +++ b/cw-orch-daemon/src/queriers/bank.rs @@ -1,4 +1,4 @@ -use crate::{cosmos_modules, error::DaemonError, senders::sender_trait::SenderTrait, DaemonBase}; +use crate::{cosmos_modules, error::DaemonError, senders::tx::TxSender, DaemonBase}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmwasm_std::{Coin, StdError}; use cw_orch_core::environment::{BankQuerier, Querier, QuerierGetter}; @@ -13,7 +13,7 @@ pub struct Bank { } impl Bank { - pub fn new(daemon: &DaemonBase) -> Self { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), @@ -31,7 +31,7 @@ impl Querier for Bank { type Error = DaemonError; } -impl QuerierGetter for DaemonBase { +impl QuerierGetter for DaemonBase { fn querier(&self) -> Bank { Bank::new(self) } diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index a0d8d42f9..4f9b07363 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -1,7 +1,7 @@ use std::{marker::PhantomData, str::FromStr}; use crate::{ - cosmos_modules, error::DaemonError, senders::sender_trait::SenderTrait, DaemonBase, Wallet, + cosmos_modules, error::DaemonError, senders::tx::TxSender, DaemonBase, Wallet, }; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmrs::AccountId; @@ -27,7 +27,7 @@ pub struct CosmWasmBase { pub type CosmWasm = CosmWasmBase; -impl CosmWasmBase { +impl CosmWasmBase { pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), @@ -51,17 +51,17 @@ impl CosmWasmBase { } } -impl QuerierGetter> for DaemonBase { +impl QuerierGetter> for DaemonBase { fn querier(&self) -> CosmWasmBase { CosmWasmBase::new(self) } } -impl Querier for CosmWasmBase { +impl Querier for CosmWasmBase { type Error = DaemonError; } -impl CosmWasmBase { +impl CosmWasmBase { /// Query code_id by hash pub async fn _code_id_hash(&self, code_id: u64) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; @@ -233,7 +233,7 @@ impl CosmWasmBase { } } -impl WasmQuerier for CosmWasmBase { +impl WasmQuerier for CosmWasmBase { type Chain = DaemonBase; fn code_id_hash(&self, code_id: u64) -> Result { self.rt_handle diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index 420998b15..dac93c5b4 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -1,8 +1,8 @@ use cw_orch_core::environment::{EnvironmentInfo, EnvironmentQuerier}; -use crate::{senders::sender_trait::SenderTrait, DaemonBase}; +use crate::{senders::tx::TxSender, DaemonBase}; -impl EnvironmentQuerier for DaemonBase { +impl EnvironmentQuerier for DaemonBase { fn env_info(&self) -> EnvironmentInfo { EnvironmentInfo { chain_id: self.daemon.sender.chain_info().chain_id.clone(), diff --git a/cw-orch-daemon/src/queriers/node.rs b/cw-orch-daemon/src/queriers/node.rs index da6232a6a..a0dd91794 100644 --- a/cw-orch-daemon/src/queriers/node.rs +++ b/cw-orch-daemon/src/queriers/node.rs @@ -1,7 +1,7 @@ use std::{cmp::min, time::Duration}; use crate::{ - cosmos_modules, env::DaemonEnvVars, error::DaemonError, senders::sender_trait::SenderTrait, + cosmos_modules, env::DaemonEnvVars, error::DaemonError, senders::tx::TxSender, tx_resp::CosmTxResponse, DaemonBase, }; @@ -29,7 +29,7 @@ pub struct Node { } impl Node { - pub fn new(daemon: &DaemonBase) -> Self { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), @@ -43,7 +43,7 @@ impl Node { } } -impl QuerierGetter for DaemonBase { +impl QuerierGetter for DaemonBase { fn querier(&self) -> Node { Node::new(self) } diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index c4495b3cd..c2c8b409f 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -43,7 +43,7 @@ use std::str::FromStr; use cosmos_modules::vesting::PeriodicVestingAccount; use tonic::transport::Channel; -use super::sender_trait::SenderTrait; +use super::tx::TxSender; const GAS_BUFFER: f64 = 1.3; const BUFFER_THRESHOLD: u64 = 200_000; @@ -99,7 +99,7 @@ impl SenderOptions { } } -impl SenderTrait for Wallet { +impl TxSender for Wallet { type Error = DaemonError; type SenderOptions = SenderOptions; diff --git a/cw-orch-daemon/src/senders/batch_sender.rs b/cw-orch-daemon/src/senders/batch_sender.rs index 9a3e3e479..6e53eb27b 100644 --- a/cw-orch-daemon/src/senders/batch_sender.rs +++ b/cw-orch-daemon/src/senders/batch_sender.rs @@ -10,7 +10,7 @@ use prost::Name; use std::sync::{Arc, Mutex}; -use super::{base_sender::SenderOptions, sender_trait::SenderTrait}; +use super::{base_sender::SenderOptions, tx::TxSender}; pub type BatchDaemon = DaemonBase; @@ -24,7 +24,7 @@ pub struct BatchSender { pub sender: Wallet, } -impl SenderTrait for BatchSender { +impl TxSender for BatchSender { type Error = DaemonError; type SenderOptions = SenderOptions; diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index 475d8f6c6..d1a6e9858 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -1,4 +1,5 @@ pub mod base_sender; pub mod batch_sender; pub mod no_sender; -pub mod sender_trait; +pub mod tx; +pub mod query; diff --git a/cw-orch-daemon/src/senders/no_sender.rs b/cw-orch-daemon/src/senders/no_sender.rs index 8e04c3dc0..1274595e4 100644 --- a/cw-orch-daemon/src/senders/no_sender.rs +++ b/cw-orch-daemon/src/senders/no_sender.rs @@ -6,12 +6,13 @@ use cw_orch_core::environment::ChainInfoOwned; use tonic::transport::Channel; -use super::sender_trait::SenderTrait; +use super::tx::TxSender; -pub type QuerierDaemon = DaemonBase; +/// Daemon that does not support signing. +/// Will err on any attempt to sign a transaction or retrieve a sender address. +pub type QueryOnlyDaemon = DaemonBase; /// Signer of the transactions and helper for address derivation -/// This is the main interface for simulating and signing transactions #[derive(Clone)] pub struct NoSender { /// gRPC channel @@ -20,7 +21,7 @@ pub struct NoSender { pub chain_info: ChainInfoOwned, } -impl SenderTrait for NoSender { +impl TxSender for NoSender { type Error = DaemonError; type SenderOptions = (); diff --git a/cw-orch-daemon/src/senders/query.rs b/cw-orch-daemon/src/senders/query.rs new file mode 100644 index 000000000..e1081b90c --- /dev/null +++ b/cw-orch-daemon/src/senders/query.rs @@ -0,0 +1,60 @@ + +use cosmrs::{tx::Msg, AccountId, Any}; +use cosmwasm_std::Addr; +use cw_orch_core::environment::ChainInfoOwned; +use tonic::transport::Channel; + +use crate::{CosmTxResponse, DaemonError}; + +pub trait QuerySender: Clone { + type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; + /// Options for the sender + type SenderOptions: Default + Clone; + + /// Build a new `Sender`. + fn build( + chain_info: ChainInfoOwned, + grpc_channel: Channel, + sender_options: Self::SenderOptions, + ) -> Result; + + /// Set the Sender options + fn set_options(&mut self, options: Self::SenderOptions); + + /// Get the address of the sender. + fn address(&self) -> Result; + + /// Get the chain_information for the sender + fn chain_info(&self) -> &ChainInfoOwned; + + /// Get the channel for the sender (TODO: return mut ref to Retry Sender) + fn grpc_channel(&self) -> Channel; + + /// Returns the `AccountId` of the sender. + /// If an authz granter is set, returns the authz granter + /// Else, returns the address associated with the current private key + fn msg_sender(&self) -> Result; + + /// Commit a transaction to the chain using this sender. + fn commit_tx( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> impl std::future::Future> + Send { + let msgs = msgs + .into_iter() + .map(Msg::into_any) + .collect::, _>>() + .unwrap(); + + self.commit_tx_any(msgs, memo) + } + + /// Commit a proto `Any` message to the chain using this sender. + fn commit_tx_any( + &self, + msgs: Vec, + memo: Option<&str>, + ) -> impl std::future::Future> + Send; + +} diff --git a/cw-orch-daemon/src/senders/sender_trait.rs b/cw-orch-daemon/src/senders/tx.rs similarity index 75% rename from cw-orch-daemon/src/senders/sender_trait.rs rename to cw-orch-daemon/src/senders/tx.rs index 6fc921062..1c3baabeb 100644 --- a/cw-orch-daemon/src/senders/sender_trait.rs +++ b/cw-orch-daemon/src/senders/tx.rs @@ -5,21 +5,36 @@ use tonic::transport::Channel; use crate::{CosmTxResponse, DaemonError}; -pub trait SenderTrait: Clone { +pub trait TxSender: Clone { type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; + /// Options for the sender type SenderOptions: Default + Clone; + + /// Build a new `Sender`. + fn build( + chain_info: ChainInfoOwned, + grpc_channel: Channel, + sender_options: Self::SenderOptions, + ) -> Result; + + /// Set the Sender options + fn set_options(&mut self, options: Self::SenderOptions); + /// Get the address of the sender. fn address(&self) -> Result; + /// Get the chain_information for the sender fn chain_info(&self) -> &ChainInfoOwned; + /// Get the channel for the sender (TODO: return mut ref to Retry Sender) fn grpc_channel(&self) -> Channel; - /// Returns the actual sender of every message sent. + /// Returns the `AccountId` of the sender. /// If an authz granter is set, returns the authz granter /// Else, returns the address associated with the current private key fn msg_sender(&self) -> Result; + /// Commit a transaction to the chain using this sender. fn commit_tx( &self, msgs: Vec, @@ -34,17 +49,11 @@ pub trait SenderTrait: Clone { self.commit_tx_any(msgs, memo) } + /// Commit a proto `Any` message to the chain using this sender. fn commit_tx_any( &self, msgs: Vec, memo: Option<&str>, ) -> impl std::future::Future> + Send; - fn set_options(&mut self, options: Self::SenderOptions); - - fn build( - chain_info: ChainInfoOwned, - grpc_channel: Channel, - sender_options: Self::SenderOptions, - ) -> Result; } diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 88edba663..91f0c9129 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,4 +1,4 @@ -use crate::senders::sender_trait::SenderTrait; +use crate::senders::tx::TxSender; use crate::{DaemonAsyncBuilderBase, DaemonBase, DaemonState, Wallet, RUNTIME}; use cw_orch_core::environment::ChainInfoOwned; @@ -16,7 +16,7 @@ use super::super::error::DaemonError; /// .build() /// .unwrap(); /// ``` -pub struct DaemonBuilderBase { +pub struct DaemonBuilderBase { // # Required pub(crate) chain: Option, // # Optional @@ -37,7 +37,7 @@ pub struct DaemonBuilderBase { pub(crate) sender_options: Sender::SenderOptions, } -impl Default for DaemonBuilderBase { +impl Default for DaemonBuilderBase { fn default() -> Self { Self { chain: Default::default(), @@ -83,7 +83,7 @@ impl DaemonBuilder { } } -impl DaemonBuilderBase { +impl DaemonBuilderBase { /// Set the chain the Daemon will connect to pub fn chain(&mut self, chain: impl Into) -> &mut Self { self.chain = Some(chain.into()); @@ -117,7 +117,7 @@ impl DaemonBuilderBase { /// Specifies a sender to use with this chain /// This will be used in priority when set on the builder - pub fn sender( + pub fn sender( &self, wallet: OtherSender, ) -> DaemonBuilderBase { diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 8234d1e92..8718baa18 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -15,7 +15,7 @@ use serde::Serialize; use tokio::runtime::Handle; use tonic::transport::Channel; -use crate::senders::sender_trait::SenderTrait; +use crate::senders::tx::TxSender; #[derive(Clone)] /** @@ -43,7 +43,7 @@ use crate::senders::sender_trait::SenderTrait; Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. See [Querier](crate::queriers) for examples. */ -pub struct DaemonBase { +pub struct DaemonBase { pub daemon: DaemonAsyncBase, /// Runtime handle to execute async tasks pub rt_handle: Handle, @@ -51,7 +51,7 @@ pub struct DaemonBase { pub type Daemon = DaemonBase; -impl DaemonBase { +impl DaemonBase { /// Get the daemon builder pub fn builder() -> DaemonBuilderBase { DaemonBuilderBase::default() @@ -86,7 +86,7 @@ impl DaemonBase { } } -impl ChainState for DaemonBase { +impl ChainState for DaemonBase { type Out = DaemonState; fn state(&self) -> Self::Out { @@ -95,7 +95,7 @@ impl ChainState for DaemonBase { } // Execute on the real chain, returns tx response -impl TxHandler for DaemonBase { +impl TxHandler for DaemonBase { type Response = CosmTxResponse; type Error = DaemonError; type ContractSource = WasmPath; @@ -165,7 +165,7 @@ impl TxHandler for DaemonBase { } } -impl Stargate for DaemonBase { +impl Stargate for DaemonBase { fn commit_any( &self, msgs: Vec, @@ -187,7 +187,7 @@ impl Stargate for DaemonBase { } } -impl QueryHandler for DaemonBase { +impl QueryHandler for DaemonBase { type Error = DaemonError; fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { @@ -209,7 +209,7 @@ impl QueryHandler for DaemonBase { } } -impl DefaultQueriers for DaemonBase { +impl DefaultQueriers for DaemonBase { type Bank = Bank; type Wasm = CosmWasmBase; type Node = Node; From fa2dee329b067bf915188f7fd034391db8a312ee Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Mon, 1 Jul 2024 15:38:35 +0200 Subject: [PATCH 067/108] split up traits to enable finer implementations --- cw-orch-daemon/examples/manual_sender.rs | 6 +- cw-orch-daemon/src/builder.rs | 157 ++++++---------- cw-orch-daemon/src/channel.rs | 13 +- cw-orch-daemon/src/core.rs | 200 +++++++++++---------- cw-orch-daemon/src/queriers/cosmwasm.rs | 4 +- cw-orch-daemon/src/senders/base_sender.rs | 142 ++++++++------- cw-orch-daemon/src/senders/batch_sender.rs | 57 +++--- cw-orch-daemon/src/senders/builder.rs | 20 +++ cw-orch-daemon/src/senders/mod.rs | 3 +- cw-orch-daemon/src/senders/no_sender.rs | 51 ++---- cw-orch-daemon/src/senders/query.rs | 48 +---- cw-orch-daemon/src/senders/tx.rs | 23 +-- cw-orch-daemon/src/sync/builder.rs | 150 +++------------- cw-orch-daemon/src/sync/core.rs | 34 ++-- 14 files changed, 374 insertions(+), 534 deletions(-) create mode 100644 cw-orch-daemon/src/senders/builder.rs diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index 5ed63208b..584b90735 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -69,7 +69,7 @@ pub struct ManualSender { impl TxSender for ManualSender { type Error = DaemonError; - type SenderOptions = ManualSenderOptions; + type Options = ManualSenderOptions; async fn commit_tx_any( &self, @@ -118,7 +118,7 @@ impl TxSender for ManualSender { fn build( chain_info: cw_orch_core::environment::ChainInfoOwned, grpc_channel: tonic::transport::Channel, - sender_options: Self::SenderOptions, + sender_options: Self::Options, ) -> Result { Ok(Self { chain_info, @@ -131,7 +131,7 @@ impl TxSender for ManualSender { }) } - fn set_options(&mut self, options: Self::SenderOptions) { + fn set_options(&mut self, options: Self::Options) { self.sender = Addr::unchecked( options .sender_address diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 9e41b859b..d55f1c19b 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,6 +1,12 @@ use crate::{ - log::print_if_log_disabled, senders::tx::TxSender, DaemonAsyncBase, - DaemonBuilderBase, DaemonStateFile, GrpcChannel, Wallet, + log::print_if_log_disabled, + senders::{ + base_sender::{CosmosOptions, Sender}, + builder::SenderBuilder, + query::QuerySender, + tx::TxSender, + }, + DaemonAsyncBase, DaemonBuilder, DaemonStateFile, GrpcChannel, Wallet, }; use super::{error::DaemonError, state::DaemonState}; @@ -21,76 +27,27 @@ pub const DEFAULT_DEPLOYMENT: &str = "default"; /// .await.unwrap(); /// # }) /// ``` -pub struct DaemonAsyncBuilderBase { +pub struct DaemonAsyncBuilder { // # Required - pub(crate) chain: Option, + pub(crate) chain: ChainInfoOwned, // # Optional pub(crate) deployment_id: Option, pub(crate) state_path: Option, /// State from rebuild or existing daemon pub(crate) state: Option, pub(crate) write_on_change: Option, - - // Sender options - - // # Optional indicated sender - pub(crate) sender: Option, - - /// Specify Daemon Sender Options - pub(crate) sender_options: Sender::SenderOptions, } -pub type DaemonAsyncBuilder = DaemonAsyncBuilderBase; - -impl Default for DaemonAsyncBuilderBase { - fn default() -> Self { +impl DaemonAsyncBuilder { + pub fn new(chain: impl Into) -> Self { Self { - chain: Default::default(), - deployment_id: Default::default(), - sender_options: Default::default(), - sender: Default::default(), - state_path: Default::default(), - state: Default::default(), - write_on_change: Default::default(), + chain: chain.into(), + deployment_id: None, + state_path: None, + state: None, + write_on_change: None, } } -} - -impl DaemonAsyncBuilder { - /// Specifies whether authz should be used with this daemon - pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { - 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 - } - - /// Specifies the hd_index of the daemon sender - pub fn hd_index(&mut self, index: u32) -> &mut Self { - self.sender_options.hd_index = Some(index); - self - } - - /// Set the mnemonic to use with this chain. - /// Defaults to env variable depending on the environment. - /// - /// Variables: LOCAL_MNEMONIC, TEST_MNEMONIC and MAIN_MNEMONIC - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.sender_options.mnemonic = Some(mnemonic.to_string()); - self - } -} - -impl DaemonAsyncBuilderBase { - /// Set the chain the daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } /// Set the deployment id to use for the daemon interactions /// Defaults to `default` @@ -99,23 +56,6 @@ impl DaemonAsyncBuilderBase { self } - /// Specifies a sender to use with this chain - /// This will be used in priority when set on the builder - pub fn sender( - &self, - wallet: NewSender, - ) -> DaemonAsyncBuilderBase { - DaemonAsyncBuilderBase { - chain: self.chain.clone(), - deployment_id: self.deployment_id.clone(), - state_path: self.state_path.clone(), - state: self.state.clone(), - sender: Some(wallet), - sender_options: NewSender::SenderOptions::default(), - write_on_change: self.write_on_change, - } - } - /// Reuse already existent [`DaemonState`] /// Useful for multi-chain scenarios pub fn state(&mut self, state: DaemonState) -> &mut Self { @@ -142,21 +82,18 @@ impl DaemonAsyncBuilderBase { self } - /// Build a daemon - pub async fn build(&self) -> Result, DaemonError> { - let chain_info = self - .chain - .clone() - .ok_or(DaemonError::BuilderMissing("chain information".into()))?; + /// Returns a built state + pub(crate) fn build_state(&self) -> Result { let deployment_id = self .deployment_id .clone() .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); + let chain_info = self.chain.clone(); let state = match &self.state { Some(state) => { let mut state = state.clone(); - state.chain_data = chain_info.clone(); + state.chain_data = chain_info; state.deployment_id = deployment_id; if let Some(write_on_change) = self.write_on_change { state.write_on_change = write_on_change; @@ -183,29 +120,43 @@ impl DaemonAsyncBuilderBase { DaemonState::new( json_file_path, - chain_info.clone(), + chain_info, deployment_id, false, self.write_on_change.unwrap_or(true), )? } }; + Ok(state) + } + + /// Build a daemon with env-var mnemonic + pub async fn build(&self) -> Result, DaemonError> { + let chain_info = self.chain.clone(); + + let state = self.build_state()?; // if mnemonic provided, use it. Else use env variables to retrieve mnemonic - let sender = if let Some(sender) = &self.sender { - let mut sender = sender.clone(); - sender.set_options(self.sender_options.clone()); - sender - } else { - let chain_id = chain_info.chain_id.clone(); - let grpc_urls = chain_info.grpc_urls.clone(); - Sender::build( - chain_info, - GrpcChannel::connect(&grpc_urls, &chain_id).await?, - self.sender_options.clone(), - ) - .map_err(Into::into)? - }; + let sender = Wallet::build(chain_info, CosmosOptions::default()).await?; + + let daemon = DaemonAsyncBase { state, sender }; + + print_if_log_disabled()?; + Ok(daemon) + } + + /// Build a daemon + pub async fn build_sender( + &self, + sender_options: Sender::Options, + ) -> Result, DaemonError> { + let chain_info = self.chain.clone(); + + let state = self.build_state()?; + + let sender = Sender::build(chain_info, sender_options) + .await + .map_err(Into::into)?; let daemon = DaemonAsyncBase { state, sender }; @@ -214,13 +165,11 @@ impl DaemonAsyncBuilderBase { } } -impl From> for DaemonAsyncBuilderBase { - fn from(value: DaemonBuilderBase) -> Self { - DaemonAsyncBuilderBase { +impl From for DaemonAsyncBuilder { + fn from(value: DaemonBuilder) -> Self { + DaemonAsyncBuilder { chain: value.chain, deployment_id: value.deployment_id, - sender_options: value.sender_options, - sender: value.sender, state: value.state, state_path: value.state_path, write_on_change: value.write_on_change, diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/channel.rs index 96cfb6837..7075e1e83 100644 --- a/cw-orch-daemon/src/channel.rs +++ b/cw-orch-daemon/src/channel.rs @@ -1,7 +1,7 @@ use cosmrs::proto::cosmos::base::tendermint::v1beta1::{ service_client::ServiceClient, GetNodeInfoRequest, }; -use cw_orch_core::log::connectivity_target; +use cw_orch_core::{environment::ChainInfoOwned, log::connectivity_target}; use tonic::transport::{Channel, ClientTlsConfig}; use super::error::DaemonError; @@ -90,6 +90,11 @@ impl GrpcChannel { Ok(successful_connections.pop().unwrap()) } + + /// Create a gRPC channel from the chain info + pub async fn from_chain_info(chain_info: &ChainInfoOwned) -> Result { + GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await + } } #[cfg(test)] @@ -108,8 +113,7 @@ mod tests { let grpcs = &["https://127.0.0.1:99999"]; chain.grpc_urls = grpcs; - let build_res = DaemonAsync::builder() - .chain(chain) + let build_res = DaemonAsync::builder(chain) .deployment_id("v0.1.0") .build() .await; @@ -128,8 +132,7 @@ mod tests { let grpcs = &[]; chain.grpc_urls = grpcs; - let build_res = DaemonAsync::builder() - .chain(chain) + let build_res = DaemonAsync::builder(chain) .deployment_id("v0.1.0") .build() .await; diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 34a5da48c..96cca47b8 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -1,8 +1,12 @@ -use crate::{queriers::CosmWasm, DaemonAsyncBuilderBase, DaemonState}; +use crate::{ + queriers::CosmWasm, + senders::{builder::SenderBuilder, query::QuerySender}, + DaemonAsyncBuilder, DaemonState, +}; use super::{ - builder::DaemonAsyncBuilder, cosmos_modules, error::DaemonError, queriers::Node, - senders::base_sender::Wallet, tx_resp::CosmTxResponse, + cosmos_modules, error::DaemonError, queriers::Node, senders::base_sender::Wallet, + tx_resp::CosmTxResponse, }; use cosmrs::{ @@ -14,7 +18,7 @@ use cosmrs::{ use cosmwasm_std::{Addr, Binary, Coin}; use cw_orch_core::{ contract::interface_traits::Uploadable, - environment::{AsyncWasmQuerier, ChainState, IndexResponse, Querier}, + environment::{AsyncWasmQuerier, ChainInfoOwned, ChainState, IndexResponse, Querier}, log::transaction_target, }; use flate2::{write, Compression}; @@ -66,24 +70,24 @@ pub const INSTANTIATE_2_TYPE_URL: &str = "/cosmwasm.wasm.v1.MsgInstantiateContra If you do so, you WILL get account sequence errors and your transactions won't get broadcasted. Use a Mutex on top of this DaemonAsync to avoid such errors. */ -pub struct DaemonAsyncBase { +pub struct DaemonAsyncBase { /// Sender to send transactions to the chain - pub sender: Sender, + pub(crate) sender: Sender, /// State of the daemon - pub state: DaemonState, + pub(crate) state: DaemonState, } pub type DaemonAsync = DaemonAsyncBase; -impl DaemonAsyncBase { +impl DaemonAsyncBase { /// Get the daemon builder - pub fn builder() -> DaemonAsyncBuilder { - DaemonAsyncBuilder::default() + pub fn builder(chain: impl Into) -> DaemonAsyncBuilder { + DaemonAsyncBuilder::new(chain) } - /// Get the channel configured for this DaemonAsync. - pub fn channel(&self) -> Channel { - self.sender.grpc_channel() + /// Get the mutable Sender object + pub fn sender_mut(&mut self) -> &mut Sender { + &mut self.sender } /// Flushes all the state related to the current chain @@ -91,9 +95,94 @@ impl DaemonAsyncBase { pub fn flush_state(&mut self) -> Result<(), DaemonError> { self.state.flush() } + + /// Returns a new [`DaemonAsyncBuilder`] with the current configuration. + /// Does not consume the original [`DaemonAsync`]. + pub fn rebuild(&self) -> DaemonAsyncBuilder { + DaemonAsyncBuilder { + state: Some(self.state()), + chain: self.state.chain_data.clone(), + deployment_id: Some(self.state.deployment_id.clone()), + state_path: None, + write_on_change: None, + } + } } -impl ChainState for DaemonAsyncBase { +impl DaemonAsyncBase { + /// Get the channel configured for this DaemonAsync. + pub fn channel(&self) -> Channel { + self.sender.grpc_channel() + } + + /// Query a contract. + pub async fn query( + &self, + query_msg: &Q, + contract_address: &Addr, + ) -> Result { + let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new(self.channel()); + let resp = client + .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { + address: contract_address.to_string(), + query_data: serde_json::to_vec(&query_msg)?, + }) + .await?; + + Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) + } + + /// Wait for a given amount of blocks. + pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { + let mut last_height = Node::new_async(self.channel())._block_height().await?; + let end_height = last_height + amount; + + let average_block_speed = Node::new_async(self.channel()) + ._average_block_speed(Some(0.9)) + .await?; + + let wait_time = average_block_speed.mul_f64(amount as f64); + + // now wait for that amount of time + tokio::time::sleep(wait_time).await; + // now check every block until we hit the target + while last_height < end_height { + // wait + + tokio::time::sleep(average_block_speed).await; + + // ping latest block + last_height = Node::new_async(self.channel())._block_height().await?; + } + Ok(()) + } + + /// Wait for a given amount of seconds. + pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { + tokio::time::sleep(Duration::from_secs(secs)).await; + + Ok(()) + } + + /// Wait for the next block. + pub async fn next_block(&self) -> Result<(), DaemonError> { + self.wait_blocks(1).await + } + + /// Get the current block info. + pub async fn block_info(&self) -> Result { + let block = Node::new_async(self.channel())._latest_block().await?; + let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; + let time = cosmwasm_std::Timestamp::from_nanos(since_epoch.as_nanos() as u64); + Ok(cosmwasm_std::BlockInfo { + height: block.header.height.value(), + time, + chain_id: block.header.chain_id.to_string(), + }) + } +} + +impl ChainState for DaemonAsyncBase { type Out = DaemonState; fn state(&self) -> Self::Out { @@ -108,18 +197,6 @@ impl DaemonAsyncBase { self.sender.address().unwrap() } - /// Returns a new [`DaemonAsyncBuilder`] with the current configuration. - /// Does not consume the original [`DaemonAsync`]. - pub fn rebuild(&self) -> DaemonAsyncBuilderBase { - let mut builder = DaemonAsyncBuilder { - state: Some(self.state()), - ..Default::default() - }; - builder - .chain(self.sender.chain_info().clone()) - .sender(self.sender.clone()) - } - /// Execute a message on a contract. pub async fn execute( &self, @@ -212,23 +289,6 @@ impl DaemonAsyncBase { Ok(result) } - /// Query a contract. - pub async fn query( - &self, - query_msg: &Q, - contract_address: &Addr, - ) -> Result { - let mut client = cosmos_modules::cosmwasm::query_client::QueryClient::new(self.channel()); - let resp = client - .smart_contract_state(cosmos_modules::cosmwasm::QuerySmartContractStateRequest { - address: contract_address.to_string(), - query_data: serde_json::to_vec(&query_msg)?, - }) - .await?; - - Ok(from_str(from_utf8(&resp.into_inner().data).unwrap())?) - } - /// Migration a contract. pub async fn migrate( &self, @@ -250,55 +310,6 @@ impl DaemonAsyncBase { Ok(result) } - /// Wait for a given amount of blocks. - pub async fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { - let mut last_height = Node::new_async(self.channel())._block_height().await?; - let end_height = last_height + amount; - - let average_block_speed = Node::new_async(self.channel()) - ._average_block_speed(Some(0.9)) - .await?; - - let wait_time = average_block_speed.mul_f64(amount as f64); - - // now wait for that amount of time - tokio::time::sleep(wait_time).await; - // now check every block until we hit the target - while last_height < end_height { - // wait - - tokio::time::sleep(average_block_speed).await; - - // ping latest block - last_height = Node::new_async(self.channel())._block_height().await?; - } - Ok(()) - } - - /// Wait for a given amount of seconds. - pub async fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> { - tokio::time::sleep(Duration::from_secs(secs)).await; - - Ok(()) - } - - /// Wait for the next block. - pub async fn next_block(&self) -> Result<(), DaemonError> { - self.wait_blocks(1).await - } - - /// Get the current block info. - pub async fn block_info(&self) -> Result { - let block = Node::new_async(self.channel())._latest_block().await?; - let since_epoch = block.header.time.duration_since(Time::unix_epoch())?; - let time = cosmwasm_std::Timestamp::from_nanos(since_epoch.as_nanos() as u64); - Ok(cosmwasm_std::BlockInfo { - height: block.header.height.value(), - time, - chain_id: block.header.chain_id.to_string(), - }) - } - /// Upload a contract to the chain. pub async fn upload( &self, @@ -337,10 +348,7 @@ impl DaemonAsyncBase { } /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender( - self, - sender: NewSender, - ) -> DaemonAsyncBase { + pub fn set_sender(self, sender: NewSender) -> DaemonAsyncBase { DaemonAsyncBase { sender, state: self.state, diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 4f9b07363..16264d1d8 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -1,8 +1,6 @@ use std::{marker::PhantomData, str::FromStr}; -use crate::{ - cosmos_modules, error::DaemonError, senders::tx::TxSender, DaemonBase, Wallet, -}; +use crate::{cosmos_modules, error::DaemonError, senders::tx::TxSender, DaemonBase, Wallet}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmrs::AccountId; use cosmwasm_std::{ diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index c2c8b409f..a5cfd474c 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -6,6 +6,7 @@ use crate::{ account_sequence_strategy, assert_broadcast_code_cosm_response, insufficient_fee_strategy, TxBroadcaster, }, + Daemon, DaemonAsyncBuilder, DaemonBuilder, GrpcChannel, }; use crate::proto::injective::InjectiveEthAccount; @@ -43,7 +44,7 @@ use std::str::FromStr; use cosmos_modules::vesting::PeriodicVestingAccount; use tonic::transport::Channel; -use super::tx::TxSender; +use super::{builder::SenderBuilder, query::QuerySender, tx::TxSender}; const GAS_BUFFER: f64 = 1.3; const BUFFER_THRESHOLD: u64 = 200_000; @@ -62,20 +63,20 @@ pub struct Sender { pub grpc_channel: Channel, /// Information about the chain pub chain_info: ChainInfoOwned, - pub(crate) options: SenderOptions, + pub(crate) options: CosmosOptions, } /// Options for how txs should be constructed for this sender. #[derive(Default, Clone)] #[non_exhaustive] -pub struct SenderOptions { +pub struct CosmosOptions { pub authz_granter: Option, pub fee_granter: Option, pub hd_index: Option, pub mnemonic: Option, } -impl SenderOptions { +impl CosmosOptions { pub fn authz_granter(mut self, granter: impl ToString) -> Self { self.authz_granter = Some(granter.to_string()); self @@ -99,10 +100,79 @@ impl SenderOptions { } } -impl TxSender for Wallet { +impl Daemon { + /// Specifies wether authz should be used with this daemon + pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { + self.sender_mut().set_authz_granter(granter.to_string()); + self + } + + /// Specifies wether feegrant should be used with this daemon + pub fn fee_granter(&mut self, granter: impl ToString) -> &mut Self { + self.sender_mut().set_fee_granter(granter.to_string()); + self + } +} + +impl SenderBuilder for Wallet { type Error = DaemonError; - type SenderOptions = SenderOptions; + type Options = CosmosOptions; + + async fn build( + chain_info: ChainInfoOwned, + sender_options: Self::Options, + ) -> Result { + let channel = GrpcChannel::from_chain_info(&chain_info).await?; + + if let Some(mnemonic) = &sender_options.mnemonic { + Sender::from_mnemonic_with_options( + chain_info, + channel, + &mnemonic.clone(), + sender_options, + ) + } else { + Sender::new_with_options(chain_info, channel, sender_options) + } + } +} + +impl QuerySender for Wallet { + fn chain_info(&self) -> &ChainInfoOwned { + &self.chain_info + } + + fn grpc_channel(&self) -> Channel { + self.channel() + } + + fn set_options(&mut self, options: Self::Options) { + if let Some(mnemonic) = options.mnemonic.clone() { + let new_sender = Sender::from_mnemonic_with_options( + self.chain_info.clone(), + self.channel(), + &mnemonic, + options, + ) + .unwrap(); + *self = new_sender; + } else if options.hd_index.is_some() { + // Need to generate new sender as hd_index impacts private key + let new_sender = Sender::from_raw_key_with_options( + self.chain_info.clone(), + self.channel(), + &self.private_key.raw_key(), + options, + ) + .unwrap(); + *self = new_sender + } else { + self.options = options + } + } +} +impl TxSender for Wallet { async fn commit_tx_any( &self, msgs: Vec, @@ -156,61 +226,11 @@ impl TxSender for Wallet { self.pub_addr() } } - - fn chain_info(&self) -> &ChainInfoOwned { - &self.chain_info - } - - fn grpc_channel(&self) -> Channel { - self.channel() - } - - fn set_options(&mut self, options: Self::SenderOptions) { - if let Some(mnemonic) = options.mnemonic.clone() { - let new_sender = Sender::from_mnemonic_with_options( - self.chain_info.clone(), - self.channel(), - &mnemonic, - options, - ) - .unwrap(); - *self = new_sender; - } else if options.hd_index.is_some() { - // Need to generate new sender as hd_index impacts private key - let new_sender = Sender::from_raw_key_with_options( - self.chain_info.clone(), - self.channel(), - &self.private_key.raw_key(), - options, - ) - .unwrap(); - *self = new_sender - } else { - self.options = options - } - } - - fn build( - chain_info: ChainInfoOwned, - grpc_channel: Channel, - sender_options: Self::SenderOptions, - ) -> Result { - if let Some(mnemonic) = &sender_options.mnemonic { - Sender::from_mnemonic_with_options( - chain_info, - grpc_channel, - &mnemonic.clone(), - sender_options, - ) - } else { - Sender::new_with_options(chain_info, grpc_channel, sender_options) - } - } } impl Wallet { pub fn new(chain_info: ChainInfoOwned, channel: Channel) -> Result { - Self::new_with_options(chain_info, channel, SenderOptions::default()) + Self::new_with_options(chain_info, channel, CosmosOptions::default()) } pub fn channel(&self) -> Channel { @@ -220,7 +240,7 @@ impl Wallet { pub fn new_with_options( chain_info: ChainInfoOwned, channel: Channel, - options: SenderOptions, + options: CosmosOptions, ) -> Result { let mnemonic = get_mnemonic_env(&chain_info.kind)?; @@ -233,7 +253,7 @@ impl Wallet { channel: Channel, mnemonic: &str, ) -> Result { - Self::from_mnemonic_with_options(chain_info, channel, mnemonic, SenderOptions::default()) + Self::from_mnemonic_with_options(chain_info, channel, mnemonic, CosmosOptions::default()) } /// Construct a new Sender from a mnemonic with additional options @@ -241,7 +261,7 @@ impl Wallet { chain_info: ChainInfoOwned, channel: Channel, mnemonic: &str, - options: SenderOptions, + options: CosmosOptions, ) -> Result { let secp = Secp256k1::new(); let p_key: PrivateKey = PrivateKey::from_words( @@ -273,7 +293,7 @@ impl Wallet { chain_info: ChainInfoOwned, channel: Channel, raw_key: &[u8], - options: SenderOptions, + options: CosmosOptions, ) -> Result { let secp = Secp256k1::new(); let p_key: PrivateKey = PrivateKey::from_raw_key( diff --git a/cw-orch-daemon/src/senders/batch_sender.rs b/cw-orch-daemon/src/senders/batch_sender.rs index 6e53eb27b..f4903557e 100644 --- a/cw-orch-daemon/src/senders/batch_sender.rs +++ b/cw-orch-daemon/src/senders/batch_sender.rs @@ -10,7 +10,9 @@ use prost::Name; use std::sync::{Arc, Mutex}; -use super::{base_sender::SenderOptions, tx::TxSender}; +use super::builder::SenderBuilder; +use super::query::QuerySender; +use super::{base_sender::CosmosOptions, tx::TxSender}; pub type BatchDaemon = DaemonBase; @@ -24,10 +26,36 @@ pub struct BatchSender { pub sender: Wallet, } -impl TxSender for BatchSender { +impl SenderBuilder for BatchSender { type Error = DaemonError; - type SenderOptions = SenderOptions; + type Options = CosmosOptions; + + async fn build( + chain_info: cw_orch_core::environment::ChainInfoOwned, + sender_options: Self::Options, + ) -> Result { + Ok(Self { + msgs: Default::default(), + sender: Wallet::build(chain_info, sender_options).await?, + }) + } +} + +impl QuerySender for BatchSender { + fn chain_info(&self) -> &cw_orch_core::environment::ChainInfoOwned { + self.sender.chain_info() + } + + fn grpc_channel(&self) -> tonic::transport::Channel { + self.sender.grpc_channel() + } + + fn set_options(&mut self, options: Self::Options) { + self.sender.set_options(options) + } +} +impl TxSender for BatchSender { async fn commit_tx_any( &self, msgs: Vec, @@ -68,29 +96,6 @@ impl TxSender for BatchSender { fn msg_sender(&self) -> Result { self.sender.msg_sender() } - - fn chain_info(&self) -> &cw_orch_core::environment::ChainInfoOwned { - self.sender.chain_info() - } - - fn grpc_channel(&self) -> tonic::transport::Channel { - self.sender.grpc_channel() - } - - fn build( - chain_info: cw_orch_core::environment::ChainInfoOwned, - grpc_channel: tonic::transport::Channel, - sender_options: Self::SenderOptions, - ) -> Result { - Ok(Self { - msgs: Default::default(), - sender: Wallet::build(chain_info, grpc_channel, sender_options)?, - }) - } - - fn set_options(&mut self, options: Self::SenderOptions) { - self.sender.set_options(options) - } } impl BatchSender { diff --git a/cw-orch-daemon/src/senders/builder.rs b/cw-orch-daemon/src/senders/builder.rs new file mode 100644 index 000000000..29551a290 --- /dev/null +++ b/cw-orch-daemon/src/senders/builder.rs @@ -0,0 +1,20 @@ +use cosmrs::{tx::Msg, AccountId, Any}; +use cosmwasm_std::Addr; +use cw_orch_core::environment::ChainInfoOwned; +use tonic::transport::Channel; + +use crate::DaemonError; + +/// Allows building a `Sender` from `SenderBuilder::Options` +/// `async`` because it could do network requests during build +pub trait SenderBuilder: Clone { + type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; + /// Options for the sender + type Options: Default + Clone; + + /// Build a new `Sender`. + async fn build( + chain_info: ChainInfoOwned, + sender_options: Self::Options, + ) -> Result; +} diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index d1a6e9858..4067c4850 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -1,5 +1,6 @@ pub mod base_sender; pub mod batch_sender; +pub mod builder; pub mod no_sender; -pub mod tx; pub mod query; +pub mod tx; diff --git a/cw-orch-daemon/src/senders/no_sender.rs b/cw-orch-daemon/src/senders/no_sender.rs index 1274595e4..4c1184916 100644 --- a/cw-orch-daemon/src/senders/no_sender.rs +++ b/cw-orch-daemon/src/senders/no_sender.rs @@ -1,4 +1,4 @@ -use crate::{error::DaemonError, tx_resp::CosmTxResponse, DaemonBase}; +use crate::{error::DaemonError, tx_resp::CosmTxResponse, DaemonBase, GrpcChannel}; use cosmrs::{AccountId, Any}; use cosmwasm_std::Addr; @@ -6,9 +6,9 @@ use cw_orch_core::environment::ChainInfoOwned; use tonic::transport::Channel; -use super::tx::TxSender; +use super::{builder::SenderBuilder, query::QuerySender, tx::TxSender}; -/// Daemon that does not support signing. +/// Daemon that does not support signing. /// Will err on any attempt to sign a transaction or retrieve a sender address. pub type QueryOnlyDaemon = DaemonBase; @@ -16,49 +16,36 @@ pub type QueryOnlyDaemon = DaemonBase; #[derive(Clone)] pub struct NoSender { /// gRPC channel - pub grpc_channel: Channel, + pub channel: Channel, /// Information about the chain pub chain_info: ChainInfoOwned, } -impl TxSender for NoSender { +impl SenderBuilder for NoSender { type Error = DaemonError; - type SenderOptions = (); + type Options = (); - async fn commit_tx_any( - &self, - _msgs: Vec, - _memo: Option<&str>, - ) -> Result { - unimplemented!("You used the DaemonQuerier, which can't send transactions"); - } - - fn address(&self) -> Result { - unimplemented!("You used the DaemonQuerier, which doesn't have an associated address"); - } + async fn build( + chain_info: ChainInfoOwned, + _sender_options: Self::Options, + ) -> Result { + let channel = GrpcChannel::from_chain_info(&chain_info).await?; - fn msg_sender(&self) -> Result { - unimplemented!("You used the DaemonQuerier, which doesn't have an associated msg sender"); + Ok(NoSender { + channel, + chain_info, + }) } +} +impl QuerySender for NoSender { fn chain_info(&self) -> &ChainInfoOwned { &self.chain_info } fn grpc_channel(&self) -> Channel { - self.grpc_channel.clone() + self.channel.clone() } - fn set_options(&mut self, _options: Self::SenderOptions) {} - - fn build( - chain_info: ChainInfoOwned, - grpc_channel: Channel, - _sender_options: Self::SenderOptions, - ) -> Result { - Ok(NoSender { - grpc_channel, - chain_info, - }) - } + fn set_options(&mut self, _options: Self::Options) {} } diff --git a/cw-orch-daemon/src/senders/query.rs b/cw-orch-daemon/src/senders/query.rs index e1081b90c..99d0d239a 100644 --- a/cw-orch-daemon/src/senders/query.rs +++ b/cw-orch-daemon/src/senders/query.rs @@ -1,4 +1,3 @@ - use cosmrs::{tx::Msg, AccountId, Any}; use cosmwasm_std::Addr; use cw_orch_core::environment::ChainInfoOwned; @@ -6,55 +5,16 @@ use tonic::transport::Channel; use crate::{CosmTxResponse, DaemonError}; -pub trait QuerySender: Clone { - type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; - /// Options for the sender - type SenderOptions: Default + Clone; - - /// Build a new `Sender`. - fn build( - chain_info: ChainInfoOwned, - grpc_channel: Channel, - sender_options: Self::SenderOptions, - ) -> Result; +use super::builder::SenderBuilder; +/// A sender that can query information over a connection. +pub trait QuerySender: SenderBuilder { /// Set the Sender options - fn set_options(&mut self, options: Self::SenderOptions); - - /// Get the address of the sender. - fn address(&self) -> Result; + fn set_options(&mut self, options: Self::Options); /// Get the chain_information for the sender fn chain_info(&self) -> &ChainInfoOwned; /// Get the channel for the sender (TODO: return mut ref to Retry Sender) fn grpc_channel(&self) -> Channel; - - /// Returns the `AccountId` of the sender. - /// If an authz granter is set, returns the authz granter - /// Else, returns the address associated with the current private key - fn msg_sender(&self) -> Result; - - /// Commit a transaction to the chain using this sender. - fn commit_tx( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> impl std::future::Future> + Send { - let msgs = msgs - .into_iter() - .map(Msg::into_any) - .collect::, _>>() - .unwrap(); - - self.commit_tx_any(msgs, memo) - } - - /// Commit a proto `Any` message to the chain using this sender. - fn commit_tx_any( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> impl std::future::Future> + Send; - } diff --git a/cw-orch-daemon/src/senders/tx.rs b/cw-orch-daemon/src/senders/tx.rs index 1c3baabeb..a9ac3fa1a 100644 --- a/cw-orch-daemon/src/senders/tx.rs +++ b/cw-orch-daemon/src/senders/tx.rs @@ -5,30 +5,12 @@ use tonic::transport::Channel; use crate::{CosmTxResponse, DaemonError}; -pub trait TxSender: Clone { - type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; - /// Options for the sender - type SenderOptions: Default + Clone; - - /// Build a new `Sender`. - fn build( - chain_info: ChainInfoOwned, - grpc_channel: Channel, - sender_options: Self::SenderOptions, - ) -> Result; - - /// Set the Sender options - fn set_options(&mut self, options: Self::SenderOptions); +use super::query::QuerySender; +pub trait TxSender: QuerySender { /// Get the address of the sender. fn address(&self) -> Result; - /// Get the chain_information for the sender - fn chain_info(&self) -> &ChainInfoOwned; - - /// Get the channel for the sender (TODO: return mut ref to Retry Sender) - fn grpc_channel(&self) -> Channel; - /// Returns the `AccountId` of the sender. /// If an authz granter is set, returns the authz granter /// Else, returns the address associated with the current private key @@ -55,5 +37,4 @@ pub trait TxSender: Clone { msgs: Vec, memo: Option<&str>, ) -> impl std::future::Future> + Send; - } diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 91f0c9129..68c5a235b 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,5 +1,6 @@ +use crate::senders::no_sender::NoSender; use crate::senders::tx::TxSender; -use crate::{DaemonAsyncBuilderBase, DaemonBase, DaemonState, Wallet, RUNTIME}; +use crate::{DaemonAsyncBuilder, DaemonBase, DaemonState, Wallet, RUNTIME}; use cw_orch_core::environment::ChainInfoOwned; use super::super::error::DaemonError; @@ -16,79 +17,30 @@ use super::super::error::DaemonError; /// .build() /// .unwrap(); /// ``` -pub struct DaemonBuilderBase { +pub struct DaemonBuilder { // # Required - pub(crate) chain: Option, + pub(crate) chain: ChainInfoOwned, + // # Optional pub(crate) handle: Option, pub(crate) deployment_id: Option, - pub(crate) overwrite_grpc_url: Option, - pub(crate) gas_denom: Option, - pub(crate) gas_fee: Option, pub(crate) state_path: Option, /// State from rebuild or existing daemon pub(crate) state: Option, pub(crate) write_on_change: Option, - - pub(crate) sender: Option, - - // /* Sender Options */ - /// Specify Daemon Sender Options - pub(crate) sender_options: Sender::SenderOptions, } -impl Default for DaemonBuilderBase { - fn default() -> Self { +impl DaemonBuilder { + pub fn new(chain: impl Into) -> Self { Self { - chain: Default::default(), - handle: Default::default(), - deployment_id: Default::default(), - overwrite_grpc_url: Default::default(), - gas_denom: Default::default(), - gas_fee: Default::default(), - state_path: Default::default(), - sender: Default::default(), - sender_options: Default::default(), - state: Default::default(), - write_on_change: Default::default(), + chain: chain.into(), + handle: None, + deployment_id: None, + state_path: None, + state: None, + write_on_change: None, } } -} - -pub type DaemonBuilder = DaemonBuilderBase; - -impl DaemonBuilder { - /// Set the mnemonic to use with this chain. - pub fn mnemonic(&mut self, mnemonic: impl ToString) -> &mut Self { - self.sender_options.mnemonic = Some(mnemonic.to_string()); - self - } - - /// Specifies wether authz should be used with this daemon - pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { - self.sender_options.set_authz_granter(granter.to_string()); - self - } - - /// Specifies wether feegrant 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 - } - - /// Specifies the hd_index of the daemon sender - pub fn hd_index(&mut self, index: u32) -> &mut Self { - self.sender_options.hd_index = Some(index); - self - } -} - -impl DaemonBuilderBase { - /// Set the chain the Daemon will connect to - pub fn chain(&mut self, chain: impl Into) -> &mut Self { - self.chain = Some(chain.into()); - self - } /// Set the deployment id to use for the Daemon interactions /// Defaults to `default` @@ -115,36 +67,9 @@ impl DaemonBuilderBase { self } - /// Specifies a sender to use with this chain - /// This will be used in priority when set on the builder - pub fn sender( - &self, - wallet: OtherSender, - ) -> DaemonBuilderBase { - DaemonBuilderBase { - chain: self.chain.clone(), - deployment_id: self.deployment_id.clone(), - state_path: self.state_path.clone(), - state: self.state.clone(), - sender: Some(wallet), - sender_options: OtherSender::SenderOptions::default(), - handle: self.handle.clone(), - overwrite_grpc_url: self.overwrite_grpc_url.clone(), - gas_denom: self.gas_denom.clone(), - gas_fee: self.gas_fee, - write_on_change: self.write_on_change, - } - } - - /// Specifies sender builder options - pub fn options(&mut self, options: Sender::SenderOptions) -> &mut Self { - self.sender_options = options; - self - } - /// Overwrites the grpc_url used to interact with the chain - pub fn grpc_url(&mut self, url: &str) -> &mut Self { - self.overwrite_grpc_url = Some(url.to_string()); + pub fn grpc_url(&mut self, url: impl Into) -> &mut Self { + (&mut self.chain).grpc_urls = vec![url.into()]; self } @@ -152,9 +77,14 @@ impl DaemonBuilderBase { /// Behavior : /// - If no gas denom is provided, the first gas denom specified in the `self.chain` is used /// - If no gas fee is provided, the first gas fee specified in the self.chain is used - pub fn gas(&mut self, gas_denom: Option<&str>, gas_fee: Option) -> &mut Self { - self.gas_denom = gas_denom.map(ToString::to_string); - self.gas_fee = gas_fee.map(Into::into); + pub fn gas(&mut self, gas_denom: Option<&str>, gas_price: Option) -> &mut Self { + if let Some(denom) = gas_denom { + (&mut self.chain).gas_denom = denom.to_string() + } + if let Some(price) = gas_price { + (&mut self.chain).gas_price = price; + } + self } @@ -184,48 +114,20 @@ impl DaemonBuilderBase { self } - /// Build a Daemon - pub fn build(&self) -> Result, DaemonError> { + /// Build a Daemon with the default [`Wallet`] implementation. + pub fn build(&self) -> Result, DaemonError> { let rt_handle = self .handle .clone() .unwrap_or_else(|| RUNTIME.handle().clone()); - let chain = self - .get_built_chain_object() - .ok_or(DaemonError::BuilderMissing("chain information".into()))?; - let mut builder = self.clone(); - builder.chain = Some(chain); // build the underlying daemon - let daemon = rt_handle.block_on(DaemonAsyncBuilderBase::from(builder).build())?; + let daemon = rt_handle.block_on(DaemonAsyncBuilder::from(builder).build())?; Ok(DaemonBase { rt_handle, daemon }) } - - fn get_built_chain_object(&self) -> Option { - self.chain.clone().map(|mut chain| { - // Override gas fee - overwrite_fee(&mut chain, self.gas_denom.clone(), self.gas_fee); - // Override grpc_url - overwrite_grpc_url(&mut chain, self.overwrite_grpc_url.clone()); - chain - }) - } -} - -fn overwrite_fee(chain: &mut ChainInfoOwned, denom: Option, amount: Option) { - if let Some(denom) = denom { - chain.gas_denom = denom.to_string() - } - chain.gas_price = amount.unwrap_or(chain.gas_price); -} - -fn overwrite_grpc_url(chain: &mut ChainInfoOwned, grpc_url: Option) { - if let Some(grpc_url) = grpc_url { - chain.grpc_urls = vec![grpc_url.to_string()] - } } #[cfg(test)] diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 8718baa18..611e76aee 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -3,12 +3,12 @@ use std::fmt::Debug; use super::super::senders::base_sender::Wallet; use crate::{ queriers::{Bank, CosmWasmBase, Node}, - CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonBuilderBase, DaemonError, DaemonState, + CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonError, DaemonState, }; use cosmwasm_std::{Addr, Coin}; use cw_orch_core::{ contract::{interface_traits::Uploadable, WasmPath}, - environment::{ChainState, DefaultQueriers, QueryHandler, TxHandler}, + environment::{ChainInfoOwned, ChainState, DefaultQueriers, QueryHandler, TxHandler}, }; use cw_orch_traits::stargate::Stargate; use serde::Serialize; @@ -44,7 +44,7 @@ use crate::senders::tx::TxSender; See [Querier](crate::queriers) for examples. */ pub struct DaemonBase { - pub daemon: DaemonAsyncBase, + pub(crate) daemon: DaemonAsyncBase, /// Runtime handle to execute async tasks pub rt_handle: Handle, } @@ -53,8 +53,8 @@ pub type Daemon = DaemonBase; impl DaemonBase { /// Get the daemon builder - pub fn builder() -> DaemonBuilderBase { - DaemonBuilderBase::default() + pub fn builder(chain: impl Into) -> DaemonBuilder { + DaemonBuilder::new(chain) } /// Get the channel configured for this Daemon @@ -63,20 +63,26 @@ impl DaemonBase { } /// Get the channel configured for this Daemon - pub fn wallet(&self) -> Sender { + pub fn sender(&self) -> Sender { self.daemon.sender.clone() } + /// Get the mutable Sender object + pub fn sender_mut(&mut self) -> &mut Sender { + self.daemon.sender_mut() + } + /// Returns a new [`DaemonBuilder`] with the current configuration. /// Does not consume the original [`Daemon`]. - pub fn rebuild(&self) -> DaemonBuilderBase { - let mut builder = DaemonBuilder { + pub fn rebuild(&self) -> DaemonBuilder { + DaemonBuilder { state: Some(self.state()), - ..Default::default() - }; - builder - .chain(self.daemon.sender.chain_info().clone()) - .sender(self.daemon.sender.clone()) + chain: self.daemon.sender.chain_info().clone(), + deployment_id: Some(self.daemon.state.deployment_id.clone()), + state_path: None, + write_on_change: None, + handle: Some(self.rt_handle.clone()), + } } /// Flushes all the state related to the current chain @@ -173,7 +179,7 @@ impl Stargate for DaemonBase { ) -> Result { self.rt_handle .block_on( - self.wallet().commit_tx_any( + self.sender().commit_tx_any( msgs.iter() .map(|msg| cosmrs::Any { type_url: msg.type_url.clone(), From d317158e3da5fb9572f91ca7f6084dfaf04be8f7 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Mon, 1 Jul 2024 16:13:52 +0200 Subject: [PATCH 068/108] propagate query traits --- cw-orch-daemon/src/builder.rs | 2 +- cw-orch-daemon/src/senders/no_sender.rs | 13 +++++++++++++ cw-orch-daemon/src/sync/builder.rs | 19 +++++++++++++++++++ cw-orch-daemon/src/sync/core.rs | 15 +++++++-------- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index d55f1c19b..9604f8354 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -83,7 +83,7 @@ impl DaemonAsyncBuilder { } /// Returns a built state - pub(crate) fn build_state(&self) -> Result { + pub fn build_state(&self) -> Result { let deployment_id = self .deployment_id .clone() diff --git a/cw-orch-daemon/src/senders/no_sender.rs b/cw-orch-daemon/src/senders/no_sender.rs index 4c1184916..f3bfc63c4 100644 --- a/cw-orch-daemon/src/senders/no_sender.rs +++ b/cw-orch-daemon/src/senders/no_sender.rs @@ -49,3 +49,16 @@ impl QuerySender for NoSender { fn set_options(&mut self, _options: Self::Options) {} } + +#[cfg(test)] +mod tests { + use cw_orch_networks::networks::JUNO_1; + + use crate::DaemonBuilder; + + + #[test] + fn build() { + let builder: QueryOnlyDaemon = DaemonBuilder::new(JUNO_1).build_sender(())?; + } +} \ No newline at end of file diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 68c5a235b..c0ad73694 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,3 +1,4 @@ +use crate::senders::builder::SenderBuilder; use crate::senders::no_sender::NoSender; use crate::senders::tx::TxSender; use crate::{DaemonAsyncBuilder, DaemonBase, DaemonState, Wallet, RUNTIME}; @@ -128,6 +129,24 @@ impl DaemonBuilder { Ok(DaemonBase { rt_handle, daemon }) } + + /// Build a daemon + pub fn build_sender( + &self, + sender_options: Sender::Options, + ) -> Result, DaemonError> { + let rt_handle = self + .handle + .clone() + .unwrap_or_else(|| RUNTIME.handle().clone()); + + let builder = self.clone(); + + // build the underlying daemon + let daemon = rt_handle.block_on(DaemonAsyncBuilder::from(builder).build_sender(sender_options))?; + + Ok(DaemonBase { rt_handle, daemon }) + } } #[cfg(test)] diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 611e76aee..29b75e141 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -2,8 +2,7 @@ use std::fmt::Debug; use super::super::senders::base_sender::Wallet; use crate::{ - queriers::{Bank, CosmWasmBase, Node}, - CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonError, DaemonState, + queriers::{Bank, CosmWasm, Node}, senders::query::QuerySender, CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonError, DaemonState }; use cosmwasm_std::{Addr, Coin}; use cw_orch_core::{ @@ -43,7 +42,7 @@ use crate::senders::tx::TxSender; Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. See [Querier](crate::queriers) for examples. */ -pub struct DaemonBase { +pub struct DaemonBase { pub(crate) daemon: DaemonAsyncBase, /// Runtime handle to execute async tasks pub rt_handle: Handle, @@ -51,7 +50,7 @@ pub struct DaemonBase { pub type Daemon = DaemonBase; -impl DaemonBase { +impl DaemonBase { /// Get the daemon builder pub fn builder(chain: impl Into) -> DaemonBuilder { DaemonBuilder::new(chain) @@ -92,7 +91,7 @@ impl DaemonBase { } } -impl ChainState for DaemonBase { +impl ChainState for DaemonBase { type Out = DaemonState; fn state(&self) -> Self::Out { @@ -193,7 +192,7 @@ impl Stargate for DaemonBase { } } -impl QueryHandler for DaemonBase { +impl QueryHandler for DaemonBase { type Error = DaemonError; fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> { @@ -215,8 +214,8 @@ impl QueryHandler for DaemonBase { } } -impl DefaultQueriers for DaemonBase { +impl DefaultQueriers for DaemonBase { type Bank = Bank; - type Wasm = CosmWasmBase; + type Wasm = CosmWasm; type Node = Node; } From cd9609c4836b413b3e50d1d2731bcdee7054953d Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Mon, 1 Jul 2024 16:43:23 +0200 Subject: [PATCH 069/108] fix compilation --- cw-orch-daemon/examples/manual_sender.rs | 72 ++++++++++--------- cw-orch-daemon/src/builder.rs | 25 +++++-- cw-orch-daemon/src/core.rs | 1 + cw-orch-daemon/src/queriers/bank.rs | 6 +- cw-orch-daemon/src/queriers/cosmwasm.rs | 18 ++--- cw-orch-daemon/src/queriers/env.rs | 4 +- cw-orch-daemon/src/queriers/node.rs | 6 +- cw-orch-daemon/src/senders/base_sender.rs | 2 +- cw-orch-daemon/src/senders/builder.rs | 7 +- cw-orch-daemon/src/senders/no_sender.rs | 9 +-- cw-orch-daemon/src/senders/query.rs | 4 -- cw-orch-daemon/src/senders/tx.rs | 4 +- cw-orch-daemon/src/sync/builder.rs | 16 ++++- cw-orch-daemon/src/sync/core.rs | 40 ++++++----- cw-orch-interchain/examples/doc_daemon.rs | 3 +- .../interchain-daemon/src/interchain_env.rs | 6 +- packages/interchain/starship/src/lib.rs | 7 +- 17 files changed, 125 insertions(+), 105 deletions(-) diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index 584b90735..7fbf69c0f 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -2,6 +2,8 @@ use cw_orch_daemon::proto::injective::InjectiveEthAccount; use cw_orch_daemon::queriers::Node; +use cw_orch_daemon::senders::builder::SenderBuilder; +use cw_orch_daemon::senders::query::QuerySender; use cw_orch_daemon::tx_broadcaster::assert_broadcast_code_cosm_response; use cw_orch_daemon::{DaemonBase, TxBuilder}; @@ -67,10 +69,46 @@ pub struct ManualSender { pub grpc_channel: Channel, } -impl TxSender for ManualSender { +impl SenderBuilder for ManualSender { type Error = DaemonError; type Options = ManualSenderOptions; + fn build( + chain_info: cw_orch_core::environment::ChainInfoOwned, + grpc_channel: tonic::transport::Channel, + sender_options: Self::Options, + ) -> Result { + Ok(Self { + chain_info, + grpc_channel, + sender: Addr::unchecked( + sender_options + .sender_address + .expect("Manual sender needs an address"), + ), + }) + } +} + +impl QuerySender for ManualSender { + fn chain_info(&self) -> &cw_orch_core::environment::ChainInfoOwned { + &self.chain_info + } + + fn grpc_channel(&self) -> tonic::transport::Channel { + self.grpc_channel.clone() + } + + fn set_options(&mut self, options: Self::Options) { + self.sender = Addr::unchecked( + options + .sender_address + .expect("Manual sender needs an address"), + ) + } +} + +impl TxSender for ManualSender { async fn commit_tx_any( &self, msgs: Vec, @@ -106,38 +144,6 @@ impl TxSender for ManualSender { fn msg_sender(&self) -> Result { self.sender.clone().to_string().parse().map_err(Into::into) } - - fn chain_info(&self) -> &cw_orch_core::environment::ChainInfoOwned { - &self.chain_info - } - - fn grpc_channel(&self) -> tonic::transport::Channel { - self.grpc_channel.clone() - } - - fn build( - chain_info: cw_orch_core::environment::ChainInfoOwned, - grpc_channel: tonic::transport::Channel, - sender_options: Self::Options, - ) -> Result { - Ok(Self { - chain_info, - grpc_channel, - sender: Addr::unchecked( - sender_options - .sender_address - .expect("Manual sender needs an address"), - ), - }) - } - - fn set_options(&mut self, options: Self::Options) { - self.sender = Addr::unchecked( - options - .sender_address - .expect("Manual sender needs an address"), - ) - } } impl ManualSender { diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 9604f8354..b0d11eda7 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,12 +1,9 @@ use crate::{ log::print_if_log_disabled, senders::{ - base_sender::{CosmosOptions, Sender}, - builder::SenderBuilder, - query::QuerySender, - tx::TxSender, + base_sender::CosmosOptions, builder::SenderBuilder, query::QuerySender, tx::TxSender, }, - DaemonAsyncBase, DaemonBuilder, DaemonStateFile, GrpcChannel, Wallet, + DaemonAsyncBase, DaemonBuilder, DaemonStateFile, Wallet, }; use super::{error::DaemonError, state::DaemonState}; @@ -36,6 +33,8 @@ pub struct DaemonAsyncBuilder { /// State from rebuild or existing daemon pub(crate) state: Option, pub(crate) write_on_change: Option, + + pub(crate) mnemonic: Option, } impl DaemonAsyncBuilder { @@ -46,6 +45,7 @@ impl DaemonAsyncBuilder { state_path: None, state: None, write_on_change: None, + mnemonic: None, } } @@ -72,6 +72,12 @@ impl DaemonAsyncBuilder { self } + /// Set the mnemonic used for the default Cosmos wallet + pub fn mnemonic(&mut self, mnemonic: impl Into) -> &mut Self { + self.mnemonic = Some(mnemonic.into()); + self + } + /// Specifies path to the daemon state file /// Defaults to env variable. /// @@ -130,14 +136,18 @@ impl DaemonAsyncBuilder { Ok(state) } - /// Build a daemon with env-var mnemonic + /// Build a daemon with provided mnemonic or env-var mnemonic pub async fn build(&self) -> Result, DaemonError> { let chain_info = self.chain.clone(); let state = self.build_state()?; // if mnemonic provided, use it. Else use env variables to retrieve mnemonic - let sender = Wallet::build(chain_info, CosmosOptions::default()).await?; + let options = CosmosOptions { + mnemonic: self.mnemonic.clone(), + ..Default::default() + }; + let sender = Wallet::build(chain_info, options).await?; let daemon = DaemonAsyncBase { state, sender }; @@ -173,6 +183,7 @@ impl From for DaemonAsyncBuilder { state: value.state, state_path: value.state_path, write_on_change: value.write_on_change, + mnemonic: value.mnemonic, } } } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 96cca47b8..d98a59b51 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -105,6 +105,7 @@ impl DaemonAsyncBase { deployment_id: Some(self.state.deployment_id.clone()), state_path: None, write_on_change: None, + mnemonic: None, } } } diff --git a/cw-orch-daemon/src/queriers/bank.rs b/cw-orch-daemon/src/queriers/bank.rs index 85ed8e0c4..d5b765184 100644 --- a/cw-orch-daemon/src/queriers/bank.rs +++ b/cw-orch-daemon/src/queriers/bank.rs @@ -1,4 +1,4 @@ -use crate::{cosmos_modules, error::DaemonError, senders::tx::TxSender, DaemonBase}; +use crate::{cosmos_modules, error::DaemonError, senders::query::QuerySender, DaemonBase}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmwasm_std::{Coin, StdError}; use cw_orch_core::environment::{BankQuerier, Querier, QuerierGetter}; @@ -13,7 +13,7 @@ pub struct Bank { } impl Bank { - pub fn new(daemon: &DaemonBase) -> Self { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), @@ -31,7 +31,7 @@ impl Querier for Bank { type Error = DaemonError; } -impl QuerierGetter for DaemonBase { +impl QuerierGetter for DaemonBase { fn querier(&self) -> Bank { Bank::new(self) } diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 16264d1d8..9f3ddbab5 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -1,6 +1,8 @@ use std::{marker::PhantomData, str::FromStr}; -use crate::{cosmos_modules, error::DaemonError, senders::tx::TxSender, DaemonBase, Wallet}; +use crate::senders::no_sender::NoSender; +use crate::senders::query::QuerySender; +use crate::{cosmos_modules, error::DaemonError, DaemonBase}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmrs::AccountId; use cosmwasm_std::{ @@ -17,15 +19,15 @@ use tonic::transport::Channel; /// Querier for the CosmWasm SDK module /// All the async function are prefixed with `_` -pub struct CosmWasmBase { +pub struct CosmWasmBase { pub channel: Channel, pub rt_handle: Option, _sender: PhantomData, } -pub type CosmWasm = CosmWasmBase; +pub type CosmWasm = CosmWasmBase; -impl CosmWasmBase { +impl CosmWasmBase { pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), @@ -49,17 +51,17 @@ impl CosmWasmBase { } } -impl QuerierGetter> for DaemonBase { +impl QuerierGetter> for DaemonBase { fn querier(&self) -> CosmWasmBase { CosmWasmBase::new(self) } } -impl Querier for CosmWasmBase { +impl Querier for CosmWasmBase { type Error = DaemonError; } -impl CosmWasmBase { +impl CosmWasmBase { /// Query code_id by hash pub async fn _code_id_hash(&self, code_id: u64) -> Result { use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest}; @@ -231,7 +233,7 @@ impl CosmWasmBase { } } -impl WasmQuerier for CosmWasmBase { +impl WasmQuerier for CosmWasmBase { type Chain = DaemonBase; fn code_id_hash(&self, code_id: u64) -> Result { self.rt_handle diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index dac93c5b4..6366fd763 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -1,8 +1,8 @@ use cw_orch_core::environment::{EnvironmentInfo, EnvironmentQuerier}; -use crate::{senders::tx::TxSender, DaemonBase}; +use crate::{senders::query::QuerySender, DaemonBase}; -impl EnvironmentQuerier for DaemonBase { +impl EnvironmentQuerier for DaemonBase { fn env_info(&self) -> EnvironmentInfo { EnvironmentInfo { chain_id: self.daemon.sender.chain_info().chain_id.clone(), diff --git a/cw-orch-daemon/src/queriers/node.rs b/cw-orch-daemon/src/queriers/node.rs index a0dd91794..08b811169 100644 --- a/cw-orch-daemon/src/queriers/node.rs +++ b/cw-orch-daemon/src/queriers/node.rs @@ -1,7 +1,7 @@ use std::{cmp::min, time::Duration}; use crate::{ - cosmos_modules, env::DaemonEnvVars, error::DaemonError, senders::tx::TxSender, + cosmos_modules, env::DaemonEnvVars, error::DaemonError, senders::query::QuerySender, tx_resp::CosmTxResponse, DaemonBase, }; @@ -29,7 +29,7 @@ pub struct Node { } impl Node { - pub fn new(daemon: &DaemonBase) -> Self { + pub fn new(daemon: &DaemonBase) -> Self { Self { channel: daemon.channel(), rt_handle: Some(daemon.rt_handle.clone()), @@ -43,7 +43,7 @@ impl Node { } } -impl QuerierGetter for DaemonBase { +impl QuerierGetter for DaemonBase { fn querier(&self) -> Node { Node::new(self) } diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index a5cfd474c..34ea84f84 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -6,7 +6,7 @@ use crate::{ account_sequence_strategy, assert_broadcast_code_cosm_response, insufficient_fee_strategy, TxBroadcaster, }, - Daemon, DaemonAsyncBuilder, DaemonBuilder, GrpcChannel, + Daemon, GrpcChannel, }; use crate::proto::injective::InjectiveEthAccount; diff --git a/cw-orch-daemon/src/senders/builder.rs b/cw-orch-daemon/src/senders/builder.rs index 29551a290..0edd68674 100644 --- a/cw-orch-daemon/src/senders/builder.rs +++ b/cw-orch-daemon/src/senders/builder.rs @@ -1,7 +1,4 @@ -use cosmrs::{tx::Msg, AccountId, Any}; -use cosmwasm_std::Addr; use cw_orch_core::environment::ChainInfoOwned; -use tonic::transport::Channel; use crate::DaemonError; @@ -13,8 +10,8 @@ pub trait SenderBuilder: Clone { type Options: Default + Clone; /// Build a new `Sender`. - async fn build( + fn build( chain_info: ChainInfoOwned, sender_options: Self::Options, - ) -> Result; + ) -> impl std::future::Future> + Send; } diff --git a/cw-orch-daemon/src/senders/no_sender.rs b/cw-orch-daemon/src/senders/no_sender.rs index f3bfc63c4..1e73026f8 100644 --- a/cw-orch-daemon/src/senders/no_sender.rs +++ b/cw-orch-daemon/src/senders/no_sender.rs @@ -1,12 +1,10 @@ -use crate::{error::DaemonError, tx_resp::CosmTxResponse, DaemonBase, GrpcChannel}; +use crate::{error::DaemonError, DaemonBase, GrpcChannel}; -use cosmrs::{AccountId, Any}; -use cosmwasm_std::Addr; use cw_orch_core::environment::ChainInfoOwned; use tonic::transport::Channel; -use super::{builder::SenderBuilder, query::QuerySender, tx::TxSender}; +use super::{builder::SenderBuilder, query::QuerySender}; /// Daemon that does not support signing. /// Will err on any attempt to sign a transaction or retrieve a sender address. @@ -56,9 +54,8 @@ mod tests { use crate::DaemonBuilder; - #[test] fn build() { let builder: QueryOnlyDaemon = DaemonBuilder::new(JUNO_1).build_sender(())?; } -} \ No newline at end of file +} diff --git a/cw-orch-daemon/src/senders/query.rs b/cw-orch-daemon/src/senders/query.rs index 99d0d239a..587026de8 100644 --- a/cw-orch-daemon/src/senders/query.rs +++ b/cw-orch-daemon/src/senders/query.rs @@ -1,10 +1,6 @@ -use cosmrs::{tx::Msg, AccountId, Any}; -use cosmwasm_std::Addr; use cw_orch_core::environment::ChainInfoOwned; use tonic::transport::Channel; -use crate::{CosmTxResponse, DaemonError}; - use super::builder::SenderBuilder; /// A sender that can query information over a connection. diff --git a/cw-orch-daemon/src/senders/tx.rs b/cw-orch-daemon/src/senders/tx.rs index a9ac3fa1a..31058e477 100644 --- a/cw-orch-daemon/src/senders/tx.rs +++ b/cw-orch-daemon/src/senders/tx.rs @@ -1,9 +1,7 @@ use cosmrs::{tx::Msg, AccountId, Any}; use cosmwasm_std::Addr; -use cw_orch_core::environment::ChainInfoOwned; -use tonic::transport::Channel; -use crate::{CosmTxResponse, DaemonError}; +use crate::CosmTxResponse; use super::query::QuerySender; diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index c0ad73694..73e821f4e 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,5 +1,5 @@ use crate::senders::builder::SenderBuilder; -use crate::senders::no_sender::NoSender; + use crate::senders::tx::TxSender; use crate::{DaemonAsyncBuilder, DaemonBase, DaemonState, Wallet, RUNTIME}; use cw_orch_core::environment::ChainInfoOwned; @@ -29,6 +29,8 @@ pub struct DaemonBuilder { /// State from rebuild or existing daemon pub(crate) state: Option, pub(crate) write_on_change: Option, + + pub(crate) mnemonic: Option, } impl DaemonBuilder { @@ -40,6 +42,7 @@ impl DaemonBuilder { state_path: None, state: None, write_on_change: None, + mnemonic: None, } } @@ -74,6 +77,12 @@ impl DaemonBuilder { self } + /// Set the mnemonic used for the default Cosmos wallet + pub fn mnemonic(&mut self, mnemonic: impl Into) -> &mut Self { + self.mnemonic = Some(mnemonic.into()); + self + } + /// Overwrites the gas denom used for broadcasting transactions. /// Behavior : /// - If no gas denom is provided, the first gas denom specified in the `self.chain` is used @@ -122,7 +131,7 @@ impl DaemonBuilder { .clone() .unwrap_or_else(|| RUNTIME.handle().clone()); - let mut builder = self.clone(); + let builder = self.clone(); // build the underlying daemon let daemon = rt_handle.block_on(DaemonAsyncBuilder::from(builder).build())?; @@ -143,7 +152,8 @@ impl DaemonBuilder { let builder = self.clone(); // build the underlying daemon - let daemon = rt_handle.block_on(DaemonAsyncBuilder::from(builder).build_sender(sender_options))?; + let daemon = + rt_handle.block_on(DaemonAsyncBuilder::from(builder).build_sender(sender_options))?; Ok(DaemonBase { rt_handle, daemon }) } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 29b75e141..4129d9661 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -2,7 +2,9 @@ use std::fmt::Debug; use super::super::senders::base_sender::Wallet; use crate::{ - queriers::{Bank, CosmWasm, Node}, senders::query::QuerySender, CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonError, DaemonState + queriers::{Bank, CosmWasmBase, Node}, + senders::query::QuerySender, + CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonError, DaemonState, }; use cosmwasm_std::{Addr, Coin}; use cw_orch_core::{ @@ -50,10 +52,10 @@ pub struct DaemonBase { pub type Daemon = DaemonBase; -impl DaemonBase { - /// Get the daemon builder - pub fn builder(chain: impl Into) -> DaemonBuilder { - DaemonBuilder::new(chain) +impl DaemonBase { + /// Get the channel configured for this Daemon + pub fn wallet(&self) -> Sender { + self.daemon.sender.clone() } /// Get the channel configured for this Daemon @@ -61,17 +63,8 @@ impl DaemonBase { self.daemon.sender.grpc_channel() } - /// Get the channel configured for this Daemon - pub fn sender(&self) -> Sender { - self.daemon.sender.clone() - } - - /// Get the mutable Sender object - pub fn sender_mut(&mut self) -> &mut Sender { - self.daemon.sender_mut() - } - /// Returns a new [`DaemonBuilder`] with the current configuration. + /// **Does not copy the `Sender`** /// Does not consume the original [`Daemon`]. pub fn rebuild(&self) -> DaemonBuilder { DaemonBuilder { @@ -81,8 +74,21 @@ impl DaemonBase { state_path: None, write_on_change: None, handle: Some(self.rt_handle.clone()), + mnemonic: None, } } +} + +impl DaemonBase { + /// Get the daemon builder + pub fn builder(chain: impl Into) -> DaemonBuilder { + DaemonBuilder::new(chain) + } + + /// Get the mutable Sender object + pub fn sender_mut(&mut self) -> &mut Sender { + self.daemon.sender_mut() + } /// Flushes all the state related to the current chain /// Only works on Local networks @@ -178,7 +184,7 @@ impl Stargate for DaemonBase { ) -> Result { self.rt_handle .block_on( - self.sender().commit_tx_any( + self.wallet().commit_tx_any( msgs.iter() .map(|msg| cosmrs::Any { type_url: msg.type_url.clone(), @@ -216,6 +222,6 @@ impl QueryHandler for DaemonBase { impl DefaultQueriers for DaemonBase { type Bank = Bank; - type Wasm = CosmWasm; + type Wasm = CosmWasmBase; type Node = Node; } diff --git a/cw-orch-interchain/examples/doc_daemon.rs b/cw-orch-interchain/examples/doc_daemon.rs index dcf36fd2b..2f9b7eeb1 100644 --- a/cw-orch-interchain/examples/doc_daemon.rs +++ b/cw-orch-interchain/examples/doc_daemon.rs @@ -18,9 +18,8 @@ fn create_daemon_env() -> cw_orch::anyhow::Result<(Runtime, DaemonInterchainEnv) let _local_juno: Daemon = interchain.chain("testing")?; let _local_osmo: Daemon = interchain.chain("localosmosis")?; - let local_migaloo = DaemonBuilder::default() + let local_migaloo = DaemonBuilder::new(LOCAL_MIGALOO) .handle(rt.handle()) - .chain(LOCAL_MIGALOO) .build()?; interchain.add_daemons(vec![local_migaloo]); diff --git a/packages/interchain/interchain-daemon/src/interchain_env.rs b/packages/interchain/interchain-daemon/src/interchain_env.rs index b0ec67507..ebea2183e 100644 --- a/packages/interchain/interchain-daemon/src/interchain_env.rs +++ b/packages/interchain/interchain-daemon/src/interchain_env.rs @@ -87,10 +87,10 @@ impl DaemonInterchainEnv { &mut self, runtime: &Handle, chain_data: ChainInfoOwned, - mnemonic: Option, + mnemonic: Option>, ) -> IcDaemonResult<()> { - let mut daemon_builder = Daemon::builder(); - let mut daemon_builder = daemon_builder.chain(chain_data.clone()).handle(runtime); + let mut daemon_builder = Daemon::builder(chain_data); + let mut daemon_builder = daemon_builder.handle(runtime); daemon_builder = if let Some(mn) = mnemonic { daemon_builder.mnemonic(mn) diff --git a/packages/interchain/starship/src/lib.rs b/packages/interchain/starship/src/lib.rs index 63c9c1023..bdc39642d 100644 --- a/packages/interchain/starship/src/lib.rs +++ b/packages/interchain/starship/src/lib.rs @@ -36,11 +36,8 @@ impl Starship { .unwrap() }); - let mut daemon_builder = DaemonBuilder::default(); - let mut daemon_builder = daemon_builder - .chain(chain_data_conversion(chain.clone())) - .mnemonic(mnemonic) - .handle(rt_handle); + let mut daemon_builder = DaemonBuilder::new(chain_data_conversion(chain.clone())); + let mut daemon_builder = daemon_builder.mnemonic(mnemonic).handle(rt_handle); if let Some(existing_daemon) = daemons.values().next() { daemon_builder = daemon_builder.state(existing_daemon.state()) From 671e5ae5a9b86380cee1c1808053960b2cdfe815 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Tue, 2 Jul 2024 15:13:36 +0200 Subject: [PATCH 070/108] apply nits --- cw-orch-daemon/src/builder.rs | 6 ++++++ cw-orch-daemon/src/senders/no_sender.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index b0d11eda7..28ce27291 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -78,6 +78,12 @@ impl DaemonAsyncBuilder { self } + /// Overwrite the chain info + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = chain.into(); + self + } + /// Specifies path to the daemon state file /// Defaults to env variable. /// diff --git a/cw-orch-daemon/src/senders/no_sender.rs b/cw-orch-daemon/src/senders/no_sender.rs index 1e73026f8..b2d2a8940 100644 --- a/cw-orch-daemon/src/senders/no_sender.rs +++ b/cw-orch-daemon/src/senders/no_sender.rs @@ -53,6 +53,7 @@ mod tests { use cw_orch_networks::networks::JUNO_1; use crate::DaemonBuilder; + use super::QueryOnlyDaemon; #[test] fn build() { From 7bf8a19fcf7e98787202fa97246651f0e36716bf Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 2 Jul 2024 17:22:01 +0300 Subject: [PATCH 071/108] tests compile --- contracts/counter/examples/async.rs | 2 +- contracts/counter/examples/deploy.rs | 2 +- cw-orch-daemon/examples/batch-sender.rs | 2 +- .../examples/daemon-capabilities.rs | 2 +- cw-orch-daemon/examples/manual_sender.rs | 17 ++++------ cw-orch-daemon/examples/querier-daemon.rs | 2 +- cw-orch-daemon/src/builder.rs | 4 +-- cw-orch-daemon/src/core.rs | 6 +--- cw-orch-daemon/src/senders/base_sender.rs | 1 + cw-orch-daemon/src/senders/no_sender.rs | 6 ++-- cw-orch-daemon/src/sync/builder.rs | 23 ++++++------- cw-orch-daemon/tests/daemon_state.rs | 33 +++++++------------ cw-orch/examples/async_daemon.rs | 4 +-- cw-orch/examples/complex_testnet_daemon.rs | 7 ++-- cw-orch/examples/injective.rs | 4 +-- cw-orch/examples/local_daemon.rs | 4 +-- cw-orch/examples/queries/bank_query.rs | 3 +- cw-orch/examples/queries/testnet_queries.rs | 3 +- cw-orch/examples/testnet_daemon.rs | 4 +-- 19 files changed, 48 insertions(+), 81 deletions(-) diff --git a/contracts/counter/examples/async.rs b/contracts/counter/examples/async.rs index 2c1fc7dd1..320b595c8 100644 --- a/contracts/counter/examples/async.rs +++ b/contracts/counter/examples/async.rs @@ -13,7 +13,7 @@ pub async fn main() -> anyhow::Result<()> { pretty_env_logger::init(); // Used to log contract and chain interactions let network = networks::LOCAL_JUNO; - let chain = DaemonAsyncBuilder::default().chain(network).build().await?; + let chain = DaemonAsyncBuilder::new(network).build().await?; let counter = CounterContract::new(chain); diff --git a/contracts/counter/examples/deploy.rs b/contracts/counter/examples/deploy.rs index df07750ee..2ba8a43ef 100644 --- a/contracts/counter/examples/deploy.rs +++ b/contracts/counter/examples/deploy.rs @@ -13,7 +13,7 @@ pub fn main() -> anyhow::Result<()> { pretty_env_logger::init(); // Used to log contract and chain interactions let network = networks::LOCAL_JUNO; - let chain = DaemonBuilder::default().chain(network).build()?; + let chain = DaemonBuilder::new(network).build()?; // ANCHOR_END: chain_construction // ANCHOR: contract_interaction diff --git a/cw-orch-daemon/examples/batch-sender.rs b/cw-orch-daemon/examples/batch-sender.rs index 2e5909eee..65da0c913 100644 --- a/cw-orch-daemon/examples/batch-sender.rs +++ b/cw-orch-daemon/examples/batch-sender.rs @@ -12,7 +12,7 @@ pub fn main() -> anyhow::Result<()> { pretty_env_logger::init(); // Used to log contract and chain interactions let network = networks::LOCAL_JUNO; - let chain = BatchDaemon::builder().chain(network).build()?; + let chain: BatchDaemon = BatchDaemon::builder(network).build_sender(Default::default())?; let counter = CounterContract::new(chain.clone()); diff --git a/cw-orch-daemon/examples/daemon-capabilities.rs b/cw-orch-daemon/examples/daemon-capabilities.rs index 23e474ce4..a22b9e450 100644 --- a/cw-orch-daemon/examples/daemon-capabilities.rs +++ b/cw-orch-daemon/examples/daemon-capabilities.rs @@ -13,7 +13,7 @@ pub fn main() -> anyhow::Result<()> { std::env::set_var("LOCAL_MNEMONIC", LOCAL_MNEMONIC); let network = networks::LOCAL_JUNO; - let mut daemon = DaemonBuilder::default().chain(network).build()?; + let mut daemon = DaemonBuilder::new(network).build()?; daemon.flush_state()?; diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index 7fbf69c0f..4559877da 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -5,7 +5,7 @@ use cw_orch_daemon::queriers::Node; use cw_orch_daemon::senders::builder::SenderBuilder; use cw_orch_daemon::senders::query::QuerySender; use cw_orch_daemon::tx_broadcaster::assert_broadcast_code_cosm_response; -use cw_orch_daemon::{DaemonBase, TxBuilder}; +use cw_orch_daemon::{DaemonBase, GrpcChannel, TxBuilder}; use cw_orch_daemon::{error::DaemonError, tx_resp::CosmTxResponse}; @@ -33,12 +33,9 @@ pub fn main() -> anyhow::Result<()> { let network = cw_orch_networks::networks::JUNO_1; let sender = "juno1xjf5xscdk08c5es2m7epmerrpqmkmc3n98650t"; - let chain = ManualDaemon::builder() - .options(ManualSenderOptions { - sender_address: Some(sender.to_string()), - }) - .chain(network) - .build()?; + let chain: ManualDaemon = ManualDaemon::builder(network).build_sender(ManualSenderOptions { + sender_address: Some(sender.to_string()), + })?; let counter = CounterContract::new(chain.clone()); @@ -73,19 +70,19 @@ impl SenderBuilder for ManualSender { type Error = DaemonError; type Options = ManualSenderOptions; - fn build( + async fn build( chain_info: cw_orch_core::environment::ChainInfoOwned, - grpc_channel: tonic::transport::Channel, sender_options: Self::Options, ) -> Result { + let grpc_channel = GrpcChannel::from_chain_info(&chain_info).await?; Ok(Self { chain_info, - grpc_channel, sender: Addr::unchecked( sender_options .sender_address .expect("Manual sender needs an address"), ), + grpc_channel, }) } } diff --git a/cw-orch-daemon/examples/querier-daemon.rs b/cw-orch-daemon/examples/querier-daemon.rs index d80b7b2f4..ebb86909f 100644 --- a/cw-orch-daemon/examples/querier-daemon.rs +++ b/cw-orch-daemon/examples/querier-daemon.rs @@ -11,7 +11,7 @@ pub fn main() -> anyhow::Result<()> { let network = networks::LOCAL_JUNO; // There is no need to register a mnemonic to use this daemon querier - let chain = QueryOnlyDaemon::builder().chain(network).build()?; + let chain: QueryOnlyDaemon = QueryOnlyDaemon::builder(network).build_sender(())?; let balances = chain.bank_querier().balance(LOCAL_JUNO_SENDER, None)?; assert!(!balances.is_empty()); diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 28ce27291..e8e589e4e 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,8 +1,6 @@ use crate::{ log::print_if_log_disabled, - senders::{ - base_sender::CosmosOptions, builder::SenderBuilder, query::QuerySender, tx::TxSender, - }, + senders::{base_sender::CosmosOptions, builder::SenderBuilder}, DaemonAsyncBase, DaemonBuilder, DaemonStateFile, Wallet, }; diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index d98a59b51..68e3f85b5 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -1,8 +1,4 @@ -use crate::{ - queriers::CosmWasm, - senders::{builder::SenderBuilder, query::QuerySender}, - DaemonAsyncBuilder, DaemonState, -}; +use crate::{queriers::CosmWasm, senders::query::QuerySender, DaemonAsyncBuilder, DaemonState}; use super::{ cosmos_modules, error::DaemonError, queriers::Node, senders::base_sender::Wallet, diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index 34ea84f84..b8c479240 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -76,6 +76,7 @@ pub struct CosmosOptions { pub mnemonic: Option, } +// TODO: builder impl CosmosOptions { pub fn authz_granter(mut self, granter: impl ToString) -> Self { self.authz_granter = Some(granter.to_string()); diff --git a/cw-orch-daemon/src/senders/no_sender.rs b/cw-orch-daemon/src/senders/no_sender.rs index b2d2a8940..e3b13b24a 100644 --- a/cw-orch-daemon/src/senders/no_sender.rs +++ b/cw-orch-daemon/src/senders/no_sender.rs @@ -52,11 +52,13 @@ impl QuerySender for NoSender { mod tests { use cw_orch_networks::networks::JUNO_1; - use crate::DaemonBuilder; use super::QueryOnlyDaemon; + use crate::DaemonBuilder; #[test] + #[serial_test::serial] fn build() { - let builder: QueryOnlyDaemon = DaemonBuilder::new(JUNO_1).build_sender(())?; + let _query_only_daemon: QueryOnlyDaemon = + DaemonBuilder::new(JUNO_1).build_sender(()).unwrap(); } } diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 73e821f4e..f6ac92f58 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -1,6 +1,5 @@ use crate::senders::builder::SenderBuilder; -use crate::senders::tx::TxSender; use crate::{DaemonAsyncBuilder, DaemonBase, DaemonState, Wallet, RUNTIME}; use cw_orch_core::environment::ChainInfoOwned; @@ -164,7 +163,7 @@ mod test { use cw_orch_core::environment::TxHandler; use cw_orch_networks::networks::OSMOSIS_1; - use crate::DaemonBuilder; + use crate::{senders::base_sender::CosmosOptions, DaemonBase, DaemonBuilder, Wallet}; pub const DUMMY_MNEMONIC:&str = "chapter wrist alcohol shine angry noise mercy simple rebel recycle vehicle wrap morning giraffe lazy outdoor noise blood ginger sort reunion boss crowd dutch"; #[test] @@ -172,8 +171,7 @@ mod test { fn grpc_override() { let mut chain = OSMOSIS_1; chain.grpc_urls = &[]; - let daemon = DaemonBuilder::default() - .chain(chain) + let daemon = DaemonBuilder::new(chain) .mnemonic(DUMMY_MNEMONIC) .grpc_url(OSMOSIS_1.grpc_urls[0]) .build() @@ -190,8 +188,7 @@ mod test { #[serial_test::serial] fn fee_amount_override() { let fee_amount = 1.3238763; - let daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .gas(None, Some(fee_amount)) .build() @@ -205,8 +202,7 @@ mod test { #[serial_test::serial] fn fee_denom_override() { let token = "my_token"; - let daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .gas(Some(token), None) .build() @@ -220,8 +216,7 @@ mod test { fn fee_override() { let fee_amount = 1.3238763; let token = "my_token"; - let daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .gas(Some(token), Some(fee_amount)) .build() @@ -235,13 +230,15 @@ mod test { #[test] #[serial_test::serial] fn hd_index_re_generates_sender() -> anyhow::Result<()> { - let daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build() .unwrap(); - let indexed_daemon = daemon.rebuild().hd_index(56).build().unwrap(); + let indexed_daemon: DaemonBase = daemon.rebuild().build_sender(CosmosOptions { + hd_index: Some(56), + ..Default::default() + })?; assert_ne!( daemon.sender().to_string(), diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index a59b34447..9bc4f4c2d 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -16,8 +16,7 @@ const TEST2_STATE_FILE: &str = "./tests/test2.json"; #[serial_test::serial] fn simultaneous_read() { std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - let daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build() .unwrap(); @@ -60,8 +59,7 @@ fn simultaneous_read() { #[serial_test::serial] fn simultaneous_write() { std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - let daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build() .unwrap(); @@ -101,8 +99,7 @@ fn simultaneous_write() { #[serial_test::serial] fn simultaneous_write_rebuilt() { std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - let daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build() .unwrap(); @@ -144,14 +141,12 @@ fn simultaneous_write_rebuilt() { #[serial_test::serial] fn error_when_another_daemon_holds_it() { std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - let _daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let _daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build() .unwrap(); - let daemon_res = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon_res = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build(); @@ -166,16 +161,14 @@ fn error_when_another_daemon_holds_it() { #[serial_test::serial] fn does_not_error_when_previous_daemon_dropped_state() { std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - let daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build() .unwrap(); drop(daemon); - let daemon_res = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon_res = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build(); @@ -187,16 +180,14 @@ fn does_not_error_when_previous_daemon_dropped_state() { #[serial_test::serial] fn does_not_error_when_using_different_files() { std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - let _daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let _daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build() .unwrap(); // Different file std::env::set_var(STATE_FILE_ENV_NAME, TEST2_STATE_FILE); - let daemon_res = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon_res = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build(); @@ -208,14 +199,12 @@ fn does_not_error_when_using_different_files() { #[serial_test::serial] fn reuse_same_state_multichain() { std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); - let daemon = DaemonBuilder::default() - .chain(OSMOSIS_1) + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build() .unwrap(); - let daemon_res = DaemonBuilder::default() - .chain(JUNO_1) + let daemon_res = DaemonBuilder::new(JUNO_1) .state(daemon.state()) .mnemonic(DUMMY_MNEMONIC) .build(); diff --git a/cw-orch/examples/async_daemon.rs b/cw-orch/examples/async_daemon.rs index d8c6c8f45..48e5d9512 100644 --- a/cw-orch/examples/async_daemon.rs +++ b/cw-orch/examples/async_daemon.rs @@ -18,9 +18,7 @@ pub async fn main() -> anyhow::Result<()> { // We can now create a daemon. This daemon will be used to interact with the chain. // In the background, the `build` function uses the `TEST_MNEMONIC` variable, don't forget to set it ! - let daemon = DaemonAsync::builder() - // set the network to use - .chain(cw_orch::daemon::networks::UNI_6) + let daemon = DaemonAsync::builder(cw_orch::daemon::networks::UNI_6) // set the network to use .build() .await?; diff --git a/cw-orch/examples/complex_testnet_daemon.rs b/cw-orch/examples/complex_testnet_daemon.rs index 52229cc13..8d9e01508 100644 --- a/cw-orch/examples/complex_testnet_daemon.rs +++ b/cw-orch/examples/complex_testnet_daemon.rs @@ -31,9 +31,7 @@ pub fn main() { // We can now create a daemon. This daemon will be used to interact with the chain. // In the background, the `build` function uses the `TEST_MNEMONIC` variable, don't forget to set it ! - let daemon = Daemon::builder() - // set the network to use - .chain(cw_orch::daemon::networks::UNI_6) + let daemon = Daemon::builder(cw_orch::daemon::networks::UNI_6) // set the network to use .build() .unwrap(); @@ -105,8 +103,7 @@ pub fn main() { .rt_handle .block_on( daemon - .daemon - .sender + .wallet() .bank_send(&contract_addr, coins(50_000, denom.clone())), ) .unwrap(); diff --git a/cw-orch/examples/injective.rs b/cw-orch/examples/injective.rs index 272ad66d1..e022e5ef3 100644 --- a/cw-orch/examples/injective.rs +++ b/cw-orch/examples/injective.rs @@ -18,9 +18,7 @@ pub fn main() { env_logger::init(); // We can now create a daemon. This daemon will be used to interact with the chain. - let res = Daemon::builder() - // set the network to use - .chain(networks::INJECTIVE_888) + let res = Daemon::builder(networks::INJECTIVE_888) // set the network to use .mnemonic(TESTNET_MNEMONIC) .build(); diff --git a/cw-orch/examples/local_daemon.rs b/cw-orch/examples/local_daemon.rs index 7269627a9..a20d59e21 100644 --- a/cw-orch/examples/local_daemon.rs +++ b/cw-orch/examples/local_daemon.rs @@ -19,9 +19,7 @@ pub fn main() { // ANCHOR: daemon_creation // We start by creating a daemon. This daemon will be used to interact with the chain. - let daemon = Daemon::builder() - // set the network to use - .chain(cw_orch::daemon::networks::LOCAL_JUNO) // chain parameter + let daemon = Daemon::builder(cw_orch::daemon::networks::LOCAL_JUNO) // chain parameter .build() .unwrap(); diff --git a/cw-orch/examples/queries/bank_query.rs b/cw-orch/examples/queries/bank_query.rs index 10e55cdce..894dd645c 100644 --- a/cw-orch/examples/queries/bank_query.rs +++ b/cw-orch/examples/queries/bank_query.rs @@ -5,8 +5,7 @@ use cw_orch::prelude::QuerierGetter; use cw_orch_daemon::queriers::Bank; pub fn main() { // We can now create a daemon. This daemon will be used to interact with the chain. - let daemon = Daemon::builder() - .chain(cw_orch::daemon::networks::LOCAL_JUNO) // chain parameter + let daemon = Daemon::builder(cw_orch::daemon::networks::LOCAL_JUNO) // chain parameter .build() .unwrap(); diff --git a/cw-orch/examples/queries/testnet_queries.rs b/cw-orch/examples/queries/testnet_queries.rs index 1ceafabe9..9c797c4ed 100644 --- a/cw-orch/examples/queries/testnet_queries.rs +++ b/cw-orch/examples/queries/testnet_queries.rs @@ -8,8 +8,7 @@ pub const TEST_MNEMONIC: &str="scare silent genuine cheese monitor industry item pub fn main() -> AnyResult<()> { // We start by creating a daemon. This daemon will be used to interact with the chain. - let daemon = Daemon::builder() - .chain(cw_orch::daemon::networks::JUNO_1) // chain parameter + let daemon = Daemon::builder(cw_orch::daemon::networks::JUNO_1) // chain parameter .mnemonic(TEST_MNEMONIC) .build()?; diff --git a/cw-orch/examples/testnet_daemon.rs b/cw-orch/examples/testnet_daemon.rs index a4641f665..b28a4e093 100644 --- a/cw-orch/examples/testnet_daemon.rs +++ b/cw-orch/examples/testnet_daemon.rs @@ -22,9 +22,7 @@ pub fn main() { // We can now create a daemon. This daemon will be used to interact with the chain. // In the background, the `build` function uses the `TEST_MNEMONIC` variable, don't forget to set it ! - let daemon = Daemon::builder() - // set the network to use - .chain(cw_orch::daemon::networks::UNI_6) + let daemon = Daemon::builder(cw_orch::daemon::networks::UNI_6) // set the network to use .build() .unwrap(); From 1ab44b8d1f7399899733ebec65111e4d95ef069f Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 2 Jul 2024 17:22:42 +0300 Subject: [PATCH 072/108] clippy --- cw-orch-daemon/src/sync/builder.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index f6ac92f58..daa3fac55 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -72,7 +72,7 @@ impl DaemonBuilder { /// Overwrites the grpc_url used to interact with the chain pub fn grpc_url(&mut self, url: impl Into) -> &mut Self { - (&mut self.chain).grpc_urls = vec![url.into()]; + self.chain.grpc_urls = vec![url.into()]; self } @@ -88,10 +88,10 @@ impl DaemonBuilder { /// - If no gas fee is provided, the first gas fee specified in the self.chain is used pub fn gas(&mut self, gas_denom: Option<&str>, gas_price: Option) -> &mut Self { if let Some(denom) = gas_denom { - (&mut self.chain).gas_denom = denom.to_string() + self.chain.gas_denom = denom.to_string() } if let Some(price) = gas_price { - (&mut self.chain).gas_price = price; + self.chain.gas_price = price; } self From 2b364486f4839e2026683e800ae67ca59411e5da Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 2 Jul 2024 17:35:58 +0300 Subject: [PATCH 073/108] fix tests for all targets and features --- cw-orch-daemon/src/senders/base_sender.rs | 14 ++++++++++++++ cw-orch-daemon/src/sync/builder.rs | 9 ++++----- cw-orch-daemon/tests/authz.rs | 23 ++++++++++------------- cw-orch-daemon/tests/daemon_helpers.rs | 10 ++-------- cw-orch-daemon/tests/index.rs | 12 ++++++------ cw-orch-daemon/tests/instantiate2.rs | 5 +---- cw-orch-daemon/tests/querier.rs | 5 +---- 7 files changed, 38 insertions(+), 40 deletions(-) diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index b8c479240..fc660e638 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -82,23 +82,37 @@ impl CosmosOptions { 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 hd_index(mut self, index: u32) -> Self { self.hd_index = Some(index); self } + + pub fn mnemonic(mut self, mnemonic: impl ToString) -> Self { + self.mnemonic = Some(mnemonic.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()); } + pub fn set_hd_index(&mut self, index: u32) { self.hd_index = Some(index); } + + pub fn set_mnemonic(&mut self, mnemonic: impl ToString) { + self.mnemonic = Some(mnemonic.to_string()) + } } impl Daemon { diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index daa3fac55..8e2a791dd 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -163,7 +163,7 @@ mod test { use cw_orch_core::environment::TxHandler; use cw_orch_networks::networks::OSMOSIS_1; - use crate::{senders::base_sender::CosmosOptions, DaemonBase, DaemonBuilder, Wallet}; + use crate::{DaemonBase, DaemonBuilder, Wallet}; pub const DUMMY_MNEMONIC:&str = "chapter wrist alcohol shine angry noise mercy simple rebel recycle vehicle wrap morning giraffe lazy outdoor noise blood ginger sort reunion boss crowd dutch"; #[test] @@ -235,10 +235,9 @@ mod test { .build() .unwrap(); - let indexed_daemon: DaemonBase = daemon.rebuild().build_sender(CosmosOptions { - hd_index: Some(56), - ..Default::default() - })?; + let indexed_daemon: DaemonBase = daemon + .rebuild() + .build_sender(daemon.wallet().options.hd_index(56))?; assert_ne!( daemon.sender().to_string(), diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index 930d694c4..fbebbaeae 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -15,7 +15,7 @@ mod tests { use cosmwasm_std::coins; use cw_orch_core::environment::QuerierGetter; use cw_orch_core::environment::{BankQuerier, DefaultQueriers, QueryHandler, TxHandler}; - use cw_orch_daemon::{queriers::Authz, Daemon}; + use cw_orch_daemon::{queriers::Authz, senders::base_sender::CosmosOptions, Daemon}; use cw_orch_networks::networks::LOCAL_JUNO; use cw_orch_traits::Stargate; use prost::Message; @@ -28,18 +28,17 @@ mod tests { fn authz() -> anyhow::Result<()> { use cw_orch_networks::networks; - let daemon = Daemon::builder() - .chain(networks::LOCAL_JUNO) - .build() - .unwrap(); + let daemon = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); let sender = daemon.sender().to_string(); - let second_daemon = daemon + let second_daemon: Daemon = daemon .rebuild() - .authz_granter(sender.clone()) - .mnemonic(SECOND_MNEMONIC) - .build() + .build_sender( + CosmosOptions::default() + .mnemonic(SECOND_MNEMONIC) + .authz_granter(sender.clone()), + ) .unwrap(); let runtime = daemon.rt_handle.clone(); @@ -119,16 +118,14 @@ mod tests { // The we send some funds to the account runtime.block_on( daemon - .daemon - .sender + .wallet() .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 + .wallet() .bank_send(&grantee, coins(5_000_000, LOCAL_JUNO.gas_denom)), )?; diff --git a/cw-orch-daemon/tests/daemon_helpers.rs b/cw-orch-daemon/tests/daemon_helpers.rs index 3f4bc6dc9..1f7075001 100644 --- a/cw-orch-daemon/tests/daemon_helpers.rs +++ b/cw-orch-daemon/tests/daemon_helpers.rs @@ -20,10 +20,7 @@ mod tests { fn helper_traits() { use cw_orch_networks::networks; - let mut daemon = Daemon::builder() - .chain(networks::LOCAL_JUNO) - .build() - .unwrap(); + let mut daemon = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); daemon.flush_state().unwrap(); @@ -118,10 +115,7 @@ mod tests { fn cw_orch_interface_traits() { use cw_orch_networks::networks; - let daemon = Daemon::builder() - .chain(networks::LOCAL_JUNO) - .build() - .unwrap(); + let daemon = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); let sender = daemon.sender(); diff --git a/cw-orch-daemon/tests/index.rs b/cw-orch-daemon/tests/index.rs index fb5264977..3b59423ca 100644 --- a/cw-orch-daemon/tests/index.rs +++ b/cw-orch-daemon/tests/index.rs @@ -3,20 +3,20 @@ mod common; mod tests { use cw_orch_core::environment::TxHandler; - use cw_orch_daemon::Daemon; + use cw_orch_daemon::{senders::base_sender::CosmosOptions, Daemon}; #[test] #[serial_test::serial] fn mnemonic_index() -> anyhow::Result<()> { use cw_orch_networks::networks; - let daemon = Daemon::builder() - .chain(networks::LOCAL_JUNO) - .build() - .unwrap(); + let daemon = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); let daemon_sender = daemon.sender().to_string(); - let indexed_daemon = daemon.rebuild().hd_index(56).build().unwrap(); + let indexed_daemon: Daemon = daemon + .rebuild() + .build_sender(CosmosOptions::default().hd_index(56)) + .unwrap(); assert_ne!(daemon_sender, indexed_daemon.sender().to_string()); diff --git a/cw-orch-daemon/tests/instantiate2.rs b/cw-orch-daemon/tests/instantiate2.rs index 72c9acbc1..2bdbccaac 100644 --- a/cw-orch-daemon/tests/instantiate2.rs +++ b/cw-orch-daemon/tests/instantiate2.rs @@ -15,10 +15,7 @@ pub mod test { #[test] #[serial_test::serial] fn instantiate2() -> anyhow::Result<()> { - let app = Daemon::builder() - .chain(networks::LOCAL_JUNO) - .build() - .unwrap(); + let app = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); let salt = Binary(vec![12, 89, 156, 63]); let mock_contract = MockContract::new("mock-contract", app.clone()); diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index a5d94d813..d51f82669 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -213,10 +213,7 @@ mod queriers { let rt = Runtime::new().unwrap(); let channel = rt.block_on(build_channel()); let cosm_wasm = CosmWasm::new_async(channel); - let daemon = Daemon::builder() - .chain(networks::LOCAL_JUNO) - .build() - .unwrap(); + let daemon = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); let sender = daemon.sender(); From 5f0d0ffe7b27d0ed031ceac44f68187edc761e08 Mon Sep 17 00:00:00 2001 From: Buckram Date: Tue, 2 Jul 2024 17:59:27 +0300 Subject: [PATCH 074/108] docs fix --- cw-orch-daemon/examples/manual_sender.rs | 2 +- cw-orch-daemon/src/builder.rs | 3 +-- cw-orch-daemon/src/channel.rs | 5 ++++- cw-orch-daemon/src/core.rs | 3 +-- cw-orch-daemon/src/queriers.rs | 3 +-- cw-orch-daemon/src/senders/base_sender.rs | 6 +++++- cw-orch-daemon/src/senders/no_sender.rs | 2 +- cw-orch-daemon/src/sync/builder.rs | 9 ++++----- cw-orch-daemon/src/sync/core.rs | 3 +-- cw-orch-daemon/tests/daemon_state.rs | 7 +++++-- docs/src/integrations/daemon.md | 7 ++----- docs/src/interchain/integrations/daemon.md | 4 +--- docs/src/interchain/quick-start.md | 6 ++---- 13 files changed, 29 insertions(+), 31 deletions(-) diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index 4559877da..fcd15dc59 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -74,7 +74,7 @@ impl SenderBuilder for ManualSender { chain_info: cw_orch_core::environment::ChainInfoOwned, sender_options: Self::Options, ) -> Result { - let grpc_channel = GrpcChannel::from_chain_info(&chain_info).await?; + let grpc_channel = GrpcChannel::from_chain_info(chain_info.clone()).await?; Ok(Self { chain_info, sender: Addr::unchecked( diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index e8e589e4e..91ce8ac46 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -15,8 +15,7 @@ pub const DEFAULT_DEPLOYMENT: &str = "default"; /// ```no_run /// # tokio_test::block_on(async { /// use cw_orch_daemon::{DaemonAsyncBuilder, networks}; -/// let daemon = DaemonAsyncBuilder::default() -/// .chain(networks::LOCAL_JUNO) +/// let daemon = DaemonAsyncBuilder::new(networks::LOCAL_JUNO) /// .deployment_id("v0.1.0") /// .build() /// .await.unwrap(); diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/channel.rs index 7075e1e83..c65ffd908 100644 --- a/cw-orch-daemon/src/channel.rs +++ b/cw-orch-daemon/src/channel.rs @@ -92,7 +92,10 @@ impl GrpcChannel { } /// Create a gRPC channel from the chain info - pub async fn from_chain_info(chain_info: &ChainInfoOwned) -> Result { + pub async fn from_chain_info( + chain_info: impl Into, + ) -> Result { + let chain_info: ChainInfoOwned = chain_info.into(); GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await } } diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 68e3f85b5..32f227aea 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -44,8 +44,7 @@ pub const INSTANTIATE_2_TYPE_URL: &str = "/cosmwasm.wasm.v1.MsgInstantiateContra # tokio_test::block_on(async { use cw_orch_daemon::{DaemonAsync, networks}; - let daemon: DaemonAsync = DaemonAsync::builder() - .chain(networks::JUNO_1) + let daemon: DaemonAsync = DaemonAsync::builder(networks::JUNO_1) .build() .await.unwrap(); # }) diff --git a/cw-orch-daemon/src/queriers.rs b/cw-orch-daemon/src/queriers.rs index c6b0e8f17..627f591c1 100644 --- a/cw-orch-daemon/src/queriers.rs +++ b/cw-orch-daemon/src/queriers.rs @@ -12,8 +12,7 @@ //! use cw_orch_daemon::{queriers::Node, DaemonAsync, networks}; //! # tokio_test::block_on(async { //! // call the builder and configure it as you need -//! let daemon = DaemonAsync::builder() -//! .chain(networks::LOCAL_JUNO) +//! let daemon = DaemonAsync::builder(networks::LOCAL_JUNO) //! .build() //! .await.unwrap(); //! // now you can use the Node querier: diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/base_sender.rs index fc660e638..ea120ebc8 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/base_sender.rs @@ -137,7 +137,7 @@ impl SenderBuilder for Wallet { chain_info: ChainInfoOwned, sender_options: Self::Options, ) -> Result { - let channel = GrpcChannel::from_chain_info(&chain_info).await?; + let channel = GrpcChannel::from_chain_info(chain_info.clone()).await?; if let Some(mnemonic) = &sender_options.mnemonic { Sender::from_mnemonic_with_options( @@ -252,6 +252,10 @@ impl Wallet { self.grpc_channel.clone() } + pub fn options(&self) -> CosmosOptions { + self.options.clone() + } + pub fn new_with_options( chain_info: ChainInfoOwned, channel: Channel, diff --git a/cw-orch-daemon/src/senders/no_sender.rs b/cw-orch-daemon/src/senders/no_sender.rs index e3b13b24a..f751600cd 100644 --- a/cw-orch-daemon/src/senders/no_sender.rs +++ b/cw-orch-daemon/src/senders/no_sender.rs @@ -27,7 +27,7 @@ impl SenderBuilder for NoSender { chain_info: ChainInfoOwned, _sender_options: Self::Options, ) -> Result { - let channel = GrpcChannel::from_chain_info(&chain_info).await?; + let channel = GrpcChannel::from_chain_info(chain_info.clone()).await?; Ok(NoSender { channel, diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 8e2a791dd..ad56447c7 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -11,8 +11,7 @@ use super::super::error::DaemonError; /// ```no_run /// use cw_orch_daemon::{networks, DaemonBuilder}; /// -/// let Daemon = DaemonBuilder::default() -/// .chain(networks::LOCAL_JUNO) +/// let Daemon = DaemonBuilder::new(networks::LOCAL_JUNO) /// .deployment_id("v0.1.0") /// .build() /// .unwrap(); @@ -56,10 +55,10 @@ impl DaemonBuilder { /// /// ## Example /// ```no_run - /// use cw_orch_daemon::Daemon; + /// use cw_orch_daemon::{Daemon, networks}; /// use tokio::runtime::Runtime; /// let rt = Runtime::new().unwrap(); - /// let Daemon = Daemon::builder() + /// let Daemon = Daemon::builder(networks::LOCAL_JUNO) /// .handle(rt.handle()) /// // ... /// .build() @@ -237,7 +236,7 @@ mod test { let indexed_daemon: DaemonBase = daemon .rebuild() - .build_sender(daemon.wallet().options.hd_index(56))?; + .build_sender(daemon.wallet().options().hd_index(56))?; assert_ne!( daemon.sender().to_string(), diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 4129d9661..f5a6de335 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -30,8 +30,7 @@ use crate::senders::tx::TxSender; use tokio::runtime::Runtime; let rt = Runtime::new().unwrap(); - let daemon: Daemon = Daemon::builder() - .chain(networks::JUNO_1) + let daemon: Daemon = Daemon::builder(networks::JUNO_1) .build() .unwrap(); ``` diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 9bc4f4c2d..6a71d8f6d 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -5,7 +5,7 @@ use cw_orch_daemon::{ env::STATE_FILE_ENV_NAME, json_lock::JsonLockedState, networks::{JUNO_1, OSMOSIS_1}, - DaemonBuilder, DaemonError, DaemonStateFile, + Daemon, DaemonBuilder, DaemonError, DaemonStateFile, }; pub const DUMMY_MNEMONIC:&str = "chapter wrist alcohol shine angry noise mercy simple rebel recycle vehicle wrap morning giraffe lazy outdoor noise blood ginger sort reunion boss crowd dutch"; @@ -107,7 +107,10 @@ fn simultaneous_write_rebuilt() { let mut handles = vec![]; // Note this one has lower iterations since rebuild is pretty long process for i in 0..10 { - let daemon = daemon.rebuild().build().unwrap(); + let daemon: Daemon = daemon + .rebuild() + .build_sender(daemon.wallet().options()) + .unwrap(); let mut daemon_state = daemon.state(); let handle = std::thread::spawn(move || { if let DaemonStateFile::FullAccess { json_file_state } = &daemon_state.json_state { diff --git a/docs/src/integrations/daemon.md b/docs/src/integrations/daemon.md index 0aec1ebd1..99675bbbe 100644 --- a/docs/src/integrations/daemon.md +++ b/docs/src/integrations/daemon.md @@ -26,13 +26,10 @@ This simple script actually hides another parameter which is the `LOCAL_MNEMONIC When using multiple Daemons with the same state file, you should re-use a single Daemon State to avoid conflicts and panics: ```rust,ignore -let daemon1 = Daemon::builder() - .chain(OSMOSIS_1) - .build()?; +let daemon1 = Daemon::builder(OSMOSIS_1).build()?; // If you don't use the `state` method here, this will fail with: // State file already locked, use another state file, clone daemon which holds the lock, or use `state` method of Builder -let daemon2 = Daemon::builder() - .chain(JUNO_1) +let daemon2 = Daemon::builder(JUNO_1) .state(daemon1.state()) .build()?; ``` diff --git a/docs/src/interchain/integrations/daemon.md b/docs/src/interchain/integrations/daemon.md index 8c12fb70a..985508a73 100644 --- a/docs/src/interchain/integrations/daemon.md +++ b/docs/src/interchain/integrations/daemon.md @@ -41,9 +41,7 @@ where the argument of the `chain` method is the chain id of the chain you are in You can also add daemons manually to the `interchain` object: ```rust,ignore -let local_migaloo = DaemonBuilder::default() - .chain(LOCAL_MIGALOO) - .build()?; +let local_migaloo = DaemonBuilder::default(LOCAL_MIGALOO).build()?; interchain.add_daemons(vec![local_migaloo]); ``` diff --git a/docs/src/interchain/quick-start.md b/docs/src/interchain/quick-start.md index 0a60f9e2a..c07f9f77f 100644 --- a/docs/src/interchain/quick-start.md +++ b/docs/src/interchain/quick-start.md @@ -196,14 +196,12 @@ use cw_orch::tokio; let rt = tokio::runtime::Runtime::new().unwrap(); // We create the daemons ourselves to interact with actual running chains (testnet here) -let juno = Daemon::builder() - .chain(cw_orch::daemon::networks::UNI_6) +let juno = Daemon::builder(cw_orch::daemon::networks::UNI_6) .build() .unwrap(); // We create the daemons ourselves to interact with actual running chains (testnet here) -let osmosis = Daemon::builder() - .chain(cw_orch::daemon::networks::OSMO_5) +let osmosis = Daemon::builder(cw_orch::daemon::networks::OSMO_5) .build() .unwrap(); From b13557a58f5cdeec806f2269770a691ad63045f9 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Tue, 2 Jul 2024 21:23:45 +0200 Subject: [PATCH 075/108] add chain constructor to builder and move around internal impl --- cw-orch-daemon/src/builder.rs | 78 +++++++++++++++--------------- cw-orch-daemon/src/sync/builder.rs | 20 +++++--- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index 91ce8ac46..b95e24d9a 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -91,8 +91,46 @@ impl DaemonAsyncBuilder { self } + /// Build a daemon with provided mnemonic or env-var mnemonic + pub async fn build(&self) -> Result, DaemonError> { + let chain_info = self.chain.clone(); + + let state = self.build_state()?; + // if mnemonic provided, use it. Else use env variables to retrieve mnemonic + + let options = CosmosOptions { + mnemonic: self.mnemonic.clone(), + ..Default::default() + }; + let sender = Wallet::build(chain_info, options).await?; + + let daemon = DaemonAsyncBase { state, sender }; + + print_if_log_disabled()?; + Ok(daemon) + } + + /// Build a daemon + pub async fn build_sender( + &self, + sender_options: Sender::Options, + ) -> Result, DaemonError> { + let chain_info = self.chain.clone(); + + let state = self.build_state()?; + + let sender = Sender::build(chain_info, sender_options) + .await + .map_err(Into::into)?; + + let daemon = DaemonAsyncBase { state, sender }; + + print_if_log_disabled()?; + Ok(daemon) + } + /// Returns a built state - pub fn build_state(&self) -> Result { + fn build_state(&self) -> Result { let deployment_id = self .deployment_id .clone() @@ -138,44 +176,6 @@ impl DaemonAsyncBuilder { }; Ok(state) } - - /// Build a daemon with provided mnemonic or env-var mnemonic - pub async fn build(&self) -> Result, DaemonError> { - let chain_info = self.chain.clone(); - - let state = self.build_state()?; - // if mnemonic provided, use it. Else use env variables to retrieve mnemonic - - let options = CosmosOptions { - mnemonic: self.mnemonic.clone(), - ..Default::default() - }; - let sender = Wallet::build(chain_info, options).await?; - - let daemon = DaemonAsyncBase { state, sender }; - - print_if_log_disabled()?; - Ok(daemon) - } - - /// Build a daemon - pub async fn build_sender( - &self, - sender_options: Sender::Options, - ) -> Result, DaemonError> { - let chain_info = self.chain.clone(); - - let state = self.build_state()?; - - let sender = Sender::build(chain_info, sender_options) - .await - .map_err(Into::into)?; - - let daemon = DaemonAsyncBase { state, sender }; - - print_if_log_disabled()?; - Ok(daemon) - } } impl From for DaemonAsyncBuilder { diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index ad56447c7..24493fff7 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -44,13 +44,6 @@ impl DaemonBuilder { } } - /// Set the deployment id to use for the Daemon interactions - /// Defaults to `default` - pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { - self.deployment_id = Some(deployment_id.into()); - self - } - /// Set a custom tokio runtime handle to use for the Daemon /// /// ## Example @@ -69,6 +62,13 @@ impl DaemonBuilder { self } + /// Set the deployment id to use for the Daemon interactions + /// Defaults to `default` + pub fn deployment_id(&mut self, deployment_id: impl Into) -> &mut Self { + self.deployment_id = Some(deployment_id.into()); + self + } + /// Overwrites the grpc_url used to interact with the chain pub fn grpc_url(&mut self, url: impl Into) -> &mut Self { self.chain.grpc_urls = vec![url.into()]; @@ -112,6 +112,12 @@ impl DaemonBuilder { self } + /// Overwrite the chain info + pub fn chain(&mut self, chain: impl Into) -> &mut Self { + self.chain = chain.into(); + self + } + /// Specifies path to the daemon state file /// Defaults to env variable. /// From 864868459c393012cfb7a60de41010424f6d2302 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Tue, 2 Jul 2024 21:24:49 +0200 Subject: [PATCH 076/108] move around sync builder impl --- cw-orch-daemon/src/sync/builder.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 24493fff7..ec7877594 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -118,16 +118,6 @@ impl DaemonBuilder { self } - /// Specifies path to the daemon state file - /// Defaults to env variable. - /// - /// Variable: STATE_FILE_ENV_NAME. - #[allow(unused)] - pub(crate) fn state_path(&mut self, path: impl ToString) -> &mut Self { - self.state_path = Some(path.to_string()); - self - } - /// Build a Daemon with the default [`Wallet`] implementation. pub fn build(&self) -> Result, DaemonError> { let rt_handle = self @@ -161,6 +151,16 @@ impl DaemonBuilder { Ok(DaemonBase { rt_handle, daemon }) } + + /// Specifies path to the daemon state file + /// Defaults to env variable. + /// + /// Variable: STATE_FILE_ENV_NAME. + #[allow(unused)] + pub(crate) fn state_path(&mut self, path: impl ToString) -> &mut Self { + self.state_path = Some(path.to_string()); + self + } } #[cfg(test)] From 534c6d201b6eab4d74509f75d32268a620306fdd Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:24:36 +0200 Subject: [PATCH 077/108] rename and restructure sender files --- .../src/senders/{base_sender.rs => cosmos.rs} | 467 ++++++------------ .../{batch_sender.rs => cosmos_batch.rs} | 115 +++-- cw-orch-daemon/src/senders/cosmos_options.rs | 91 ++++ cw-orch-daemon/src/senders/mod.rs | 17 +- .../senders/{no_sender.rs => query_only.rs} | 32 +- 5 files changed, 350 insertions(+), 372 deletions(-) rename cw-orch-daemon/src/senders/{base_sender.rs => cosmos.rs} (66%) rename cw-orch-daemon/src/senders/{batch_sender.rs => cosmos_batch.rs} (57%) create mode 100644 cw-orch-daemon/src/senders/cosmos_options.rs rename cw-orch-daemon/src/senders/{no_sender.rs => query_only.rs} (61%) diff --git a/cw-orch-daemon/src/senders/base_sender.rs b/cw-orch-daemon/src/senders/cosmos.rs similarity index 66% rename from cw-orch-daemon/src/senders/base_sender.rs rename to cw-orch-daemon/src/senders/cosmos.rs index ea120ebc8..003608697 100644 --- a/cw-orch-daemon/src/senders/base_sender.rs +++ b/cw-orch-daemon/src/senders/cosmos.rs @@ -6,7 +6,7 @@ use crate::{ account_sequence_strategy, assert_broadcast_code_cosm_response, insufficient_fee_strategy, TxBroadcaster, }, - Daemon, GrpcChannel, + CosmosOptions, Daemon, GrpcChannel, }; use crate::proto::injective::InjectiveEthAccount; @@ -39,213 +39,95 @@ use cw_orch_core::{ use crate::env::{LOCAL_MNEMONIC_ENV_NAME, MAIN_MNEMONIC_ENV_NAME, TEST_MNEMONIC_ENV_NAME}; use bitcoin::secp256k1::{All, Secp256k1, Signing}; -use std::str::FromStr; +use std::{rc::Rc, str::FromStr, sync::Arc}; use cosmos_modules::vesting::PeriodicVestingAccount; use tonic::transport::Channel; -use super::{builder::SenderBuilder, query::QuerySender, tx::TxSender}; +use super::{ + builder::SenderBuilder, cosmos_options::CosmosWalletKey, query::QuerySender, tx::TxSender, +}; const GAS_BUFFER: f64 = 1.3; const BUFFER_THRESHOLD: u64 = 200_000; const SMALL_GAS_BUFFER: f64 = 1.4; /// A wallet is a sender of transactions, can be safely cloned and shared within the same thread. -pub type Wallet = Sender; +pub type Wallet = CosmosSender; /// 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 struct CosmosSender { pub private_key: PrivateKey, - pub secp: Secp256k1, /// gRPC channel pub grpc_channel: Channel, /// Information about the chain - pub chain_info: ChainInfoOwned, + pub chain_info: Arc, pub(crate) options: CosmosOptions, + pub secp: Secp256k1, + // Private field to ensure the struct is not constructible outside of this module + _private: (), } -/// Options for how txs should be constructed for this sender. -#[derive(Default, Clone)] -#[non_exhaustive] -pub struct CosmosOptions { - pub authz_granter: Option, - pub fee_granter: Option, - pub hd_index: Option, - pub mnemonic: Option, -} - -// TODO: builder -impl CosmosOptions { - 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 hd_index(mut self, index: u32) -> Self { - self.hd_index = Some(index); - self - } - - pub fn mnemonic(mut self, mnemonic: impl ToString) -> Self { - self.mnemonic = Some(mnemonic.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()); - } - - pub fn set_hd_index(&mut self, index: u32) { - self.hd_index = Some(index); - } - - pub fn set_mnemonic(&mut self, mnemonic: impl ToString) { - self.mnemonic = Some(mnemonic.to_string()) - } -} - -impl Daemon { - /// Specifies wether authz should be used with this daemon - pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { - self.sender_mut().set_authz_granter(granter.to_string()); - self - } - - /// Specifies wether feegrant should be used with this daemon - pub fn fee_granter(&mut self, granter: impl ToString) -> &mut Self { - self.sender_mut().set_fee_granter(granter.to_string()); - self - } -} - -impl SenderBuilder for Wallet { - type Error = DaemonError; - type Options = CosmosOptions; - - async fn build( - chain_info: ChainInfoOwned, - sender_options: Self::Options, - ) -> Result { - let channel = GrpcChannel::from_chain_info(chain_info.clone()).await?; - - if let Some(mnemonic) = &sender_options.mnemonic { - Sender::from_mnemonic_with_options( - chain_info, - channel, - &mnemonic.clone(), - sender_options, - ) - } else { - Sender::new_with_options(chain_info, channel, sender_options) - } - } -} - -impl QuerySender for Wallet { - fn chain_info(&self) -> &ChainInfoOwned { - &self.chain_info - } - - fn grpc_channel(&self) -> Channel { - self.channel() - } +impl Wallet { + pub async fn new( + chain_info: &Arc, + options: CosmosOptions, + ) -> Result { + let secp = Secp256k1::new(); - fn set_options(&mut self, options: Self::Options) { - if let Some(mnemonic) = options.mnemonic.clone() { - let new_sender = Sender::from_mnemonic_with_options( - self.chain_info.clone(), - self.channel(), - &mnemonic, - options, - ) - .unwrap(); - *self = new_sender; - } else if options.hd_index.is_some() { - // Need to generate new sender as hd_index impacts private key - let new_sender = Sender::from_raw_key_with_options( - self.chain_info.clone(), - self.channel(), - &self.private_key.raw_key(), - options, + let pk_from_mnemonic = |mnemonic: &str| -> Result { + PrivateKey::from_words( + &secp, + mnemonic, + 0, + options.hd_index.unwrap_or(0), + chain_info.network_info.coin_type, ) - .unwrap(); - *self = new_sender - } else { - self.options = options - } - } -} - -impl TxSender for Wallet { - async fn commit_tx_any( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> Result { - let timeout_height = Node::new_async(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); - - // We retry broadcasting the tx, with the following strategies - // 1. In case there is an `incorrect account sequence` error, we can retry as much as possible (doesn't cost anything to the user) - // 2. In case there is an insufficient_fee error, we retry once (costs fee to the user everytime we submit this kind of tx) - // 3. In case there is an other error, we fail - let tx_response = TxBroadcaster::default() - .add_strategy(insufficient_fee_strategy()) - .add_strategy(account_sequence_strategy()) - .broadcast(tx_builder, self) - .await?; - - let resp = Node::new_async(self.channel()) - ._find_tx(tx_response.txhash) - .await?; - - assert_broadcast_code_cosm_response(resp) - } + let pk: PrivateKey = match &options.key { + CosmosWalletKey::Mnemonic(mnemonic) => pk_from_mnemonic(mnemonic)?, + CosmosWalletKey::Env => { + let mnemonic = get_mnemonic_env(&chain_info.kind)?; + pk_from_mnemonic(&mnemonic)? + } + CosmosWalletKey::RawKey(bytes) => PrivateKey::from_raw_key( + &secp, + bytes, + 0, + options.hd_index.unwrap_or(0), + chain_info.network_info.coin_type, + )?, + }; - fn address(&self) -> Result { - Ok(Addr::unchecked(self.pub_addr_str()?)) - } + // ensure address is valid + AccountId::new( + &chain_info.network_info.pub_address_prefix, + &pk.public_key(&secp).raw_address.unwrap(), + )?; - fn msg_sender(&self) -> Result { - if let Some(sender) = &self.options.authz_granter { - Ok(sender.parse()?) - } else { - self.pub_addr() - } + Ok(Self { + chain_info: chain_info.clone(), + grpc_channel: GrpcChannel::from_chain_info(chain_info.as_ref()).await?, + private_key: pk, + secp, + options, + _private: (), + }) } -} -impl Wallet { - pub fn new(chain_info: ChainInfoOwned, channel: Channel) -> Result { - Self::new_with_options(chain_info, channel, CosmosOptions::default()) + /// Construct a new Sender from a mnemonic + pub async fn from_mnemonic( + chain_info: &Arc, + mnemonic: &str, + ) -> Result { + let options = CosmosOptions { + key: CosmosWalletKey::Mnemonic(mnemonic.to_string()), + ..Default::default() + }; + Self::new(chain_info, options).await } pub fn channel(&self) -> Channel { @@ -256,88 +138,6 @@ impl Wallet { self.options.clone() } - pub fn new_with_options( - chain_info: ChainInfoOwned, - channel: Channel, - options: CosmosOptions, - ) -> Result { - let mnemonic = get_mnemonic_env(&chain_info.kind)?; - - Self::from_mnemonic_with_options(chain_info, channel, &mnemonic, options) - } - - /// Construct a new Sender from a mnemonic with additional options - pub fn from_mnemonic( - chain_info: ChainInfoOwned, - channel: Channel, - mnemonic: &str, - ) -> Result { - Self::from_mnemonic_with_options(chain_info, channel, mnemonic, CosmosOptions::default()) - } - - /// Construct a new Sender from a mnemonic with additional options - pub fn from_mnemonic_with_options( - chain_info: ChainInfoOwned, - channel: Channel, - mnemonic: &str, - options: CosmosOptions, - ) -> Result { - let secp = Secp256k1::new(); - let p_key: PrivateKey = PrivateKey::from_words( - &secp, - mnemonic, - 0, - options.hd_index.unwrap_or(0), - chain_info.network_info.coin_type, - )?; - - let sender = Sender { - chain_info, - grpc_channel: channel, - private_key: p_key, - secp, - options, - }; - log::info!( - target: &local_target(), - "Interacting with {} using address: {}", - sender.chain_info.chain_id, - sender.pub_addr_str()? - ); - Ok(sender) - } - - /// Construct a new Sender from a raw key with additional options - pub fn from_raw_key_with_options( - chain_info: ChainInfoOwned, - channel: Channel, - raw_key: &[u8], - options: CosmosOptions, - ) -> Result { - let secp = Secp256k1::new(); - let p_key: PrivateKey = PrivateKey::from_raw_key( - &secp, - raw_key, - 0, - options.hd_index.unwrap_or(0), - chain_info.network_info.coin_type, - )?; - let sender = Sender { - private_key: p_key, - secp, - options, - grpc_channel: channel, - chain_info, - }; - log::info!( - target: &local_target(), - "Interacting with {} using address: {}", - sender.chain_info.chain_id, - sender.pub_addr_str()? - ); - Ok(sender) - } - pub fn set_authz_granter(&mut self, granter: impl Into) { self.options.authz_granter = Some(granter.into()); } @@ -346,19 +146,8 @@ impl Wallet { self.options.fee_granter = Some(granter.into()); } - fn cosmos_private_key(&self) -> SigningKey { - SigningKey::from_slice(&self.private_key.raw_key()).unwrap() - } - - pub fn pub_addr(&self) -> Result { - Ok(AccountId::new( - &self.chain_info.network_info.pub_address_prefix, - &self.private_key.public_key(&self.secp).raw_address.unwrap(), - )?) - } - - pub fn pub_addr_str(&self) -> Result { - Ok(self.pub_addr()?.to_string()) + pub fn pub_addr_str(&self) -> String { + self.account_id().to_string() } pub async fn broadcast_tx( @@ -378,12 +167,12 @@ impl Wallet { } pub async fn bank_send( - &self, + &mut self, recipient: &str, coins: Vec, ) -> Result { let msg_send = MsgSend { - from_address: self.msg_sender()?, + from_address: self.account_id(), to_address: AccountId::from_str(recipient)?, amount: parse_cw_coins(&coins)?, }; @@ -391,29 +180,6 @@ impl Wallet { self.commit_tx(vec![msg_send], Some("sending tokens")).await } - pub(crate) fn get_fee_token(&self) -> String { - self.chain_info.gas_denom.to_string() - } - - /// Compute the gas fee from the expected gas in the transaction - /// Applies a Gas Buffer for including signature verification - pub(crate) fn get_fee_from_gas(&self, gas: u64) -> Result<(u64, u128), DaemonError> { - let mut gas_expected = if let Some(gas_buffer) = DaemonEnvVars::gas_buffer() { - gas as f64 * gas_buffer - } else if gas < BUFFER_THRESHOLD { - gas as f64 * SMALL_GAS_BUFFER - } else { - gas as f64 * GAS_BUFFER - }; - - if let Some(min_gas) = DaemonEnvVars::min_gas() { - gas_expected = (min_gas as f64).max(gas_expected); - } - let fee_amount = gas_expected * (self.chain_info.gas_price + 0.00001); - - Ok((gas_expected as u64, fee_amount as u128)) - } - /// Computes the gas needed for submitting a transaction pub async fn calculate_gas( &self, @@ -476,7 +242,7 @@ impl Wallet { } pub async fn commit_tx( - &self, + &mut self, msgs: Vec, memo: Option<&str>, ) -> Result { @@ -505,7 +271,7 @@ impl Wallet { } pub async fn base_account(&self) -> Result { - let addr = self.pub_addr().unwrap().to_string(); + let addr = self.address().to_string(); let mut client = cosmos_modules::auth::query_client::QueryClient::new(self.channel()); @@ -548,7 +314,7 @@ impl Wallet { let bank = Bank::new_async(self.channel()); let balance = bank - ._balance(self.address()?, Some(fee.denom.clone())) + ._balance(self.address(), Some(fee.denom.clone())) .await?[0] .clone(); @@ -556,7 +322,7 @@ impl Wallet { "Checking balance {} on chain {}, address {}. Expecting {}{}", balance.amount, chain_info.chain_id, - self.address()?, + self.address(), fee, fee.denom ); @@ -572,7 +338,7 @@ impl Wallet { Needed: {}{} but only have: {}. Press 'y' when the wallet balance has been increased to resume deployment", chain_info.chain_id, - self.address()?, + self.address(), fee, fee.denom, balance @@ -598,6 +364,99 @@ impl Wallet { }); } } + + pub(crate) fn get_fee_token(&self) -> String { + self.chain_info.gas_denom.to_string() + } + + fn cosmos_private_key(&self) -> SigningKey { + SigningKey::from_slice(&self.private_key.raw_key()).unwrap() + } + + /// Compute the gas fee from the expected gas in the transaction + /// Applies a Gas Buffer for including signature verification + pub(crate) fn get_fee_from_gas(&self, gas: u64) -> Result<(u64, u128), DaemonError> { + let mut gas_expected = if let Some(gas_buffer) = DaemonEnvVars::gas_buffer() { + gas as f64 * gas_buffer + } else if gas < BUFFER_THRESHOLD { + gas as f64 * SMALL_GAS_BUFFER + } else { + gas as f64 * GAS_BUFFER + }; + + if let Some(min_gas) = DaemonEnvVars::min_gas() { + gas_expected = (min_gas as f64).max(gas_expected); + } + let fee_amount = gas_expected * (self.chain_info.gas_price + 0.00001); + + Ok((gas_expected as u64, fee_amount as u128)) + } +} + +impl QuerySender for Wallet { + type Error = DaemonError; + type Options = CosmosOptions; + + fn chain_info(&self) -> &ChainInfoOwned { + self.chain_info.as_ref() + } + + fn grpc_channel(&self) -> Channel { + self.channel() + } +} + +impl TxSender for Wallet { + async fn commit_tx_any( + &mut self, + msgs: Vec, + memo: Option<&str>, + ) -> Result { + let timeout_height = Node::new_async(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); + + // We retry broadcasting the tx, with the following strategies + // 1. In case there is an `incorrect account sequence` error, we can retry as much as possible (doesn't cost anything to the user) + // 2. In case there is an insufficient_fee error, we retry once (costs fee to the user everytime we submit this kind of tx) + // 3. In case there is an other error, we fail + let tx_response = TxBroadcaster::default() + .add_strategy(insufficient_fee_strategy()) + .add_strategy(account_sequence_strategy()) + .broadcast(tx_builder, self) + .await?; + + let resp = Node::new_async(self.channel()) + ._find_tx(tx_response.txhash) + .await?; + + assert_broadcast_code_cosm_response(resp) + } + + fn account_id(&self) -> AccountId { + AccountId::new( + &self.chain_info.network_info.pub_address_prefix, + &self.private_key.public_key(&self.secp).raw_address.unwrap(), + ) + // unwrap as address is validated on construction + .unwrap() + } } fn get_mnemonic_env(chain_kind: &ChainKind) -> Result { diff --git a/cw-orch-daemon/src/senders/batch_sender.rs b/cw-orch-daemon/src/senders/cosmos_batch.rs similarity index 57% rename from cw-orch-daemon/src/senders/batch_sender.rs rename to cw-orch-daemon/src/senders/cosmos_batch.rs index f4903557e..b2c29f94b 100644 --- a/cw-orch-daemon/src/senders/batch_sender.rs +++ b/cw-orch-daemon/src/senders/cosmos_batch.rs @@ -1,47 +1,89 @@ -use crate::{DaemonBase, Wallet, INSTANTIATE_2_TYPE_URL}; +use crate::{DaemonBase, INSTANTIATE_2_TYPE_URL}; use crate::{error::DaemonError, tx_resp::CosmTxResponse}; use cosmrs::proto::cosmwasm::wasm::v1::{MsgInstantiateContract, MsgStoreCode}; use cosmrs::{AccountId, Any}; use cosmwasm_std::Addr; +use cw_orch_core::environment::ChainInfoOwned; use cw_orch_core::log::transaction_target; +use options::CosmosBatchOptions; use prost::Name; +use std::mem::take; use std::sync::{Arc, Mutex}; use super::builder::SenderBuilder; +use super::cosmos::Wallet; use super::query::QuerySender; -use super::{base_sender::CosmosOptions, tx::TxSender}; +use super::{tx::TxSender, CosmosOptions}; -pub type BatchDaemon = DaemonBase; +pub mod options { + use super::super::CosmosOptions; -/// Signer of the transactions and helper for address derivation -/// This is the main interface for simulating and signing transactions + pub struct CosmosBatchOptions(pub(crate) CosmosOptions); + + impl From for CosmosBatchOptions { + fn from(options: CosmosOptions) -> Self { + Self(options) + } + } + + impl CosmosBatchOptions { + pub fn new(options: CosmosOptions) -> Self { + Self(options) + } + } +} + +pub type BatchDaemon = DaemonBase; + +/// Signer of Message batch transactions +/// This is a wrapper around the `Wallet` struct, with the addition of a `msgs` field that cache messages before they are sent. #[derive(Clone)] -pub struct BatchSender { +pub struct CosmosBatchSender { /// Contains the different messages to broadcast - /// These are behind an Arc Mutex, because `commit_tx_any function` doesn't have access to a mutable reference to the object - pub msgs: Arc>>, + pub msgs: Vec, pub sender: Wallet, } -impl SenderBuilder for BatchSender { +impl CosmosBatchSender { + /// Broadcast the cached messages in a transaction. + pub async fn broadcast(&mut self, memo: Option<&str>) -> Result { + let msgs = take(&mut self.msgs); + + log::info!( + target: &transaction_target(), + "[Broadcast] {} msgs in a single transaction", + msgs.len() + ); + let tx_result = self.sender.commit_tx_any(msgs, memo).await?; + log::info!( + target: &transaction_target(), + "[Broadcasted] Success: {}", + tx_result.txhash + ); + + Ok(tx_result) + } +} + +impl SenderBuilder for CosmosBatchOptions { type Error = DaemonError; - type Options = CosmosOptions; + type Sender = CosmosBatchSender; - async fn build( - chain_info: cw_orch_core::environment::ChainInfoOwned, - sender_options: Self::Options, - ) -> Result { - Ok(Self { + async fn build(&self, chain_info: &Arc) -> Result { + Ok(CosmosBatchSender { msgs: Default::default(), - sender: Wallet::build(chain_info, sender_options).await?, + sender: self.0.build(chain_info).await?, }) } } -impl QuerySender for BatchSender { +impl QuerySender for CosmosBatchSender { + type Error = DaemonError; + type Options = CosmosBatchOptions; + fn chain_info(&self) -> &cw_orch_core::environment::ChainInfoOwned { self.sender.chain_info() } @@ -49,15 +91,11 @@ impl QuerySender for BatchSender { fn grpc_channel(&self) -> tonic::transport::Channel { self.sender.grpc_channel() } - - fn set_options(&mut self, options: Self::Options) { - self.sender.set_options(options) - } } -impl TxSender for BatchSender { +impl TxSender for CosmosBatchSender { async fn commit_tx_any( - &self, + &mut self, msgs: Vec, memo: Option<&str>, ) -> Result { @@ -82,40 +120,17 @@ impl TxSender for BatchSender { target: &transaction_target(), "Transaction not sent, use `DaemonBase::wallet().broadcast(), to broadcast the batched transactions", ); - let mut msg_storage = self.msgs.lock().unwrap(); - msg_storage.extend(msgs); + self.msgs.extend(msgs); Ok(CosmTxResponse::default()) } } - fn address(&self) -> Result { + fn address(&self) -> Addr { self.sender.address() } - fn msg_sender(&self) -> Result { - self.sender.msg_sender() - } -} - -impl BatchSender { - pub async fn broadcast(&self, memo: Option<&str>) -> Result { - let msgs = self.msgs.lock().unwrap().to_vec(); - log::info!( - target: &transaction_target(), - "[Broadcast] {} msgs in a single transaction", - msgs.len() - ); - let tx_result = self.sender.commit_tx_any(msgs, memo).await?; - log::info!( - target: &transaction_target(), - "[Broadcasted] Success: {}", - tx_result.txhash - ); - - let mut msgs_to_empty = self.msgs.lock().unwrap(); - *msgs_to_empty = vec![]; - - Ok(tx_result) + fn account_id(&self) -> AccountId { + self.sender.account_id() } } diff --git a/cw-orch-daemon/src/senders/cosmos_options.rs b/cw-orch-daemon/src/senders/cosmos_options.rs new file mode 100644 index 000000000..fc0937f95 --- /dev/null +++ b/cw-orch-daemon/src/senders/cosmos_options.rs @@ -0,0 +1,91 @@ +use std::{str::FromStr, sync::Arc}; + +use cosmrs::AccountId; +use cw_orch_core::environment::ChainInfoOwned; + +use crate::{CosmosSender, DaemonError, Wallet}; + +use super::builder::SenderBuilder; + +/// Options for how txs should be constructed for this sender. +#[derive(Default, Clone)] +#[non_exhaustive] +pub struct CosmosOptions { + pub authz_granter: Option, + pub fee_granter: Option, + pub hd_index: Option, + /// Used to derive the private key + pub(crate) key: CosmosWalletKey, +} + +#[derive(Clone)] +pub enum CosmosWalletKey { + Mnemonic(String), + RawKey(Vec), + Env, +} + +impl Default for CosmosWalletKey { + fn default() -> Self { + CosmosWalletKey::Env + } +} + +impl CosmosOptions { + pub fn check(&self) -> Result<(), DaemonError> { + if let Some(addr) = &self.authz_granter { + AccountId::from_str(addr)?; + } + + if let Some(addr) = &self.fee_granter { + AccountId::from_str(addr)?; + } + + Ok(()) + } + + 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 hd_index(mut self, index: u32) -> Self { + self.hd_index = Some(index); + self + } + + pub fn mnemonic(mut self, mnemonic: impl ToString) -> Self { + self.key = CosmosWalletKey::Mnemonic(mnemonic.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()); + } + + pub fn set_hd_index(&mut self, index: u32) { + self.hd_index = Some(index); + } + + pub fn set_mnemonic(&mut self, mnemonic: impl ToString) { + self.key = CosmosWalletKey::Mnemonic(mnemonic.to_string()); + } +} + +impl SenderBuilder for CosmosOptions { + type Error = DaemonError; + type Sender = Wallet; + + async fn build(&self, chain_info: &Arc) -> Result { + CosmosSender::new(chain_info, self.clone()).await + } +} diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index 4067c4850..9fba6bc07 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -1,6 +1,17 @@ -pub mod base_sender; -pub mod batch_sender; +// Core Sender traits pub mod builder; -pub mod no_sender; pub mod query; pub mod tx; + +// Senders +mod cosmos; +mod cosmos_batch; +mod cosmos_options; +mod query_only; + +pub use { + cosmos::{CosmosSender, Wallet}, + cosmos_batch::{options::CosmosBatchOptions, CosmosBatchSender}, + cosmos_options::{CosmosOptions, CosmosWalletKey}, + query_only::{QueryOnlyDaemon, QueryOnlySender}, +}; diff --git a/cw-orch-daemon/src/senders/no_sender.rs b/cw-orch-daemon/src/senders/query_only.rs similarity index 61% rename from cw-orch-daemon/src/senders/no_sender.rs rename to cw-orch-daemon/src/senders/query_only.rs index f751600cd..0be4da617 100644 --- a/cw-orch-daemon/src/senders/no_sender.rs +++ b/cw-orch-daemon/src/senders/query_only.rs @@ -1,3 +1,5 @@ +use std::{rc::Rc, sync::Arc}; + use crate::{error::DaemonError, DaemonBase, GrpcChannel}; use cw_orch_core::environment::ChainInfoOwned; @@ -8,35 +10,37 @@ use super::{builder::SenderBuilder, query::QuerySender}; /// Daemon that does not support signing. /// Will err on any attempt to sign a transaction or retrieve a sender address. -pub type QueryOnlyDaemon = DaemonBase; +pub type QueryOnlyDaemon = DaemonBase; + +pub struct QueryOnlySenderOptions {} /// Signer of the transactions and helper for address derivation #[derive(Clone)] -pub struct NoSender { +pub struct QueryOnlySender { /// gRPC channel pub channel: Channel, /// Information about the chain - pub chain_info: ChainInfoOwned, + pub chain_info: Arc, } -impl SenderBuilder for NoSender { +impl SenderBuilder for QueryOnlySenderOptions { type Error = DaemonError; - type Options = (); + type Sender = QueryOnlySender; - async fn build( - chain_info: ChainInfoOwned, - _sender_options: Self::Options, - ) -> Result { - let channel = GrpcChannel::from_chain_info(chain_info.clone()).await?; + async fn build(&self, chain_info: &Arc) -> Result { + let channel = GrpcChannel::from_chain_info(chain_info.as_ref()).await?; - Ok(NoSender { + Ok(QueryOnlySender { channel, - chain_info, + chain_info: chain_info.clone(), }) } } -impl QuerySender for NoSender { +impl QuerySender for QueryOnlySender { + type Error = DaemonError; + type Options = QueryOnlySenderOptions; + fn chain_info(&self) -> &ChainInfoOwned { &self.chain_info } @@ -44,8 +48,6 @@ impl QuerySender for NoSender { fn grpc_channel(&self) -> Channel { self.channel.clone() } - - fn set_options(&mut self, _options: Self::Options) {} } #[cfg(test)] From c423179ea56a7603aa344965ee9866b564b16168 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:25:50 +0200 Subject: [PATCH 078/108] Simplify sender traits --- cw-orch-daemon/src/senders/builder.rs | 13 +++++++------ cw-orch-daemon/src/senders/query.rs | 11 +++++++---- cw-orch-daemon/src/senders/tx.rs | 28 +++++++++++++-------------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/cw-orch-daemon/src/senders/builder.rs b/cw-orch-daemon/src/senders/builder.rs index 0edd68674..aa6d5a6da 100644 --- a/cw-orch-daemon/src/senders/builder.rs +++ b/cw-orch-daemon/src/senders/builder.rs @@ -1,17 +1,18 @@ +use std::{rc::Rc, sync::Arc}; + use cw_orch_core::environment::ChainInfoOwned; use crate::DaemonError; /// Allows building a `Sender` from `SenderBuilder::Options` /// `async`` because it could do network requests during build -pub trait SenderBuilder: Clone { +pub trait SenderBuilder { type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; - /// Options for the sender - type Options: Default + Clone; + type Sender; /// Build a new `Sender`. fn build( - chain_info: ChainInfoOwned, - sender_options: Self::Options, - ) -> impl std::future::Future> + Send; + &self, + chain_info: &Arc, + ) -> impl std::future::Future> + Send; } diff --git a/cw-orch-daemon/src/senders/query.rs b/cw-orch-daemon/src/senders/query.rs index 587026de8..2c9c4b02e 100644 --- a/cw-orch-daemon/src/senders/query.rs +++ b/cw-orch-daemon/src/senders/query.rs @@ -1,16 +1,19 @@ use cw_orch_core::environment::ChainInfoOwned; use tonic::transport::Channel; +use crate::DaemonError; + use super::builder::SenderBuilder; /// A sender that can query information over a connection. -pub trait QuerySender: SenderBuilder { - /// Set the Sender options - fn set_options(&mut self, options: Self::Options); +pub trait QuerySender: Clone { + type Error: Into + std::error::Error + std::fmt::Debug + Send + Sync + 'static; + /// Options for this sender + type Options: SenderBuilder; /// Get the chain_information for the sender fn chain_info(&self) -> &ChainInfoOwned; - /// Get the channel for the sender (TODO: return mut ref to Retry Sender) + /// Get the channel for the sender fn grpc_channel(&self) -> Channel; } diff --git a/cw-orch-daemon/src/senders/tx.rs b/cw-orch-daemon/src/senders/tx.rs index 31058e477..15fcc7196 100644 --- a/cw-orch-daemon/src/senders/tx.rs +++ b/cw-orch-daemon/src/senders/tx.rs @@ -6,17 +6,24 @@ use crate::CosmTxResponse; use super::query::QuerySender; pub trait TxSender: QuerySender { - /// Get the address of the sender. - fn address(&self) -> Result; + /// Returns the `AccountId` of the sender that commits the transaction. + fn account_id(&self) -> AccountId; - /// Returns the `AccountId` of the sender. - /// If an authz granter is set, returns the authz granter - /// Else, returns the address associated with the current private key - fn msg_sender(&self) -> Result; + /// Commit a proto `Any` message to the chain using this sender. + fn commit_tx_any( + &mut self, + msgs: Vec, + memo: Option<&str>, + ) -> impl std::future::Future> + Send; + + /// Get the address of the sender. + fn address(&self) -> Addr { + Addr::unchecked(self.account_id().to_string()) + } /// Commit a transaction to the chain using this sender. fn commit_tx( - &self, + &mut self, msgs: Vec, memo: Option<&str>, ) -> impl std::future::Future> + Send { @@ -28,11 +35,4 @@ pub trait TxSender: QuerySender { self.commit_tx_any(msgs, memo) } - - /// Commit a proto `Any` message to the chain using this sender. - fn commit_tx_any( - &self, - msgs: Vec, - memo: Option<&str>, - ) -> impl std::future::Future> + Send; } From d80978bdd24b8c9b6b62bb0602fe54fa368d13fe Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:26:56 +0200 Subject: [PATCH 079/108] reflect re-exports in cw-orch --- cw-orch/src/daemon.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cw-orch/src/daemon.rs b/cw-orch/src/daemon.rs index bbca578b1..247e06057 100644 --- a/cw-orch/src/daemon.rs +++ b/cw-orch/src/daemon.rs @@ -1,5 +1,4 @@ //! `Daemon` and `DaemonAsync` execution environments. //! //! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. -pub use cw_orch_daemon::senders::base_sender::Wallet; pub use cw_orch_daemon::*; From 4d62b76e569f048cfe102267560f5ca3d9eb9cf6 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:27:31 +0200 Subject: [PATCH 080/108] refactor exports --- cw-orch-daemon/src/lib.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index 70b54e3c3..b290bd970 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -1,29 +1,33 @@ //! `Daemon` and `DaemonAsync` execution environments. //! //! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. - -pub mod builder; -pub mod channel; -pub mod core; -pub mod error; pub mod json_lock; /// Proto types for different blockchains pub mod proto; -pub mod senders; -pub mod state; -pub mod sync; -pub mod tx_resp; // expose these as mods as they can grow pub mod env; pub mod keys; pub mod live_mock; -mod log; pub mod queriers; +pub mod senders; pub mod tx_broadcaster; pub mod tx_builder; + +mod builder; +mod channel; +mod core; +mod error; +mod log; +mod state; +mod sync; +mod tx_resp; + pub use self::{builder::*, channel::*, core::*, error::*, state::*, sync::*, tx_resp::*}; pub use cw_orch_networks::networks; -pub use senders::base_sender::Wallet; +pub use senders::{ + CosmosBatchOptions, CosmosBatchSender, CosmosOptions, CosmosSender, QueryOnlyDaemon, + QueryOnlySender, Wallet, +}; pub use tx_builder::TxBuilder; mod cosmos_proto_patches; From 87368649c4e071ff459bd7188c4090a187abcb5a Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:30:00 +0200 Subject: [PATCH 081/108] make daemon hold RwLock of Sender --- cw-orch-daemon/src/core.rs | 69 +++++++++++++--------- cw-orch-daemon/src/sync/core.rs | 101 +++++++++++++++++++------------- 2 files changed, 103 insertions(+), 67 deletions(-) diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 32f227aea..0bca2f809 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -1,8 +1,7 @@ use crate::{queriers::CosmWasm, senders::query::QuerySender, DaemonAsyncBuilder, DaemonState}; use super::{ - cosmos_modules, error::DaemonError, queriers::Node, senders::base_sender::Wallet, - tx_resp::CosmTxResponse, + cosmos_modules, error::DaemonError, queriers::Node, senders::Wallet, tx_resp::CosmTxResponse, }; use cosmrs::{ @@ -22,9 +21,12 @@ use prost::Message; use serde::{de::DeserializeOwned, Serialize}; use serde_json::from_str; use std::{ + borrow::BorrowMut, fmt::Debug, io::Write, + ops::Deref, str::{from_utf8, FromStr}, + sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, time::Duration, }; @@ -67,7 +69,7 @@ pub const INSTANTIATE_2_TYPE_URL: &str = "/cosmwasm.wasm.v1.MsgInstantiateContra */ pub struct DaemonAsyncBase { /// Sender to send transactions to the chain - pub(crate) sender: Sender, + sender: Arc>, /// State of the daemon pub(crate) state: DaemonState, } @@ -75,14 +77,26 @@ pub struct DaemonAsyncBase { pub type DaemonAsync = DaemonAsyncBase; impl DaemonAsyncBase { + pub(crate) fn new(sender: Sender, state: DaemonState) -> Self { + Self { + sender: Arc::new(RwLock::new(sender)), + state, + } + } + /// Get the daemon builder pub fn builder(chain: impl Into) -> DaemonAsyncBuilder { DaemonAsyncBuilder::new(chain) } - /// Get the mutable Sender object - pub fn sender_mut(&mut self) -> &mut Sender { - &mut self.sender + /// Get a mutable Sender + pub fn sender_mut(&self) -> RwLockWriteGuard { + self.sender.write().unwrap() + } + + // Get a read-only Sender + pub fn sender(&self) -> RwLockReadGuard { + self.sender.read().unwrap() } /// Flushes all the state related to the current chain @@ -96,7 +110,7 @@ impl DaemonAsyncBase { pub fn rebuild(&self) -> DaemonAsyncBuilder { DaemonAsyncBuilder { state: Some(self.state()), - chain: self.state.chain_data.clone(), + chain: self.state.chain_data.deref().clone(), deployment_id: Some(self.state.deployment_id.clone()), state_path: None, write_on_change: None, @@ -108,7 +122,7 @@ impl DaemonAsyncBase { impl DaemonAsyncBase { /// Get the channel configured for this DaemonAsync. pub fn channel(&self) -> Channel { - self.sender.grpc_channel() + self.sender().grpc_channel() } /// Query a contract. @@ -189,8 +203,8 @@ impl ChainState for DaemonAsyncBase { // Execute on the real chain, returns tx response. impl DaemonAsyncBase { /// Get the sender address - pub fn sender(&self) -> Addr { - self.sender.address().unwrap() + pub fn sender_addr(&self) -> Addr { + self.sender().address() } /// Execute a message on a contract. @@ -201,13 +215,13 @@ impl DaemonAsyncBase { contract_address: &Addr, ) -> Result { let exec_msg: MsgExecuteContract = MsgExecuteContract { - sender: self.sender.msg_sender().map_err(Into::into)?, + sender: self.sender().account_id(), contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&exec_msg)?, funds: parse_cw_coins(coins)?, }; let result = self - .sender + .sender_mut() .commit_tx(vec![exec_msg], None) .await .map_err(Into::into)?; @@ -225,18 +239,17 @@ impl DaemonAsyncBase { admin: Option<&Addr>, coins: &[Coin], ) -> Result { - let sender = &self.sender; - let init_msg = MsgInstantiateContract { 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.msg_sender().map_err(Into::into)?, + sender: self.sender().account_id(), msg: serde_json::to_vec(&init_msg)?, funds: parse_cw_coins(coins)?, }; - let result = sender + let result = self + .sender_mut() .commit_tx(vec![init_msg], None) .await .map_err(Into::into)?; @@ -256,20 +269,19 @@ impl DaemonAsyncBase { coins: &[Coin], salt: Binary, ) -> Result { - let sender = &self.sender; - let init_msg = MsgInstantiateContract2 { code_id, label: label.unwrap_or("instantiate_contract").to_string(), admin: admin.map(Into::into).unwrap_or_default(), - sender: sender.address().map_err(Into::into)?.to_string(), + sender: self.sender_addr().to_string(), msg: serde_json::to_vec(&init_msg)?, funds: proto_parse_cw_coins(coins)?, salt: salt.to_vec(), fix_msg: false, }; - let result = sender + let result = self + .sender_mut() .commit_tx_any( vec![Any { type_url: INSTANTIATE_2_TYPE_URL.to_string(), @@ -293,13 +305,13 @@ impl DaemonAsyncBase { contract_address: &Addr, ) -> Result { let exec_msg: MsgMigrateContract = MsgMigrateContract { - sender: self.sender.msg_sender().map_err(Into::into)?, + sender: self.sender().account_id(), contract: AccountId::from_str(contract_address.as_str())?, msg: serde_json::to_vec(&migrate_msg)?, code_id: new_code_id, }; let result = self - .sender + .sender_mut() .commit_tx(vec![exec_msg], None) .await .map_err(Into::into)?; @@ -311,8 +323,8 @@ impl DaemonAsyncBase { &self, _uploadable: &T, ) -> Result { - let sender = &self.sender; - let wasm_path = ::wasm(self.sender.chain_info()); + let sender = self.sender(); + let wasm_path = ::wasm(sender.chain_info()); log::debug!(target: &transaction_target(), "Uploading file at {:?}", wasm_path); @@ -321,12 +333,15 @@ impl DaemonAsyncBase { e.write_all(&file_contents)?; let wasm_byte_code = e.finish()?; let store_msg = cosmrs::cosmwasm::MsgStoreCode { - sender: self.sender.msg_sender().map_err(Into::into)?, + sender: sender.account_id(), wasm_byte_code, instantiate_permission: None, }; - let result = sender + drop(sender); + + let result = self + .sender_mut() .commit_tx(vec![store_msg], None) .await .map_err(Into::into)?; @@ -346,7 +361,7 @@ impl DaemonAsyncBase { /// Set the sender to use with this DaemonAsync to be the given wallet pub fn set_sender(self, sender: NewSender) -> DaemonAsyncBase { DaemonAsyncBase { - sender, + sender: Arc::new(RwLock::new(sender)), state: self.state, } } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index f5a6de335..0f503f6e3 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -1,6 +1,11 @@ -use std::fmt::Debug; +use std::{ + borrow::BorrowMut, + fmt::Debug, + ops::DerefMut, + sync::{RwLockReadGuard, RwLockWriteGuard}, +}; -use super::super::senders::base_sender::Wallet; +use super::super::senders::Wallet; use crate::{ queriers::{Bank, CosmWasmBase, Node}, senders::query::QuerySender, @@ -18,30 +23,32 @@ use tonic::transport::Channel; use crate::senders::tx::TxSender; +pub type Daemon = DaemonBase; + #[derive(Clone)] /** - Represents a blockchain node. - Is constructed with the [DaemonBuilder]. +Represents a blockchain node. +Is constructed with the [DaemonBuilder]. - ## Usage +## Usage - ```rust,no_run - use cw_orch_daemon::{Daemon, networks}; - use tokio::runtime::Runtime; +```rust,no_run +use cw_orch_daemon::{Daemon, networks}; +use tokio::runtime::Runtime; - let rt = Runtime::new().unwrap(); - let daemon: Daemon = Daemon::builder(networks::JUNO_1) - .build() - .unwrap(); - ``` - ## Environment Execution +let rt = Runtime::new().unwrap(); +let daemon: Daemon = Daemon::builder(networks::JUNO_1) + .build() + .unwrap(); +``` +## Environment Execution - The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. +The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain. - ## Querying +## Querying - Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. - See [Querier](crate::queriers) for examples. +Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client`] method with a specific querier. +See [Querier](crate::queriers) for examples. */ pub struct DaemonBase { pub(crate) daemon: DaemonAsyncBase, @@ -49,17 +56,33 @@ pub struct DaemonBase { pub rt_handle: Handle, } -pub type Daemon = DaemonBase; +impl DaemonBase { + /// Get the daemon builder + pub fn builder(chain: impl Into) -> DaemonBuilder { + DaemonBuilder::new(chain) + } + + /// Get the mutable Sender object + pub fn sender_mut(&self) -> RwLockWriteGuard { + self.daemon.sender_mut() + } -impl DaemonBase { /// Get the channel configured for this Daemon - pub fn wallet(&self) -> Sender { - self.daemon.sender.clone() + pub fn sender(&self) -> RwLockReadGuard { + self.daemon.sender() + } + + /// Flushes all the state related to the current chain + /// Only works on Local networks + pub fn flush_state(&mut self) -> Result<(), DaemonError> { + self.daemon.flush_state() } +} +impl DaemonBase { /// Get the channel configured for this Daemon pub fn channel(&self) -> Channel { - self.daemon.sender.grpc_channel() + self.daemon.sender().grpc_channel() } /// Returns a new [`DaemonBuilder`] with the current configuration. @@ -68,7 +91,7 @@ impl DaemonBase { pub fn rebuild(&self) -> DaemonBuilder { DaemonBuilder { state: Some(self.state()), - chain: self.daemon.sender.chain_info().clone(), + chain: self.daemon.sender().chain_info().clone(), deployment_id: Some(self.daemon.state.deployment_id.clone()), state_path: None, write_on_change: None, @@ -78,21 +101,18 @@ impl DaemonBase { } } -impl DaemonBase { - /// Get the daemon builder - pub fn builder(chain: impl Into) -> DaemonBuilder { - DaemonBuilder::new(chain) +// Helpers for Daemon with [`Wallet`] sender. +impl Daemon { + /// Specifies wether authz should be used with this daemon + pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { + self.sender_mut().set_authz_granter(granter.to_string()); + self } - /// Get the mutable Sender object - pub fn sender_mut(&mut self) -> &mut Sender { - self.daemon.sender_mut() - } - - /// Flushes all the state related to the current chain - /// Only works on Local networks - pub fn flush_state(&mut self) -> Result<(), DaemonError> { - self.daemon.flush_state() + /// Specifies wether feegrant should be used with this daemon + pub fn fee_granter(&mut self, granter: impl ToString) -> &mut Self { + self.sender_mut().set_fee_granter(granter.to_string()); + self } } @@ -112,11 +132,12 @@ impl TxHandler for DaemonBase { type Sender = Sender; fn sender(&self) -> Addr { - self.daemon.sender.address().unwrap() + self.daemon.sender_addr() } fn set_sender(&mut self, sender: Self::Sender) { - self.daemon.sender = sender + let mut daemon_sender = self.daemon.sender_mut(); + (*daemon_sender.deref_mut()) = sender; } fn upload(&self, uploadable: &T) -> Result { @@ -183,7 +204,7 @@ impl Stargate for DaemonBase { ) -> Result { self.rt_handle .block_on( - self.wallet().commit_tx_any( + self.sender_mut().commit_tx_any( msgs.iter() .map(|msg| cosmrs::Any { type_url: msg.type_url.clone(), From 53f32f2daf1300a7de22f61120d4493bedc52aa9 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:30:45 +0200 Subject: [PATCH 082/108] make state hold Arc --- cw-orch-daemon/src/state.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index c3105d4e6..876cb54c8 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -28,7 +28,7 @@ pub struct DaemonState { /// Deployment identifier pub deployment_id: String, /// Information about the chain - pub chain_data: ChainInfoOwned, + pub chain_data: Arc, /// Whether to write on every change of the state pub write_on_change: bool, } @@ -58,7 +58,7 @@ impl DaemonState { /// Attempts to connect to any of the provided gRPC endpoints. pub fn new( mut json_file_path: String, - chain_data: ChainInfoOwned, + chain_data: &Arc, deployment_id: String, read_only: bool, write_on_change: bool, @@ -115,7 +115,7 @@ impl DaemonState { Ok(DaemonState { json_state, deployment_id, - chain_data, + chain_data: chain_data.clone(), write_on_change, }) } From 202255ee7a35ce67fc2d7150d2370f486e54a831 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:33:09 +0200 Subject: [PATCH 083/108] create channel from reference chaininfo --- cw-orch-daemon/src/channel.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/channel.rs index c65ffd908..7075e1e83 100644 --- a/cw-orch-daemon/src/channel.rs +++ b/cw-orch-daemon/src/channel.rs @@ -92,10 +92,7 @@ impl GrpcChannel { } /// Create a gRPC channel from the chain info - pub async fn from_chain_info( - chain_info: impl Into, - ) -> Result { - let chain_info: ChainInfoOwned = chain_info.into(); + pub async fn from_chain_info(chain_info: &ChainInfoOwned) -> Result { GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await } } From 09cfc9e13a3a5670a60c2b8048aad1152f1a309e Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:33:15 +0200 Subject: [PATCH 084/108] update examples --- cw-orch-daemon/examples/batch-sender.rs | 2 +- cw-orch-daemon/examples/manual_sender.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cw-orch-daemon/examples/batch-sender.rs b/cw-orch-daemon/examples/batch-sender.rs index 65da0c913..1ce446cc2 100644 --- a/cw-orch-daemon/examples/batch-sender.rs +++ b/cw-orch-daemon/examples/batch-sender.rs @@ -2,7 +2,7 @@ use counter_contract::{ msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, CounterQueryMsgFns, }; -use cw_orch::{anyhow, daemon::senders::batch_sender::BatchDaemon, prelude::*}; +use cw_orch::{anyhow, daemon::senders::cosmos_batch::BatchDaemon, prelude::*}; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index fcd15dc59..eed990e90 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -138,7 +138,7 @@ impl TxSender for ManualSender { Ok(self.sender.clone()) } - fn msg_sender(&self) -> Result { + fn account_id(&self) -> Result { self.sender.clone().to_string().parse().map_err(Into::into) } } From cb664663cc16291a7081e284bf749b6a71b2a188 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:34:08 +0200 Subject: [PATCH 085/108] update builders --- cw-orch-daemon/src/builder.rs | 31 +++++++++++++++++------------- cw-orch-daemon/src/sync/builder.rs | 26 +++++++++++++++---------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index b95e24d9a..ab9a6df29 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,6 +1,8 @@ +use std::sync::{Arc, Mutex, RwLock}; + use crate::{ log::print_if_log_disabled, - senders::{base_sender::CosmosOptions, builder::SenderBuilder}, + senders::{builder::SenderBuilder, CosmosOptions, CosmosWalletKey}, DaemonAsyncBase, DaemonBuilder, DaemonStateFile, Wallet, }; @@ -93,37 +95,40 @@ impl DaemonAsyncBuilder { /// Build a daemon with provided mnemonic or env-var mnemonic pub async fn build(&self) -> Result, DaemonError> { - let chain_info = self.chain.clone(); + let chain_info = Arc::new(self.chain.clone()); let state = self.build_state()?; // if mnemonic provided, use it. Else use env variables to retrieve mnemonic let options = CosmosOptions { - mnemonic: self.mnemonic.clone(), + key: self.mnemonic.as_ref().map_or(CosmosWalletKey::Env, |m| { + CosmosWalletKey::Mnemonic(m.clone()) + }), ..Default::default() }; - let sender = Wallet::build(chain_info, options).await?; + let sender = options.build(&chain_info).await?; - let daemon = DaemonAsyncBase { state, sender }; + let daemon = DaemonAsyncBase::new(sender, state); print_if_log_disabled()?; Ok(daemon) } /// Build a daemon - pub async fn build_sender( + pub async fn build_sender( &self, - sender_options: Sender::Options, - ) -> Result, DaemonError> { - let chain_info = self.chain.clone(); + sender_options: T, + ) -> Result, DaemonError> { + let chain_info = Arc::new(self.chain.clone()); let state = self.build_state()?; - let sender = Sender::build(chain_info, sender_options) + let sender = sender_options + .build(&chain_info) .await .map_err(Into::into)?; - let daemon = DaemonAsyncBase { state, sender }; + let daemon = DaemonAsyncBase::new(sender, state); print_if_log_disabled()?; Ok(daemon) @@ -135,7 +140,7 @@ impl DaemonAsyncBuilder { .deployment_id .clone() .unwrap_or(DEFAULT_DEPLOYMENT.to_string()); - let chain_info = self.chain.clone(); + let chain_info = Arc::new(self.chain.clone()); let state = match &self.state { Some(state) => { @@ -167,7 +172,7 @@ impl DaemonAsyncBuilder { DaemonState::new( json_file_path, - chain_info, + &chain_info, deployment_id, false, self.write_on_change.unwrap_or(true), diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index ec7877594..0902fd6f3 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -134,10 +134,10 @@ impl DaemonBuilder { } /// Build a daemon - pub fn build_sender( + pub fn build_sender( &self, - sender_options: Sender::Options, - ) -> Result, DaemonError> { + sender_options: T, + ) -> Result, DaemonError> { let rt_handle = self .handle .clone() @@ -182,9 +182,9 @@ mod test { .build() .unwrap(); - assert_eq!(daemon.daemon.sender.chain_info.grpc_urls.len(), 1); + assert_eq!(daemon.daemon.sender().chain_info.grpc_urls.len(), 1); assert_eq!( - daemon.daemon.sender.chain_info.grpc_urls[0], + daemon.daemon.sender().chain_info.grpc_urls[0], OSMOSIS_1.grpc_urls[0].to_string(), ); } @@ -198,9 +198,9 @@ mod test { .gas(None, Some(fee_amount)) .build() .unwrap(); - println!("chain {:?}", daemon.daemon.sender.chain_info); + println!("chain {:?}", daemon.daemon.sender().chain_info); - assert_eq!(daemon.daemon.sender.chain_info.gas_price, fee_amount); + assert_eq!(daemon.daemon.sender().chain_info.gas_price, fee_amount); } #[test] @@ -213,7 +213,10 @@ mod test { .build() .unwrap(); - assert_eq!(daemon.daemon.sender.chain_info.gas_denom, token.to_string()); + assert_eq!( + daemon.daemon.sender().chain_info.gas_denom, + token.to_string() + ); } #[test] @@ -227,9 +230,12 @@ mod test { .build() .unwrap(); - assert_eq!(daemon.daemon.sender.chain_info.gas_denom, token.to_string()); + assert_eq!( + daemon.daemon.sender().chain_info.gas_denom, + token.to_string() + ); - assert_eq!(daemon.daemon.sender.chain_info.gas_price, fee_amount); + assert_eq!(daemon.daemon.sender().chain_info.gas_price, fee_amount); } #[test] From 820f728b8b9c89e736e4e9dae11481ca46b9039a Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:34:15 +0200 Subject: [PATCH 086/108] update queriers --- cw-orch-daemon/src/queriers/cosmwasm.rs | 8 ++++---- cw-orch-daemon/src/queriers/env.rs | 12 ++++-------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 9f3ddbab5..6a3763192 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -1,7 +1,7 @@ use std::{marker::PhantomData, str::FromStr}; -use crate::senders::no_sender::NoSender; use crate::senders::query::QuerySender; +use crate::senders::QueryOnlySender; use crate::{cosmos_modules, error::DaemonError, DaemonBase}; use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; use cosmrs::AccountId; @@ -19,13 +19,13 @@ use tonic::transport::Channel; /// Querier for the CosmWasm SDK module /// All the async function are prefixed with `_` -pub struct CosmWasmBase { +pub struct CosmWasmBase { pub channel: Channel, pub rt_handle: Option, _sender: PhantomData, } -pub type CosmWasm = CosmWasmBase; +pub type CosmWasm = CosmWasmBase; impl CosmWasmBase { pub fn new(daemon: &DaemonBase) -> Self { @@ -310,7 +310,7 @@ impl WasmQuerier for CosmWasmBase { &self, contract: &T, ) -> Result { - ::wasm(contract.environment().daemon.sender.chain_info()).checksum() + ::wasm(contract.environment().daemon.sender().chain_info()).checksum() } } diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index 6366fd763..72dcf8289 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -4,15 +4,11 @@ use crate::{senders::query::QuerySender, DaemonBase}; impl EnvironmentQuerier for DaemonBase { fn env_info(&self) -> EnvironmentInfo { + let binding = self.daemon.sender(); + let info = binding.chain_info(); EnvironmentInfo { - chain_id: self.daemon.sender.chain_info().chain_id.clone(), - chain_name: self - .daemon - .sender - .chain_info() - .network_info - .chain_name - .clone(), + chain_id: info.chain_id.clone(), + chain_name: info.network_info.chain_name.clone(), deployment_id: self.daemon.state.deployment_id.clone(), } } From 33398da4b66dc3c71240712033f9a2436ac2525f Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:34:20 +0200 Subject: [PATCH 087/108] update tests --- cw-orch-daemon/tests/authz.rs | 2 +- cw-orch-daemon/tests/index.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index fbebbaeae..0a0338fc1 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -15,7 +15,7 @@ mod tests { use cosmwasm_std::coins; use cw_orch_core::environment::QuerierGetter; use cw_orch_core::environment::{BankQuerier, DefaultQueriers, QueryHandler, TxHandler}; - use cw_orch_daemon::{queriers::Authz, senders::base_sender::CosmosOptions, Daemon}; + use cw_orch_daemon::{queriers::Authz, senders::CosmosOptions, Daemon}; use cw_orch_networks::networks::LOCAL_JUNO; use cw_orch_traits::Stargate; use prost::Message; diff --git a/cw-orch-daemon/tests/index.rs b/cw-orch-daemon/tests/index.rs index 3b59423ca..c9ed26ef5 100644 --- a/cw-orch-daemon/tests/index.rs +++ b/cw-orch-daemon/tests/index.rs @@ -3,7 +3,7 @@ mod common; mod tests { use cw_orch_core::environment::TxHandler; - use cw_orch_daemon::{senders::base_sender::CosmosOptions, Daemon}; + use cw_orch_daemon::{senders::CosmosOptions, Daemon}; #[test] #[serial_test::serial] From 5f6e6bcf8cf762937a4ef0aa34ada8e412e14d2d Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:41:05 +0200 Subject: [PATCH 088/108] rename QuerySender `grpc_channel` fn to `channel` --- cw-orch-daemon/examples/manual_sender.rs | 10 +++++----- cw-orch-daemon/src/core.rs | 2 +- cw-orch-daemon/src/senders/cosmos.rs | 2 +- cw-orch-daemon/src/senders/cosmos_batch.rs | 4 ++-- cw-orch-daemon/src/senders/query.rs | 6 +----- cw-orch-daemon/src/senders/query_only.rs | 2 +- cw-orch-daemon/src/sync/core.rs | 7 ++++++- 7 files changed, 17 insertions(+), 16 deletions(-) diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index eed990e90..16197df78 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -92,7 +92,7 @@ impl QuerySender for ManualSender { &self.chain_info } - fn grpc_channel(&self) -> tonic::transport::Channel { + fn channel(&self) -> tonic::transport::Channel { self.grpc_channel.clone() } @@ -127,7 +127,7 @@ impl TxSender for ManualSender { .expect("Failed to read line"); let txhash = txhash.trim_end(); - let resp = Node::new_async(self.grpc_channel()) + let resp = Node::new_async(self.channel()) ._find_tx(txhash.to_string()) .await?; @@ -145,7 +145,7 @@ impl TxSender for ManualSender { impl ManualSender { pub async fn simulate(&self, msgs: Vec, memo: Option<&str>) -> Result { - let timeout_height = Node::new_async(self.grpc_channel())._block_height().await? + 10u64; + let timeout_height = Node::new_async(self.channel())._block_height().await? + 10u64; let tx_body = TxBuilder::build_body(msgs, memo, timeout_height); @@ -179,7 +179,7 @@ impl ManualSender { } .into(); - Node::new_async(self.grpc_channel()) + Node::new_async(self.channel()) ._simulate_tx(tx_raw.to_bytes()?) .await } @@ -188,7 +188,7 @@ impl ManualSender { let addr = self.address()?.to_string(); let mut client = cosmrs::proto::cosmos::auth::v1beta1::query_client::QueryClient::new( - self.grpc_channel(), + self.channel(), ); let resp = client diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 0bca2f809..01ae7751f 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -122,7 +122,7 @@ impl DaemonAsyncBase { impl DaemonAsyncBase { /// Get the channel configured for this DaemonAsync. pub fn channel(&self) -> Channel { - self.sender().grpc_channel() + self.sender().channel() } /// Query a contract. diff --git a/cw-orch-daemon/src/senders/cosmos.rs b/cw-orch-daemon/src/senders/cosmos.rs index 003608697..676f99ef0 100644 --- a/cw-orch-daemon/src/senders/cosmos.rs +++ b/cw-orch-daemon/src/senders/cosmos.rs @@ -401,7 +401,7 @@ impl QuerySender for Wallet { self.chain_info.as_ref() } - fn grpc_channel(&self) -> Channel { + fn channel(&self) -> Channel { self.channel() } } diff --git a/cw-orch-daemon/src/senders/cosmos_batch.rs b/cw-orch-daemon/src/senders/cosmos_batch.rs index b2c29f94b..d7fffb0a0 100644 --- a/cw-orch-daemon/src/senders/cosmos_batch.rs +++ b/cw-orch-daemon/src/senders/cosmos_batch.rs @@ -88,8 +88,8 @@ impl QuerySender for CosmosBatchSender { self.sender.chain_info() } - fn grpc_channel(&self) -> tonic::transport::Channel { - self.sender.grpc_channel() + fn channel(&self) -> tonic::transport::Channel { + self.sender.channel() } } diff --git a/cw-orch-daemon/src/senders/query.rs b/cw-orch-daemon/src/senders/query.rs index 2c9c4b02e..208c3f0a7 100644 --- a/cw-orch-daemon/src/senders/query.rs +++ b/cw-orch-daemon/src/senders/query.rs @@ -1,4 +1,3 @@ -use cw_orch_core::environment::ChainInfoOwned; use tonic::transport::Channel; use crate::DaemonError; @@ -11,9 +10,6 @@ pub trait QuerySender: Clone { /// Options for this sender type Options: SenderBuilder; - /// Get the chain_information for the sender - fn chain_info(&self) -> &ChainInfoOwned; - /// Get the channel for the sender - fn grpc_channel(&self) -> Channel; + fn channel(&self) -> Channel; } diff --git a/cw-orch-daemon/src/senders/query_only.rs b/cw-orch-daemon/src/senders/query_only.rs index 0be4da617..7237a2c49 100644 --- a/cw-orch-daemon/src/senders/query_only.rs +++ b/cw-orch-daemon/src/senders/query_only.rs @@ -45,7 +45,7 @@ impl QuerySender for QueryOnlySender { &self.chain_info } - fn grpc_channel(&self) -> Channel { + fn channel(&self) -> Channel { self.channel.clone() } } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 0f503f6e3..6c8c92688 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -77,12 +77,17 @@ impl DaemonBase { pub fn flush_state(&mut self) -> Result<(), DaemonError> { self.daemon.flush_state() } + + /// Return the chain info for this daemon + pub fn chain_info(&self) -> &ChainInfoOwned { + self.daemon.state.chain_data.as_ref() + } } impl DaemonBase { /// Get the channel configured for this Daemon pub fn channel(&self) -> Channel { - self.daemon.sender().grpc_channel() + self.daemon.sender().channel() } /// Returns a new [`DaemonBuilder`] with the current configuration. From 65dda2120df1eda48efc2cdf59b8e3204367b1e7 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:46:12 +0200 Subject: [PATCH 089/108] Remove `chain_info` fn from QuerySender --- cw-orch-daemon/examples/manual_sender.rs | 11 ----------- cw-orch-daemon/src/core.rs | 11 ++++++----- cw-orch-daemon/src/queriers/cosmwasm.rs | 2 +- cw-orch-daemon/src/queriers/env.rs | 3 +-- cw-orch-daemon/src/senders/cosmos.rs | 4 ---- cw-orch-daemon/src/senders/cosmos_batch.rs | 4 ---- cw-orch-daemon/src/senders/query_only.rs | 4 ---- cw-orch-daemon/src/sync/core.rs | 4 ++-- 8 files changed, 10 insertions(+), 33 deletions(-) diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index 16197df78..a77f1c5ff 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -88,21 +88,10 @@ impl SenderBuilder for ManualSender { } impl QuerySender for ManualSender { - fn chain_info(&self) -> &cw_orch_core::environment::ChainInfoOwned { - &self.chain_info - } fn channel(&self) -> tonic::transport::Channel { self.grpc_channel.clone() } - - fn set_options(&mut self, options: Self::Options) { - self.sender = Addr::unchecked( - options - .sender_address - .expect("Manual sender needs an address"), - ) - } } impl TxSender for ManualSender { diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 01ae7751f..d78831611 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -84,6 +84,10 @@ impl DaemonAsyncBase { } } + pub fn chain_info(&self) -> &ChainInfoOwned { + self.state.chain_data.as_ref() + } + /// Get the daemon builder pub fn builder(chain: impl Into) -> DaemonAsyncBuilder { DaemonAsyncBuilder::new(chain) @@ -323,8 +327,7 @@ impl DaemonAsyncBase { &self, _uploadable: &T, ) -> Result { - let sender = self.sender(); - let wasm_path = ::wasm(sender.chain_info()); + let wasm_path = ::wasm(self.chain_info()); log::debug!(target: &transaction_target(), "Uploading file at {:?}", wasm_path); @@ -333,13 +336,11 @@ impl DaemonAsyncBase { e.write_all(&file_contents)?; let wasm_byte_code = e.finish()?; let store_msg = cosmrs::cosmwasm::MsgStoreCode { - sender: sender.account_id(), + sender: self.sender().account_id(), wasm_byte_code, instantiate_permission: None, }; - drop(sender); - let result = self .sender_mut() .commit_tx(vec![store_msg], None) diff --git a/cw-orch-daemon/src/queriers/cosmwasm.rs b/cw-orch-daemon/src/queriers/cosmwasm.rs index 6a3763192..5dd15329b 100644 --- a/cw-orch-daemon/src/queriers/cosmwasm.rs +++ b/cw-orch-daemon/src/queriers/cosmwasm.rs @@ -310,7 +310,7 @@ impl WasmQuerier for CosmWasmBase { &self, contract: &T, ) -> Result { - ::wasm(contract.environment().daemon.sender().chain_info()).checksum() + ::wasm(contract.environment().daemon.chain_info()).checksum() } } diff --git a/cw-orch-daemon/src/queriers/env.rs b/cw-orch-daemon/src/queriers/env.rs index 72dcf8289..28277c657 100644 --- a/cw-orch-daemon/src/queriers/env.rs +++ b/cw-orch-daemon/src/queriers/env.rs @@ -4,8 +4,7 @@ use crate::{senders::query::QuerySender, DaemonBase}; impl EnvironmentQuerier for DaemonBase { fn env_info(&self) -> EnvironmentInfo { - let binding = self.daemon.sender(); - let info = binding.chain_info(); + let info = self.daemon.chain_info(); EnvironmentInfo { chain_id: info.chain_id.clone(), chain_name: info.network_info.chain_name.clone(), diff --git a/cw-orch-daemon/src/senders/cosmos.rs b/cw-orch-daemon/src/senders/cosmos.rs index 676f99ef0..41e6160c2 100644 --- a/cw-orch-daemon/src/senders/cosmos.rs +++ b/cw-orch-daemon/src/senders/cosmos.rs @@ -397,10 +397,6 @@ impl QuerySender for Wallet { type Error = DaemonError; type Options = CosmosOptions; - fn chain_info(&self) -> &ChainInfoOwned { - self.chain_info.as_ref() - } - fn channel(&self) -> Channel { self.channel() } diff --git a/cw-orch-daemon/src/senders/cosmos_batch.rs b/cw-orch-daemon/src/senders/cosmos_batch.rs index d7fffb0a0..034a11e2b 100644 --- a/cw-orch-daemon/src/senders/cosmos_batch.rs +++ b/cw-orch-daemon/src/senders/cosmos_batch.rs @@ -84,10 +84,6 @@ impl QuerySender for CosmosBatchSender { type Error = DaemonError; type Options = CosmosBatchOptions; - fn chain_info(&self) -> &cw_orch_core::environment::ChainInfoOwned { - self.sender.chain_info() - } - fn channel(&self) -> tonic::transport::Channel { self.sender.channel() } diff --git a/cw-orch-daemon/src/senders/query_only.rs b/cw-orch-daemon/src/senders/query_only.rs index 7237a2c49..1d9523116 100644 --- a/cw-orch-daemon/src/senders/query_only.rs +++ b/cw-orch-daemon/src/senders/query_only.rs @@ -41,10 +41,6 @@ impl QuerySender for QueryOnlySender { type Error = DaemonError; type Options = QueryOnlySenderOptions; - fn chain_info(&self) -> &ChainInfoOwned { - &self.chain_info - } - fn channel(&self) -> Channel { self.channel.clone() } diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 6c8c92688..cddaf6f35 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -80,7 +80,7 @@ impl DaemonBase { /// Return the chain info for this daemon pub fn chain_info(&self) -> &ChainInfoOwned { - self.daemon.state.chain_data.as_ref() + self.daemon.chain_info() } } @@ -96,7 +96,7 @@ impl DaemonBase { pub fn rebuild(&self) -> DaemonBuilder { DaemonBuilder { state: Some(self.state()), - chain: self.daemon.sender().chain_info().clone(), + chain: self.daemon.chain_info().clone(), deployment_id: Some(self.daemon.state.deployment_id.clone()), state_path: None, write_on_change: None, From 0d39d824809707388bc241de360cf10914b71f60 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 17:47:44 +0200 Subject: [PATCH 090/108] clippy --- cw-orch-daemon/examples/manual_sender.rs | 6 ++---- cw-orch-daemon/examples/querier-daemon.rs | 2 +- cw-orch-daemon/src/builder.rs | 2 +- cw-orch-daemon/src/core.rs | 2 +- cw-orch-daemon/src/lib.rs | 5 +---- cw-orch-daemon/src/senders/builder.rs | 2 +- cw-orch-daemon/src/senders/cosmos.rs | 11 ++++------- cw-orch-daemon/src/senders/cosmos_batch.rs | 4 ++-- cw-orch-daemon/src/senders/query_only.rs | 2 +- 9 files changed, 14 insertions(+), 22 deletions(-) diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index a77f1c5ff..c107a9508 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -88,7 +88,6 @@ impl SenderBuilder for ManualSender { } impl QuerySender for ManualSender { - fn channel(&self) -> tonic::transport::Channel { self.grpc_channel.clone() } @@ -176,9 +175,8 @@ impl ManualSender { async fn base_account(&self) -> Result { let addr = self.address()?.to_string(); - let mut client = cosmrs::proto::cosmos::auth::v1beta1::query_client::QueryClient::new( - self.channel(), - ); + let mut client = + cosmrs::proto::cosmos::auth::v1beta1::query_client::QueryClient::new(self.channel()); let resp = client .account(cosmrs::proto::cosmos::auth::v1beta1::QueryAccountRequest { address: addr }) diff --git a/cw-orch-daemon/examples/querier-daemon.rs b/cw-orch-daemon/examples/querier-daemon.rs index ebb86909f..b44d45ba8 100644 --- a/cw-orch-daemon/examples/querier-daemon.rs +++ b/cw-orch-daemon/examples/querier-daemon.rs @@ -1,7 +1,7 @@ // ANCHOR: full_counter_example use cw_orch::{anyhow, prelude::*}; -use cw_orch_daemon::senders::no_sender::QueryOnlyDaemon; +use cw_orch_daemon::senders::QueryOnlyDaemon; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L1 pub const LOCAL_JUNO_SENDER: &str = "juno16g2rahf5846rxzp3fwlswy08fz8ccuwk03k57y"; diff --git a/cw-orch-daemon/src/builder.rs b/cw-orch-daemon/src/builder.rs index ab9a6df29..f2de6e60c 100644 --- a/cw-orch-daemon/src/builder.rs +++ b/cw-orch-daemon/src/builder.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::Arc; use crate::{ log::print_if_log_disabled, diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index d78831611..c21477da6 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -26,7 +26,7 @@ use std::{ io::Write, ops::Deref, str::{from_utf8, FromStr}, - sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, + sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, time::Duration, }; diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index b290bd970..d8d54096f 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -24,10 +24,7 @@ mod tx_resp; pub use self::{builder::*, channel::*, core::*, error::*, state::*, sync::*, tx_resp::*}; pub use cw_orch_networks::networks; -pub use senders::{ - CosmosBatchOptions, CosmosBatchSender, CosmosOptions, CosmosSender, QueryOnlyDaemon, - QueryOnlySender, Wallet, -}; +pub use senders::Wallet; pub use tx_builder::TxBuilder; mod cosmos_proto_patches; diff --git a/cw-orch-daemon/src/senders/builder.rs b/cw-orch-daemon/src/senders/builder.rs index aa6d5a6da..bcdf9ea09 100644 --- a/cw-orch-daemon/src/senders/builder.rs +++ b/cw-orch-daemon/src/senders/builder.rs @@ -1,4 +1,4 @@ -use std::{rc::Rc, sync::Arc}; +use std::sync::Arc; use cw_orch_core::environment::ChainInfoOwned; diff --git a/cw-orch-daemon/src/senders/cosmos.rs b/cw-orch-daemon/src/senders/cosmos.rs index 41e6160c2..c74efaf00 100644 --- a/cw-orch-daemon/src/senders/cosmos.rs +++ b/cw-orch-daemon/src/senders/cosmos.rs @@ -6,7 +6,7 @@ use crate::{ account_sequence_strategy, assert_broadcast_code_cosm_response, insufficient_fee_strategy, TxBroadcaster, }, - CosmosOptions, Daemon, GrpcChannel, + CosmosOptions, GrpcChannel, }; use crate::proto::injective::InjectiveEthAccount; @@ -30,23 +30,20 @@ use cosmrs::{ tx::{self, ModeInfo, Msg, Raw, SignDoc, SignMode, SignerInfo}, AccountId, Any, }; -use cosmwasm_std::{coin, Addr, Coin}; +use cosmwasm_std::{coin, Coin}; use cw_orch_core::{ environment::{ChainInfoOwned, ChainKind}, - log::local_target, CoreEnvVars, CwEnvError, }; use crate::env::{LOCAL_MNEMONIC_ENV_NAME, MAIN_MNEMONIC_ENV_NAME, TEST_MNEMONIC_ENV_NAME}; use bitcoin::secp256k1::{All, Secp256k1, Signing}; -use std::{rc::Rc, str::FromStr, sync::Arc}; +use std::{str::FromStr, sync::Arc}; use cosmos_modules::vesting::PeriodicVestingAccount; use tonic::transport::Channel; -use super::{ - builder::SenderBuilder, cosmos_options::CosmosWalletKey, query::QuerySender, tx::TxSender, -}; +use super::{cosmos_options::CosmosWalletKey, query::QuerySender, tx::TxSender}; const GAS_BUFFER: f64 = 1.3; const BUFFER_THRESHOLD: u64 = 200_000; diff --git a/cw-orch-daemon/src/senders/cosmos_batch.rs b/cw-orch-daemon/src/senders/cosmos_batch.rs index 034a11e2b..6181c6b83 100644 --- a/cw-orch-daemon/src/senders/cosmos_batch.rs +++ b/cw-orch-daemon/src/senders/cosmos_batch.rs @@ -11,12 +11,12 @@ use options::CosmosBatchOptions; use prost::Name; use std::mem::take; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use super::builder::SenderBuilder; use super::cosmos::Wallet; use super::query::QuerySender; -use super::{tx::TxSender, CosmosOptions}; +use super::tx::TxSender; pub mod options { use super::super::CosmosOptions; diff --git a/cw-orch-daemon/src/senders/query_only.rs b/cw-orch-daemon/src/senders/query_only.rs index 1d9523116..621d63b1a 100644 --- a/cw-orch-daemon/src/senders/query_only.rs +++ b/cw-orch-daemon/src/senders/query_only.rs @@ -1,4 +1,4 @@ -use std::{rc::Rc, sync::Arc}; +use std::sync::Arc; use crate::{error::DaemonError, DaemonBase, GrpcChannel}; From 910152d322490076eb661e7b290eaddea2f9921c Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 18:03:52 +0200 Subject: [PATCH 091/108] clippy --- cw-orch-daemon/examples/querier-daemon.rs | 4 ++-- cw-orch-daemon/src/core.rs | 1 - cw-orch-daemon/src/lib.rs | 4 +++- cw-orch-daemon/src/senders/cosmos_batch.rs | 4 ++-- cw-orch-daemon/src/senders/cosmos_options.rs | 13 ++++--------- cw-orch-daemon/src/senders/mod.rs | 5 +++-- cw-orch-daemon/src/sync/builder.rs | 4 ++-- cw-orch-daemon/src/sync/core.rs | 5 ++++- cw-orch-daemon/tests/daemon_state.rs | 4 +++- packages/clone-testing/src/state.rs | 4 ++-- 10 files changed, 25 insertions(+), 23 deletions(-) diff --git a/cw-orch-daemon/examples/querier-daemon.rs b/cw-orch-daemon/examples/querier-daemon.rs index b44d45ba8..37390a710 100644 --- a/cw-orch-daemon/examples/querier-daemon.rs +++ b/cw-orch-daemon/examples/querier-daemon.rs @@ -1,7 +1,7 @@ // ANCHOR: full_counter_example use cw_orch::{anyhow, prelude::*}; -use cw_orch_daemon::senders::QueryOnlyDaemon; +use cw_orch_daemon::senders::{QueryOnlyDaemon, QueryOnlySenderOptions}; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L1 pub const LOCAL_JUNO_SENDER: &str = "juno16g2rahf5846rxzp3fwlswy08fz8ccuwk03k57y"; @@ -11,7 +11,7 @@ pub fn main() -> anyhow::Result<()> { let network = networks::LOCAL_JUNO; // There is no need to register a mnemonic to use this daemon querier - let chain: QueryOnlyDaemon = QueryOnlyDaemon::builder(network).build_sender(())?; + let chain: QueryOnlyDaemon = QueryOnlyDaemon::builder(network).build_sender(QueryOnlySenderOptions {})?; let balances = chain.bank_querier().balance(LOCAL_JUNO_SENDER, None)?; assert!(!balances.is_empty()); diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index c21477da6..9bcdcf248 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -21,7 +21,6 @@ use prost::Message; use serde::{de::DeserializeOwned, Serialize}; use serde_json::from_str; use std::{ - borrow::BorrowMut, fmt::Debug, io::Write, ops::Deref, diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index d8d54096f..87899d8c1 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -1,3 +1,5 @@ +// TODO: Figure out better mutex locking for senders +#![allow(clippy::await_holding_lock)] //! `Daemon` and `DaemonAsync` execution environments. //! //! The `Daemon` type is a synchronous wrapper around the `DaemonAsync` type and can be used as a contract execution environment. @@ -24,7 +26,7 @@ mod tx_resp; pub use self::{builder::*, channel::*, core::*, error::*, state::*, sync::*, tx_resp::*}; pub use cw_orch_networks::networks; -pub use senders::Wallet; +pub use senders::{Wallet, CosmosOptions, tx::TxSender, query::QuerySender}; pub use tx_builder::TxBuilder; mod cosmos_proto_patches; diff --git a/cw-orch-daemon/src/senders/cosmos_batch.rs b/cw-orch-daemon/src/senders/cosmos_batch.rs index 6181c6b83..f7351d47e 100644 --- a/cw-orch-daemon/src/senders/cosmos_batch.rs +++ b/cw-orch-daemon/src/senders/cosmos_batch.rs @@ -18,6 +18,8 @@ use super::cosmos::Wallet; use super::query::QuerySender; use super::tx::TxSender; +pub type BatchDaemon = DaemonBase; + pub mod options { use super::super::CosmosOptions; @@ -36,8 +38,6 @@ pub mod options { } } -pub type BatchDaemon = DaemonBase; - /// Signer of Message batch transactions /// This is a wrapper around the `Wallet` struct, with the addition of a `msgs` field that cache messages before they are sent. #[derive(Clone)] diff --git a/cw-orch-daemon/src/senders/cosmos_options.rs b/cw-orch-daemon/src/senders/cosmos_options.rs index fc0937f95..741a6015f 100644 --- a/cw-orch-daemon/src/senders/cosmos_options.rs +++ b/cw-orch-daemon/src/senders/cosmos_options.rs @@ -3,9 +3,9 @@ use std::{str::FromStr, sync::Arc}; use cosmrs::AccountId; use cw_orch_core::environment::ChainInfoOwned; -use crate::{CosmosSender, DaemonError, Wallet}; +use crate::{DaemonError, Wallet}; -use super::builder::SenderBuilder; +use super::{builder::SenderBuilder, CosmosSender}; /// Options for how txs should be constructed for this sender. #[derive(Default, Clone)] @@ -18,19 +18,14 @@ pub struct CosmosOptions { pub(crate) key: CosmosWalletKey, } -#[derive(Clone)] +#[derive(Default, Clone)] pub enum CosmosWalletKey { Mnemonic(String), RawKey(Vec), + #[default] Env, } -impl Default for CosmosWalletKey { - fn default() -> Self { - CosmosWalletKey::Env - } -} - impl CosmosOptions { pub fn check(&self) -> Result<(), DaemonError> { if let Some(addr) = &self.authz_granter { diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index 9fba6bc07..bf0c46f43 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -11,7 +11,8 @@ mod query_only; pub use { cosmos::{CosmosSender, Wallet}, - cosmos_batch::{options::CosmosBatchOptions, CosmosBatchSender}, cosmos_options::{CosmosOptions, CosmosWalletKey}, - query_only::{QueryOnlyDaemon, QueryOnlySender}, + + cosmos_batch::{options::CosmosBatchOptions, CosmosBatchSender, BatchDaemon}, + query_only::{QueryOnlyDaemon, QueryOnlySender, QueryOnlySenderOptions}, }; diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 0902fd6f3..3ecdd51e6 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -248,10 +248,10 @@ mod test { let indexed_daemon: DaemonBase = daemon .rebuild() - .build_sender(daemon.wallet().options().hd_index(56))?; + .build_sender(daemon.sender().options().hd_index(56))?; assert_ne!( - daemon.sender().to_string(), + daemon.sender_addr(), indexed_daemon.sender().to_string() ); diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index cddaf6f35..4a37b4730 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -1,5 +1,4 @@ use std::{ - borrow::BorrowMut, fmt::Debug, ops::DerefMut, sync::{RwLockReadGuard, RwLockWriteGuard}, @@ -108,6 +107,10 @@ impl DaemonBase { // Helpers for Daemon with [`Wallet`] sender. impl Daemon { + pub fn sender_addr(&self) -> Addr { + self.daemon.sender_addr() + } + /// Specifies wether authz should be used with this daemon pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { self.sender_mut().set_authz_granter(granter.to_string()); diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 6a71d8f6d..fe997af3b 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -104,12 +104,14 @@ fn simultaneous_write_rebuilt() { .build() .unwrap(); + let options = daemon.sender().options().clone(); + let mut handles = vec![]; // Note this one has lower iterations since rebuild is pretty long process for i in 0..10 { let daemon: Daemon = daemon .rebuild() - .build_sender(daemon.wallet().options()) + .build_sender(options.clone()) .unwrap(); let mut daemon_state = daemon.state(); let handle = std::thread::spawn(move || { diff --git a/packages/clone-testing/src/state.rs b/packages/clone-testing/src/state.rs index e4e2363b5..5fde618c2 100644 --- a/packages/clone-testing/src/state.rs +++ b/packages/clone-testing/src/state.rs @@ -5,7 +5,7 @@ use cw_orch_core::{ }; use cw_orch_daemon::DaemonState; use itertools::Itertools; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; #[derive(Clone, Debug)] /// Mock state for testing, stores addresses and code-ids. @@ -26,7 +26,7 @@ impl MockState { code_ids: HashMap::new(), daemon_state: DaemonState::new( DaemonState::state_file_path().unwrap(), - chain, + &Arc::new(chain), deployment_id.to_string(), true, false, From 0dc729c1bcad9ecf64e8cede5418af241c713394 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Wed, 3 Jul 2024 18:30:20 +0200 Subject: [PATCH 092/108] update exports and examples --- cw-orch-daemon/examples/batch-sender.rs | 2 +- cw-orch-daemon/examples/daemon-capabilities.rs | 5 ++++- cw-orch-daemon/examples/manual_sender.rs | 2 +- cw-orch-daemon/examples/querier-daemon.rs | 3 ++- cw-orch-daemon/src/lib.rs | 2 +- cw-orch-daemon/src/senders/mod.rs | 3 +-- cw-orch-daemon/src/senders/query_only.rs | 7 ++++--- cw-orch-daemon/src/senders/tx.rs | 6 ++++-- cw-orch-daemon/src/sync/builder.rs | 2 +- cw-orch-daemon/tests/daemon_state.rs | 5 +---- 10 files changed, 20 insertions(+), 17 deletions(-) diff --git a/cw-orch-daemon/examples/batch-sender.rs b/cw-orch-daemon/examples/batch-sender.rs index 1ce446cc2..4e2f89939 100644 --- a/cw-orch-daemon/examples/batch-sender.rs +++ b/cw-orch-daemon/examples/batch-sender.rs @@ -2,7 +2,7 @@ use counter_contract::{ msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, CounterQueryMsgFns, }; -use cw_orch::{anyhow, daemon::senders::cosmos_batch::BatchDaemon, prelude::*}; +use cw_orch::{anyhow, daemon::senders::BatchDaemon, prelude::*}; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; diff --git a/cw-orch-daemon/examples/daemon-capabilities.rs b/cw-orch-daemon/examples/daemon-capabilities.rs index a22b9e450..652e97b7f 100644 --- a/cw-orch-daemon/examples/daemon-capabilities.rs +++ b/cw-orch-daemon/examples/daemon-capabilities.rs @@ -1,3 +1,4 @@ +use std::ops::DerefMut; use std::str::FromStr; use cosmrs::{tx::Msg, AccountId, Coin, Denom}; @@ -19,7 +20,9 @@ pub fn main() -> anyhow::Result<()> { // We commit the tx (also resimulates the tx) // ANCHOR: send_tx - let wallet = daemon.wallet(); + let mut lock = daemon.sender_mut(); + let wallet = lock.deref_mut(); + let rt = daemon.rt_handle.clone(); rt.block_on(wallet.bank_send("", coins(345, "ujunox")))?; // ANCHOR_END: send_tx diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index c107a9508..fa3b3219d 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -7,7 +7,7 @@ use cw_orch_daemon::senders::query::QuerySender; use cw_orch_daemon::tx_broadcaster::assert_broadcast_code_cosm_response; use cw_orch_daemon::{DaemonBase, GrpcChannel, TxBuilder}; -use cw_orch_daemon::{error::DaemonError, tx_resp::CosmTxResponse}; +use cw_orch_daemon::{CosmTxResponse, DaemonError}; use cosmrs::proto::cosmos; use cosmrs::proto::cosmos::auth::v1beta1::BaseAccount; diff --git a/cw-orch-daemon/examples/querier-daemon.rs b/cw-orch-daemon/examples/querier-daemon.rs index 37390a710..c0f4dc598 100644 --- a/cw-orch-daemon/examples/querier-daemon.rs +++ b/cw-orch-daemon/examples/querier-daemon.rs @@ -11,7 +11,8 @@ pub fn main() -> anyhow::Result<()> { let network = networks::LOCAL_JUNO; // There is no need to register a mnemonic to use this daemon querier - let chain: QueryOnlyDaemon = QueryOnlyDaemon::builder(network).build_sender(QueryOnlySenderOptions {})?; + let chain: QueryOnlyDaemon = + QueryOnlyDaemon::builder(network).build_sender(QueryOnlySenderOptions {})?; let balances = chain.bank_querier().balance(LOCAL_JUNO_SENDER, None)?; assert!(!balances.is_empty()); diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index 87899d8c1..4f5bffcb3 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -26,7 +26,7 @@ mod tx_resp; pub use self::{builder::*, channel::*, core::*, error::*, state::*, sync::*, tx_resp::*}; pub use cw_orch_networks::networks; -pub use senders::{Wallet, CosmosOptions, tx::TxSender, query::QuerySender}; +pub use senders::{query::QuerySender, tx::TxSender, CosmosOptions, Wallet}; pub use tx_builder::TxBuilder; mod cosmos_proto_patches; diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index bf0c46f43..2b14ed3bb 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -11,8 +11,7 @@ mod query_only; pub use { cosmos::{CosmosSender, Wallet}, + cosmos_batch::{options::CosmosBatchOptions, BatchDaemon, CosmosBatchSender}, cosmos_options::{CosmosOptions, CosmosWalletKey}, - - cosmos_batch::{options::CosmosBatchOptions, CosmosBatchSender, BatchDaemon}, query_only::{QueryOnlyDaemon, QueryOnlySender, QueryOnlySenderOptions}, }; diff --git a/cw-orch-daemon/src/senders/query_only.rs b/cw-orch-daemon/src/senders/query_only.rs index 621d63b1a..ad5cf5c0e 100644 --- a/cw-orch-daemon/src/senders/query_only.rs +++ b/cw-orch-daemon/src/senders/query_only.rs @@ -50,13 +50,14 @@ impl QuerySender for QueryOnlySender { mod tests { use cw_orch_networks::networks::JUNO_1; - use super::QueryOnlyDaemon; + use super::{QueryOnlyDaemon, QueryOnlySenderOptions}; use crate::DaemonBuilder; #[test] #[serial_test::serial] fn build() { - let _query_only_daemon: QueryOnlyDaemon = - DaemonBuilder::new(JUNO_1).build_sender(()).unwrap(); + let _query_only_daemon: QueryOnlyDaemon = DaemonBuilder::new(JUNO_1) + .build_sender(QueryOnlySenderOptions {}) + .unwrap(); } } diff --git a/cw-orch-daemon/src/senders/tx.rs b/cw-orch-daemon/src/senders/tx.rs index 15fcc7196..f7d74933e 100644 --- a/cw-orch-daemon/src/senders/tx.rs +++ b/cw-orch-daemon/src/senders/tx.rs @@ -1,7 +1,9 @@ -use cosmrs::{tx::Msg, AccountId, Any}; +use std::{future::Future, str::FromStr}; + +use cosmrs::{bank::MsgSend, tx::Msg, AccountId, Any}; use cosmwasm_std::Addr; -use crate::CosmTxResponse; +use crate::{parse_cw_coins, CosmTxResponse, DaemonError}; use super::query::QuerySender; diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 3ecdd51e6..da7824b00 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -252,7 +252,7 @@ mod test { assert_ne!( daemon.sender_addr(), - indexed_daemon.sender().to_string() + indexed_daemon.sender_addr().to_string() ); Ok(()) diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index fe997af3b..275dffd18 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -109,10 +109,7 @@ fn simultaneous_write_rebuilt() { let mut handles = vec![]; // Note this one has lower iterations since rebuild is pretty long process for i in 0..10 { - let daemon: Daemon = daemon - .rebuild() - .build_sender(options.clone()) - .unwrap(); + let daemon: Daemon = daemon.rebuild().build_sender(options.clone()).unwrap(); let mut daemon_state = daemon.state(); let handle = std::thread::spawn(move || { if let DaemonStateFile::FullAccess { json_file_state } = &daemon_state.json_state { From 120cd67d6d5051b8ebc1a25d3dde67c757e51ed4 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 13:33:00 +0200 Subject: [PATCH 093/108] don't use Arc RwLock for sender --- cw-orch-daemon/src/core.rs | 45 +++++++++++++++------------------ cw-orch-daemon/src/sync/core.rs | 12 +++------ 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 9bcdcf248..f336a422b 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -25,7 +25,6 @@ use std::{ io::Write, ops::Deref, str::{from_utf8, FromStr}, - sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, time::Duration, }; @@ -63,12 +62,11 @@ pub const INSTANTIATE_2_TYPE_URL: &str = "/cosmwasm.wasm.v1.MsgInstantiateContra This daemon is thread safe and can be used between threads. However, please make sure that you are not trying to broadcast multiple transactions at once when using this Daemon on different threads. - If you do so, you WILL get account sequence errors and your transactions won't get broadcasted. - Use a Mutex on top of this DaemonAsync to avoid such errors. + If you do so, you will get account sequence errors and your transactions won't get broadcasted. */ pub struct DaemonAsyncBase { /// Sender to send transactions to the chain - sender: Arc>, + sender: Sender, /// State of the daemon pub(crate) state: DaemonState, } @@ -77,10 +75,7 @@ pub type DaemonAsync = DaemonAsyncBase; impl DaemonAsyncBase { pub(crate) fn new(sender: Sender, state: DaemonState) -> Self { - Self { - sender: Arc::new(RwLock::new(sender)), - state, - } + Self { sender, state } } pub fn chain_info(&self) -> &ChainInfoOwned { @@ -92,14 +87,22 @@ impl DaemonAsyncBase { DaemonAsyncBuilder::new(chain) } + /// Set the Sender for use with this Daemon + pub fn set_sender(self, sender: NewSender) -> DaemonAsyncBase { + DaemonAsyncBase { + sender, + state: self.state, + } + } + /// Get a mutable Sender - pub fn sender_mut(&self) -> RwLockWriteGuard { - self.sender.write().unwrap() + pub fn sender_mut(&mut self) -> &mut Sender { + &mut self.sender } // Get a read-only Sender - pub fn sender(&self) -> RwLockReadGuard { - self.sender.read().unwrap() + pub fn sender(&self) -> &Sender { + &self.sender } /// Flushes all the state related to the current chain @@ -224,7 +227,7 @@ impl DaemonAsyncBase { funds: parse_cw_coins(coins)?, }; let result = self - .sender_mut() + .sender() .commit_tx(vec![exec_msg], None) .await .map_err(Into::into)?; @@ -252,7 +255,7 @@ impl DaemonAsyncBase { }; let result = self - .sender_mut() + .sender() .commit_tx(vec![init_msg], None) .await .map_err(Into::into)?; @@ -284,7 +287,7 @@ impl DaemonAsyncBase { }; let result = self - .sender_mut() + .sender() .commit_tx_any( vec![Any { type_url: INSTANTIATE_2_TYPE_URL.to_string(), @@ -314,7 +317,7 @@ impl DaemonAsyncBase { code_id: new_code_id, }; let result = self - .sender_mut() + .sender() .commit_tx(vec![exec_msg], None) .await .map_err(Into::into)?; @@ -341,7 +344,7 @@ impl DaemonAsyncBase { }; let result = self - .sender_mut() + .sender() .commit_tx(vec![store_msg], None) .await .map_err(Into::into)?; @@ -357,14 +360,6 @@ impl DaemonAsyncBase { } Ok(result) } - - /// Set the sender to use with this DaemonAsync to be the given wallet - pub fn set_sender(self, sender: NewSender) -> DaemonAsyncBase { - DaemonAsyncBase { - sender: Arc::new(RwLock::new(sender)), - state: self.state, - } - } } impl Querier for DaemonAsync { diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 4a37b4730..dd5900e73 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -1,8 +1,4 @@ -use std::{ - fmt::Debug, - ops::DerefMut, - sync::{RwLockReadGuard, RwLockWriteGuard}, -}; +use std::{fmt::Debug, ops::DerefMut}; use super::super::senders::Wallet; use crate::{ @@ -62,12 +58,12 @@ impl DaemonBase { } /// Get the mutable Sender object - pub fn sender_mut(&self) -> RwLockWriteGuard { + pub fn sender_mut(&mut self) -> &mut Sender { self.daemon.sender_mut() } /// Get the channel configured for this Daemon - pub fn sender(&self) -> RwLockReadGuard { + pub fn sender(&self) -> &Sender { self.daemon.sender() } @@ -212,7 +208,7 @@ impl Stargate for DaemonBase { ) -> Result { self.rt_handle .block_on( - self.sender_mut().commit_tx_any( + self.sender().commit_tx_any( msgs.iter() .map(|msg| cosmrs::Any { type_url: msg.type_url.clone(), From c8f9a89a3af3f09299a74225fc412a8d1006ff42 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 13:49:24 +0200 Subject: [PATCH 094/108] rename `set_sender` to `new_sender` to prevent conflict with `TxHandler` trait --- cw-orch-daemon/src/core.rs | 16 ++++++++++++++-- cw-orch-daemon/src/sync/core.rs | 19 ++++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index f336a422b..34b34f161 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -1,4 +1,8 @@ -use crate::{queriers::CosmWasm, senders::query::QuerySender, DaemonAsyncBuilder, DaemonState}; +use crate::{ + queriers::CosmWasm, + senders::{builder::SenderBuilder, query::QuerySender}, + DaemonAsyncBuilder, DaemonState, +}; use super::{ cosmos_modules, error::DaemonError, queriers::Node, senders::Wallet, tx_resp::CosmTxResponse, @@ -88,7 +92,15 @@ impl DaemonAsyncBase { } /// Set the Sender for use with this Daemon - pub fn set_sender(self, sender: NewSender) -> DaemonAsyncBase { + /// The sender will be configured with the chain's data. + pub async fn new_sender( + self, + sender_options: T, + ) -> DaemonAsyncBase { + let sender = sender_options + .build(&self.state.chain_data) + .await + .expect("Failed to build sender"); DaemonAsyncBase { sender, state: self.state, diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index dd5900e73..8cc9fc545 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -3,7 +3,7 @@ use std::{fmt::Debug, ops::DerefMut}; use super::super::senders::Wallet; use crate::{ queriers::{Bank, CosmWasmBase, Node}, - senders::query::QuerySender, + senders::{builder::SenderBuilder, query::QuerySender}, CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonError, DaemonState, }; use cosmwasm_std::{Addr, Coin}; @@ -67,6 +67,21 @@ impl DaemonBase { self.daemon.sender() } + /// Set the Sender for use with this Daemon + /// The sender will be configured with the chain's data. + pub fn new_sender( + self, + sender_options: T, + ) -> DaemonBase<::Sender> { + let new_daemon = self + .rt_handle + .block_on(self.daemon.new_sender(sender_options)); + DaemonBase { + daemon: new_daemon, + rt_handle: self.rt_handle.clone(), + } + } + /// Flushes all the state related to the current chain /// Only works on Local networks pub fn flush_state(&mut self) -> Result<(), DaemonError> { @@ -139,6 +154,8 @@ impl TxHandler for DaemonBase { self.daemon.sender_addr() } + /// Overwrite the sender manually, could result in unexpected behavior. + /// Use native [`Daemon::new_sender`] instead! fn set_sender(&mut self, sender: Self::Sender) { let mut daemon_sender = self.daemon.sender_mut(); (*daemon_sender.deref_mut()) = sender; From e08dbcae65f5f04f3586495108b949fb35a96de2 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 13:49:39 +0200 Subject: [PATCH 095/108] nits on senders --- cw-orch-daemon/src/senders/cosmos.rs | 9 +++------ cw-orch-daemon/src/senders/cosmos_batch.rs | 19 +++++++++++-------- cw-orch-daemon/src/senders/tx.rs | 10 ++++------ 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/cw-orch-daemon/src/senders/cosmos.rs b/cw-orch-daemon/src/senders/cosmos.rs index c74efaf00..256a679e4 100644 --- a/cw-orch-daemon/src/senders/cosmos.rs +++ b/cw-orch-daemon/src/senders/cosmos.rs @@ -63,8 +63,6 @@ pub struct CosmosSender { pub chain_info: Arc, pub(crate) options: CosmosOptions, pub secp: Secp256k1, - // Private field to ensure the struct is not constructible outside of this module - _private: (), } impl Wallet { @@ -111,7 +109,6 @@ impl Wallet { private_key: pk, secp, options, - _private: (), }) } @@ -164,7 +161,7 @@ impl Wallet { } pub async fn bank_send( - &mut self, + &self, recipient: &str, coins: Vec, ) -> Result { @@ -239,7 +236,7 @@ impl Wallet { } pub async fn commit_tx( - &mut self, + &self, msgs: Vec, memo: Option<&str>, ) -> Result { @@ -401,7 +398,7 @@ impl QuerySender for Wallet { impl TxSender for Wallet { async fn commit_tx_any( - &mut self, + &self, msgs: Vec, memo: Option<&str>, ) -> Result { diff --git a/cw-orch-daemon/src/senders/cosmos_batch.rs b/cw-orch-daemon/src/senders/cosmos_batch.rs index f7351d47e..d939ff20c 100644 --- a/cw-orch-daemon/src/senders/cosmos_batch.rs +++ b/cw-orch-daemon/src/senders/cosmos_batch.rs @@ -10,8 +10,7 @@ use cw_orch_core::log::transaction_target; use options::CosmosBatchOptions; use prost::Name; -use std::mem::take; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use super::builder::SenderBuilder; use super::cosmos::Wallet; @@ -23,6 +22,7 @@ pub type BatchDaemon = DaemonBase; pub mod options { use super::super::CosmosOptions; + #[derive(Clone, Default)] pub struct CosmosBatchOptions(pub(crate) CosmosOptions); impl From for CosmosBatchOptions { @@ -43,15 +43,14 @@ pub mod options { #[derive(Clone)] pub struct CosmosBatchSender { /// Contains the different messages to broadcast - pub msgs: Vec, + pub msgs: Arc>>, pub sender: Wallet, } impl CosmosBatchSender { /// Broadcast the cached messages in a transaction. - pub async fn broadcast(&mut self, memo: Option<&str>) -> Result { - let msgs = take(&mut self.msgs); - + pub async fn broadcast(&self, memo: Option<&str>) -> Result { + let msgs = self.msgs.lock().unwrap().to_vec(); log::info!( target: &transaction_target(), "[Broadcast] {} msgs in a single transaction", @@ -64,6 +63,9 @@ impl CosmosBatchSender { tx_result.txhash ); + let mut msgs_to_empty = self.msgs.lock().unwrap(); + *msgs_to_empty = vec![]; + Ok(tx_result) } } @@ -91,7 +93,7 @@ impl QuerySender for CosmosBatchSender { impl TxSender for CosmosBatchSender { async fn commit_tx_any( - &mut self, + &self, msgs: Vec, memo: Option<&str>, ) -> Result { @@ -116,7 +118,8 @@ impl TxSender for CosmosBatchSender { target: &transaction_target(), "Transaction not sent, use `DaemonBase::wallet().broadcast(), to broadcast the batched transactions", ); - self.msgs.extend(msgs); + let mut msg_storage = self.msgs.lock().unwrap(); + msg_storage.extend(msgs); Ok(CosmTxResponse::default()) } diff --git a/cw-orch-daemon/src/senders/tx.rs b/cw-orch-daemon/src/senders/tx.rs index f7d74933e..619d78ff9 100644 --- a/cw-orch-daemon/src/senders/tx.rs +++ b/cw-orch-daemon/src/senders/tx.rs @@ -1,9 +1,7 @@ -use std::{future::Future, str::FromStr}; - -use cosmrs::{bank::MsgSend, tx::Msg, AccountId, Any}; +use cosmrs::{tx::Msg, AccountId, Any}; use cosmwasm_std::Addr; -use crate::{parse_cw_coins, CosmTxResponse, DaemonError}; +use crate::CosmTxResponse; use super::query::QuerySender; @@ -13,7 +11,7 @@ pub trait TxSender: QuerySender { /// Commit a proto `Any` message to the chain using this sender. fn commit_tx_any( - &mut self, + &self, msgs: Vec, memo: Option<&str>, ) -> impl std::future::Future> + Send; @@ -25,7 +23,7 @@ pub trait TxSender: QuerySender { /// Commit a transaction to the chain using this sender. fn commit_tx( - &mut self, + &self, msgs: Vec, memo: Option<&str>, ) -> impl std::future::Future> + Send { From 4acf937f8a69248d6fed074e07de816c43166f72 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 13:49:46 +0200 Subject: [PATCH 096/108] fix examples --- cw-orch-daemon/examples/batch-sender.rs | 8 ++-- .../examples/daemon-capabilities.rs | 3 +- cw-orch-daemon/examples/manual_sender.rs | 41 ++++++++----------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/cw-orch-daemon/examples/batch-sender.rs b/cw-orch-daemon/examples/batch-sender.rs index 4e2f89939..0e166f9d9 100644 --- a/cw-orch-daemon/examples/batch-sender.rs +++ b/cw-orch-daemon/examples/batch-sender.rs @@ -3,6 +3,7 @@ use counter_contract::{ msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, CounterQueryMsgFns, }; use cw_orch::{anyhow, daemon::senders::BatchDaemon, prelude::*}; +use cw_orch_daemon::{senders::CosmosBatchOptions, CosmosOptions}; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; @@ -12,7 +13,8 @@ pub fn main() -> anyhow::Result<()> { pretty_env_logger::init(); // Used to log contract and chain interactions let network = networks::LOCAL_JUNO; - let chain: BatchDaemon = BatchDaemon::builder(network).build_sender(Default::default())?; + let chain: BatchDaemon = + BatchDaemon::builder(network).build_sender(CosmosBatchOptions::default())?; let counter = CounterContract::new(chain.clone()); @@ -25,7 +27,7 @@ pub fn main() -> anyhow::Result<()> { let count = counter.get_count()?; assert_eq!(count.count, 0); - chain.rt_handle.block_on(chain.wallet().broadcast(None))?; + chain.rt_handle.block_on(chain.sender().broadcast(None))?; let count = counter.get_count()?; assert_eq!(count.count, 1); @@ -38,7 +40,7 @@ pub fn main() -> anyhow::Result<()> { counter.increment()?; counter.increment()?; - chain.rt_handle.block_on(chain.wallet().broadcast(None))?; + chain.rt_handle.block_on(chain.sender().broadcast(None))?; let count = counter.get_count()?; assert_eq!(count.count, 7); diff --git a/cw-orch-daemon/examples/daemon-capabilities.rs b/cw-orch-daemon/examples/daemon-capabilities.rs index 652e97b7f..de8499261 100644 --- a/cw-orch-daemon/examples/daemon-capabilities.rs +++ b/cw-orch-daemon/examples/daemon-capabilities.rs @@ -20,8 +20,7 @@ pub fn main() -> anyhow::Result<()> { // We commit the tx (also resimulates the tx) // ANCHOR: send_tx - let mut lock = daemon.sender_mut(); - let wallet = lock.deref_mut(); + let wallet = daemon.sender(); let rt = daemon.rt_handle.clone(); rt.block_on(wallet.bank_send("", coins(345, "ujunox")))?; diff --git a/cw-orch-daemon/examples/manual_sender.rs b/cw-orch-daemon/examples/manual_sender.rs index fa3b3219d..9c9b4fdc4 100644 --- a/cw-orch-daemon/examples/manual_sender.rs +++ b/cw-orch-daemon/examples/manual_sender.rs @@ -20,6 +20,7 @@ use cw_orch::prelude::*; use cw_orch_core::environment::ChainInfoOwned; use prost::Message; use std::io::{self, Write}; +use std::sync::Arc; use tonic::transport::Channel; // ANCHOR: full_counter_example @@ -34,7 +35,7 @@ pub fn main() -> anyhow::Result<()> { let network = cw_orch_networks::networks::JUNO_1; let sender = "juno1xjf5xscdk08c5es2m7epmerrpqmkmc3n98650t"; let chain: ManualDaemon = ManualDaemon::builder(network).build_sender(ManualSenderOptions { - sender_address: Some(sender.to_string()), + sender_address: sender.to_string(), })?; let counter = CounterContract::new(chain.clone()); @@ -54,7 +55,7 @@ pub type ManualDaemon = DaemonBase; #[derive(Clone, Default)] pub struct ManualSenderOptions { - pub sender_address: Option, + pub sender_address: String, } /// Signer of the transactions and helper for address derivation @@ -62,32 +63,28 @@ pub struct ManualSenderOptions { #[derive(Clone)] pub struct ManualSender { pub sender: Addr, - pub chain_info: ChainInfoOwned, + pub chain_info: Arc, pub grpc_channel: Channel, } -impl SenderBuilder for ManualSender { +impl SenderBuilder for ManualSenderOptions { type Error = DaemonError; - type Options = ManualSenderOptions; + type Sender = ManualSender; - async fn build( - chain_info: cw_orch_core::environment::ChainInfoOwned, - sender_options: Self::Options, - ) -> Result { - let grpc_channel = GrpcChannel::from_chain_info(chain_info.clone()).await?; - Ok(Self { - chain_info, - sender: Addr::unchecked( - sender_options - .sender_address - .expect("Manual sender needs an address"), - ), + async fn build(&self, chain_info: &Arc) -> Result { + let grpc_channel = GrpcChannel::from_chain_info(chain_info.as_ref()).await?; + Ok(ManualSender { + chain_info: chain_info.clone(), + sender: Addr::unchecked(self.sender_address.clone()), grpc_channel, }) } } impl QuerySender for ManualSender { + type Error = DaemonError; + type Options = ManualSenderOptions; + fn channel(&self) -> tonic::transport::Channel { self.grpc_channel.clone() } @@ -122,12 +119,8 @@ impl TxSender for ManualSender { assert_broadcast_code_cosm_response(resp) } - fn address(&self) -> Result { - Ok(self.sender.clone()) - } - - fn account_id(&self) -> Result { - self.sender.clone().to_string().parse().map_err(Into::into) + fn account_id(&self) -> AccountId { + self.sender.clone().to_string().parse().unwrap() } } @@ -173,7 +166,7 @@ impl ManualSender { } async fn base_account(&self) -> Result { - let addr = self.address()?.to_string(); + let addr = self.address().to_string(); let mut client = cosmrs::proto::cosmos::auth::v1beta1::query_client::QueryClient::new(self.channel()); From 96cb0529ff1885f62cfde580613462a38ac0450d Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 14:00:25 +0200 Subject: [PATCH 097/108] add `sender_addr` function to `TxHandler` and deprecate `.sender` --- .../cw-orch-core/src/environment/cosmwasm_environment.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/cw-orch-core/src/environment/cosmwasm_environment.rs b/packages/cw-orch-core/src/environment/cosmwasm_environment.rs index e2e6b5080..dd557cc10 100644 --- a/packages/cw-orch-core/src/environment/cosmwasm_environment.rs +++ b/packages/cw-orch-core/src/environment/cosmwasm_environment.rs @@ -26,8 +26,16 @@ pub trait TxHandler: ChainState + Clone { type Sender: Clone; /// Gets the address of the current wallet used to sign transactions. + #[deprecated( + since = "1.1.2", + note = "Please use `sender_addr` instead. This method will be changed in the next release." + )] + // TODO: return &Self::Sender here in the next breaking release fn sender(&self) -> Addr; + /// Gets the address of the current wallet used to sign transactions. + fn sender_addr(&self) -> Addr; + /// Sets wallet to sign transactions. fn set_sender(&mut self, sender: Self::Sender); From 652def8a61f2874e3a9a54a44b756e55eec242f1 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 14:00:36 +0200 Subject: [PATCH 098/108] update tests --- cw-orch-daemon/tests/authz.rs | 8 ++++---- cw-orch-daemon/tests/index.rs | 4 ++-- cw-orch-daemon/tests/querier.rs | 2 +- cw-orch-interchain/examples/timeout_packet.rs | 4 ++-- cw-orch-interchain/tests/common/bank.rs | 2 +- cw-orch-interchain/tests/timeout_packet_mock.rs | 4 ++-- cw-orch/examples/complex_testnet_daemon.rs | 2 +- packages/interchain/proto/src/ics20.rs | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index 0a0338fc1..ee9296ee3 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -30,7 +30,7 @@ mod tests { let daemon = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); - let sender = daemon.sender().to_string(); + let sender = daemon.sender().address().to_string(); let second_daemon: Daemon = daemon .rebuild() @@ -43,7 +43,7 @@ mod tests { let runtime = daemon.rt_handle.clone(); - let grantee = second_daemon.sender().to_string(); + let grantee = second_daemon.sender().address().to_string(); let current_timestamp = daemon.block_info()?.time; @@ -118,14 +118,14 @@ mod tests { // The we send some funds to the account runtime.block_on( daemon - .wallet() + .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 - .wallet() + .sender() .bank_send(&grantee, coins(5_000_000, LOCAL_JUNO.gas_denom)), )?; diff --git a/cw-orch-daemon/tests/index.rs b/cw-orch-daemon/tests/index.rs index c9ed26ef5..42f8f4c8d 100644 --- a/cw-orch-daemon/tests/index.rs +++ b/cw-orch-daemon/tests/index.rs @@ -12,13 +12,13 @@ mod tests { let daemon = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); - let daemon_sender = daemon.sender().to_string(); + let daemon_sender = daemon.sender_addr().to_string(); let indexed_daemon: Daemon = daemon .rebuild() .build_sender(CosmosOptions::default().hd_index(56)) .unwrap(); - assert_ne!(daemon_sender, indexed_daemon.sender().to_string()); + assert_ne!(daemon_sender, indexed_daemon.sender_addr().to_string()); Ok(()) } diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index d51f82669..188e0ae43 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -225,7 +225,7 @@ mod queriers { contract.upload().unwrap(); contract - .instantiate(&InstantiateMsg {}, Some(&sender), None) + .instantiate(&InstantiateMsg {}, Some(&sender.address()), None) .unwrap(); let contract_address = contract.address().unwrap(); diff --git a/cw-orch-interchain/examples/timeout_packet.rs b/cw-orch-interchain/examples/timeout_packet.rs index 95bd394da..a4246afd4 100644 --- a/cw-orch-interchain/examples/timeout_packet.rs +++ b/cw-orch-interchain/examples/timeout_packet.rs @@ -49,8 +49,8 @@ fn main() -> cw_orch::anyhow::Result<()> { amount: "100_000".to_string(), denom: "ujuno".to_string(), }), - sender: juno.sender().to_string(), - receiver: stargaze.sender().to_string(), + sender: juno.sender_addr().to_string(), + receiver: stargaze.sender_addr().to_string(), timeout_height: Some(Height { revision_number: 1, revision_height: stargaze_height.height, diff --git a/cw-orch-interchain/tests/common/bank.rs b/cw-orch-interchain/tests/common/bank.rs index 6c1885b13..1c83e119e 100644 --- a/cw-orch-interchain/tests/common/bank.rs +++ b/cw-orch-interchain/tests/common/bank.rs @@ -37,6 +37,6 @@ impl BankModule for Daemon { funds: Vec, ) -> Result<::Response, ::Error> { self.rt_handle - .block_on(self.wallet().bank_send(recipient.as_ref(), funds)) + .block_on(self.sender().bank_send(recipient.as_ref(), funds)) } } diff --git a/cw-orch-interchain/tests/timeout_packet_mock.rs b/cw-orch-interchain/tests/timeout_packet_mock.rs index 81ed310ad..2da3055ba 100644 --- a/cw-orch-interchain/tests/timeout_packet_mock.rs +++ b/cw-orch-interchain/tests/timeout_packet_mock.rs @@ -29,12 +29,12 @@ fn timeout_packet_mock() -> cw_orch::anyhow::Result<()> { .interchain_channel .get_ordered_ports_from("juno-1")?; - juno.add_balance(juno.sender().to_string(), vec![coin(100_000, "ujuno")])?; + juno.add_balance(juno.sender_addr().to_string(), vec![coin(100_000, "ujuno")])?; let tx_resp = juno.app.borrow_mut().execute( juno.sender(), CosmosMsg::Ibc(IbcMsg::Transfer { channel_id: channel.0.channel.unwrap().to_string(), - to_address: stargaze.sender().to_string(), + to_address: stargaze.sender_addr().to_string(), amount: coin(100_000, "ujuno"), timeout: IbcTimeout::with_block(IbcTimeoutBlock { revision: 1, diff --git a/cw-orch/examples/complex_testnet_daemon.rs b/cw-orch/examples/complex_testnet_daemon.rs index 8d9e01508..9dd9cca39 100644 --- a/cw-orch/examples/complex_testnet_daemon.rs +++ b/cw-orch/examples/complex_testnet_daemon.rs @@ -63,7 +63,7 @@ pub fn main() { let query_res = counter.get_count(); assert!(query_res.is_ok()); - let sender_addr = daemon.sender().to_string(); + let sender_addr = daemon.sender_addr().to_string(); // We create a denom daemon .commit_any::( diff --git a/packages/interchain/proto/src/ics20.rs b/packages/interchain/proto/src/ics20.rs index c1265f5fd..c93fc112a 100644 --- a/packages/interchain/proto/src/ics20.rs +++ b/packages/interchain/proto/src/ics20.rs @@ -165,7 +165,7 @@ mod test { )> { let chain1 = interchain.chain(chain_id1).unwrap(); - let sender = chain1.sender().to_string(); + let sender = chain1.sender_addr().to_string(); let token_subdenom = format!( "{}{}", From 927800b553fc48fd0f1316592ea0382e730da61e Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 14:10:09 +0200 Subject: [PATCH 099/108] implement `sender_addr` for environments --- contracts/counter/tests/osmosis-test-tube.rs | 2 +- cw-orch-daemon/src/sync/core.rs | 2 +- cw-orch-daemon/tests/authz.rs | 4 ++-- cw-orch-daemon/tests/daemon_helpers.rs | 4 ++-- cw-orch-daemon/tests/querier.rs | 1 + cw-orch/examples/complex_testnet_daemon.rs | 4 ++-- cw-orch/examples/injective.rs | 2 +- cw-orch/examples/local_daemon.rs | 2 +- cw-orch/examples/testnet_daemon.rs | 2 +- packages/clone-testing/src/core.rs | 2 +- .../src/environment/cosmwasm_environment.rs | 11 ++++++++--- packages/cw-orch-mock/src/core.rs | 2 +- packages/cw-orch-osmosis-test-tube/src/core.rs | 2 +- packages/interchain/proto/src/ics20.rs | 4 ++-- 14 files changed, 25 insertions(+), 19 deletions(-) diff --git a/contracts/counter/tests/osmosis-test-tube.rs b/contracts/counter/tests/osmosis-test-tube.rs index cfac902db..4fefcf84c 100644 --- a/contracts/counter/tests/osmosis-test-tube.rs +++ b/contracts/counter/tests/osmosis-test-tube.rs @@ -28,7 +28,7 @@ fn setup(chain: Chain) -> CounterContract { // Instantiate the contract let msg = InstantiateMsg { count: 1i32 }; let init_resp = contract - .instantiate(&msg, Some(&chain.sender()), None) + .instantiate(&msg, Some(&chain.sender_addr()), None) .unwrap(); // Get the address from the response diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index 8cc9fc545..a2f153bbe 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -150,7 +150,7 @@ impl TxHandler for DaemonBase { type ContractSource = WasmPath; type Sender = Sender; - fn sender(&self) -> Addr { + fn sender_addr(&self) -> Addr { self.daemon.sender_addr() } diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index ee9296ee3..8c4fbc861 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -30,7 +30,7 @@ mod tests { let daemon = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); - let sender = daemon.sender().address().to_string(); + let sender = daemon.sender_addr().to_string(); let second_daemon: Daemon = daemon .rebuild() @@ -43,7 +43,7 @@ mod tests { let runtime = daemon.rt_handle.clone(); - let grantee = second_daemon.sender().address().to_string(); + let grantee = second_daemon.sender_addr().to_string(); let current_timestamp = daemon.block_info()?.time; diff --git a/cw-orch-daemon/tests/daemon_helpers.rs b/cw-orch-daemon/tests/daemon_helpers.rs index 1f7075001..e4f383d20 100644 --- a/cw-orch-daemon/tests/daemon_helpers.rs +++ b/cw-orch-daemon/tests/daemon_helpers.rs @@ -24,7 +24,7 @@ mod tests { daemon.flush_state().unwrap(); - let sender = daemon.sender(); + let sender = daemon.sender_addr(); let contract = mock_contract::MockContract::new( format!("test:mock_contract:{}", Id::new()), @@ -117,7 +117,7 @@ mod tests { let daemon = Daemon::builder(networks::LOCAL_JUNO).build().unwrap(); - let sender = daemon.sender(); + let sender = daemon.sender_addr(); let contract = mock_contract::MockContract::new( format!("test:mock_contract:{}", Id::new()), diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index 188e0ae43..69e120dba 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -208,6 +208,7 @@ mod queriers { #[serial_test::serial] fn contract_info() { use crate::common::Id; + use cw_orch_daemon::TxSender; use cw_orch_networks::networks; let rt = Runtime::new().unwrap(); diff --git a/cw-orch/examples/complex_testnet_daemon.rs b/cw-orch/examples/complex_testnet_daemon.rs index 9dd9cca39..edd05baec 100644 --- a/cw-orch/examples/complex_testnet_daemon.rs +++ b/cw-orch/examples/complex_testnet_daemon.rs @@ -44,7 +44,7 @@ pub fn main() { let init_res = counter.instantiate( &InstantiateMsg { count: 0 }, - Some(&counter.environment().sender()), + Some(&counter.environment().sender_addr()), None, ); assert!(init_res.is_ok()); @@ -103,7 +103,7 @@ pub fn main() { .rt_handle .block_on( daemon - .wallet() + .sender() .bank_send(&contract_addr, coins(50_000, denom.clone())), ) .unwrap(); diff --git a/cw-orch/examples/injective.rs b/cw-orch/examples/injective.rs index e022e5ef3..232492d89 100644 --- a/cw-orch/examples/injective.rs +++ b/cw-orch/examples/injective.rs @@ -33,7 +33,7 @@ pub fn main() { let init_res = counter.instantiate( &InstantiateMsg { count: 0 }, - Some(&counter.environment().sender()), + Some(&counter.environment().sender_addr()), None, ); assert!(init_res.is_ok()); diff --git a/cw-orch/examples/local_daemon.rs b/cw-orch/examples/local_daemon.rs index a20d59e21..166e307f4 100644 --- a/cw-orch/examples/local_daemon.rs +++ b/cw-orch/examples/local_daemon.rs @@ -33,7 +33,7 @@ pub fn main() { let init_res = counter.instantiate( &InstantiateMsg { count: 0 }, - Some(&counter.environment().sender()), + Some(&counter.environment().sender_addr()), None, ); assert!(init_res.is_ok()); diff --git a/cw-orch/examples/testnet_daemon.rs b/cw-orch/examples/testnet_daemon.rs index b28a4e093..efe2e1b3e 100644 --- a/cw-orch/examples/testnet_daemon.rs +++ b/cw-orch/examples/testnet_daemon.rs @@ -35,7 +35,7 @@ pub fn main() { let init_res = counter.instantiate( &InstantiateMsg { count: 0 }, - Some(&counter.environment().sender()), + Some(&counter.environment().sender_addr()), None, ); assert!(init_res.is_ok()); diff --git a/packages/clone-testing/src/core.rs b/packages/clone-testing/src/core.rs index 8da6420cd..23c57c1b0 100644 --- a/packages/clone-testing/src/core.rs +++ b/packages/clone-testing/src/core.rs @@ -240,7 +240,7 @@ impl TxHandler for CloneTesting { type ContractSource = Box>; type Sender = Addr; - fn sender(&self) -> Addr { + fn sender_addr(&self) -> Addr { self.sender.clone() } diff --git a/packages/cw-orch-core/src/environment/cosmwasm_environment.rs b/packages/cw-orch-core/src/environment/cosmwasm_environment.rs index dd557cc10..355ae198e 100644 --- a/packages/cw-orch-core/src/environment/cosmwasm_environment.rs +++ b/packages/cw-orch-core/src/environment/cosmwasm_environment.rs @@ -31,10 +31,15 @@ pub trait TxHandler: ChainState + Clone { note = "Please use `sender_addr` instead. This method will be changed in the next release." )] // TODO: return &Self::Sender here in the next breaking release - fn sender(&self) -> Addr; + fn sender(&self) -> Addr { + self.sender_addr() + } /// Gets the address of the current wallet used to sign transactions. - fn sender_addr(&self) -> Addr; + fn sender_addr(&self) -> Addr { + // TODO: remove this default implementation in the next breaking release + unimplemented!("implement `sender_addr` for your chain handler"); + } /// Sets wallet to sign transactions. fn set_sender(&mut self, sender: Self::Sender); @@ -144,7 +149,7 @@ mod tests { type Sender = (); - fn sender(&self) -> Addr { + fn sender_addr(&self) -> Addr { unimplemented!() } diff --git a/packages/cw-orch-mock/src/core.rs b/packages/cw-orch-mock/src/core.rs index 21661982a..173433563 100644 --- a/packages/cw-orch-mock/src/core.rs +++ b/packages/cw-orch-mock/src/core.rs @@ -134,7 +134,7 @@ impl TxHandler for MockBase { type ContractSource = Box>; type Sender = Addr; - fn sender(&self) -> Addr { + fn sender_addr(&self) -> Addr { self.sender.clone() } diff --git a/packages/cw-orch-osmosis-test-tube/src/core.rs b/packages/cw-orch-osmosis-test-tube/src/core.rs index ad9cb0680..f8f907ad6 100644 --- a/packages/cw-orch-osmosis-test-tube/src/core.rs +++ b/packages/cw-orch-osmosis-test-tube/src/core.rs @@ -211,7 +211,7 @@ impl TxHandler for OsmosisTestTube { type Response = AppResponse; type Sender = Rc; - fn sender(&self) -> Addr { + fn sender_addr(&self) -> Addr { Addr::unchecked(self.sender.address()) } diff --git a/packages/interchain/proto/src/ics20.rs b/packages/interchain/proto/src/ics20.rs index c93fc112a..e6ab1feaf 100644 --- a/packages/interchain/proto/src/ics20.rs +++ b/packages/interchain/proto/src/ics20.rs @@ -227,7 +227,7 @@ mod test { // This should pass ok, the timeout was set right let success_outcome = transfer_tokens( chain1, - chain2.sender().as_str(), + chain2.sender_addr().as_str(), &coin(TEST_AMOUNT / 2, denom.clone()), &interchain, &interchain_channel, @@ -244,7 +244,7 @@ mod test { // This should timeout let timeout_outcome = transfer_tokens( chain1, - chain2.sender().as_str(), + chain2.sender_addr().as_str(), &coin(TEST_AMOUNT / 2, denom), &interchain, &interchain_channel, From cb91005609de0759199a739c33ee83dd114cbe35 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 14:13:15 +0200 Subject: [PATCH 100/108] replace uses of `.sender` with `.sender_addr` --- cw-orch-daemon/examples/batch-sender.rs | 2 +- cw-orch-daemon/examples/daemon-capabilities.rs | 2 +- cw-orch-daemon/src/sync/builder.rs | 1 - cw-orch-interchain/examples/timeout_packet.rs | 2 +- cw-orch-interchain/tests/timeout_packet_mock.rs | 2 +- cw-orch/examples/local_daemon.rs | 2 +- cw-orch/examples/testnet_daemon.rs | 2 +- cw-orch/tests/entry_point_macro.rs | 2 +- cw-orch/tests/interface_macro.rs | 2 +- cw-orch/tests/interface_macro_generics.rs | 2 +- packages/clone-testing/src/core.rs | 4 ++-- packages/cw-orch-mock/src/core.rs | 2 +- packages/cw-orch-mock/src/queriers/wasm.rs | 6 +++--- packages/cw-orch-mock/tests/conditional.rs | 2 +- packages/cw-orch-mock/tests/instantiate2.rs | 4 ++-- packages/interchain/proto/src/ics20.rs | 1 - packages/interchain/proto/src/tokenfactory.rs | 8 ++++---- 17 files changed, 22 insertions(+), 24 deletions(-) diff --git a/cw-orch-daemon/examples/batch-sender.rs b/cw-orch-daemon/examples/batch-sender.rs index 0e166f9d9..2292f14ba 100644 --- a/cw-orch-daemon/examples/batch-sender.rs +++ b/cw-orch-daemon/examples/batch-sender.rs @@ -3,7 +3,7 @@ use counter_contract::{ msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, CounterQueryMsgFns, }; use cw_orch::{anyhow, daemon::senders::BatchDaemon, prelude::*}; -use cw_orch_daemon::{senders::CosmosBatchOptions, CosmosOptions}; +use cw_orch_daemon::{senders::CosmosBatchOptions}; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; diff --git a/cw-orch-daemon/examples/daemon-capabilities.rs b/cw-orch-daemon/examples/daemon-capabilities.rs index de8499261..60d0e63b9 100644 --- a/cw-orch-daemon/examples/daemon-capabilities.rs +++ b/cw-orch-daemon/examples/daemon-capabilities.rs @@ -1,4 +1,4 @@ -use std::ops::DerefMut; + use std::str::FromStr; use cosmrs::{tx::Msg, AccountId, Coin, Denom}; diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index da7824b00..af3a695c3 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -165,7 +165,6 @@ impl DaemonBuilder { #[cfg(test)] mod test { - use cw_orch_core::environment::TxHandler; use cw_orch_networks::networks::OSMOSIS_1; use crate::{DaemonBase, DaemonBuilder, Wallet}; diff --git a/cw-orch-interchain/examples/timeout_packet.rs b/cw-orch-interchain/examples/timeout_packet.rs index a4246afd4..f2bf0328a 100644 --- a/cw-orch-interchain/examples/timeout_packet.rs +++ b/cw-orch-interchain/examples/timeout_packet.rs @@ -7,7 +7,7 @@ use cosmos_sdk_proto::{ Any, }; use cw_orch::{ - environment::{QueryHandler, TxHandler}, + environment::QueryHandler, prelude::Stargate, tokio::runtime::Runtime, }; diff --git a/cw-orch-interchain/tests/timeout_packet_mock.rs b/cw-orch-interchain/tests/timeout_packet_mock.rs index 2da3055ba..9daa58f53 100644 --- a/cw-orch-interchain/tests/timeout_packet_mock.rs +++ b/cw-orch-interchain/tests/timeout_packet_mock.rs @@ -31,7 +31,7 @@ fn timeout_packet_mock() -> cw_orch::anyhow::Result<()> { juno.add_balance(juno.sender_addr().to_string(), vec![coin(100_000, "ujuno")])?; let tx_resp = juno.app.borrow_mut().execute( - juno.sender(), + juno.sender_addr(), CosmosMsg::Ibc(IbcMsg::Transfer { channel_id: channel.0.channel.unwrap().to_string(), to_address: stargaze.sender_addr().to_string(), diff --git a/cw-orch/examples/local_daemon.rs b/cw-orch/examples/local_daemon.rs index 166e307f4..960923419 100644 --- a/cw-orch/examples/local_daemon.rs +++ b/cw-orch/examples/local_daemon.rs @@ -4,7 +4,7 @@ use counter_contract::{ }; use cw_orch::{ environment::Environment, - prelude::{CwOrchExecute, CwOrchInstantiate, CwOrchQuery, CwOrchUpload, Daemon, TxHandler}, + prelude::{CwOrchExecute, CwOrchInstantiate, CwOrchQuery, CwOrchUpload, Daemon}, }; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 diff --git a/cw-orch/examples/testnet_daemon.rs b/cw-orch/examples/testnet_daemon.rs index efe2e1b3e..43b12770f 100644 --- a/cw-orch/examples/testnet_daemon.rs +++ b/cw-orch/examples/testnet_daemon.rs @@ -4,7 +4,7 @@ use counter_contract::{ }; use cw_orch::{ environment::Environment, - prelude::{CwOrchExecute, CwOrchInstantiate, CwOrchQuery, CwOrchUpload, Daemon, TxHandler}, + prelude::{CwOrchExecute, CwOrchInstantiate, CwOrchQuery, CwOrchUpload, Daemon}, }; /// In order to use this script, you need to set the following env variables diff --git a/cw-orch/tests/entry_point_macro.rs b/cw-orch/tests/entry_point_macro.rs index e1b122ed3..7f5425e18 100644 --- a/cw-orch/tests/entry_point_macro.rs +++ b/cw-orch/tests/entry_point_macro.rs @@ -65,7 +65,7 @@ fn test_migrate() { contract.upload().unwrap(); contract - .instantiate(&InstantiateMsg {}, Some(&chain.sender()), None) + .instantiate(&InstantiateMsg {}, Some(&chain.sender_addr()), None) .unwrap(); contract diff --git a/cw-orch/tests/interface_macro.rs b/cw-orch/tests/interface_macro.rs index fdebef666..e4fe07235 100644 --- a/cw-orch/tests/interface_macro.rs +++ b/cw-orch/tests/interface_macro.rs @@ -95,7 +95,7 @@ fn test_migrate() { contract.upload().unwrap(); contract - .instantiate(&InstantiateMsg {}, Some(&chain.sender()), None) + .instantiate(&InstantiateMsg {}, Some(&chain.sender_addr()), None) .unwrap(); contract diff --git a/cw-orch/tests/interface_macro_generics.rs b/cw-orch/tests/interface_macro_generics.rs index a14d2bed0..e1a0cd270 100644 --- a/cw-orch/tests/interface_macro_generics.rs +++ b/cw-orch/tests/interface_macro_generics.rs @@ -86,7 +86,7 @@ fn test_migrate() { contract.upload().unwrap(); contract - .instantiate(&InstantiateMsg {}, Some(&chain.sender()), None) + .instantiate(&InstantiateMsg {}, Some(&chain.sender_addr()), None) .unwrap(); contract diff --git a/packages/clone-testing/src/core.rs b/packages/clone-testing/src/core.rs index 23c57c1b0..cc65d12e8 100644 --- a/packages/clone-testing/src/core.rs +++ b/packages/clone-testing/src/core.rs @@ -508,7 +508,7 @@ mod test { let rt = Runtime::new().unwrap(); let chain = CloneTesting::new(&rt, chain)?; - let sender = chain.sender(); + let sender = chain.sender_addr(); let recipient = &chain.init_account(); chain @@ -522,7 +522,7 @@ mod test { asserting("sender is correct") .that(&sender) - .is_equal_to(chain.sender()); + .is_equal_to(chain.sender_addr()); let init_res = chain.upload(&MockCw20).unwrap(); let code_id = (1 + LOCAL_RUST_CODE_OFFSET) as u64; diff --git a/packages/cw-orch-mock/src/core.rs b/packages/cw-orch-mock/src/core.rs index 173433563..55e209c43 100644 --- a/packages/cw-orch-mock/src/core.rs +++ b/packages/cw-orch-mock/src/core.rs @@ -308,7 +308,7 @@ mod test { asserting("sender is correct") .that(&sender.to_string()) - .is_equal_to(chain.sender().to_string()); + .is_equal_to(chain.sender_addr().to_string()); let contract_source = Box::new( ContractWrapper::new(execute, cw20_base::contract::instantiate, query) diff --git a/packages/cw-orch-mock/src/queriers/wasm.rs b/packages/cw-orch-mock/src/queriers/wasm.rs index 58e2a97c1..b3e4bd440 100644 --- a/packages/cw-orch-mock/src/queriers/wasm.rs +++ b/packages/cw-orch-mock/src/queriers/wasm.rs @@ -228,7 +228,7 @@ mod tests { )?; mock.wasm_querier() - .instantiate2_addr(1, mock.sender(), Binary(b"salt-test".to_vec()))?; + .instantiate2_addr(1, mock.sender_addr(), Binary(b"salt-test".to_vec()))?; Ok(()) } @@ -251,7 +251,7 @@ mod tests { )?; mock.wasm_querier() - .instantiate2_addr(1, mock.sender(), Binary(b"salt-test".to_vec()))?; + .instantiate2_addr(1, mock.sender_addr(), Binary(b"salt-test".to_vec()))?; Ok(()) } @@ -262,7 +262,7 @@ mod tests { let addr = mock.wasm_querier().instantiate2_addr( 0, - mock.sender(), + mock.sender_addr(), Binary(b"salt-test".to_vec()), )?; diff --git a/packages/cw-orch-mock/tests/conditional.rs b/packages/cw-orch-mock/tests/conditional.rs index d4f066916..06276248f 100644 --- a/packages/cw-orch-mock/tests/conditional.rs +++ b/packages/cw-orch-mock/tests/conditional.rs @@ -97,7 +97,7 @@ mod tests { #[test] fn cw_orch_interface_traits() { let chain = Mock::new("sender"); - let sender = chain.sender(); + let sender = chain.sender_addr(); let contract = mock_contract::MockContract::new("test:mock_contract", chain.clone()); diff --git a/packages/cw-orch-mock/tests/instantiate2.rs b/packages/cw-orch-mock/tests/instantiate2.rs index 056b77359..763239c8f 100644 --- a/packages/cw-orch-mock/tests/instantiate2.rs +++ b/packages/cw-orch-mock/tests/instantiate2.rs @@ -22,7 +22,7 @@ fn instantiate2() -> anyhow::Result<()> { let expected_address = app.wasm_querier().instantiate2_addr( mock_contract.code_id()?, - app.sender(), + app.sender_addr(), salt.clone(), )?; @@ -37,7 +37,7 @@ fn instantiate2() -> anyhow::Result<()> { .app .borrow() .api() - .addr_canonicalize(app.sender().as_str())?; + .addr_canonicalize(app.sender_addr().as_str())?; // If there is a `Invalid input: canonical address length not correct` error, that means this env doesn't work with instantiate2 correctly assert_eq!( diff --git a/packages/interchain/proto/src/ics20.rs b/packages/interchain/proto/src/ics20.rs index e6ab1feaf..f6b6fc25c 100644 --- a/packages/interchain/proto/src/ics20.rs +++ b/packages/interchain/proto/src/ics20.rs @@ -129,7 +129,6 @@ mod test { use anyhow::Result as AnyResult; use cosmwasm_std::coin; - use cw_orch_core::environment::TxHandler; use crate::tokenfactory::{ create_denom, create_transfer_channel, get_denom, mint, transfer_tokens, diff --git a/packages/interchain/proto/src/tokenfactory.rs b/packages/interchain/proto/src/tokenfactory.rs index 943568fa6..773997f15 100644 --- a/packages/interchain/proto/src/tokenfactory.rs +++ b/packages/interchain/proto/src/tokenfactory.rs @@ -29,7 +29,7 @@ pub fn create_denom( chain: &Chain, token_name: &str, ) -> Result<(), ::Error> { - let creator = chain.sender().to_string(); + let creator = chain.sender_addr().to_string(); let any = MsgCreateDenom { sender: creator, @@ -54,7 +54,7 @@ pub fn create_denom( /// This actually creates the denom for a token created by an address (which is here taken to be the daemon sender address) /// This is mainly used for tests, but feel free to use that in production as well pub fn get_denom(daemon: &Chain, token_name: &str) -> String { - let sender = daemon.sender().to_string(); + let sender = daemon.sender_addr().to_string(); format!("factory/{}/{}", sender, token_name) } @@ -67,7 +67,7 @@ pub fn mint( token_name: &str, amount: u128, ) -> Result<(), ::Error> { - let sender = chain.sender().to_string(); + let sender = chain.sender_addr().to_string(); let denom = get_denom(chain, token_name); let any = MsgMint { @@ -119,7 +119,7 @@ pub fn transfer_tokens Date: Fri, 5 Jul 2024 14:39:17 +0200 Subject: [PATCH 101/108] rm `sender_addr` from daemon --- cw-orch-daemon/src/sync/core.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index a2f153bbe..3f602c2a7 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -118,10 +118,6 @@ impl DaemonBase { // Helpers for Daemon with [`Wallet`] sender. impl Daemon { - pub fn sender_addr(&self) -> Addr { - self.daemon.sender_addr() - } - /// Specifies wether authz should be used with this daemon pub fn authz_granter(&mut self, granter: impl ToString) -> &mut Self { self.sender_mut().set_authz_granter(granter.to_string()); From f4cfe592f8eb1740456e12a37e89234753d8374e Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 14:41:33 +0200 Subject: [PATCH 102/108] fix tests --- cw-orch-daemon/src/channel.rs | 6 +++--- cw-orch-daemon/src/senders/query_only.rs | 4 ++-- cw-orch-daemon/src/sync/builder.rs | 1 + cw-orch-interchain/examples/timeout_packet.rs | 2 +- cw-orch/Cargo.toml | 2 -- cw-orch/examples/local_daemon.rs | 2 +- cw-orch/examples/testnet_daemon.rs | 2 +- packages/interchain/proto/src/ics20.rs | 2 ++ 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/channel.rs index 7075e1e83..bad8b2b45 100644 --- a/cw-orch-daemon/src/channel.rs +++ b/cw-orch-daemon/src/channel.rs @@ -103,7 +103,7 @@ mod tests { This test asserts breaking issues around the GRPC connection */ - use crate::DaemonAsync; + use crate::{senders::QueryOnlySenderOptions, DaemonAsync}; use speculoos::prelude::*; #[tokio::test] @@ -115,7 +115,7 @@ mod tests { let build_res = DaemonAsync::builder(chain) .deployment_id("v0.1.0") - .build() + .build_sender(QueryOnlySenderOptions {}) .await; asserting!("there is no GRPC connection") @@ -134,7 +134,7 @@ mod tests { let build_res = DaemonAsync::builder(chain) .deployment_id("v0.1.0") - .build() + .build_sender(QueryOnlySenderOptions {}) .await; asserting!("GRPC list is empty") diff --git a/cw-orch-daemon/src/senders/query_only.rs b/cw-orch-daemon/src/senders/query_only.rs index ad5cf5c0e..fc5fa213c 100644 --- a/cw-orch-daemon/src/senders/query_only.rs +++ b/cw-orch-daemon/src/senders/query_only.rs @@ -48,7 +48,7 @@ impl QuerySender for QueryOnlySender { #[cfg(test)] mod tests { - use cw_orch_networks::networks::JUNO_1; + use cw_orch_networks::networks::OSMOSIS_1; use super::{QueryOnlyDaemon, QueryOnlySenderOptions}; use crate::DaemonBuilder; @@ -56,7 +56,7 @@ mod tests { #[test] #[serial_test::serial] fn build() { - let _query_only_daemon: QueryOnlyDaemon = DaemonBuilder::new(JUNO_1) + let _query_only_daemon: QueryOnlyDaemon = DaemonBuilder::new(OSMOSIS_1) .build_sender(QueryOnlySenderOptions {}) .unwrap(); } diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index af3a695c3..74adcf8a6 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -166,6 +166,7 @@ impl DaemonBuilder { #[cfg(test)] mod test { use cw_orch_networks::networks::OSMOSIS_1; + use cw_orch_core::environment::TxHandler; use crate::{DaemonBase, DaemonBuilder, Wallet}; pub const DUMMY_MNEMONIC:&str = "chapter wrist alcohol shine angry noise mercy simple rebel recycle vehicle wrap morning giraffe lazy outdoor noise blood ginger sort reunion boss crowd dutch"; diff --git a/cw-orch-interchain/examples/timeout_packet.rs b/cw-orch-interchain/examples/timeout_packet.rs index f2bf0328a..1cefa475b 100644 --- a/cw-orch-interchain/examples/timeout_packet.rs +++ b/cw-orch-interchain/examples/timeout_packet.rs @@ -8,7 +8,7 @@ use cosmos_sdk_proto::{ }; use cw_orch::{ environment::QueryHandler, - prelude::Stargate, + prelude::*, tokio::runtime::Runtime, }; use cw_orch_interchain_core::InterchainEnv; diff --git a/cw-orch/Cargo.toml b/cw-orch/Cargo.toml index e9693070c..dd5d5b4c8 100644 --- a/cw-orch/Cargo.toml +++ b/cw-orch/Cargo.toml @@ -46,7 +46,6 @@ default = [] # enable the optional dependencies daemon = [ "dep:tokio", - # "dep:tonic", "dep:cosmrs", "dep:cw-orch-daemon", "dep:cw-orch-networks", @@ -59,7 +58,6 @@ snapshot-testing = ["dep:insta", "dep:sanitize-filename"] cw-orch-contract-derive = { workspace = true } cw-orch-fns-derive = { workspace = true } - [target.'cfg(not(target_arch = "wasm32"))'.dependencies] # Daemon deps diff --git a/cw-orch/examples/local_daemon.rs b/cw-orch/examples/local_daemon.rs index 960923419..166e307f4 100644 --- a/cw-orch/examples/local_daemon.rs +++ b/cw-orch/examples/local_daemon.rs @@ -4,7 +4,7 @@ use counter_contract::{ }; use cw_orch::{ environment::Environment, - prelude::{CwOrchExecute, CwOrchInstantiate, CwOrchQuery, CwOrchUpload, Daemon}, + prelude::{CwOrchExecute, CwOrchInstantiate, CwOrchQuery, CwOrchUpload, Daemon, TxHandler}, }; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 diff --git a/cw-orch/examples/testnet_daemon.rs b/cw-orch/examples/testnet_daemon.rs index 43b12770f..efe2e1b3e 100644 --- a/cw-orch/examples/testnet_daemon.rs +++ b/cw-orch/examples/testnet_daemon.rs @@ -4,7 +4,7 @@ use counter_contract::{ }; use cw_orch::{ environment::Environment, - prelude::{CwOrchExecute, CwOrchInstantiate, CwOrchQuery, CwOrchUpload, Daemon}, + prelude::{CwOrchExecute, CwOrchInstantiate, CwOrchQuery, CwOrchUpload, Daemon, TxHandler}, }; /// In order to use this script, you need to set the following env variables diff --git a/packages/interchain/proto/src/ics20.rs b/packages/interchain/proto/src/ics20.rs index f6b6fc25c..a159f7a8b 100644 --- a/packages/interchain/proto/src/ics20.rs +++ b/packages/interchain/proto/src/ics20.rs @@ -212,6 +212,8 @@ mod test { #[ignore] #[test] pub fn transfer_ics20_test() -> AnyResult<()> { + use cw_orch_core::environment::TxHandler; + logger_test_init(); let rt = Runtime::new().unwrap(); From 6774e1ee29718ffeacc2656f9e2d58f7da3af71c Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 15:08:26 +0200 Subject: [PATCH 103/108] final test changes --- cw-orch-daemon/src/senders/cosmos.rs | 20 +++++++++++++------- cw-orch-daemon/tests/authz.rs | 8 ++++---- cw-orch-daemon/tests/daemon_state.rs | 1 + 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/cw-orch-daemon/src/senders/cosmos.rs b/cw-orch-daemon/src/senders/cosmos.rs index 256a679e4..4d884fd82 100644 --- a/cw-orch-daemon/src/senders/cosmos.rs +++ b/cw-orch-daemon/src/senders/cosmos.rs @@ -165,8 +165,14 @@ impl Wallet { recipient: &str, coins: Vec, ) -> Result { + let acc_id = if let Some(granter) = self.options.authz_granter.as_ref() { + AccountId::from_str(granter).unwrap() + } else { + self.account_id() + }; + let msg_send = MsgSend { - from_address: self.account_id(), + from_address: acc_id, to_address: AccountId::from_str(recipient)?, amount: parse_cw_coins(&coins)?, }; @@ -440,12 +446,12 @@ impl TxSender for Wallet { } fn account_id(&self) -> AccountId { - AccountId::new( - &self.chain_info.network_info.pub_address_prefix, - &self.private_key.public_key(&self.secp).raw_address.unwrap(), - ) - // unwrap as address is validated on construction - .unwrap() + AccountId::new( + &self.chain_info.network_info.pub_address_prefix, + &self.private_key.public_key(&self.secp).raw_address.unwrap(), + ) + // unwrap as address is validated on construction + .unwrap() } } diff --git a/cw-orch-daemon/tests/authz.rs b/cw-orch-daemon/tests/authz.rs index 8c4fbc861..aa367ebe3 100644 --- a/cw-orch-daemon/tests/authz.rs +++ b/cw-orch-daemon/tests/authz.rs @@ -119,23 +119,23 @@ mod tests { runtime.block_on( daemon .sender() - .bank_send(&grantee, coins(1_000_000, LOCAL_JUNO.gas_denom)), + .bank_send(&grantee, coins(100_000, LOCAL_JUNO.gas_denom)), )?; // And send a large amount of tokens on their behalf runtime.block_on( second_daemon .sender() - .bank_send(&grantee, coins(5_000_000, LOCAL_JUNO.gas_denom)), + .bank_send(&grantee, coins(500_000, LOCAL_JUNO.gas_denom)), )?; - // the balance of the grantee whould be 6_000_000 or close + // the balance of the grantee whould be 600_000 or close let grantee_balance = daemon .bank_querier() .balance(grantee.clone(), Some(LOCAL_JUNO.gas_denom.to_string()))?; - assert_eq!(grantee_balance.first().unwrap().amount.u128(), 6_000_000); + assert_eq!(grantee_balance.first().unwrap().amount.u128(), 600_000); Ok(()) } diff --git a/cw-orch-daemon/tests/daemon_state.rs b/cw-orch-daemon/tests/daemon_state.rs index 275dffd18..421b23120 100644 --- a/cw-orch-daemon/tests/daemon_state.rs +++ b/cw-orch-daemon/tests/daemon_state.rs @@ -201,6 +201,7 @@ fn does_not_error_when_using_different_files() { #[serial_test::serial] fn reuse_same_state_multichain() { std::env::set_var(STATE_FILE_ENV_NAME, TEST_STATE_FILE); + let daemon = DaemonBuilder::new(OSMOSIS_1) .mnemonic(DUMMY_MNEMONIC) .build() From 2d47230413a396ff92d9608c5aca5e1af5259845 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 15:09:14 +0200 Subject: [PATCH 104/108] format --- cw-orch-daemon/examples/batch-sender.rs | 2 +- cw-orch-daemon/examples/daemon-capabilities.rs | 1 - cw-orch-daemon/src/senders/cosmos.rs | 12 ++++++------ cw-orch-daemon/src/sync/builder.rs | 2 +- cw-orch-interchain/examples/timeout_packet.rs | 6 +----- packages/cw-orch-mock/src/queriers/wasm.rs | 14 ++++++++++---- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/cw-orch-daemon/examples/batch-sender.rs b/cw-orch-daemon/examples/batch-sender.rs index 2292f14ba..fedbbb444 100644 --- a/cw-orch-daemon/examples/batch-sender.rs +++ b/cw-orch-daemon/examples/batch-sender.rs @@ -3,7 +3,7 @@ use counter_contract::{ msg::InstantiateMsg, CounterContract, CounterExecuteMsgFns, CounterQueryMsgFns, }; use cw_orch::{anyhow, daemon::senders::BatchDaemon, prelude::*}; -use cw_orch_daemon::{senders::CosmosBatchOptions}; +use cw_orch_daemon::senders::CosmosBatchOptions; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L2 const LOCAL_MNEMONIC: &str = "clip hire initial neck maid actor venue client foam budget lock catalog sweet steak waste crater broccoli pipe steak sister coyote moment obvious choose"; diff --git a/cw-orch-daemon/examples/daemon-capabilities.rs b/cw-orch-daemon/examples/daemon-capabilities.rs index 60d0e63b9..9f3a2b6a5 100644 --- a/cw-orch-daemon/examples/daemon-capabilities.rs +++ b/cw-orch-daemon/examples/daemon-capabilities.rs @@ -1,4 +1,3 @@ - use std::str::FromStr; use cosmrs::{tx::Msg, AccountId, Coin, Denom}; diff --git a/cw-orch-daemon/src/senders/cosmos.rs b/cw-orch-daemon/src/senders/cosmos.rs index 4d884fd82..788a9a9b6 100644 --- a/cw-orch-daemon/src/senders/cosmos.rs +++ b/cw-orch-daemon/src/senders/cosmos.rs @@ -446,12 +446,12 @@ impl TxSender for Wallet { } fn account_id(&self) -> AccountId { - AccountId::new( - &self.chain_info.network_info.pub_address_prefix, - &self.private_key.public_key(&self.secp).raw_address.unwrap(), - ) - // unwrap as address is validated on construction - .unwrap() + AccountId::new( + &self.chain_info.network_info.pub_address_prefix, + &self.private_key.public_key(&self.secp).raw_address.unwrap(), + ) + // unwrap as address is validated on construction + .unwrap() } } diff --git a/cw-orch-daemon/src/sync/builder.rs b/cw-orch-daemon/src/sync/builder.rs index 74adcf8a6..da7824b00 100644 --- a/cw-orch-daemon/src/sync/builder.rs +++ b/cw-orch-daemon/src/sync/builder.rs @@ -165,8 +165,8 @@ impl DaemonBuilder { #[cfg(test)] mod test { - use cw_orch_networks::networks::OSMOSIS_1; use cw_orch_core::environment::TxHandler; + use cw_orch_networks::networks::OSMOSIS_1; use crate::{DaemonBase, DaemonBuilder, Wallet}; pub const DUMMY_MNEMONIC:&str = "chapter wrist alcohol shine angry noise mercy simple rebel recycle vehicle wrap morning giraffe lazy outdoor noise blood ginger sort reunion boss crowd dutch"; diff --git a/cw-orch-interchain/examples/timeout_packet.rs b/cw-orch-interchain/examples/timeout_packet.rs index 1cefa475b..e6fabb712 100644 --- a/cw-orch-interchain/examples/timeout_packet.rs +++ b/cw-orch-interchain/examples/timeout_packet.rs @@ -6,11 +6,7 @@ use cosmos_sdk_proto::{ traits::{Message, Name}, Any, }; -use cw_orch::{ - environment::QueryHandler, - prelude::*, - tokio::runtime::Runtime, -}; +use cw_orch::{environment::QueryHandler, prelude::*, tokio::runtime::Runtime}; use cw_orch_interchain_core::InterchainEnv; use cw_orch_interchain_daemon::ChannelCreator as _; use cw_orch_starship::Starship; diff --git a/packages/cw-orch-mock/src/queriers/wasm.rs b/packages/cw-orch-mock/src/queriers/wasm.rs index b3e4bd440..c7b497a4a 100644 --- a/packages/cw-orch-mock/src/queriers/wasm.rs +++ b/packages/cw-orch-mock/src/queriers/wasm.rs @@ -227,8 +227,11 @@ mod tests { )), )?; - mock.wasm_querier() - .instantiate2_addr(1, mock.sender_addr(), Binary(b"salt-test".to_vec()))?; + mock.wasm_querier().instantiate2_addr( + 1, + mock.sender_addr(), + Binary(b"salt-test".to_vec()), + )?; Ok(()) } @@ -250,8 +253,11 @@ mod tests { )), )?; - mock.wasm_querier() - .instantiate2_addr(1, mock.sender_addr(), Binary(b"salt-test".to_vec()))?; + mock.wasm_querier().instantiate2_addr( + 1, + mock.sender_addr(), + Binary(b"salt-test".to_vec()), + )?; Ok(()) } From 725c25d7abf09533f8dd45687254a3c055e3d14e Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 15:20:04 +0200 Subject: [PATCH 105/108] remove `QueryOnlySenderOptions` in favor of `()` --- cw-orch-daemon/examples/querier-daemon.rs | 2 +- cw-orch-daemon/src/channel.rs | 4 ++-- cw-orch-daemon/src/senders/query_only.rs | 8 +++----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cw-orch-daemon/examples/querier-daemon.rs b/cw-orch-daemon/examples/querier-daemon.rs index c0f4dc598..8ab278ea1 100644 --- a/cw-orch-daemon/examples/querier-daemon.rs +++ b/cw-orch-daemon/examples/querier-daemon.rs @@ -12,7 +12,7 @@ pub fn main() -> anyhow::Result<()> { let network = networks::LOCAL_JUNO; // There is no need to register a mnemonic to use this daemon querier let chain: QueryOnlyDaemon = - QueryOnlyDaemon::builder(network).build_sender(QueryOnlySenderOptions {})?; + QueryOnlyDaemon::builder(network).build_sender(())?; let balances = chain.bank_querier().balance(LOCAL_JUNO_SENDER, None)?; assert!(!balances.is_empty()); diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/channel.rs index bad8b2b45..909b399d4 100644 --- a/cw-orch-daemon/src/channel.rs +++ b/cw-orch-daemon/src/channel.rs @@ -115,7 +115,7 @@ mod tests { let build_res = DaemonAsync::builder(chain) .deployment_id("v0.1.0") - .build_sender(QueryOnlySenderOptions {}) + .build_sender(()) .await; asserting!("there is no GRPC connection") @@ -134,7 +134,7 @@ mod tests { let build_res = DaemonAsync::builder(chain) .deployment_id("v0.1.0") - .build_sender(QueryOnlySenderOptions {}) + .build_sender(()) .await; asserting!("GRPC list is empty") diff --git a/cw-orch-daemon/src/senders/query_only.rs b/cw-orch-daemon/src/senders/query_only.rs index fc5fa213c..67e32e465 100644 --- a/cw-orch-daemon/src/senders/query_only.rs +++ b/cw-orch-daemon/src/senders/query_only.rs @@ -12,8 +12,6 @@ use super::{builder::SenderBuilder, query::QuerySender}; /// Will err on any attempt to sign a transaction or retrieve a sender address. pub type QueryOnlyDaemon = DaemonBase; -pub struct QueryOnlySenderOptions {} - /// Signer of the transactions and helper for address derivation #[derive(Clone)] pub struct QueryOnlySender { @@ -23,7 +21,7 @@ pub struct QueryOnlySender { pub chain_info: Arc, } -impl SenderBuilder for QueryOnlySenderOptions { +impl SenderBuilder for () { type Error = DaemonError; type Sender = QueryOnlySender; @@ -39,7 +37,7 @@ impl SenderBuilder for QueryOnlySenderOptions { impl QuerySender for QueryOnlySender { type Error = DaemonError; - type Options = QueryOnlySenderOptions; + type Options = (); fn channel(&self) -> Channel { self.channel.clone() @@ -57,7 +55,7 @@ mod tests { #[serial_test::serial] fn build() { let _query_only_daemon: QueryOnlyDaemon = DaemonBuilder::new(OSMOSIS_1) - .build_sender(QueryOnlySenderOptions {}) + .build_sender(()) .unwrap(); } } From 3e1ad1249d027fef836bba0f23c59bd1d6f3e2a8 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 15:24:29 +0200 Subject: [PATCH 106/108] fix rename --- cw-orch-daemon/examples/querier-daemon.rs | 5 ++--- cw-orch-daemon/src/channel.rs | 2 +- cw-orch-daemon/src/senders/mod.rs | 2 +- cw-orch-daemon/src/senders/query_only.rs | 7 +++---- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cw-orch-daemon/examples/querier-daemon.rs b/cw-orch-daemon/examples/querier-daemon.rs index 8ab278ea1..b44d45ba8 100644 --- a/cw-orch-daemon/examples/querier-daemon.rs +++ b/cw-orch-daemon/examples/querier-daemon.rs @@ -1,7 +1,7 @@ // ANCHOR: full_counter_example use cw_orch::{anyhow, prelude::*}; -use cw_orch_daemon::senders::{QueryOnlyDaemon, QueryOnlySenderOptions}; +use cw_orch_daemon::senders::QueryOnlyDaemon; // From https://github.com/CosmosContracts/juno/blob/32568dba828ff7783aea8cb5bb4b8b5832888255/docker/test-user.env#L1 pub const LOCAL_JUNO_SENDER: &str = "juno16g2rahf5846rxzp3fwlswy08fz8ccuwk03k57y"; @@ -11,8 +11,7 @@ pub fn main() -> anyhow::Result<()> { let network = networks::LOCAL_JUNO; // There is no need to register a mnemonic to use this daemon querier - let chain: QueryOnlyDaemon = - QueryOnlyDaemon::builder(network).build_sender(())?; + let chain: QueryOnlyDaemon = QueryOnlyDaemon::builder(network).build_sender(())?; let balances = chain.bank_querier().balance(LOCAL_JUNO_SENDER, None)?; assert!(!balances.is_empty()); diff --git a/cw-orch-daemon/src/channel.rs b/cw-orch-daemon/src/channel.rs index 909b399d4..5a3837ff0 100644 --- a/cw-orch-daemon/src/channel.rs +++ b/cw-orch-daemon/src/channel.rs @@ -103,7 +103,7 @@ mod tests { This test asserts breaking issues around the GRPC connection */ - use crate::{senders::QueryOnlySenderOptions, DaemonAsync}; + use crate::DaemonAsync; use speculoos::prelude::*; #[tokio::test] diff --git a/cw-orch-daemon/src/senders/mod.rs b/cw-orch-daemon/src/senders/mod.rs index 2b14ed3bb..e637e4470 100644 --- a/cw-orch-daemon/src/senders/mod.rs +++ b/cw-orch-daemon/src/senders/mod.rs @@ -13,5 +13,5 @@ pub use { cosmos::{CosmosSender, Wallet}, cosmos_batch::{options::CosmosBatchOptions, BatchDaemon, CosmosBatchSender}, cosmos_options::{CosmosOptions, CosmosWalletKey}, - query_only::{QueryOnlyDaemon, QueryOnlySender, QueryOnlySenderOptions}, + query_only::{QueryOnlyDaemon, QueryOnlySender}, }; diff --git a/cw-orch-daemon/src/senders/query_only.rs b/cw-orch-daemon/src/senders/query_only.rs index 67e32e465..1f30ede1d 100644 --- a/cw-orch-daemon/src/senders/query_only.rs +++ b/cw-orch-daemon/src/senders/query_only.rs @@ -48,14 +48,13 @@ impl QuerySender for QueryOnlySender { mod tests { use cw_orch_networks::networks::OSMOSIS_1; - use super::{QueryOnlyDaemon, QueryOnlySenderOptions}; + use super::QueryOnlyDaemon; use crate::DaemonBuilder; #[test] #[serial_test::serial] fn build() { - let _query_only_daemon: QueryOnlyDaemon = DaemonBuilder::new(OSMOSIS_1) - .build_sender(()) - .unwrap(); + let _query_only_daemon: QueryOnlyDaemon = + DaemonBuilder::new(OSMOSIS_1).build_sender(()).unwrap(); } } From 2b7938ac54dcd27b71e304aa6ee7777a6b0323d7 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Fri, 5 Jul 2024 15:24:57 +0200 Subject: [PATCH 107/108] rm unused import --- cw-orch-daemon/tests/querier.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cw-orch-daemon/tests/querier.rs b/cw-orch-daemon/tests/querier.rs index 69e120dba..4c917950f 100644 --- a/cw-orch-daemon/tests/querier.rs +++ b/cw-orch-daemon/tests/querier.rs @@ -4,7 +4,6 @@ mod common; mod queriers { use cw_orch_core::contract::interface_traits::*; - use cw_orch_core::environment::TxHandler; use cw_orch_daemon::{queriers::Bank, GrpcChannel}; use cw_orch_networks::networks; use mock_contract::InstantiateMsg; From 419ea7dca8cc462ec45088b5f666aa1dc9fff987 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Mon, 8 Jul 2024 09:59:02 +0200 Subject: [PATCH 108/108] update channel to service --- cw-orch-daemon/src/core.rs | 2 +- cw-orch-daemon/src/lib.rs | 2 +- cw-orch-daemon/src/queriers/authz.rs | 6 +- cw-orch-daemon/src/queriers/feegrant.rs | 6 +- cw-orch-daemon/src/queriers/gov.rs | 6 +- cw-orch-daemon/src/queriers/ibc.rs | 6 +- cw-orch-daemon/src/queriers/node.rs | 3 +- cw-orch-daemon/src/queriers/staking.rs | 6 +- cw-orch-daemon/src/senders/cosmos.rs | 14 ++- cw-orch-daemon/src/service.rs | 120 ------------------------ cw-orch-daemon/src/service/channel.rs | 78 +++++++++++++++ cw-orch-daemon/src/service/factory.rs | 76 +++++++++++++++ cw-orch-daemon/src/service/mod.rs | 63 +++++++++++++ cw-orch-daemon/src/state.rs | 2 +- cw-orch-daemon/src/sync/core.rs | 2 +- 15 files changed, 249 insertions(+), 143 deletions(-) delete mode 100644 cw-orch-daemon/src/service.rs create mode 100644 cw-orch-daemon/src/service/channel.rs create mode 100644 cw-orch-daemon/src/service/factory.rs create mode 100644 cw-orch-daemon/src/service/mod.rs diff --git a/cw-orch-daemon/src/core.rs b/cw-orch-daemon/src/core.rs index 809d29264..e31cacc66 100644 --- a/cw-orch-daemon/src/core.rs +++ b/cw-orch-daemon/src/core.rs @@ -86,7 +86,7 @@ impl DaemonAsyncBase { pub async fn service(&self) -> Result { self.state.service().await } - + pub fn chain_info(&self) -> &ChainInfoOwned { self.state.chain_data.as_ref() } diff --git a/cw-orch-daemon/src/lib.rs b/cw-orch-daemon/src/lib.rs index 742154f38..ead035524 100644 --- a/cw-orch-daemon/src/lib.rs +++ b/cw-orch-daemon/src/lib.rs @@ -11,8 +11,8 @@ pub mod env; pub mod keys; pub mod live_mock; pub mod queriers; -pub mod service; pub mod senders; +pub mod service; pub mod tx_broadcaster; pub mod tx_builder; diff --git a/cw-orch-daemon/src/queriers/authz.rs b/cw-orch-daemon/src/queriers/authz.rs index 74621b311..47b56d979 100644 --- a/cw-orch-daemon/src/queriers/authz.rs +++ b/cw-orch-daemon/src/queriers/authz.rs @@ -14,9 +14,9 @@ pub struct Authz { impl Authz { pub fn new(daemon: &Daemon) -> Result { Ok(Self { - service: daemon.service()?, - rt_handle: Some(daemon.rt_handle.clone()), - }) + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } pub fn new_async(service: DaemonService) -> Self { diff --git a/cw-orch-daemon/src/queriers/feegrant.rs b/cw-orch-daemon/src/queriers/feegrant.rs index a0f456c02..5241cc01f 100644 --- a/cw-orch-daemon/src/queriers/feegrant.rs +++ b/cw-orch-daemon/src/queriers/feegrant.rs @@ -14,9 +14,9 @@ pub struct FeeGrant { impl FeeGrant { pub fn new(daemon: &Daemon) -> Result { Ok(Self { - service: daemon.service()?, - rt_handle: Some(daemon.rt_handle.clone()), - }) + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } pub fn new_async(service: DaemonService) -> Self { diff --git a/cw-orch-daemon/src/queriers/gov.rs b/cw-orch-daemon/src/queriers/gov.rs index aca009dac..6fea6931c 100644 --- a/cw-orch-daemon/src/queriers/gov.rs +++ b/cw-orch-daemon/src/queriers/gov.rs @@ -14,9 +14,9 @@ pub struct Gov { impl Gov { pub fn new(daemon: &Daemon) -> Result { Ok(Self { - service: daemon.service()?, - rt_handle: Some(daemon.rt_handle.clone()), - }) + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } pub fn new_async(service: DaemonService) -> Self { diff --git a/cw-orch-daemon/src/queriers/ibc.rs b/cw-orch-daemon/src/queriers/ibc.rs index 6d27c600f..954d1c51a 100644 --- a/cw-orch-daemon/src/queriers/ibc.rs +++ b/cw-orch-daemon/src/queriers/ibc.rs @@ -24,9 +24,9 @@ pub struct Ibc { impl Ibc { pub fn new(daemon: &Daemon) -> Result { Ok(Self { - service: daemon.service()?, - rt_handle: Some(daemon.rt_handle.clone()), - }) + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } pub fn new_async(service: DaemonService) -> Self { diff --git a/cw-orch-daemon/src/queriers/node.rs b/cw-orch-daemon/src/queriers/node.rs index d6d50b44d..fb55ec632 100644 --- a/cw-orch-daemon/src/queriers/node.rs +++ b/cw-orch-daemon/src/queriers/node.rs @@ -1,7 +1,8 @@ use std::{cmp::min, time::Duration}; use crate::{ - cosmos_modules, env::DaemonEnvVars, error::DaemonError, senders::query::QuerySender, service::DaemonService, tx_resp::CosmTxResponse, DaemonBase + cosmos_modules, env::DaemonEnvVars, error::DaemonError, senders::query::QuerySender, + service::DaemonService, tx_resp::CosmTxResponse, DaemonBase, }; use cosmrs::{ diff --git a/cw-orch-daemon/src/queriers/staking.rs b/cw-orch-daemon/src/queriers/staking.rs index 0b2d9b137..787a008db 100644 --- a/cw-orch-daemon/src/queriers/staking.rs +++ b/cw-orch-daemon/src/queriers/staking.rs @@ -19,9 +19,9 @@ pub struct Staking { impl Staking { pub fn new(daemon: &Daemon) -> Result { Ok(Self { - service: daemon.service()?, - rt_handle: Some(daemon.rt_handle.clone()), - }) + service: daemon.service()?, + rt_handle: Some(daemon.rt_handle.clone()), + }) } pub fn new_async(service: DaemonService) -> Self { diff --git a/cw-orch-daemon/src/senders/cosmos.rs b/cw-orch-daemon/src/senders/cosmos.rs index 1cbbf0e04..3aa01cc04 100644 --- a/cw-orch-daemon/src/senders/cosmos.rs +++ b/cw-orch-daemon/src/senders/cosmos.rs @@ -1,5 +1,9 @@ use crate::{ - env::DaemonEnvVars, proto::injective::ETHEREUM_COIN_TYPE, queriers::Bank, service::{DaemonChannel, DaemonChannelFactory, DaemonService, DaemonServiceCreation}, tx_broadcaster::{ + env::DaemonEnvVars, + proto::injective::ETHEREUM_COIN_TYPE, + queriers::Bank, + service::{DaemonChannel, DaemonChannelFactory, DaemonService, DaemonServiceCreation}, + tx_broadcaster::{ account_sequence_strategy, assert_broadcast_code_cosm_response, insufficient_fee_strategy, TxBroadcaster, }, @@ -454,8 +458,12 @@ impl TxSender for Wallet { impl DaemonServiceCreation for Sender { async fn channel(&self) -> Result { - let channel = GrpcChannel::connect(&self.chain_info.grpc_urls, &self.chain_info.chain_id).await?; - Ok(DaemonService::new::(DaemonChannelFactory {}, channel)) + let channel = + GrpcChannel::connect(&self.chain_info.grpc_urls, &self.chain_info.chain_id).await?; + Ok(DaemonService::new::( + DaemonChannelFactory {}, + channel, + )) } } diff --git a/cw-orch-daemon/src/service.rs b/cw-orch-daemon/src/service.rs deleted file mode 100644 index 112b5714e..000000000 --- a/cw-orch-daemon/src/service.rs +++ /dev/null @@ -1,120 +0,0 @@ -use std::{ - cell::RefCell, error::Error, future::{ready, Future, Ready}, pin::Pin, rc::Rc, sync::Arc, task::{Context, Poll}, time::Duration -}; - -use cw_orch_core::environment::ChainInfoOwned; -use tonic::{ - body::BoxBody, - client::GrpcService, - transport::{channel, Channel, Endpoint}, - Request, -}; -use tower::{ - reconnect::Reconnect, retry::{Policy, Retry, RetryLayer}, Layer, MakeService, Service, ServiceBuilder, ServiceExt -}; - -use crate::{DaemonError, GrpcChannel}; - -/// Daemon Service is a wrapper around the [`Reconnect`] layer that can be shared within a thread. -/// This allows our services to scale between different threads while still being error-tolerant. -/// A signing daemon will have two DaemonService instances, one for transactions and one for queries. -pub type DaemonService = Rc>>; - -pub trait DaemonServiceCreation { - /// Create a new `Rc>>` service for interacting with chain. - async fn new_service(&self, chain_info: &ChainInfoOwned) -> Result { - let channel = GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?; - Ok(DaemonService::new(RefCell::new(Reconnect::new::(DaemonChannelFactory {}, channel)))); - } - - /// Get a new service for interacting with the chain. - async fn channel(&self) -> Result<&mut Reconnect, DaemonError>; -} - -#[derive(Clone)] -pub struct DaemonChannel { - // Service that retries on failures - pub(crate) svs: Retry, -} - -impl DaemonChannel { - pub fn new(channel: Channel) -> Self { - // Create the Reconnect layer with the factory - let retry_policy = MyRetryPolicy { - max_retries: 3, // Maximum number of retries - backoff: Duration::from_secs(1), // Backoff duration - }; - let retry_layer = RetryLayer::new(retry_policy); - let a: Retry = ServiceBuilder::new() - .layer(retry_layer) - .service(channel); - Self { svs: a } - } -} - -impl Service> for DaemonChannel { - type Response = http::Response; - type Error = tonic::transport::Error; - type Future = channel::ResponseFuture; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - Service::poll_ready(&mut self.svs, cx) - } - - fn call(&mut self, request: http::Request) -> Self::Future { - Service::call(&mut self.svs, request) - } -} - -pub struct DaemonChannelFactory {} - -// TODO: take different type than channel -impl Service for DaemonChannelFactory { - type Response = Retry; - type Error = Box; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, request: Channel) -> Self::Future { - let fut = async move { - Ok(DaemonChannel::new(request)) - }; - Box::pin(fut) - } -} - -/// Retry policy that retries on all errors up to a maximum number of retries. -#[derive(Debug, Clone)] -pub struct MyRetryPolicy { - pub max_retries: usize, - pub backoff: Duration, -} - -impl Policy, http::Response, E> for MyRetryPolicy -where - E: Into>, -{ - type Future = Ready; - - fn retry( - &self, - _req: &http::Request, - result: Result<&http::Response, &E>, - ) -> Option { - if self.max_retries > 0 && result.is_err() { - Some(ready(MyRetryPolicy { - max_retries: self.max_retries - 1, - backoff: self.backoff, - })) - } else { - None - } - } - - fn clone_request(&self, req: &http::Request) -> Option> { - None - } -} diff --git a/cw-orch-daemon/src/service/channel.rs b/cw-orch-daemon/src/service/channel.rs new file mode 100644 index 000000000..6e2c794ee --- /dev/null +++ b/cw-orch-daemon/src/service/channel.rs @@ -0,0 +1,78 @@ +use std::{ + cell::RefCell, + error::Error, + future::{ready, Future, Ready}, + pin::Pin, + rc::Rc, + sync::Arc, + task::{Context, Poll}, + time::Duration, +}; + +use cw_orch_core::environment::ChainInfoOwned; +use tonic::{ + body::BoxBody, + client::GrpcService, + transport::{channel, Channel, Endpoint}, + Request, +}; +use tower::{ + reconnect::Reconnect, + retry::{Policy, Retry, RetryLayer}, + Layer, MakeService, Service, ServiceBuilder, ServiceExt, +}; + +use crate::{DaemonError, GrpcChannel}; + +#[derive(Clone)] +pub struct DaemonChannel { + // Service that retries on failures + pub(crate) svs: Retry, +} + +impl DaemonChannel { + pub fn new(channel: Channel) -> Self { + // Create the Reconnect layer with the factory + let retry_policy = MyRetryPolicy { + max_retries: 3, // Maximum number of retries + backoff: Duration::from_secs(1), // Backoff duration + }; + let retry_layer = RetryLayer::new(retry_policy); + let a: Retry = + ServiceBuilder::new().layer(retry_layer).service(channel); + Self { svs: a } + } +} + +/// Retry policy that retries on all errors up to a maximum number of retries. +#[derive(Debug, Clone)] +pub struct MyRetryPolicy { + pub max_retries: usize, + pub backoff: Duration, +} + +impl Policy, http::Response, E> for MyRetryPolicy +where + E: Into>, +{ + type Future = Ready; + + fn retry( + &self, + _req: &http::Request, + result: Result<&http::Response, &E>, + ) -> Option { + if self.max_retries > 0 && result.is_err() { + Some(ready(MyRetryPolicy { + max_retries: self.max_retries - 1, + backoff: self.backoff, + })) + } else { + None + } + } + + fn clone_request(&self, req: &http::Request) -> Option> { + None + } +} diff --git a/cw-orch-daemon/src/service/factory.rs b/cw-orch-daemon/src/service/factory.rs new file mode 100644 index 000000000..9664609bc --- /dev/null +++ b/cw-orch-daemon/src/service/factory.rs @@ -0,0 +1,76 @@ +use std::{ + cell::RefCell, + error::Error, + future::{ready, Future, Ready}, + pin::Pin, + rc::Rc, + sync::Arc, + task::{Context, Poll}, + time::Duration, +}; + +use cw_orch_core::environment::{ChainInfoOwned, ChainState}; +use tonic::{ + body::BoxBody, + client::GrpcService, + transport::{channel, Channel, Endpoint}, + Request, +}; +use tower::{ + reconnect::Reconnect, + retry::{Policy, Retry, RetryLayer}, + Layer, MakeService, Service, ServiceBuilder, ServiceExt, +}; + +use crate::{DaemonBase, DaemonError, GrpcChannel}; + +use super::{ + channel::{DaemonChannel, MyRetryPolicy}, + get_channel_creator, NewChannelRequest, Request as ChannelRequest, +}; + +/// Daemon Service is a wrapper around the [`Reconnect`] layer that is owned by the thread. +pub type DaemonService = Reconnect, ()>; + +// pub trait DaemonServiceCreation { +// /// Create a new `Rc>>` service for interacting with chain. +// async fn new_service(&self, chain_info: &ChainInfoOwned) -> Result { + +// Ok(DaemonService::new(RefCell::new(Reconnect::new::(DaemonChannelFactory {}, ))); +// } + +// /// Get a new service for interacting with the chain. +// async fn channel(&self) -> Result<&mut Reconnect, DaemonError>; +// } + +// TODO: take different type than channel +impl Service for DaemonBase { + type Response = Retry; + type Error = Box; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, request: ()) -> Self::Future { + let fut = async move { + // Create request to get a new channel and re-create the service. + // TODO: Add a check here to ensure we only try to reconnect # number of times. Otherwise we will never fail. + let channel_creator = get_channel_creator().await; + let (reply_tx, reply_rx) = tokio::sync::mpsc::channel(1); + + let request = NewChannelRequest { + request: ChannelRequest { + grpc_urls: self.state().chain_data.as_ref().grpc_urls.clone(), + chain_id: self.state().chain_data.as_ref().chain_id.clone(), + }, + reply_tx, + }; + let _ = channel_creator.send(request).await; + let channel = reply_rx.recv().await.unwrap(); + Ok(DaemonChannel::new(request)) + }; + Box::pin(fut) + } +} diff --git a/cw-orch-daemon/src/service/mod.rs b/cw-orch-daemon/src/service/mod.rs new file mode 100644 index 000000000..8f8c81ec9 --- /dev/null +++ b/cw-orch-daemon/src/service/mod.rs @@ -0,0 +1,63 @@ +mod channel; +mod factory; + +use channel::DaemonChannelFactory; +use http::Uri; +use std::sync::Arc; +use tokio::sync::{mpsc, OnceCell}; +use tokio::task; +use tonic::transport::{Channel, Endpoint}; +use tower::reconnect::Reconnect; + +use crate::GrpcChannel; + +static SERVICE_FACTORY: OnceCell>> = OnceCell::const_new(); + +#[derive(Debug)] +struct NewChannelRequest { + request: Request, + reply_tx: mpsc::Sender, +} + +#[derive(Debug, Clone)] +pub struct Request { + grpc_urls: Vec, + chain_id: String, +} + +pub struct Response {} + +#[derive(Debug)] +pub struct ChannelFactory {} + +impl ChannelFactory { + pub async fn create_channel( + &self, + request: Request, + ) -> Result { + // TODO: move channel creation here, find endpoint with best connection (lowest latency / highest rate-limit) + // And round-robin new connections + let channel = GrpcChannel::connect(&chain_info.grpc_urls, &chain_info.chain_id).await?; + Ok(Response) + } +} + +async fn get_channel_creator() -> Arc> { + SERVICE_FACTORY + .get_or_init(|| async { + let (tx, mut rx) = mpsc::channel::(32); + let service_creator = Arc::new(tx.clone()); + + task::spawn(async move { + let factory = ServiceFactory {}; + while let Some(request) = rx.recv().await { + let response = factory.create_channel(request).await; + let _ = request.reply_tx.send(response).await; + } + }); + + service_creator + }) + .await + .clone() +} diff --git a/cw-orch-daemon/src/state.rs b/cw-orch-daemon/src/state.rs index 7be9ae875..efcd6e5a6 100644 --- a/cw-orch-daemon/src/state.rs +++ b/cw-orch-daemon/src/state.rs @@ -10,13 +10,13 @@ use cw_orch_core::{environment::StateInterface, log::local_target, CwEnvError}; use once_cell::sync::Lazy; use serde::Serialize; use serde_json::{json, Value}; -use tonic::transport::Channel; use std::sync::Arc; use std::{ collections::{HashMap, HashSet}, path::Path, sync::Mutex, }; +use tonic::transport::Channel; /// Global state to track which files are already open by other daemons from other threads /// This is necessary because File lock will allow same process to lock file how many times as process wants diff --git a/cw-orch-daemon/src/sync/core.rs b/cw-orch-daemon/src/sync/core.rs index e0dbb1f70..bcbe1d011 100644 --- a/cw-orch-daemon/src/sync/core.rs +++ b/cw-orch-daemon/src/sync/core.rs @@ -66,7 +66,7 @@ impl DaemonBase { pub fn service(&self) -> Result { self.rt_handle.block_on(self.daemon.service()) } - + /// Get the channel configured for this Daemon pub fn sender(&self) -> &Sender { self.daemon.sender()