From 8028521cd8f832cd7a8ef8a3866e6890aecf85d3 Mon Sep 17 00:00:00 2001 From: Art3mix Date: Sun, 24 Nov 2024 15:06:59 +0200 Subject: [PATCH 1/7] add pre_validate --- local-interchaintest/examples/manager_test.rs | 9 +++---- program-manager/src/domain/cosmos_cw.rs | 23 +++++++++------- program-manager/src/domain/mod.rs | 6 +++++ program-manager/src/library.rs | 2 +- program-manager/src/program_config.rs | 26 ++++++++++++++----- program-manager/src/program_update.rs | 2 +- 6 files changed, 43 insertions(+), 25 deletions(-) diff --git a/local-interchaintest/examples/manager_test.rs b/local-interchaintest/examples/manager_test.rs index f7d5afa3..55f42152 100644 --- a/local-interchaintest/examples/manager_test.rs +++ b/local-interchaintest/examples/manager_test.rs @@ -77,6 +77,7 @@ fn main() -> Result<(), Box> { let action_label = "swap"; builder.add_authorization( AuthorizationBuilder::new() + .with_label(action_label) .with_subroutine( AtomicSubroutineBuilder::new() .with_function( @@ -209,13 +210,9 @@ fn main() -> Result<(), Box> { // tick processor let tick_denom = build_tokenfactory_denom( &authorization_addr, - format!( - "update_service_{}_{}", - splitter_data.name, - library_1.get_id() - ) - .as_str(), + format!("update_library_{}", library_1.get_id()).as_str(), ); + println!("Ticking processor with denom: {}", tick_denom); println!("auth addr {}", authorization_addr); diff --git a/program-manager/src/domain/cosmos_cw.rs b/program-manager/src/domain/cosmos_cw.rs index ec9adefd..34d909c1 100644 --- a/program-manager/src/domain/cosmos_cw.rs +++ b/program-manager/src/domain/cosmos_cw.rs @@ -83,7 +83,7 @@ pub struct CosmosCosmwasmConnector { wallet: Wallet, code_ids: HashMap, chain_name: String, - prefix: String, + prefix: &'static str, } impl fmt::Debug for CosmosCosmwasmConnector { @@ -129,7 +129,7 @@ impl CosmosCosmwasmConnector { wallet, code_ids: code_ids.clone(), chain_name: chain_info.name.clone(), - prefix: chain_info.prefix.clone(), + prefix: chain_info.prefix.clone().leak(), }) } } @@ -214,14 +214,14 @@ impl Connector for CosmosCosmwasmConnector { let addr_canonical = instantiate2_address( &checksum, - &addr_canonicalize(&self.prefix, self.wallet.account_address.as_str()).unwrap(), + &addr_canonicalize(self.prefix, self.wallet.account_address.as_str()).unwrap(), &salt, ) .context("Failed to instantiate2 address") .map_err(CosmosCosmwasmError::Error)?; let addr = - addr_humanize(&self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; + addr_humanize(self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; Ok((addr, salt.to_vec())) } @@ -255,18 +255,15 @@ impl Connector for CosmosCosmwasmConnector { let addr_canonical = instantiate2_address( &checksum, - &addr_canonicalize( - &self.prefix, - receiving_chain_bridge_info.voice_addr.as_str(), - ) - .unwrap(), + &addr_canonicalize(self.prefix, receiving_chain_bridge_info.voice_addr.as_str()) + .unwrap(), &salt, ) .context("Failed to instantiate2 address") .map_err(CosmosCosmwasmError::Error)?; let addr = - addr_humanize(&self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; + addr_humanize(self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; Ok(addr) } @@ -875,6 +872,12 @@ impl Connector for CosmosCosmwasmConnector { Ok(from_json::(&res.program_config) .map_err(CosmosCosmwasmError::CosmwasmStdError)?) } + + fn get_api(&self) -> ConnectorResult> { + Ok(Box::new( + cosmwasm_std::testing::MockApi::default().with_prefix(self.prefix), + )) + } } // Helpers diff --git a/program-manager/src/domain/mod.rs b/program-manager/src/domain/mod.rs index d6070da5..d8483efc 100644 --- a/program-manager/src/domain/mod.rs +++ b/program-manager/src/domain/mod.rs @@ -174,6 +174,12 @@ pub trait Connector: fmt::Debug + Send + Sync { // Verify the bridge account was instantiated async fn verify_bridge_account(&mut self, bridge_addr: String) -> ConnectorResult<()>; + // This returns us the api we use to validate library configs. + // With more domains we support, we might need to wrap the api in a more generic type + // because the api is specific to cosmwasm. + // We might even want to mock a Querier that will allow us to access on-chain data. + fn get_api(&self) -> ConnectorResult>; + // --------------------------------------------------------------------------------------- // Below are functions that sohuld only be implemented on a specific domain // For example authorization contract methods should only be implemented on the main domain diff --git a/program-manager/src/library.rs b/program-manager/src/library.rs index 59538f41..7016ae86 100644 --- a/program-manager/src/library.rs +++ b/program-manager/src/library.rs @@ -274,7 +274,7 @@ impl LibraryConfig { .map_err(LibraryError::SerdeJsonError) } - pub fn soft_validate_config(&self, api: &dyn cosmwasm_std::Api) -> LibraryResult<()> { + pub fn pre_validate_config(&self, api: &dyn cosmwasm_std::Api) -> LibraryResult<()> { match self { LibraryConfig::None => Err(LibraryError::NoLibraryConfig), LibraryConfig::ValenceForwarderLibrary(config) => { diff --git a/program-manager/src/program_config.rs b/program-manager/src/program_config.rs index f2dafddc..608ecf83 100644 --- a/program-manager/src/program_config.rs +++ b/program-manager/src/program_config.rs @@ -277,10 +277,12 @@ impl ProgramConfig { account.addr = Some(addr); } + let mut libraries_salts: BTreeMap> = BTreeMap::new(); + // We first predict the library addresses // Then we update the library configs with the account predicted addresses // for all input accounts we add the library address to the approved libraries list - // and then instantiate the libraries + // We also verify the library config using pre_validate function for (_, link) in self.links.clone().iter() { let mut library = self.get_library(link.library_id)?; @@ -293,6 +295,8 @@ impl ProgramConfig { ) .await?; + libraries_salts.insert(link.library_id, salt); + let mut patterns = Vec::with_capacity(link.input_accounts_id.len() + link.output_accounts_id.len()); let mut replace_with = @@ -326,12 +330,26 @@ impl ProgramConfig { } library.config.replace_config(patterns, replace_with)?; + library + .config + .pre_validate_config(&*domain_connector.get_api()?)?; library.addr = Some(library_addr); self.save_library(link.library_id, &library); + } + + // We run over all libraries and instantiate them + for (_, link) in self.links.clone().iter() { + let library = self.get_library(link.library_id)?; + + let mut domain_connector = connectors.get_or_create_connector(&library.domain).await?; // Get processor address for this domain let processor_addr = self.get_processor_account_on_domain(library.domain.clone())?; + let salt = libraries_salts + .get(&link.library_id) + .expect("Library salt not found") + .clone(); // init the library domain_connector @@ -529,12 +547,6 @@ impl ProgramConfig { ManagerError::AccountIdNotFoundLibraryConfig(accounts) ); - // Run the soft_validate method on each library config - for _library in self.libraries.values() { - // TODO: mock api for the connector - // library.config.soft_validate_config()?; - } - Ok(()) } diff --git a/program-manager/src/program_update.rs b/program-manager/src/program_update.rs index 66dd30f6..295b5c9a 100644 --- a/program-manager/src/program_update.rs +++ b/program-manager/src/program_update.rs @@ -110,7 +110,7 @@ impl ProgramConfigUpdate { .context(ManagerError::LibraryIdIsMissing(*id).to_string())?; // Add authorization to update the library - let label = format!("update_library_{}_{}", library.name, id); + let label = format!("update_library_{}", id); // Create authorization if we don't already have one if !config.authorizations.iter().any(|auth| auth.label == label) { From b540f37efbcd8f8184fb542dce681df7904ce1fe Mon Sep 17 00:00:00 2001 From: Art3mix Date: Mon, 25 Nov 2024 12:50:46 +0200 Subject: [PATCH 2/7] fix the leak --- program-manager/src/domain/cosmos_cw.rs | 28 ++++++++++++++----------- program-manager/src/domain/mod.rs | 8 ++----- program-manager/src/program_config.rs | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/program-manager/src/domain/cosmos_cw.rs b/program-manager/src/domain/cosmos_cw.rs index 34d909c1..b7397b5c 100644 --- a/program-manager/src/domain/cosmos_cw.rs +++ b/program-manager/src/domain/cosmos_cw.rs @@ -26,7 +26,7 @@ use cosmos_grpc_client::{ cosmrs::bip32::secp256k1::sha2::{digest::Update, Digest, Sha256, Sha512}, BroadcastMode, Decimal, GrpcClient, ProstMsgNameToAny, Wallet, }; -use cosmwasm_std::{from_json, instantiate2_address, to_json_binary}; +use cosmwasm_std::{from_json, instantiate2_address, testing::MockApi, to_json_binary}; use futures::future::BoxFuture; use serde_json::to_vec; use strum::VariantNames; @@ -83,7 +83,8 @@ pub struct CosmosCosmwasmConnector { wallet: Wallet, code_ids: HashMap, chain_name: String, - prefix: &'static str, + prefix: String, + api: MockApi, } impl fmt::Debug for CosmosCosmwasmConnector { @@ -129,7 +130,9 @@ impl CosmosCosmwasmConnector { wallet, code_ids: code_ids.clone(), chain_name: chain_info.name.clone(), - prefix: chain_info.prefix.clone().leak(), + prefix: chain_info.prefix.clone(), + api: cosmwasm_std::testing::MockApi::default() + .with_prefix(Box::leak(Box::new(chain_info.prefix.clone()))), }) } } @@ -214,14 +217,14 @@ impl Connector for CosmosCosmwasmConnector { let addr_canonical = instantiate2_address( &checksum, - &addr_canonicalize(self.prefix, self.wallet.account_address.as_str()).unwrap(), + &addr_canonicalize(&self.prefix, self.wallet.account_address.as_str()).unwrap(), &salt, ) .context("Failed to instantiate2 address") .map_err(CosmosCosmwasmError::Error)?; let addr = - addr_humanize(self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; + addr_humanize(&self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; Ok((addr, salt.to_vec())) } @@ -255,15 +258,18 @@ impl Connector for CosmosCosmwasmConnector { let addr_canonical = instantiate2_address( &checksum, - &addr_canonicalize(self.prefix, receiving_chain_bridge_info.voice_addr.as_str()) - .unwrap(), + &addr_canonicalize( + &self.prefix, + receiving_chain_bridge_info.voice_addr.as_str(), + ) + .unwrap(), &salt, ) .context("Failed to instantiate2 address") .map_err(CosmosCosmwasmError::Error)?; let addr = - addr_humanize(self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; + addr_humanize(&self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; Ok(addr) } @@ -873,10 +879,8 @@ impl Connector for CosmosCosmwasmConnector { .map_err(CosmosCosmwasmError::CosmwasmStdError)?) } - fn get_api(&self) -> ConnectorResult> { - Ok(Box::new( - cosmwasm_std::testing::MockApi::default().with_prefix(self.prefix), - )) + fn get_api(&self) -> &MockApi{ + &self.api } } diff --git a/program-manager/src/domain/mod.rs b/program-manager/src/domain/mod.rs index d8483efc..b72c4f4c 100644 --- a/program-manager/src/domain/mod.rs +++ b/program-manager/src/domain/mod.rs @@ -8,6 +8,7 @@ use async_trait::async_trait; use cosmos_cw::{CosmosCosmwasmConnector, CosmosCosmwasmError}; use cosmwasm_schema::schemars::JsonSchema; +use cosmwasm_std::testing::MockApi; use serde::{Deserialize, Serialize}; // use cosmos_evm::CosmosEvmError; @@ -174,12 +175,7 @@ pub trait Connector: fmt::Debug + Send + Sync { // Verify the bridge account was instantiated async fn verify_bridge_account(&mut self, bridge_addr: String) -> ConnectorResult<()>; - // This returns us the api we use to validate library configs. - // With more domains we support, we might need to wrap the api in a more generic type - // because the api is specific to cosmwasm. - // We might even want to mock a Querier that will allow us to access on-chain data. - fn get_api(&self) -> ConnectorResult>; - + fn get_api(&self) -> &MockApi; // --------------------------------------------------------------------------------------- // Below are functions that sohuld only be implemented on a specific domain // For example authorization contract methods should only be implemented on the main domain diff --git a/program-manager/src/program_config.rs b/program-manager/src/program_config.rs index 608ecf83..cc93eb0a 100644 --- a/program-manager/src/program_config.rs +++ b/program-manager/src/program_config.rs @@ -332,7 +332,7 @@ impl ProgramConfig { library.config.replace_config(patterns, replace_with)?; library .config - .pre_validate_config(&*domain_connector.get_api()?)?; + .pre_validate_config(domain_connector.get_api())?; library.addr = Some(library_addr); self.save_library(link.library_id, &library); From ab7fb62ac0ab8fbc9619fbd5308439331b0d18e8 Mon Sep 17 00:00:00 2001 From: Art3mix Date: Mon, 25 Nov 2024 13:03:03 +0200 Subject: [PATCH 3/7] fmt --- program-manager/src/domain/cosmos_cw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program-manager/src/domain/cosmos_cw.rs b/program-manager/src/domain/cosmos_cw.rs index b7397b5c..e2e9dfed 100644 --- a/program-manager/src/domain/cosmos_cw.rs +++ b/program-manager/src/domain/cosmos_cw.rs @@ -879,7 +879,7 @@ impl Connector for CosmosCosmwasmConnector { .map_err(CosmosCosmwasmError::CosmwasmStdError)?) } - fn get_api(&self) -> &MockApi{ + fn get_api(&self) -> &MockApi { &self.api } } From 29c659a617299fbf95eb00d03be5f23c45c47cce Mon Sep 17 00:00:00 2001 From: Art3mix Date: Mon, 25 Nov 2024 19:44:26 +0200 Subject: [PATCH 4/7] add missing libraries --- Cargo.lock | 2 ++ program-manager/Cargo.toml | 28 ++++++++-------- program-manager/src/library.rs | 60 ++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fec0d39d..6d370f70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5463,9 +5463,11 @@ dependencies = [ "valence-authorization", "valence-authorization-utils", "valence-forwarder-library", + "valence-generic-ibc-transfer-library", "valence-library-base", "valence-library-utils", "valence-macros", + "valence-neutron-ibc-transfer-library", "valence-osmosis-cl-lper", "valence-osmosis-cl-withdrawer", "valence-osmosis-gamm-lper", diff --git a/program-manager/Cargo.toml b/program-manager/Cargo.toml index 031fda47..e4e4431c 100644 --- a/program-manager/Cargo.toml +++ b/program-manager/Cargo.toml @@ -23,19 +23,21 @@ valence-processor-utils = { workspace = true } valence-program-registry-utils = { workspace = true } valence-library-base = { workspace = true } -valence-splitter-library = { workspace = true } -valence-reverse-splitter-library = { workspace = true } -valence-astroport-lper = { workspace = true } -valence-astroport-withdrawer = { workspace = true } -valence-account-utils = { workspace = true } -valence-authorization = { workspace = true } -valence-processor = { workspace = true } -valence-program-registry = { workspace = true } -valence-forwarder-library = { workspace = true } -valence-osmosis-gamm-lper = { workspace = true } -valence-osmosis-gamm-withdrawer = { workspace = true } -valence-osmosis-cl-lper = { workspace = true } -valence-osmosis-cl-withdrawer = { workspace = true } +valence-splitter-library = { workspace = true } +valence-reverse-splitter-library = { workspace = true } +valence-astroport-lper = { workspace = true } +valence-astroport-withdrawer = { workspace = true } +valence-account-utils = { workspace = true } +valence-authorization = { workspace = true } +valence-processor = { workspace = true } +valence-program-registry = { workspace = true } +valence-forwarder-library = { workspace = true } +valence-osmosis-gamm-lper = { workspace = true } +valence-osmosis-gamm-withdrawer = { workspace = true } +valence-osmosis-cl-lper = { workspace = true } +valence-osmosis-cl-withdrawer = { workspace = true } +valence-generic-ibc-transfer-library = { workspace = true } +valence-neutron-ibc-transfer-library = { workspace = true } tokio = { workspace = true } aho-corasick = "1.1" diff --git a/program-manager/src/library.rs b/program-manager/src/library.rs index 7016ae86..105caba7 100644 --- a/program-manager/src/library.rs +++ b/program-manager/src/library.rs @@ -81,6 +81,8 @@ pub enum LibraryConfig { ValenceAstroportWithdrawer(valence_astroport_withdrawer::msg::LibraryConfig), ValenceOsmosisGammLper(valence_osmosis_gamm_lper::msg::LibraryConfig), ValenceOsmosisGammWithdrawer(valence_osmosis_gamm_withdrawer::msg::LibraryConfig), + ValenceGenericIbcTransferLibrary(valence_generic_ibc_transfer_library::msg::LibraryConfig), + ValenceNeutronIbcTransferLibrary(valence_neutron_ibc_transfer_library::msg::LibraryConfig), } #[derive( @@ -106,6 +108,12 @@ pub enum LibraryConfigUpdate { ValenceAstroportWithdrawer(valence_astroport_withdrawer::msg::LibraryConfigUpdate), ValenceOsmosisGammLper(valence_osmosis_gamm_lper::msg::LibraryConfigUpdate), ValenceOsmosisGammWithdrawer(valence_osmosis_gamm_withdrawer::msg::LibraryConfigUpdate), + ValenceGenericIbcTransferLibrary( + valence_generic_ibc_transfer_library::msg::LibraryConfigUpdate, + ), + ValenceNeutronIbcTransferLibrary( + valence_neutron_ibc_transfer_library::msg::LibraryConfigUpdate, + ), } impl LibraryConfigUpdate { @@ -168,6 +176,22 @@ impl LibraryConfigUpdate { new_config: library_config_update, }) } + LibraryConfigUpdate::ValenceGenericIbcTransferLibrary(library_config_update) => { + to_json_binary(&valence_library_utils::msg::ExecuteMsg::< + Empty, + valence_generic_ibc_transfer_library::msg::LibraryConfigUpdate, + >::UpdateConfig { + new_config: library_config_update, + }) + } + LibraryConfigUpdate::ValenceNeutronIbcTransferLibrary(library_config_update) => { + to_json_binary(&valence_library_utils::msg::ExecuteMsg::< + Empty, + valence_neutron_ibc_transfer_library::msg::LibraryConfigUpdate, + >::UpdateConfig { + new_config: library_config_update, + }) + } } .map_err(LibraryError::CosmwasmStdError) } @@ -225,6 +249,18 @@ impl LibraryConfig { let json = serde_json::to_string(&config)?; let res = ac.replace_all(&json, &replace_with); + *config = serde_json::from_str(&res)?; + } + LibraryConfig::ValenceGenericIbcTransferLibrary(ref mut config) => { + let json = serde_json::to_string(&config)?; + let res = ac.replace_all(&json, &replace_with); + + *config = serde_json::from_str(&res)?; + } + LibraryConfig::ValenceNeutronIbcTransferLibrary(ref mut config) => { + let json = serde_json::to_string(&config)?; + let res = ac.replace_all(&json, &replace_with); + *config = serde_json::from_str(&res)?; } } @@ -270,6 +306,16 @@ impl LibraryConfig { processor, config: config.clone(), }), + LibraryConfig::ValenceGenericIbcTransferLibrary(config) => to_vec(&InstantiateMsg { + owner, + processor, + config: config.clone(), + }), + LibraryConfig::ValenceNeutronIbcTransferLibrary(config) => to_vec(&InstantiateMsg { + owner, + processor, + config: config.clone(), + }), } .map_err(LibraryError::SerdeJsonError) } @@ -305,6 +351,14 @@ impl LibraryConfig { config.pre_validate(api)?; Ok(()) } + LibraryConfig::ValenceGenericIbcTransferLibrary(config) => { + config.pre_validate(api)?; + Ok(()) + } + LibraryConfig::ValenceNeutronIbcTransferLibrary(config) => { + config.pre_validate(api)?; + Ok(()) + } } } @@ -334,6 +388,12 @@ impl LibraryConfig { LibraryConfig::ValenceOsmosisGammWithdrawer(config) => { Self::find_account_ids(ac, serde_json::to_string(&config)?) } + LibraryConfig::ValenceGenericIbcTransferLibrary(config) => { + Self::find_account_ids(ac, serde_json::to_string(&config)?) + } + LibraryConfig::ValenceNeutronIbcTransferLibrary(config) => { + Self::find_account_ids(ac, serde_json::to_string(&config)?) + } } } From 61f13f39dd274ca74d30a46155208d5cfdc460e2 Mon Sep 17 00:00:00 2001 From: Art3mix Date: Tue, 26 Nov 2024 11:49:53 +0200 Subject: [PATCH 5/7] update schema --- .../schema/valence-program-manager.json | 167 +++++++++++++++++- 1 file changed, 160 insertions(+), 7 deletions(-) diff --git a/program-manager/schema/valence-program-manager.json b/program-manager/schema/valence-program-manager.json index b0b27243..75894f5d 100644 --- a/program-manager/schema/valence-program-manager.json +++ b/program-manager/schema/valence-program-manager.json @@ -611,6 +611,28 @@ }, "additionalProperties": false }, + "IbcTransferAmount": { + "oneOf": [ + { + "type": "string", + "enum": [ + "full_amount" + ] + }, + { + "type": "object", + "required": [ + "fixed_amount" + ], + "properties": { + "fixed_amount": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + ] + }, "Int64": { "description": "An implementation of i64 that is using strings for JSON encoding/decoding, such that the full i64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `i64` to get the value out:\n\n``` # use cosmwasm_std::Int64; let a = Int64::from(258i64); assert_eq!(a.i64(), 258); ```", "type": "string" @@ -752,6 +774,30 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "ValenceGenericIbcTransferLibrary" + ], + "properties": { + "ValenceGenericIbcTransferLibrary": { + "$ref": "#/definitions/LibraryConfigUpdate9" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ValenceNeutronIbcTransferLibrary" + ], + "properties": { + "ValenceNeutronIbcTransferLibrary": { + "$ref": "#/definitions/LibraryConfigUpdate9" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -759,7 +805,7 @@ ], "properties": { "ValenceOsmosisClLper": { - "$ref": "#/definitions/LibraryConfigUpdate9" + "$ref": "#/definitions/LibraryConfigUpdate10" } }, "additionalProperties": false @@ -771,7 +817,7 @@ ], "properties": { "ValenceOsmosisClWithdrawer": { - "$ref": "#/definitions/LibraryConfigUpdate10" + "$ref": "#/definitions/LibraryConfigUpdate11" } }, "additionalProperties": false @@ -779,6 +825,42 @@ ] }, "LibraryConfigUpdate10": { + "type": "object", + "properties": { + "input_addr": { + "anyOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + }, + { + "type": "null" + } + ] + }, + "lp_config": { + "anyOf": [ + { + "$ref": "#/definitions/LiquidityProviderConfig3" + }, + { + "type": "null" + } + ] + }, + "output_addr": { + "anyOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "LibraryConfigUpdate11": { "type": "object", "properties": { "input_addr": { @@ -1078,27 +1160,34 @@ "LibraryConfigUpdate9": { "type": "object", "properties": { - "input_addr": { + "amount": { "anyOf": [ { - "$ref": "#/definitions/LibraryAccountType" + "$ref": "#/definitions/IbcTransferAmount" }, { "type": "null" } ] }, - "lp_config": { + "denom": { "anyOf": [ { - "$ref": "#/definitions/LiquidityProviderConfig3" + "$ref": "#/definitions/UncheckedDenom" }, { "type": "null" } ] }, - "output_addr": { + "denom_to_pfm_map": { + "type": [ + "object", + "null" + ], + "additionalProperties": false + }, + "input_addr": { "anyOf": [ { "$ref": "#/definitions/LibraryAccountType" @@ -1107,6 +1196,28 @@ "type": "null" } ] + }, + "memo": { + "type": [ + "string", + "null" + ] + }, + "output_addr": { + "type": [ + "string", + "null" + ] + }, + "remote_chain_info": { + "anyOf": [ + { + "$ref": "#/definitions/RemoteChainInfo" + }, + { + "type": "null" + } + ] } }, "additionalProperties": false @@ -1381,6 +1492,26 @@ }, "additionalProperties": false }, + "PacketForwardMiddlewareConfig": { + "type": "object", + "required": [ + "hop_chain_receiver_address", + "hop_to_destination_chain_channel_id", + "local_to_hop_chain_channel_id" + ], + "properties": { + "hop_chain_receiver_address": { + "type": "string" + }, + "hop_to_destination_chain_channel_id": { + "type": "string" + }, + "local_to_hop_chain_channel_id": { + "type": "string" + } + }, + "additionalProperties": false + }, "PairType": { "oneOf": [ { @@ -1743,6 +1874,28 @@ }, "additionalProperties": false }, + "RemoteChainInfo": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "ibc_transfer_timeout": { + "anyOf": [ + { + "$ref": "#/definitions/Uint64" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, "RetryLogic": { "type": "object", "required": [ From 581f8e36e8ac2bd7dc788cf08ea32c7a415e10db Mon Sep 17 00:00:00 2001 From: Art3mix Date: Tue, 26 Nov 2024 23:43:32 +0200 Subject: [PATCH 6/7] fix --- packages/valence-macros/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/valence-macros/src/lib.rs b/packages/valence-macros/src/lib.rs index fb3aeefa..24ef111e 100644 --- a/packages/valence-macros/src/lib.rs +++ b/packages/valence-macros/src/lib.rs @@ -158,7 +158,7 @@ pub fn manager_impl_library_configs(_attr: TokenStream, input: TokenStream) -> T let mut update_msg_matches = Vec::new(); let mut replace_config_matches = Vec::new(); let mut get_instantiate_msg_matches = Vec::new(); - let mut per_validate_matches = Vec::new(); + let mut pre_validate_matches = Vec::new(); let mut get_account_ids_matches = Vec::new(); for variant in variants { @@ -181,7 +181,7 @@ pub fn manager_impl_library_configs(_attr: TokenStream, input: TokenStream) -> T get_instantiate_msg_matches.push(quote! { #enum_ident::None => return Err(LibraryError::NoLibraryConfig) }); - per_validate_matches.push(quote! { + pre_validate_matches.push(quote! { #enum_ident::None => Err(LibraryError::NoLibraryConfig) }); get_account_ids_matches.push(quote! { @@ -238,8 +238,8 @@ pub fn manager_impl_library_configs(_attr: TokenStream, input: TokenStream) -> T }) }); - // Add per_validate_config match - per_validate_matches.push(quote! { + // Add pre_validate_config match + pre_validate_matches.push(quote! { #enum_ident::#variant_ident(config) => { config.pre_validate(api)?; Ok(()) @@ -310,9 +310,9 @@ pub fn manager_impl_library_configs(_attr: TokenStream, input: TokenStream) -> T .map_err(LibraryError::SerdeJsonError) } - pub fn per_validate_config(&self, api: &dyn cosmwasm_std::Api) -> LibraryResult<()> { + pub fn pre_validate_config(&self, api: &dyn cosmwasm_std::Api) -> LibraryResult<()> { match self { - #(#per_validate_matches,)* + #(#pre_validate_matches,)* } } From eff3e19e3f259867d0cd18ff9f128c9800da48b6 Mon Sep 17 00:00:00 2001 From: Art3mix Date: Wed, 27 Nov 2024 17:48:09 +0200 Subject: [PATCH 7/7] remove the leak and copy the mockApi --- Cargo.lock | 2 + Cargo.toml | 1 + local-interchaintest/examples/test.rs | 109 ++++++++++++++ program-manager/Cargo.toml | 2 + program-manager/src/domain/cosmos_cw.rs | 37 +++-- program-manager/src/domain/mod.rs | 3 +- program-manager/src/helpers.rs | 35 ----- program-manager/src/lib.rs | 1 + program-manager/src/mock_api.rs | 185 ++++++++++++++++++++++++ 9 files changed, 322 insertions(+), 53 deletions(-) create mode 100644 local-interchaintest/examples/test.rs create mode 100644 program-manager/src/mock_api.rs diff --git a/Cargo.lock b/Cargo.lock index b457d659..f7e93e1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5427,6 +5427,7 @@ dependencies = [ "bech32 0.11.0", "config", "cosmos-grpc-client", + "cosmwasm-crypto 2.1.4", "cosmwasm-schema 2.1.4", "cosmwasm-std 2.1.4", "cw-ownable", @@ -5440,6 +5441,7 @@ dependencies = [ "once_cell", "prost 0.12.6", "prost-types 0.12.6", + "rand_core 0.6.4", "serde", "serde_json", "serde_json_any_key", diff --git a/Cargo.toml b/Cargo.toml index 63b77532..7fb54bc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ overflow-checks = true anyhow = "1.0.86" cosmwasm-std = { version = "2.1.3" } cosmwasm-schema = "2.1.3" +cosmwasm-crypto = "2.1.3" cw-denom = { package = "cw-denom", git = "https://github.com/DA0-DA0/dao-contracts", branch = "cw-std-2" } cw-ownable = "2.0.0" cw-utils = "2.0.0" diff --git a/local-interchaintest/examples/test.rs b/local-interchaintest/examples/test.rs new file mode 100644 index 00000000..b1694b65 --- /dev/null +++ b/local-interchaintest/examples/test.rs @@ -0,0 +1,109 @@ +use std::error::Error; + +use local_interchaintest::utils::{ + manager::{setup_manager, use_manager_init, SPLITTER_NAME}, + LOGS_FILE_PATH, NEUTRON_CONFIG_FILE, VALENCE_ARTIFACTS_PATH, +}; +use localic_utils::{ + ConfigChainBuilder, TestContextBuilder, GAIA_CHAIN_NAME, LOCAL_IC_API_URL, + NEUTRON_CHAIN_ADMIN_ADDR, NEUTRON_CHAIN_NAME, +}; +use valence_authorization_utils::{ + authorization_message::{Message, MessageDetails, MessageType, ParamRestriction}, + builders::{AtomicFunctionBuilder, AtomicSubroutineBuilder, AuthorizationBuilder}, +}; +use valence_library_utils::denoms::UncheckedDenom; +use valence_program_manager::{ + account::{AccountInfo, AccountType}, + library::{LibraryConfig, LibraryInfo}, + program_config_builder::ProgramConfigBuilder, +}; +use valence_splitter_library::msg::{UncheckedSplitAmount, UncheckedSplitConfig}; + +fn main() -> Result<(), Box> { + env_logger::init(); + + let mut test_ctx = TestContextBuilder::default() + .with_unwrap_raw_logs(true) + .with_api_url(LOCAL_IC_API_URL) + .with_artifacts_dir(VALENCE_ARTIFACTS_PATH) + .with_chain(ConfigChainBuilder::default_neutron().build()?) + .with_log_file_path(LOGS_FILE_PATH) + .build()?; + + setup_manager( + &mut test_ctx, + NEUTRON_CONFIG_FILE, + vec![GAIA_CHAIN_NAME], + vec![SPLITTER_NAME], + )?; + + let mut builder = ProgramConfigBuilder::new(NEUTRON_CHAIN_ADMIN_ADDR.to_string()); + let neutron_domain = + valence_program_manager::domain::Domain::CosmosCosmwasm(NEUTRON_CHAIN_NAME.to_string()); + + let account_1 = builder.add_account(AccountInfo::new( + "test_1".to_string(), + &neutron_domain, + AccountType::default(), + )); + let account_2 = builder.add_account(AccountInfo::new( + "test_2".to_string(), + &neutron_domain, + AccountType::default(), + )); + + let library_config = valence_splitter_library::msg::LibraryConfig { + input_addr: account_1.clone(), + splits: vec![UncheckedSplitConfig { + denom: UncheckedDenom::Native("test".to_string()), + account: account_2.clone(), + amount: UncheckedSplitAmount::FixedAmount(1000_u128.into()), + }], + }; + + let library_1 = builder.add_library(LibraryInfo::new( + "test_splitter".to_string(), + &neutron_domain, + LibraryConfig::ValenceSplitterLibrary(library_config.clone()), + )); + + builder.add_link(&library_1, vec![&account_1], vec![&account_2]); + + let action_label = "swap"; + builder.add_authorization( + AuthorizationBuilder::new() + .with_label(action_label) + .with_subroutine( + AtomicSubroutineBuilder::new() + .with_function( + AtomicFunctionBuilder::new() + .with_contract_address(library_1.clone()) + .with_message_details(MessageDetails { + message_type: MessageType::CosmwasmExecuteMsg, + message: Message { + name: "process_function".to_string(), + params_restrictions: Some(vec![ + ParamRestriction::MustBeIncluded(vec![ + "process_function".to_string(), + "split".to_string(), + ]), + ]), + }, + }) + .build(), + ) + .build(), + ) + .build(), + ); + + let mut program_config = builder.build(); + let mut program_config2 = program_config.clone(); + + use_manager_init(&mut program_config)?; + + use_manager_init(&mut program_config2)?; + + Ok(()) +} diff --git a/program-manager/Cargo.toml b/program-manager/Cargo.toml index 01a8b742..e20bc53f 100644 --- a/program-manager/Cargo.toml +++ b/program-manager/Cargo.toml @@ -10,6 +10,7 @@ repository = { workspace = true } [dependencies] cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } +cosmwasm-crypto = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } @@ -17,6 +18,7 @@ anyhow = { workspace = true } cw-ownable = { workspace = true } env_logger = { workspace = true } log = { workspace = true } +rand_core = "0.6.4" valence-library-utils = { workspace = true } valence-macros = { workspace = true } diff --git a/program-manager/src/domain/cosmos_cw.rs b/program-manager/src/domain/cosmos_cw.rs index d9a39d5f..4c7e27d6 100644 --- a/program-manager/src/domain/cosmos_cw.rs +++ b/program-manager/src/domain/cosmos_cw.rs @@ -8,8 +8,8 @@ use crate::{ account::{AccountType, InstantiateAccountData}, bridge::PolytoneSingleChainInfo, config::{ChainInfo, ConfigError, GLOBAL_CONFIG}, - helpers::{addr_canonicalize, addr_humanize}, library::{LibraryConfig, LibraryError}, + mock_api::MockApi, program_config::ProgramConfig, NEUTRON_CHAIN, }; @@ -26,7 +26,7 @@ use cosmos_grpc_client::{ cosmrs::bip32::secp256k1::sha2::{digest::Update, Digest, Sha256, Sha512}, BroadcastMode, Decimal, GrpcClient, ProstMsgNameToAny, Wallet, }; -use cosmwasm_std::{from_json, instantiate2_address, testing::MockApi, to_json_binary}; +use cosmwasm_std::{from_json, instantiate2_address, to_json_binary, Api}; use futures::future::BoxFuture; use serde_json::to_vec; use strum::VariantNames; @@ -86,7 +86,6 @@ pub struct CosmosCosmwasmConnector { wallet: Wallet, code_ids: HashMap, chain_name: String, - prefix: String, api: MockApi, } @@ -133,9 +132,7 @@ impl CosmosCosmwasmConnector { wallet, code_ids: code_ids.clone(), chain_name: chain_info.name.clone(), - prefix: chain_info.prefix.clone(), - api: cosmwasm_std::testing::MockApi::default() - .with_prefix(Box::leak(Box::new(chain_info.prefix.clone()))), + api: MockApi::new(chain_info.prefix.clone()), }) } } @@ -220,14 +217,20 @@ impl Connector for CosmosCosmwasmConnector { let addr_canonical = instantiate2_address( &checksum, - &addr_canonicalize(&self.prefix, self.wallet.account_address.as_str()).unwrap(), + &self + .api + .addr_canonicalize(self.wallet.account_address.as_str()) + .unwrap(), &salt, ) .context("Failed to instantiate2 address") .map_err(CosmosCosmwasmError::Error)?; - let addr = - addr_humanize(&self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; + let addr = self + .api + .addr_humanize(&addr_canonical) + .map_err(CosmosCosmwasmError::CosmwasmStdError)? + .to_string(); Ok((addr, salt.to_vec())) } @@ -261,18 +264,20 @@ impl Connector for CosmosCosmwasmConnector { let addr_canonical = instantiate2_address( &checksum, - &addr_canonicalize( - &self.prefix, - receiving_chain_bridge_info.voice_addr.as_str(), - ) - .unwrap(), + &self + .api + .addr_canonicalize(receiving_chain_bridge_info.voice_addr.as_str()) + .unwrap(), &salt, ) .context("Failed to instantiate2 address") .map_err(CosmosCosmwasmError::Error)?; - let addr = - addr_humanize(&self.prefix, &addr_canonical).map_err(CosmosCosmwasmError::Error)?; + let addr = self + .api + .addr_humanize(&addr_canonical) + .map_err(CosmosCosmwasmError::CosmwasmStdError)? + .to_string(); Ok(addr) } diff --git a/program-manager/src/domain/mod.rs b/program-manager/src/domain/mod.rs index b72c4f4c..02c8e3af 100644 --- a/program-manager/src/domain/mod.rs +++ b/program-manager/src/domain/mod.rs @@ -8,7 +8,6 @@ use async_trait::async_trait; use cosmos_cw::{CosmosCosmwasmConnector, CosmosCosmwasmError}; use cosmwasm_schema::schemars::JsonSchema; -use cosmwasm_std::testing::MockApi; use serde::{Deserialize, Serialize}; // use cosmos_evm::CosmosEvmError; @@ -17,7 +16,7 @@ use valence_authorization_utils::authorization::AuthorizationInfo; use crate::{ account::InstantiateAccountData, config::ConfigError, library::LibraryConfig, - program_config::ProgramConfig, + mock_api::MockApi, program_config::ProgramConfig, }; pub type ConnectorResult = Result; diff --git a/program-manager/src/helpers.rs b/program-manager/src/helpers.rs index 9409dbc2..d2fb1212 100644 --- a/program-manager/src/helpers.rs +++ b/program-manager/src/helpers.rs @@ -1,42 +1,7 @@ use std::collections::HashMap; -use anyhow::anyhow; -use bech32::{encode, primitives::decode::CheckedHrpstring, Bech32, Hrp}; -use cosmwasm_std::CanonicalAddr; - use crate::config::{ConfigResult, GLOBAL_CONFIG}; -fn validate_length(bytes: &[u8]) -> Result<(), anyhow::Error> { - match bytes.len() { - 1..=255 => Ok(()), - _ => Err(anyhow!("Invalid canonical address length")), - } -} - -pub fn addr_canonicalize(prefix: &str, input: &str) -> Result { - let hrp_str = - CheckedHrpstring::new::(input).map_err(|_| anyhow!("Error decoding bech32"))?; - - if !hrp_str - .hrp() - .as_bytes() - .eq_ignore_ascii_case(prefix.as_bytes()) - { - return Err(anyhow!("Wrong bech32 prefix")); - } - - let bytes: Vec = hrp_str.byte_iter().collect(); - validate_length(&bytes)?; - Ok(bytes.into()) -} - -pub fn addr_humanize(prefix: &str, canonical: &CanonicalAddr) -> Result { - validate_length(canonical.as_ref())?; - - let prefix = Hrp::parse(prefix).map_err(|_| anyhow!("Invalid bech32 prefix"))?; - encode::(prefix, canonical.as_slice()).map_err(|_| anyhow!("Bech32 encoding error")) -} - pub async fn get_polytone_info( main_chain: &str, other_chain: &str, diff --git a/program-manager/src/lib.rs b/program-manager/src/lib.rs index b505c6da..317630c1 100644 --- a/program-manager/src/lib.rs +++ b/program-manager/src/lib.rs @@ -7,6 +7,7 @@ pub mod error; pub mod helpers; pub mod library; pub mod macros; +pub mod mock_api; pub mod program_config; pub mod program_config_builder; pub mod program_migration; diff --git a/program-manager/src/mock_api.rs b/program-manager/src/mock_api.rs new file mode 100644 index 00000000..07a091e2 --- /dev/null +++ b/program-manager/src/mock_api.rs @@ -0,0 +1,185 @@ +use bech32::{encode, primitives::decode::CheckedHrpstring, Bech32, Hrp}; +use cosmwasm_std::{ + Addr, Api, CanonicalAddr, HashFunction, RecoverPubkeyError, StdError, StdResult, + VerificationError, +}; +use rand_core::OsRng; + +pub struct MockApi { + bech32_prefix: String, +} + +impl MockApi { + pub fn new(bech32_prefix: String) -> Self { + MockApi { bech32_prefix } + } +} + +impl Api for MockApi { + fn addr_validate(&self, input: &str) -> StdResult { + let canonical = self.addr_canonicalize(input)?; + let normalized = self.addr_humanize(&canonical)?; + if input != normalized.as_str() { + return Err(StdError::generic_err( + "Invalid input: address not normalized", + )); + } + Ok(Addr::unchecked(input)) + } + + fn addr_canonicalize(&self, input: &str) -> StdResult { + let hrp_str = CheckedHrpstring::new::(input) + .map_err(|_| StdError::generic_err("Error decoding bech32"))?; + + if !hrp_str + .hrp() + .as_bytes() + .eq_ignore_ascii_case(self.bech32_prefix.as_bytes()) + { + return Err(StdError::generic_err("Wrong bech32 prefix")); + } + + let bytes: Vec = hrp_str.byte_iter().collect(); + validate_length(&bytes)?; + Ok(bytes.into()) + } + + fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult { + validate_length(canonical.as_ref())?; + + let prefix = Hrp::parse(&self.bech32_prefix) + .map_err(|_| StdError::generic_err("Invalid bech32 prefix"))?; + encode::(prefix, canonical.as_slice()) + .map(Addr::unchecked) + .map_err(|_| StdError::generic_err("Bech32 encoding error")) + } + + fn bls12_381_aggregate_g1(&self, g1s: &[u8]) -> Result<[u8; 48], VerificationError> { + cosmwasm_crypto::bls12_381_aggregate_g1(g1s).map_err(Into::into) + } + + fn bls12_381_aggregate_g2(&self, g2s: &[u8]) -> Result<[u8; 96], VerificationError> { + cosmwasm_crypto::bls12_381_aggregate_g2(g2s).map_err(Into::into) + } + + fn bls12_381_pairing_equality( + &self, + ps: &[u8], + qs: &[u8], + r: &[u8], + s: &[u8], + ) -> Result { + cosmwasm_crypto::bls12_381_pairing_equality(ps, qs, r, s).map_err(Into::into) + } + + fn bls12_381_hash_to_g1( + &self, + hash_function: HashFunction, + msg: &[u8], + dst: &[u8], + ) -> Result<[u8; 48], VerificationError> { + Ok(cosmwasm_crypto::bls12_381_hash_to_g1( + hash_function.into(), + msg, + dst, + )) + } + + fn bls12_381_hash_to_g2( + &self, + hash_function: HashFunction, + msg: &[u8], + dst: &[u8], + ) -> Result<[u8; 96], VerificationError> { + Ok(cosmwasm_crypto::bls12_381_hash_to_g2( + hash_function.into(), + msg, + dst, + )) + } + + fn secp256k1_verify( + &self, + message_hash: &[u8], + signature: &[u8], + public_key: &[u8], + ) -> Result { + Ok(cosmwasm_crypto::secp256k1_verify( + message_hash, + signature, + public_key, + )?) + } + + fn secp256k1_recover_pubkey( + &self, + message_hash: &[u8], + signature: &[u8], + recovery_param: u8, + ) -> Result, RecoverPubkeyError> { + let pubkey = + cosmwasm_crypto::secp256k1_recover_pubkey(message_hash, signature, recovery_param)?; + Ok(pubkey.to_vec()) + } + + fn secp256r1_verify( + &self, + message_hash: &[u8], + signature: &[u8], + public_key: &[u8], + ) -> Result { + Ok(cosmwasm_crypto::secp256r1_verify( + message_hash, + signature, + public_key, + )?) + } + + fn secp256r1_recover_pubkey( + &self, + message_hash: &[u8], + signature: &[u8], + recovery_param: u8, + ) -> Result, RecoverPubkeyError> { + let pubkey = + cosmwasm_crypto::secp256r1_recover_pubkey(message_hash, signature, recovery_param)?; + Ok(pubkey.to_vec()) + } + + fn ed25519_verify( + &self, + message: &[u8], + signature: &[u8], + public_key: &[u8], + ) -> Result { + Ok(cosmwasm_crypto::ed25519_verify( + message, signature, public_key, + )?) + } + + fn ed25519_batch_verify( + &self, + messages: &[&[u8]], + signatures: &[&[u8]], + public_keys: &[&[u8]], + ) -> Result { + Ok(cosmwasm_crypto::ed25519_batch_verify( + &mut OsRng, + messages, + signatures, + public_keys, + )?) + } + + fn debug(&self, #[allow(unused)] message: &str) { + println!("{message}"); + } +} + +/// Does basic validation of the number of bytes in a canonical address +fn validate_length(bytes: &[u8]) -> StdResult<()> { + match bytes.len() { + 1..=255 => Ok(()), + _ => Err(StdError::generic_err("Invalid canonical address length")), + } +}