diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ddace314..b55ae7d60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Breaking +- [cw-orch-interchain-core] Modify the structure and the names of the IBC analysis and following structure. ## 0.26.0 [8. October 2024] diff --git a/Cargo.toml b/Cargo.toml index 1046dd545..a364767fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,12 +64,12 @@ cw-orch-fns-derive = { path = "packages/macros/cw-orch-fns-derive", version = "0 cw-orch-clone-testing = { version = "0.8.0", path = "packages/clone-testing" } # Interchain -cw-orch-interchain = { path = "cw-orch-interchain", version = "0.6.0" } -cw-orch-interchain-core = { path = "packages/interchain/interchain-core", version = "0.7.0" } -cw-orch-interchain-daemon = { path = "packages/interchain/interchain-daemon", version = "0.7.0" } -cw-orch-interchain-mock = { path = "packages/interchain/interchain-mock", version = "0.7.0" } +cw-orch-interchain = { path = "cw-orch-interchain", version = "0.7.0" } +cw-orch-interchain-core = { path = "packages/interchain/interchain-core", version = "0.8.0" } +cw-orch-interchain-daemon = { path = "packages/interchain/interchain-daemon", version = "0.8.0" } +cw-orch-interchain-mock = { path = "packages/interchain/interchain-mock", version = "0.8.0" } cw-orch-starship = { path = "packages/interchain/starship", version = "0.6.0" } -cw-orch-proto = { path = "packages/interchain/proto", version = "0.7.0" } +cw-orch-proto = { path = "packages/interchain/proto", version = "0.8.0" } thiserror = { version = "1.0.63" } diff --git a/cw-orch-interchain/Cargo.toml b/cw-orch-interchain/Cargo.toml index 3b1395c43..24d3eed3d 100644 --- a/cw-orch-interchain/Cargo.toml +++ b/cw-orch-interchain/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-orch-interchain" -version = "0.6.0" +version = "0.7.0" authors = { workspace = true } edition = { workspace = true } license = { workspace = true } diff --git a/cw-orch-interchain/examples/doc_daemon.rs b/cw-orch-interchain/examples/doc_daemon.rs index 78f1c2b05..4a640f20c 100644 --- a/cw-orch-interchain/examples/doc_daemon.rs +++ b/cw-orch-interchain/examples/doc_daemon.rs @@ -1,8 +1,6 @@ use cw_orch::prelude::networks::{LOCAL_JUNO, LOCAL_MIGALOO, LOCAL_OSMO}; use cw_orch::prelude::*; -use cw_orch_interchain::{ - ChannelCreationValidator, ChannelCreator, DaemonInterchain, InterchainEnv, Starship, -}; +use cw_orch_interchain::prelude::*; fn create_daemon_env() -> cw_orch::anyhow::Result { // ANCHOR: DAEMON_INTERCHAIN_CREATION diff --git a/cw-orch-interchain/examples/doc_mock.rs b/cw-orch-interchain/examples/doc_mock.rs index 99637b3fb..ba678478e 100644 --- a/cw-orch-interchain/examples/doc_mock.rs +++ b/cw-orch-interchain/examples/doc_mock.rs @@ -1,5 +1,5 @@ use cw_orch::prelude::*; -use cw_orch_interchain::{InterchainEnv, MockInterchainEnv}; +use cw_orch_interchain::prelude::*; use ibc_relayer_types::core::ics24_host::identifier::PortId; fn crate_mock_env() -> cw_orch::anyhow::Result { diff --git a/cw-orch-interchain/examples/follow_packets_txhash.rs b/cw-orch-interchain/examples/follow_packets_txhash.rs index c084b4f88..7b165fd08 100644 --- a/cw-orch-interchain/examples/follow_packets_txhash.rs +++ b/cw-orch-interchain/examples/follow_packets_txhash.rs @@ -3,7 +3,7 @@ use cw_orch::{ environment::{ChainInfo, NetworkInfo}, prelude::networks::osmosis::OSMOSIS_1, }; -use cw_orch_interchain_daemon::{ChannelCreationValidator, DaemonInterchain}; +use cw_orch_interchain::prelude::*; pub const NOBLE: NetworkInfo = NetworkInfo { chain_name: "noble", @@ -37,7 +37,7 @@ fn follow_by_tx_hash() -> cw_orch::anyhow::Result<()> { src_chain.chain_id, "D2C5459C54B394C168B8DFA214670FF9E2A0349CCBEF149CF5CB508A5B3BCB84".to_string(), )? - .into_result()?; + .assert()?; Ok(()) } diff --git a/cw-orch-interchain/examples/timeout_packet.rs b/cw-orch-interchain/examples/timeout_packet.rs index d05f9b769..ac07a0bd2 100644 --- a/cw-orch-interchain/examples/timeout_packet.rs +++ b/cw-orch-interchain/examples/timeout_packet.rs @@ -1,7 +1,7 @@ use cosmos_sdk_proto::traits::{Message, Name}; use cw_orch::{environment::QueryHandler, prelude::*}; -use cw_orch_interchain_core::InterchainEnv; -use cw_orch_interchain_daemon::ChannelCreator as _; +use cw_orch_interchain::prelude::*; +use cw_orch_interchain_core::IbcPacketOutcome; use cw_orch_starship::Starship; use ibc_proto::ibc::{ applications::transfer::v1::{MsgTransfer, MsgTransferResponse}, @@ -59,9 +59,9 @@ fn main() -> cw_orch::anyhow::Result<()> { let result = interchain.await_packets("juno-1", tx_resp)?; - match &result.packets[0].outcome { - cw_orch_interchain_core::types::IbcPacketOutcome::Timeout { .. } => {} - cw_orch_interchain_core::types::IbcPacketOutcome::Success { .. } => { + match &result.packets[0] { + IbcPacketOutcome::Timeout { .. } => {} + IbcPacketOutcome::Success { .. } => { panic!("Expected timeout") } } diff --git a/cw-orch-interchain/src/lib.rs b/cw-orch-interchain/src/lib.rs index ef6c3e383..79009faa7 100644 --- a/cw-orch-interchain/src/lib.rs +++ b/cw-orch-interchain/src/lib.rs @@ -2,7 +2,7 @@ #[cfg(not(target_arch = "wasm32"))] pub mod prelude { pub use cw_orch_interchain_core::{ - types::ChannelCreationResult, IbcQueryHandler, InterchainEnv, + results::ChannelCreationResult, IbcQueryHandler, InterchainEnv, PacketAnalysis, }; pub use cw_orch_interchain_mock::{MockBech32InterchainEnv, MockInterchainEnv}; @@ -20,15 +20,23 @@ pub mod prelude { } #[cfg(not(target_arch = "wasm32"))] -pub use cw_orch_interchain_core::*; +pub mod core { + pub use cw_orch_interchain_core::*; +} #[cfg(not(target_arch = "wasm32"))] -pub use cw_orch_interchain_mock::*; +pub mod mock { + pub use cw_orch_interchain_mock::*; +} #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "daemon")] -pub use cw_orch_interchain_daemon::*; +pub mod daemon { + pub use cw_orch_interchain_daemon::*; +} #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "daemon")] -pub use cw_orch_starship::*; +pub mod starship { + pub use cw_orch_starship::*; +} diff --git a/cw-orch-interchain/tests/timeout_packet_mock.rs b/cw-orch-interchain/tests/timeout_packet_mock.rs index 2d8a4409f..016289e86 100644 --- a/cw-orch-interchain/tests/timeout_packet_mock.rs +++ b/cw-orch-interchain/tests/timeout_packet_mock.rs @@ -46,9 +46,9 @@ fn timeout_packet_mock() -> cw_orch::anyhow::Result<()> { let result = interchain.await_packets("juno-1", tx_resp)?; - match &result.packets[0].outcome { - cw_orch_interchain_core::types::IbcPacketOutcome::Timeout { .. } => {} - cw_orch_interchain_core::types::IbcPacketOutcome::Success { .. } => { + match &result.packets[0] { + cw_orch_interchain_core::IbcPacketOutcome::Timeout { .. } => {} + cw_orch_interchain_core::IbcPacketOutcome::Success { .. } => { panic!("Expected timeout") } } diff --git a/packages/integrations/cw-plus/tests/interface_tests.rs b/packages/integrations/cw-plus/tests/interface_tests.rs index 786a1ac5a..1c8e00d24 100644 --- a/packages/integrations/cw-plus/tests/interface_tests.rs +++ b/packages/integrations/cw-plus/tests/interface_tests.rs @@ -321,7 +321,7 @@ mod cw20_ics { msg::AllowedInfo, }; use cw_orch::prelude::*; - use cw_orch_interchain::{env::contract_port, prelude::*}; + use cw_orch_interchain::{core::contract_port, prelude::*}; use cw_plus_orch::{ cw20_base::{Cw20Base, ExecuteMsgInterfaceFns as _}, cw20_ics20::{ diff --git a/packages/interchain/interchain-core/Cargo.toml b/packages/interchain/interchain-core/Cargo.toml index 710839da4..6e5880671 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.7.0" +version = "0.8.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/packages/interchain/interchain-core/src/ack_parser.rs b/packages/interchain/interchain-core/src/ack_parser.rs index f6676738b..4823b0f09 100644 --- a/packages/interchain/interchain-core/src/ack_parser.rs +++ b/packages/interchain/interchain-core/src/ack_parser.rs @@ -1,15 +1,10 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{from_json, Binary}; -use cw_orch_core::environment::CwEnv; use prost::Message; // TODO: when polytone updates to cosmwasm v2 use polytone::ack::Callback; use polytone_callback::Callback; -use crate::{ - env::decode_ack_error, - types::{parse::SuccessIbcPacket, IbcTxAnalysis}, - InterchainError, -}; +use crate::{packet::success::IbcAppResult, InterchainError}; use self::acknowledgement::{Acknowledgement, Response}; @@ -17,7 +12,7 @@ use self::acknowledgement::{Acknowledgement, Response}; pub enum IbcAckParser {} impl IbcAckParser { - /// Verifies if the given ack is an Polytone type and returns the acknowledgement if it is + /// Verifies if the given ack is an Polytone type and returns the parsed acknowledgement if it is /// /// Returns an error if there was an error in the process pub fn polytone_ack(ack: &Binary) -> Result { @@ -83,6 +78,44 @@ impl IbcAckParser { } Err(decode_ack_error(ack)) } + + /// Verifies if the given ack is a standard acknowledgement type + /// + /// Returns an error if there was an error in the parsing process + pub fn any_standard_app_result(ack: &Binary) -> Result { + if let Ok(ack) = IbcAckParser::polytone_ack(ack) { + Ok(IbcAppResult::Polytone(ack)) + } else if IbcAckParser::ics20_ack(ack).is_ok() { + Ok(IbcAppResult::Ics20) + } else if let Ok(ack) = IbcAckParser::ics004_ack(ack) { + Ok(IbcAppResult::Ics004(ack)) + } else { + Err(InterchainError::AckDecodingFailed( + ack.clone(), + String::from_utf8_lossy(ack.as_slice()).to_string(), + )) + } + } + + /// Verifies if the given ack custom acknowledgement type. + /// If it fails, tries to parse into standard ack types + /// + /// Returns an error if there was an error in the parsing process + pub fn any_standard_app_result_with_custom( + ack: &Binary, + parsing_func: fn(&Binary) -> Result, + ) -> Result, InterchainError> { + parsing_func(ack) + .map(IbcAppResult::Custom) + .or_else(|_| Self::any_standard_app_result(ack).map(|ack| ack.into_custom())) + } +} + +pub(crate) fn decode_ack_error(ack: &Binary) -> InterchainError { + InterchainError::AckDecodingFailed( + ack.clone(), + String::from_utf8_lossy(ack.as_slice()).to_string(), + ) } #[cw_serde] @@ -94,32 +127,6 @@ pub enum FungibleTokenPacketAcknowledgement { Error(String), } -impl IbcTxAnalysis { - /// Assert that all packets were not timeout - pub fn assert_no_timeout(&self) -> Result>, InterchainError> { - Ok(self - .packets - .iter() - .map(|p| p.assert_no_timeout()) - .collect::, _>>()? - .into_iter() - .flatten() - .collect()) - } - - /// Returns all packets that were successful without asserting there was no timeout - pub fn get_success_packets(&self) -> Result>, InterchainError> { - Ok(self - .packets - .iter() - .map(|p| p.get_success_packets()) - .collect::, _>>()? - .into_iter() - .flatten() - .collect()) - } -} - /// This is copied from https://github.com/cosmos/cosmos-rust/blob/4f2e3bbf9c67c8ffef44ef1e485a327fd66f060a/cosmos-sdk-proto/src/prost/ibc-go/ibc.core.channel.v1.rs#L164 /// This is the ICS-004 standard proposal pub mod acknowledgement { @@ -149,7 +156,7 @@ pub mod acknowledgement { } } -mod polytone_callback { +pub mod polytone_callback { use super::*; use cosmwasm_std::{SubMsgResponse, Uint64}; diff --git a/packages/interchain/interchain-core/src/analysis.rs b/packages/interchain/interchain-core/src/analysis.rs new file mode 100644 index 000000000..c967d6329 --- /dev/null +++ b/packages/interchain/interchain-core/src/analysis.rs @@ -0,0 +1,172 @@ +//! Analysis of committed IBC related Txs and Packets. + +use crate::packet::success::{SuccessNestedPacketsFlow, SuccessSinglePacketFlow}; +use crate::packet::{ + success::IbcPacketResult, IbcPacketOutcome, NestedPacketsFlow, SinglePacketFlow, +}; +use crate::tx::TxId; +use crate::{IbcAckParser, InterchainError}; +use cosmwasm_std::{Binary, Empty}; +use cw_orch_core::environment::CwEnv; + +/// Trait used for analysis of IBC packet flows +pub trait PacketAnalysis { + /// Result of the Analysis of the packet flows + type AnalysisResult; + + /// Asserts that there is no timeout packet inside the result structure. + fn assert_no_timeout(&self) -> Result<(), InterchainError>; + + /// Tries to parses all acknowledgements into standard acknowledgments (polytone, ics20 or ics004). + /// Errors if some packet doesn't conform to those results. + fn assert(self) -> Result, InterchainError>; + + /// Tries to parses all acknowledgements into `CustomResult` using a custom parsing function. + /// + /// If it fails, also tries with standard acknowledgements (polytone, ics20 or ics004). + /// Errors if some packet doesn't conform to those results. + fn assert_custom( + self, + parse_func: fn(&Binary) -> Result, + ) -> Result, InterchainError>; +} + +impl PacketAnalysis for TxId { + type AnalysisResult = TxId; + + fn assert_no_timeout(&self) -> Result<(), InterchainError> { + Ok(()) + } + + fn assert(self) -> Result, InterchainError> { + Ok(self) + } + + fn assert_custom( + self, + _funcs: fn(&Binary) -> Result, + ) -> Result, InterchainError> { + Ok(TxId::new(self.chain_id, self.response)) + } +} + +impl PacketAnalysis for IbcPacketOutcome { + type AnalysisResult = + IbcPacketResult, CustomResult>; + + fn assert_no_timeout(&self) -> Result<(), InterchainError> { + match &self { + IbcPacketOutcome::Success { .. } => Ok(()), + IbcPacketOutcome::Timeout { .. } => Err(InterchainError::PacketTimeout {}), + } + } + + fn assert(self) -> Result, InterchainError> { + match self { + IbcPacketOutcome::Success { + receive_tx, + ack_tx, + ack, + } => { + let ibc_app_result = IbcAckParser::any_standard_app_result(&ack)?; + Ok(IbcPacketResult { + receive_tx: receive_tx.assert()?, + ack_tx: ack_tx.assert()?, + ibc_app_result, + }) + } + IbcPacketOutcome::Timeout { .. } => Err(InterchainError::PacketTimeout {}), + } + } + + fn assert_custom( + self, + parsing_func: fn(&Binary) -> Result, + ) -> Result, InterchainError> { + match self { + IbcPacketOutcome::Success { + receive_tx, + ack_tx, + ack, + } => { + let ibc_app_result = + IbcAckParser::any_standard_app_result_with_custom(&ack, parsing_func)?; + Ok(IbcPacketResult { + receive_tx: receive_tx.assert_custom(parsing_func)?, + ack_tx: ack_tx.assert_custom(parsing_func)?, + ibc_app_result, + }) + } + IbcPacketOutcome::Timeout { .. } => Err(InterchainError::PacketTimeout {}), + } + } +} +impl PacketAnalysis for SinglePacketFlow { + type AnalysisResult = SuccessSinglePacketFlow; + + fn assert_no_timeout(&self) -> Result<(), InterchainError> { + self.outcome.assert_no_timeout() + } + + fn assert(self) -> Result, InterchainError> { + let success = self.outcome.assert()?; + + Ok(SuccessSinglePacketFlow { + send_tx: self.send_tx, + result: success, + }) + } + + fn assert_custom( + self, + parse_func: fn(&Binary) -> Result, + ) -> Result, InterchainError> { + let success = self.outcome.assert_custom(parse_func)?; + + Ok(SuccessSinglePacketFlow:: { + send_tx: self.send_tx, + result: success, + }) + } +} + +impl PacketAnalysis for NestedPacketsFlow { + type AnalysisResult = SuccessNestedPacketsFlow; + + fn assert_no_timeout(&self) -> Result<(), InterchainError> { + self.packets + .iter() + .map(|p| p.assert_no_timeout()) + .collect::, _>>()?; + Ok(()) + } + + fn assert(self) -> Result, InterchainError> { + let packets = self + .packets + .into_iter() + .map(|p| p.assert()) + .collect::, _>>()?; + + Ok(SuccessNestedPacketsFlow { + tx_id: self.tx_id, + packets, + }) + } + + fn assert_custom( + self, + parse_func: fn(&Binary) -> Result, + ) -> Result, InterchainError> { + let packets = self + .packets + .into_iter() + .map(|p| p.assert_custom(parse_func)) + .collect::, _>>()?; + + Ok(SuccessNestedPacketsFlow { + tx_id: self.tx_id, + packets, + }) + } +} diff --git a/packages/interchain/interchain-core/src/channel.rs b/packages/interchain/interchain-core/src/channel.rs index 30859fa06..98e4320d2 100644 --- a/packages/interchain/interchain-core/src/channel.rs +++ b/packages/interchain/interchain-core/src/channel.rs @@ -7,7 +7,7 @@ use ibc_relayer_types::core::ics24_host::identifier::ChannelId; use ibc_relayer_types::core::ics24_host::identifier::PortId; use crate::env::ChainId; -use crate::types::NetworkId; +use crate::results::NetworkId; use crate::InterchainError; /// Identifies a channel between two IBC connected chains. diff --git a/packages/interchain/interchain-core/src/env.rs b/packages/interchain/interchain-core/src/env.rs index 74cfe7f3d..9795561ca 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::{Binary, IbcOrder}; +use cosmwasm_std::IbcOrder; use cw_orch_core::{ contract::interface_traits::ContractInstance, environment::{CwEnv, Environment, IndexResponse, TxHandler}, @@ -11,12 +11,11 @@ use ibc_relayer_types::core::{ }; use crate::{ - ack_parser::IbcAckParser, + analysis::PacketAnalysis, channel::{IbcPort, InterchainChannel}, - types::{ - parse::SuccessIbcPacket, ChannelCreationResult, ChannelCreationTransactionsResult, - FullIbcPacketAnalysis, IbcPacketOutcome, IbcTxAnalysis, InternalChannelCreationResult, - SimpleIbcPacketAnalysis, + packet::{success::SuccessNestedPacketsFlow, NestedPacketsFlow, SinglePacketFlow}, + results::{ + ChannelCreationResult, ChannelCreationTransactionsResult, InternalChannelCreationResult, }, IbcQueryHandler, InterchainError, }; @@ -381,7 +380,7 @@ pub trait InterchainEnv: Clone { &self, chain_id: ChainId, tx_response: ::Response, - ) -> Result, Self::Error>; + ) -> Result, Self::Error>; /// Follow every IBC packets sent out during the transaction /// Parses the acks according to usual ack formats (ICS20, Polytone, ICS-004) @@ -434,14 +433,12 @@ pub trait InterchainEnv: Clone { &self, chain_id: ChainId, tx_response: ::Response, - ) -> Result<(), InterchainError> { + ) -> Result, InterchainError> { let tx_result = self .await_packets(chain_id, tx_response) .map_err(Into::into)?; - tx_result.into_result()?; - - Ok(()) + tx_result.assert() } /// Follow the execution of a single IBC packet across the chain. @@ -454,7 +451,7 @@ pub trait InterchainEnv: Clone { src_channel: ChannelId, dst_chain: ChainId, sequence: Sequence, - ) -> Result, Self::Error>; + ) -> Result, Self::Error>; } /// format the port for a contract @@ -463,95 +460,3 @@ pub fn contract_port(contract: &dyn ContractInstance) -> Po .parse() .unwrap() } - -impl IbcTxAnalysis { - /// Tries to parses all acknowledgements into polytone, ics20 and ics004 acks. - /// Errors if some packet doesn't conform to those results. - pub fn into_result(&self) -> Result<(), InterchainError> { - self.packets.iter().try_for_each(|p| p.into_result())?; - Ok(()) - } -} - -impl FullIbcPacketAnalysis { - /// Tries to parses all acknowledgements into polytone, ics20 and ics004 acks. - /// Errors if some packet doesn't conform to those results. - /// Use [`InterchainEnv::parse_ibc`] if you want to handle your own acks - pub fn into_result(&self) -> Result<(), InterchainError> { - match &self.outcome { - IbcPacketOutcome::Success { - ack, - receive_tx, - ack_tx, - } => { - receive_tx.into_result()?; - ack_tx.into_result()?; - - if IbcAckParser::polytone_ack(ack).is_ok() { - return Ok(()); - } - if IbcAckParser::ics20_ack(ack).is_ok() { - return Ok(()); - } - if IbcAckParser::ics004_ack(ack).is_ok() { - return Ok(()); - } - - Err(InterchainError::AckDecodingFailed( - ack.clone(), - String::from_utf8_lossy(ack.as_slice()).to_string(), - )) - } - IbcPacketOutcome::Timeout { .. } => Err(InterchainError::PacketTimeout {}), - } - } - - /// Returns all successful packets gathered during the packet following procedure - /// Doesn't error if a packet has timed-out - pub fn get_success_packets(&self) -> Result>, InterchainError> { - match &self.outcome { - IbcPacketOutcome::Success { - ack, - receive_tx, - ack_tx, - } => Ok([ - vec![SuccessIbcPacket { - send_tx: self.send_tx.clone().unwrap(), - packet_ack: ack.clone(), - }], - receive_tx.get_success_packets()?, - ack_tx.get_success_packets()?, - ] - .concat()), - IbcPacketOutcome::Timeout { .. } => Ok(vec![]), - } - } - - /// Returns all successful packets gathered during the packet following procedure - /// Errors if a packet has timed-out - pub fn assert_no_timeout(&self) -> Result>, InterchainError> { - match &self.outcome { - IbcPacketOutcome::Success { - ack, - receive_tx, - ack_tx, - } => Ok([ - vec![SuccessIbcPacket { - send_tx: self.send_tx.clone().unwrap(), - packet_ack: ack.clone(), - }], - receive_tx.assert_no_timeout()?, - ack_tx.assert_no_timeout()?, - ] - .concat()), - IbcPacketOutcome::Timeout { .. } => Err(InterchainError::PacketTimeout {}), - } - } -} - -pub(crate) fn decode_ack_error(ack: &Binary) -> InterchainError { - InterchainError::AckDecodingFailed( - ack.clone(), - String::from_utf8_lossy(ack.as_slice()).to_string(), - ) -} diff --git a/packages/interchain/interchain-core/src/ibc_query.rs b/packages/interchain/interchain-core/src/ibc_query.rs new file mode 100644 index 000000000..b22e6c939 --- /dev/null +++ b/packages/interchain/interchain-core/src/ibc_query.rs @@ -0,0 +1,44 @@ +use crate::results::NetworkId; +use cosmwasm_std::Api; +use cw_orch_core::environment::CwEnv; +use cw_orch_core::environment::QueryHandler; +use cw_orch_mock::{MockBase, MockState}; + +/// Adds additional capabilities to CwEnv for use with ibc environments +pub trait IbcQueryHandler: CwEnv { + /// Query handler for the environment + /// This should allow users to query anything related to IBC functionalities on the environment (if possible) + type Handler: Clone + Send + Sync; + + /// Returns the `IbcQueryHandler::Handler` associated with the environment + fn ibc_handler(&self) -> Self::Handler; + + /// Returns the chain id of the environment (for ibc identification purposes) + fn chain_id(&self) -> NetworkId; +} + +#[cfg(feature = "daemon")] +// Temporary until we can actually push to cw-orch-daemon +impl IbcQueryHandler for cw_orch_daemon::Daemon { + type Handler = tonic::transport::Channel; + + fn ibc_handler(&self) -> tonic::transport::Channel { + self.channel() + } + + fn chain_id(&self) -> NetworkId { + use cw_orch_core::environment::ChainState; + + self.state().chain_data.chain_id.to_string() + } +} + +// Temporary until we can actually push to cw-orch-mock +impl IbcQueryHandler for MockBase { + type Handler = (); + fn ibc_handler(&self) {} + + fn chain_id(&self) -> NetworkId { + self.block_info().unwrap().chain_id + } +} diff --git a/packages/interchain/interchain-core/src/lib.rs b/packages/interchain/interchain-core/src/lib.rs index 09ce77251..ff6a65446 100644 --- a/packages/interchain/interchain-core/src/lib.rs +++ b/packages/interchain/interchain-core/src/lib.rs @@ -12,10 +12,20 @@ pub mod env; mod ack_parser; mod error; +pub(crate) mod analysis; +pub(crate) mod ibc_query; +pub(crate) mod packet; /// Type definition for interchain structure and return types -pub mod types; +pub mod results; +pub(crate) mod tx; pub use ack_parser::IbcAckParser; -pub use env::InterchainEnv; +pub use analysis::PacketAnalysis; +pub use env::{contract_port, InterchainEnv}; pub use error::InterchainError; -pub use types::IbcQueryHandler; +pub use ibc_query::IbcQueryHandler; +pub use packet::{ + success::{IbcPacketResult, SuccessNestedPacketsFlow, SuccessSinglePacketFlow}, + IbcPacketInfo, IbcPacketOutcome, NestedPacketsFlow, SinglePacketFlow, +}; +pub use tx::TxId; diff --git a/packages/interchain/interchain-core/src/packet.rs b/packages/interchain/interchain-core/src/packet.rs new file mode 100644 index 000000000..8f4701882 --- /dev/null +++ b/packages/interchain/interchain-core/src/packet.rs @@ -0,0 +1,342 @@ +use cosmwasm_std::{Binary, StdError}; +use cw_orch_core::environment::CwEnv; +use cw_orch_core::environment::IndexResponse; +use ibc_relayer_types::core::{ + ics04_channel::packet::Sequence, + ics24_host::identifier::{ChannelId, PortId}, +}; + +use crate::{results::NetworkId, tx::TxId}; + +/// Structure to hold simple information about a sent packet +#[derive(Debug, Clone)] +pub struct IbcPacketInfo { + /// Port on which is packet was sent + pub src_port: PortId, + /// Channel on which is packet was sent + pub src_channel: ChannelId, + /// Packet identification (sequence is `u64` number) + pub sequence: Sequence, + /// Chain identification to which the packet was sent + pub dst_chain_id: NetworkId, +} + +/// Raw packet outcome +/// The T generic is used to allow for raw transactions or analyzed transactions to be used +#[derive(Debug, PartialEq, Clone)] +#[must_use = "We recommend using `PacketAnalysis::into_result()` to assert ibc success"] +pub enum IbcPacketOutcome { + /// Packet timeout + Timeout { + /// Only a timeout transaction gets broadcasted + timeout_tx: T, + }, + /// Packet successfully transferred + Success { + /// The packets gets transmitted to the dst chain + receive_tx: T, + /// The ack is broadcasted back on the src chain + ack_tx: T, + /// The raw binary acknowledgement retrieved from `ack_tx` + ack: Binary, + }, +} + +/// The result of awaiting the Lifecycle of Single packet across IBC +/// +/// This identifies: +/// - `send_tx`: The transaction in which the packet was sent (if available) +/// - `outcome`: The outcome of the Lifecycle and the corresponding transactions (Receive/Acknowledgement or Timeout) +#[derive(Clone)] +#[must_use = "We recommend using `PacketAnalysis::into_result()` to assert IBC success"] +pub struct SinglePacketFlow { + /// The transaction during which the packet was sent + /// + /// Can optionally be specified, depending on the environment on which the implementation is done + /// This is not available for the [`Mock`] implementation for instance + pub send_tx: Option>, + /// Outcome transactions of the packet (+ eventual acknowledgment) + pub outcome: IbcPacketOutcome>, +} + +/// The result of awaiting all packets sent across IBC during a single transaction. +/// +/// This structure is nested and allows identifying all packets emitted during the subsequent (receive/acknowledgement/timeout) transactions +/// +/// This identifies: +/// - `tx_id`: The original transaction that was used as a starting point of the awaiting procedure +/// - `packets`: For each packet sent inside this transaction, this contains the outcome of the lifecycle of the packet. +/// This also contains the result of awaiting all packets sent across IBC during each outcome transactions (receive/acknowledgement/timeout) +#[derive(Clone)] +#[must_use = "We recommend using `PacketAnalysis::into_result()` to assert IBC success"] +pub struct NestedPacketsFlow { + /// Identification of the transaction + pub tx_id: TxId, + /// Result of following a packet + Recursive Analysis of the resulting transactions for additional IBC packets + pub packets: Vec>>, +} + +impl IndexResponse for SinglePacketFlow { + fn events(&self) -> Vec { + let mut events: Vec<_> = self + .send_tx + .as_ref() + .map(|tx| tx.response.events()) + .unwrap_or_default(); + let other_events = match &self.outcome { + IbcPacketOutcome::Timeout { timeout_tx } => timeout_tx.events(), + IbcPacketOutcome::Success { + receive_tx, + ack_tx, + ack: _, + } => [receive_tx.events(), ack_tx.events()].concat(), + }; + events.extend(other_events); + + events + } + + fn event_attr_value( + &self, + event_type: &str, + attr_key: &str, + ) -> cosmwasm_std::StdResult { + self.send_tx + .as_ref() + .map(|r| r.event_attr_value(event_type, attr_key)) + .and_then(|res| res.ok()) + .or_else(|| match &self.outcome { + IbcPacketOutcome::Timeout { timeout_tx } => { + timeout_tx.event_attr_value(event_type, attr_key).ok() + } + IbcPacketOutcome::Success { + receive_tx, + ack_tx, + ack: _, + } => receive_tx + .event_attr_value(event_type, attr_key) + .or_else(|_| ack_tx.event_attr_value(event_type, attr_key)) + .ok(), + }) + .ok_or(StdError::generic_err(format!( + "event of type {event_type} does not have a value at key {attr_key}" + ))) + } + + fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec { + let mut all_results: Vec<_> = self + .send_tx + .as_ref() + .map(|tx| tx.response.event_attr_values(event_type, attr_key)) + .unwrap_or_default(); + let other_results = match &self.outcome { + IbcPacketOutcome::Timeout { timeout_tx } => { + timeout_tx.event_attr_values(event_type, attr_key) + } + IbcPacketOutcome::Success { + receive_tx, + ack_tx, + ack: _, + } => [ + receive_tx.event_attr_values(event_type, attr_key), + ack_tx.event_attr_values(event_type, attr_key), + ] + .concat(), + }; + all_results.extend(other_results); + + all_results + } + + fn data(&self) -> Option { + unimplemented!("No data fields on Ibc Packet Flow, this is not well defined") + } +} + +impl IndexResponse for NestedPacketsFlow { + fn events(&self) -> Vec { + let mut self_events = self.tx_id.response.events(); + let other_events = self + .packets + .iter() + .flat_map(|packet_result| match &packet_result { + IbcPacketOutcome::Timeout { timeout_tx } => timeout_tx.events(), + IbcPacketOutcome::Success { + receive_tx, + ack_tx, + ack: _, + } => [receive_tx.events(), ack_tx.events()].concat(), + }); + self_events.extend(other_events); + self_events + } + + fn event_attr_value( + &self, + event_type: &str, + attr_key: &str, + ) -> cosmwasm_std::StdResult { + self.tx_id + .response + .event_attr_value(event_type, attr_key) + .or_else(|_| { + self.packets + .iter() + .find_map(|packet_result| match &packet_result { + IbcPacketOutcome::Timeout { timeout_tx } => { + timeout_tx.event_attr_value(event_type, attr_key).ok() + } + IbcPacketOutcome::Success { + receive_tx, + ack_tx, + ack: _, + } => receive_tx + .event_attr_value(event_type, attr_key) + .or_else(|_| ack_tx.event_attr_value(event_type, attr_key)) + .ok(), + }) + .ok_or(StdError::generic_err(format!( + "event of type {event_type} does not have a value at key {attr_key}" + ))) + }) + } + + fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec { + let mut all_results = self.tx_id.response.event_attr_values(event_type, attr_key); + + all_results.extend(self.packets.iter().flat_map(|packet_result| { + match &packet_result { + IbcPacketOutcome::Timeout { timeout_tx } => { + timeout_tx.event_attr_values(event_type, attr_key) + } + IbcPacketOutcome::Success { + receive_tx, + ack_tx, + ack: _, + } => [ + receive_tx.event_attr_values(event_type, attr_key), + ack_tx.event_attr_values(event_type, attr_key), + ] + .concat(), + } + })); + + all_results + } + + fn data(&self) -> Option { + unimplemented!("No data fields on Ibc Tx Analysis") + } +} + +pub mod success { + use crate::{ack_parser::polytone_callback::Callback, tx::TxId}; + use cosmwasm_std::Empty; + use cw_orch_core::environment::CwEnv; + + #[derive(Debug, PartialEq, Clone)] + pub enum IbcAppResult { + Polytone(Callback), + Ics20, + Ics004(Vec), + Custom(CustomResult), + } + + impl IbcAppResult { + pub fn into_custom(self) -> IbcAppResult { + match self { + IbcAppResult::Polytone(callback) => IbcAppResult::Polytone(callback), + IbcAppResult::Ics20 => IbcAppResult::Ics20, + IbcAppResult::Ics004(vec) => IbcAppResult::Ics004(vec), + IbcAppResult::Custom(_) => unreachable!(), + } + } + } + + /// Success packet outcome. This is the result of a packet analysis. + /// The T generic is used to allow for raw transactions or analyzed transactions to be used + #[derive(Debug, PartialEq, Clone)] + pub struct IbcPacketResult { + /// The packets gets transmitted to the dst chain + pub receive_tx: T, + /// The ack is broadcasted back on the src chain + pub ack_tx: T, + /// The parsed and raw binary acknowledgement retrieved from `ack_tx` + pub ibc_app_result: IbcAppResult, + } + + /// Success Packet Flow. This is the result of a packet analysis. + /// + /// This allows identifying the different transactions involved. + #[derive(Clone)] + pub struct SuccessSinglePacketFlow { + /// The transaction during which the packet was sent + /// + /// Can optionally be specified, depending on the environment on which the implementation is done + /// This is not available for the [`Mock`] implementation for instance + pub send_tx: Option>, + /// Result of the successful packet flow + pub result: IbcPacketResult, CustomResult>, + } + + /// The result of following all packets sent across IBC during a single transaction. + /// + /// This structure is nested and will also await all packets emitted during the subsequent (receive/acknowledgement) transactions + #[derive(Clone)] + pub struct SuccessNestedPacketsFlow { + /// Identification of the transaction + pub tx_id: TxId, + /// Result of following a packet + Recursive Analysis of the resulting transactions for additional IBC packets + pub packets: + Vec, CustomResult>>, + } +} + +mod debug { + use cw_orch_core::environment::CwEnv; + + use super::{ + success::{SuccessNestedPacketsFlow, SuccessSinglePacketFlow}, + NestedPacketsFlow, SinglePacketFlow, + }; + + impl std::fmt::Debug for SinglePacketFlow { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SinglePacketFlow") + .field("send_tx", &self.send_tx) + .field("outcome", &self.outcome) + .finish() + } + } + + impl std::fmt::Debug for NestedPacketsFlow { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("NestedPacketsFlow") + .field("tx_id", &self.tx_id) + .field("packets", &self.packets) + .finish() + } + } + + impl std::fmt::Debug + for SuccessSinglePacketFlow + { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SuccessSinglePacketFlow") + .field("sent_tx", &self.send_tx) + .field("result", &self.result) + .finish() + } + } + + impl std::fmt::Debug + for SuccessNestedPacketsFlow + { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SuccessNestedPacketsFlow") + .field("tx_id", &self.tx_id) + .field("packets", &self.packets) + .finish() + } + } +} diff --git a/packages/interchain/interchain-core/src/results.rs b/packages/interchain/interchain-core/src/results.rs new file mode 100644 index 000000000..6ad9e72c1 --- /dev/null +++ b/packages/interchain/interchain-core/src/results.rs @@ -0,0 +1,36 @@ +use crate::ibc_query::IbcQueryHandler; +use crate::packet::NestedPacketsFlow; +use crate::{channel::InterchainChannel, env::ChannelCreation}; +use cw_orch_core::environment::TxHandler; +use ibc_relayer_types::core::ics24_host::identifier::ChannelId; + +/// Chain identification for cw-orch Ibc purposes +pub type NetworkId = String; + +// Return types for the env trait +/// Result returned by InterchainEnv::_internal_create_channel +pub struct InternalChannelCreationResult { + /// Channel creation result specific the used chain + pub result: ChannelCreationResult, + /// Connection id on which the channel was created. + /// This connection id is supposed to be known by the channel creation environment + pub src_connection_id: String, +} + +/// Result returned by InterchainEnv::get_channel_creation_txs +pub struct ChannelCreationTransactionsResult { + /// Id of the channel that was just created on the src chain + pub src_channel_id: ChannelId, + /// Id of the channel that was just created on the dst chain + pub dst_channel_id: ChannelId, + /// Transactions involved in the channel creation + pub channel_creation_txs: ChannelCreation<::Response>, +} + +/// Result returned by InterchainEnv::create_channel +pub struct ChannelCreationResult { + /// Channel object containing every variable needed for identifying the channel that was just created + pub interchain_channel: InterchainChannel<::Handler>, + /// Transactions involved in the channel creation + Their packet following analysis + pub channel_creation_txs: ChannelCreation>, +} diff --git a/packages/interchain/interchain-core/src/tx.rs b/packages/interchain/interchain-core/src/tx.rs new file mode 100644 index 000000000..921749ca2 --- /dev/null +++ b/packages/interchain/interchain-core/src/tx.rs @@ -0,0 +1,57 @@ +use std::marker::PhantomData; + +use cosmwasm_std::Empty; +use cw_orch_core::environment::{CwEnv, IndexResponse, TxHandler}; + +/// Identifies a transaction +#[derive(Clone)] +pub struct TxId { + /// Chain Id on which the transaction was broadcasted + pub chain_id: String, + /// Transactions response for the transaction (env specific) + pub response: ::Response, + + _phantom_data: PhantomData, +} + +impl TxId { + /// Creates a new Tx Id object identifying a transaction exactly + pub fn new(chain_id: String, response: ::Response) -> Self { + TxId { + chain_id, + response, + _phantom_data: Default::default(), + } + } +} + +impl std::fmt::Debug for TxId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TxId") + .field("chain_id", &self.chain_id) + .field("response", &self.response) + .finish() + } +} + +impl IndexResponse for TxId { + fn events(&self) -> Vec { + self.response.events() + } + + fn event_attr_value( + &self, + event_type: &str, + attr_key: &str, + ) -> cosmwasm_std::StdResult { + self.response.event_attr_value(event_type, attr_key) + } + + fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec { + self.response.event_attr_values(event_type, attr_key) + } + + fn data(&self) -> Option { + self.response.data() + } +} diff --git a/packages/interchain/interchain-core/src/types.rs b/packages/interchain/interchain-core/src/types.rs deleted file mode 100644 index 7fc3f0562..000000000 --- a/packages/interchain/interchain-core/src/types.rs +++ /dev/null @@ -1,289 +0,0 @@ -use crate::{channel::InterchainChannel, env::ChannelCreation}; -use cosmwasm_std::{Api, Binary, StdError}; -use cw_orch_core::environment::IndexResponse; -use cw_orch_core::environment::QueryHandler; -use cw_orch_core::environment::{CwEnv, TxHandler}; -use cw_orch_mock::{MockBase, MockState}; -use ibc_relayer_types::core::{ - ics04_channel::packet::Sequence, - ics24_host::identifier::{ChannelId, PortId}, -}; - -/// Chain identification for cw-orch Ibc purposes -pub type NetworkId = String; - -/// The result of following a single packet across IBC -/// This allows indentifying the different transactions involved as well as the result of the packet transmission -pub type SimpleIbcPacketAnalysis = IbcPacketAnalysis>; - -/// The result of following all packets sent across IBC during a single transaction -pub type FullIbcPacketAnalysis = IbcPacketAnalysis>; - -/// Generic type to store the outcome of an IBC packet -#[derive(Clone)] -pub struct IbcPacketAnalysis { - /// The transaction during which the packet was sent - pub send_tx: Option>, - /// Outcome transactions of the packet (+ eventual acknowledgment) - pub outcome: IbcPacketOutcome, -} - -/// Identifies a transaction -#[derive(Clone)] -pub struct TxId { - /// Chain Id on which the transaction was broadcasted - pub chain_id: String, - /// Transactions response for the transaction (env specific) - pub response: ::Response, -} - -/// Result of the analysis of all packets sent in a transaction -#[derive(Clone)] -#[must_use = "We recommend using `into_result()` on this result to assert ibc success"] -pub struct IbcTxAnalysis { - /// Identification of the transaction - pub tx_id: TxId, - /// Result of following a packet + Recursive Analysis of the resulting transactions for additional IBC packets - pub packets: Vec>, -} - -/// Raw packet outcome -/// The T generic is used to allow for raw transactions or analyzed transactions to be used -#[derive(Debug, PartialEq, Clone)] -pub enum IbcPacketOutcome { - /// Packet timeout - Timeout { - /// Only a timeout transaction gets broadcasted - timeout_tx: T, - }, - /// Packet successfully transferred - Success { - /// The packets gets transmitted to the dst chain - receive_tx: T, - /// The ack is broadcasted back on the src chain - ack_tx: T, - /// The raw binary acknowledgement retrieved from `ack_tx` - ack: Binary, - }, -} - -/// Structure to hold simple information about a sent packet -#[derive(Debug, Clone)] -pub struct IbcPacketInfo { - /// Port on which is packet was sent - pub src_port: PortId, - /// Channel on which is packet was sent - pub src_channel: ChannelId, - /// Packet identification (sequence is `u64` number) - pub sequence: Sequence, - /// Chain identification to which the packet was sent - pub dst_chain_id: NetworkId, -} - -/// Adds additional capabilities to CwEnv for use with ibc environments -pub trait IbcQueryHandler: CwEnv { - /// Query handler for the environment - /// This should allow users to query anything related to IBC functionalities on the environment (if possible) - type Handler: Clone + Send + Sync; - - /// Returns the `IbcQueryHandler::Handler` associated with the environment - fn ibc_handler(&self) -> Self::Handler; - - /// Returns the chain id of the environment (for ibc identification purposes) - fn chain_id(&self) -> NetworkId; -} - -#[cfg(feature = "daemon")] -// Temporary until we can actually push to cw-orch-daemon -impl IbcQueryHandler for cw_orch_daemon::Daemon { - type Handler = tonic::transport::Channel; - - fn ibc_handler(&self) -> tonic::transport::Channel { - self.channel() - } - - fn chain_id(&self) -> NetworkId { - use cw_orch_core::environment::ChainState; - - self.state().chain_data.chain_id.to_string() - } -} - -// Temporary until we can actually push to cw-orch-mock -impl IbcQueryHandler for MockBase { - type Handler = (); - fn ibc_handler(&self) {} - - fn chain_id(&self) -> NetworkId { - self.block_info().unwrap().chain_id - } -} - -// Return types for the env trait -/// Result returned by InterchainEnv::_internal_create_channel -pub struct InternalChannelCreationResult { - /// Channel creation result specific the used chain - pub result: ChannelCreationResult, - /// Connection id on which the channel was created. - /// This connection id is supposed to be known by the channel creation environment - pub src_connection_id: String, -} - -/// Result returned by InterchainEnv::get_channel_creation_txs -pub struct ChannelCreationTransactionsResult { - /// Id of the channel that was just created on the src chain - pub src_channel_id: ChannelId, - /// Id of the channel that was just created on the dst chain - pub dst_channel_id: ChannelId, - /// Transactions involved in the channel creation - pub channel_creation_txs: ChannelCreation<::Response>, -} - -/// Result returned by InterchainEnv::create_channel -pub struct ChannelCreationResult { - /// Channel object containing every variable needed for identifying the channel that was just created - pub interchain_channel: InterchainChannel<::Handler>, - /// Transactions involved in the channel creation + Their packet following analysis - pub channel_creation_txs: ChannelCreation>, -} - -mod debug { - use cw_orch_core::environment::CwEnv; - - use super::{IbcPacketAnalysis, IbcTxAnalysis, TxId}; - - impl std::fmt::Debug for IbcPacketAnalysis { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("IbcPacketAnalysis") - .field("send_tx", &self.send_tx) - .field("outcome", &self.outcome) - .finish() - } - } - - impl std::fmt::Debug for TxId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("TxId") - .field("chain_id", &self.chain_id) - .field("response", &self.response) - .finish() - } - } - - impl std::fmt::Debug for IbcTxAnalysis { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("IbcTxAnalysis") - .field("tx_id", &self.tx_id) - .field("packets", &self.packets) - .finish() - } - } -} - -impl IndexResponse for IbcTxAnalysis { - fn events(&self) -> Vec { - let mut self_events = self.tx_id.response.events(); - let other_events = - self.packets - .iter() - .flat_map(|packet_result| match &packet_result.outcome { - IbcPacketOutcome::Timeout { timeout_tx } => timeout_tx.events(), - IbcPacketOutcome::Success { - receive_tx, - ack_tx, - ack: _, - } => [receive_tx.events(), ack_tx.events()].concat(), - }); - self_events.extend(other_events); - self_events - } - - fn event_attr_value( - &self, - event_type: &str, - attr_key: &str, - ) -> cosmwasm_std::StdResult { - self.tx_id - .response - .event_attr_value(event_type, attr_key) - .or_else(|_| { - self.packets - .iter() - .find_map(|packet_result| match &packet_result.outcome { - IbcPacketOutcome::Timeout { timeout_tx } => { - timeout_tx.event_attr_value(event_type, attr_key).ok() - } - IbcPacketOutcome::Success { - receive_tx, - ack_tx, - ack: _, - } => receive_tx - .event_attr_value(event_type, attr_key) - .or_else(|_| ack_tx.event_attr_value(event_type, attr_key)) - .ok(), - }) - .ok_or(StdError::generic_err(format!( - "event of type {event_type} does not have a value at key {attr_key}" - ))) - }) - } - - fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec { - let mut all_results = self.tx_id.response.event_attr_values(event_type, attr_key); - - all_results.extend(self.packets.iter().flat_map(|packet_result| { - match &packet_result.outcome { - IbcPacketOutcome::Timeout { timeout_tx } => { - timeout_tx.event_attr_values(event_type, attr_key) - } - IbcPacketOutcome::Success { - receive_tx, - ack_tx, - ack: _, - } => [ - receive_tx.event_attr_values(event_type, attr_key), - ack_tx.event_attr_values(event_type, attr_key), - ] - .concat(), - } - })); - - all_results - } - - fn data(&self) -> Option { - unimplemented!("No data fields on Ibc Tx Analysis") - } -} - -/// Contains structs to hold parsed IBC ack results -pub mod parse { - use cosmwasm_std::Binary; - use cw_orch_core::environment::CwEnv; - - use super::TxId; - - /// Contains packet information after it was successfully acknowledged on the sending chain - #[derive(Clone)] - pub struct SuccessIbcPacket { - /// Identification of the transaction - pub send_tx: TxId, - /// Raw bytes returned during the acknowledgement - pub packet_ack: Binary, - } - - mod debug { - use cw_orch_core::environment::CwEnv; - - use super::SuccessIbcPacket; - - impl std::fmt::Debug for SuccessIbcPacket { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("SuccessIbcPacket") - .field("sent_tx", &self.send_tx) - .field("packet_ack", &self.packet_ack) - .finish() - } - } - } -} diff --git a/packages/interchain/interchain-daemon/Cargo.toml b/packages/interchain/interchain-daemon/Cargo.toml index e3456dea3..ed6558ec4 100644 --- a/packages/interchain/interchain-daemon/Cargo.toml +++ b/packages/interchain/interchain-daemon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-orch-interchain-daemon" -version = "0.7.0" +version = "0.8.0" description = "An interchain intergration crate for interacting with actual chain nodes (via gRPC)" authors.workspace = true edition.workspace = true diff --git a/packages/interchain/interchain-daemon/examples/starship_channel.rs b/packages/interchain/interchain-daemon/examples/starship_channel.rs index e8d6275f2..da1060fac 100644 --- a/packages/interchain/interchain-daemon/examples/starship_channel.rs +++ b/packages/interchain/interchain-daemon/examples/starship_channel.rs @@ -2,7 +2,7 @@ use cosmwasm_std::IbcOrder; use cw_orch_core::environment::QuerierGetter; use cw_orch_daemon::queriers::Ibc; use cw_orch_daemon::Daemon; -use cw_orch_interchain_core::{types::ChannelCreationResult, InterchainEnv}; +use cw_orch_interchain_core::{results::ChannelCreationResult, InterchainEnv}; use cw_orch_interchain_daemon::ChannelCreator; use cw_orch_starship::Starship; use ibc_relayer_types::core::ics24_host::identifier::PortId; diff --git a/packages/interchain/interchain-daemon/src/error.rs b/packages/interchain/interchain-daemon/src/error.rs index 7a9958da2..f31fefc77 100644 --- a/packages/interchain/interchain-daemon/src/error.rs +++ b/packages/interchain/interchain-daemon/src/error.rs @@ -1,7 +1,7 @@ #![allow(missing_docs)] use cosmwasm_std::StdError; -use cw_orch_interchain_core::{channel::InterchainChannel, types::NetworkId, InterchainError}; +use cw_orch_interchain_core::{channel::InterchainChannel, results::NetworkId, InterchainError}; use thiserror::Error; use tonic::transport::Channel; diff --git a/packages/interchain/interchain-daemon/src/interchain_env.rs b/packages/interchain/interchain-daemon/src/interchain_env.rs index 5123e33a5..f7ed82fc8 100644 --- a/packages/interchain/interchain-daemon/src/interchain_env.rs +++ b/packages/interchain/interchain-daemon/src/interchain_env.rs @@ -4,7 +4,7 @@ use cw_orch_daemon::queriers::{Ibc, Node}; use cw_orch_daemon::{CosmTxResponse, Daemon, DaemonError, RUNTIME}; use cw_orch_interchain_core::channel::{IbcPort, InterchainChannel}; use cw_orch_interchain_core::env::{ChainId, ChannelCreation}; -use cw_orch_interchain_core::InterchainEnv; +use cw_orch_interchain_core::{InterchainEnv, NestedPacketsFlow, SinglePacketFlow}; use ibc_relayer_types::core::ics04_channel::packet::Sequence; use tokio::time::sleep; @@ -17,9 +17,8 @@ use ibc_relayer_types::core::ics24_host::identifier::{ChannelId, PortId}; use crate::{IcDaemonResult, InterchainDaemonError}; -use cw_orch_interchain_core::types::{ - ChannelCreationTransactionsResult, IbcTxAnalysis, InternalChannelCreationResult, NetworkId, - SimpleIbcPacketAnalysis, +use cw_orch_interchain_core::results::{ + ChannelCreationTransactionsResult, InternalChannelCreationResult, NetworkId, }; use futures::future::try_join4; use std::collections::HashMap; @@ -242,7 +241,7 @@ impl InterchainEnv for DaemonInterchain { &self, chain_id: ChainId, tx_response: CosmTxResponse, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { log::info!( target: chain_id, "Investigating sent packet events on tx {}", @@ -270,7 +269,7 @@ impl InterchainEnv for DaemonInterchain { src_channel: ChannelId, dst_chain: ChainId, sequence: Sequence, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { // We crate an interchain env object that is safe to send between threads let interchain_env = self .rt_handle @@ -316,13 +315,13 @@ impl DaemonInterchain { /// .await_packets_for_txhash( /// src_chain.chain_id, /// "D2C5459C54B394C168B8DFA214670FF9E2A0349CCBEF149CF5CB508A5B3BCB84".to_string(), - /// ).unwrap().into_result().unwrap(); + /// ).unwrap().assert().unwrap(); /// ``` pub fn await_packets_for_txhash( &self, chain_id: ChainId, packet_send_tx_hash: String, - ) -> Result, InterchainDaemonError> { + ) -> Result, InterchainDaemonError> { let grpc_channel1 = self.get_chain(chain_id)?.channel(); let tx = self.rt_handle.block_on( diff --git a/packages/interchain/interchain-daemon/src/packet_inspector.rs b/packages/interchain/interchain-daemon/src/packet_inspector.rs index 63520b4ec..c72d4f039 100644 --- a/packages/interchain/interchain-daemon/src/packet_inspector.rs +++ b/packages/interchain/interchain-daemon/src/packet_inspector.rs @@ -9,14 +9,14 @@ use cw_orch_daemon::TxResultBlockEvent; use cw_orch_daemon::{CosmTxResponse, Daemon, DaemonError}; use cw_orch_interchain_core::channel::{IbcPort, InterchainChannel}; use cw_orch_interchain_core::env::ChainId; +use cw_orch_interchain_core::{ + IbcPacketInfo, IbcPacketOutcome, NestedPacketsFlow, SinglePacketFlow, TxId, +}; use futures_util::future::select_all; use futures_util::FutureExt; use crate::{IcDaemonResult, InterchainDaemonError}; -use cw_orch_interchain_core::types::{ - FullIbcPacketAnalysis, IbcPacketAnalysis, IbcPacketInfo, IbcPacketOutcome, IbcTxAnalysis, - NetworkId, SimpleIbcPacketAnalysis, TxId, -}; +use cw_orch_interchain_core::results::NetworkId; use futures::future::try_join_all; use ibc_relayer_types::core::ics04_channel::packet::Sequence; @@ -93,7 +93,7 @@ impl PacketInspector { &self, src_chain: NetworkId, tx: CosmTxResponse, - ) -> IcDaemonResult> { + ) -> IcDaemonResult> { // 1. Getting IBC related events for the current tx + finding all IBC packets sent out in the transaction let grpc_channel1 = self.get_grpc_channel(&src_chain).await?; @@ -120,10 +120,7 @@ impl PacketInspector { .into_iter() .collect::>(); - let send_tx_id = TxId { - chain_id: src_chain.clone(), - response: tx, - }; + let send_tx_id = TxId::new(src_chain.clone(), tx); // We follow all results from outgoing packets in the resulting transactions let full_results = try_join_all(ibc_packet_results.into_iter().map(|ibc_result| async { @@ -157,16 +154,11 @@ impl PacketInspector { }, }; - let analyzed_result = FullIbcPacketAnalysis { - send_tx: Some(send_tx_id.clone()), - outcome: analyzed_outcome, - }; - - Ok::<_, InterchainDaemonError>(analyzed_result.clone()) + Ok::<_, InterchainDaemonError>(analyzed_outcome.clone()) })) .await?; - let tx_identification = IbcTxAnalysis { + let tx_identification = NestedPacketsFlow { tx_id: send_tx_id.clone(), packets: full_results, }; @@ -197,7 +189,7 @@ impl PacketInspector { src_channel: ChannelId, dst_chain: ChainId<'a>, sequence: Sequence, - ) -> IcDaemonResult> { + ) -> IcDaemonResult> { let src_grpc_channel = self.get_grpc_channel(src_chain).await?; let dst_grpc_channel = self.get_grpc_channel(dst_chain).await?; @@ -268,7 +260,7 @@ impl PacketInspector { from: ChainId<'a>, ibc_channel: &'a InterchainChannel, sequence: Sequence, - ) -> Result, InterchainDaemonError> { + ) -> Result, InterchainDaemonError> { let (src_port, dst_port) = ibc_channel.get_ordered_ports_from(from)?; // 0. Query the send tx hash for analysis @@ -345,20 +337,11 @@ impl PacketInspector { ack_tx.txhash ); - Ok(IbcPacketAnalysis { - send_tx: Some(TxId { - response: send_tx, - chain_id: src_port.chain_id.clone(), - }), + Ok(SinglePacketFlow { + send_tx: Some(TxId::new(src_port.chain_id.clone(), send_tx)), outcome: IbcPacketOutcome::Success { - receive_tx: TxId { - chain_id: dst_port.chain_id.clone(), - response: received_tx, - }, - ack_tx: TxId { - chain_id: src_port.chain_id.clone(), - response: ack_tx, - }, + receive_tx: TxId::new(dst_port.chain_id.clone(), received_tx), + ack_tx: TxId::new(src_port.chain_id.clone(), ack_tx), ack: acknowledgment.as_bytes().into(), }, }) @@ -370,7 +353,7 @@ impl PacketInspector { from: ChainId<'a>, ibc_channel: &'a InterchainChannel, sequence: Sequence, - ) -> Result, InterchainDaemonError> { + ) -> Result, InterchainDaemonError> { // 0. Query the send tx hash for analysis let send_tx = self.get_packet_send_tx(from, ibc_channel, sequence).await?; @@ -405,16 +388,10 @@ impl PacketInspector { ); // We return the tx hash of this transaction for future analysis - Ok(IbcPacketAnalysis { - send_tx: Some(TxId { - chain_id: src_port.chain_id.clone(), - response: send_tx, - }), + Ok(SinglePacketFlow { + send_tx: Some(TxId::new(src_port.chain_id.clone(), send_tx)), outcome: IbcPacketOutcome::Timeout { - timeout_tx: TxId { - chain_id: src_port.chain_id.clone(), - response: timeout_tx, - }, + timeout_tx: TxId::new(src_port.chain_id.clone(), timeout_tx), }, }) } diff --git a/packages/interchain/interchain-mock/Cargo.toml b/packages/interchain/interchain-mock/Cargo.toml index 18621317e..582342de9 100644 --- a/packages/interchain/interchain-mock/Cargo.toml +++ b/packages/interchain/interchain-mock/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-orch-interchain-mock" -version = "0.7.0" +version = "0.8.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/packages/interchain/interchain-mock/src/interchain.rs b/packages/interchain/interchain-mock/src/interchain.rs index 3fa92486b..dfb17689b 100644 --- a/packages/interchain/interchain-mock/src/interchain.rs +++ b/packages/interchain/interchain-mock/src/interchain.rs @@ -5,12 +5,8 @@ use cw_orch_core::environment::QueryHandler; use cw_orch_interchain_core::{ channel::InterchainChannel, env::{ChainId, ChannelCreation}, - types::{ - ChannelCreationTransactionsResult, FullIbcPacketAnalysis, IbcPacketAnalysis, IbcPacketInfo, - IbcPacketOutcome, IbcTxAnalysis, InternalChannelCreationResult, SimpleIbcPacketAnalysis, - TxId, - }, - InterchainEnv, + results::{ChannelCreationTransactionsResult, InternalChannelCreationResult}, + IbcPacketInfo, IbcPacketOutcome, InterchainEnv, NestedPacketsFlow, SinglePacketFlow, TxId, }; use cw_orch_mock::{ cw_multi_test::{ @@ -227,14 +223,11 @@ impl InterchainEnv> for MockInterchainEnvBase { &self, chain_id: ChainId, tx_response: AppResponse, - ) -> Result>, Self::Error> { + ) -> Result>, Self::Error> { // We start by analyzing sent packets in the response let packets = find_ibc_packets_sent_in_tx(&self.get_chain(chain_id)?, &tx_response)?; - let send_tx_id = TxId { - chain_id: chain_id.to_string(), - response: tx_response, - }; + let send_tx_id = TxId::new(chain_id.to_string(), tx_response); let packet_analysis = packets .iter() @@ -274,18 +267,12 @@ impl InterchainEnv> for MockInterchainEnvBase { }, }; - let analyzed_result = FullIbcPacketAnalysis { - send_tx: Some(send_tx_id.clone()), - outcome: analyzed_outcome, - }; - // We return the packet analysis - - Ok(analyzed_result) + Ok(analyzed_outcome) }) .collect::, InterchainMockError>>()?; - let response = IbcTxAnalysis { + let response = NestedPacketsFlow { tx_id: send_tx_id, packets: packet_analysis, }; @@ -302,7 +289,7 @@ impl InterchainEnv> for MockInterchainEnvBase { src_channel: ChannelId, dst_chain: ChainId, sequence: Sequence, - ) -> Result>, Self::Error> { + ) -> Result>, Self::Error> { let src_mock = self.get_chain(src_chain)?; let dst_mock = self.get_chain(dst_chain)?; @@ -320,10 +307,7 @@ impl InterchainEnv> for MockInterchainEnvBase { timeout_tx, close_channel_confirm: _, } => IbcPacketOutcome::Timeout { - timeout_tx: TxId { - response: timeout_tx, - chain_id: src_chain.to_string(), - }, + timeout_tx: TxId::new(src_chain.to_string(), timeout_tx), }, relayer::RelayingResult::Acknowledgement { tx, ack } => { let ack_string = @@ -337,20 +321,14 @@ impl InterchainEnv> for MockInterchainEnvBase { ack_string, ); IbcPacketOutcome::Success { - receive_tx: TxId { - response: relay_result.receive_tx, - chain_id: dst_chain.to_string(), - }, - ack_tx: TxId { - response: tx, - chain_id: src_chain.to_string(), - }, + receive_tx: TxId::new(dst_chain.to_string(), relay_result.receive_tx), + ack_tx: TxId::new(src_chain.to_string(), tx), ack, } } }; - let analysis_result = IbcPacketAnalysis { + let analysis_result = SinglePacketFlow { send_tx: None, // This is not available in this context unfortunately outcome, }; diff --git a/packages/interchain/proto/Cargo.toml b/packages/interchain/proto/Cargo.toml index 32daef5f5..0256bfe04 100644 --- a/packages/interchain/proto/Cargo.toml +++ b/packages/interchain/proto/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cw-orch-proto" description = "A helper crate for interaction with protos from different chains. Mostly used for handling cw20 coins and ibc transfers" -version = "0.7.0" +version = "0.8.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/packages/interchain/proto/src/tokenfactory.rs b/packages/interchain/proto/src/tokenfactory.rs index bcde6ea54..cddd066e1 100644 --- a/packages/interchain/proto/src/tokenfactory.rs +++ b/packages/interchain/proto/src/tokenfactory.rs @@ -1,8 +1,7 @@ #![allow(non_snake_case)] use cw_orch_interchain_core::{ - channel::InterchainChannel, types::IbcTxAnalysis, IbcQueryHandler, InterchainEnv, - InterchainError, + channel::InterchainChannel, IbcQueryHandler, InterchainEnv, InterchainError, NestedPacketsFlow, }; use ibc_proto::ibc::{ applications::transfer::v1::MsgTransferResponse, apps::transfer::v1::MsgTransfer, @@ -94,7 +93,7 @@ pub fn transfer_tokens, timeout: Option, memo: Option, -) -> Result, InterchainError> { +) -> Result, InterchainError> { let chain_id = origin.block_info().unwrap().chain_id; let (source_port, _) = ibc_channel.get_ordered_ports_from(&chain_id)?; diff --git a/publish.sh b/publish.sh index ee9f0a730..31413b9d5 100755 --- a/publish.sh +++ b/publish.sh @@ -33,6 +33,7 @@ INTERCHAIN_PACKAGES=" starship interchain-daemon interchain-mock + proto " CORE="cw-orch-daemon cw-orch cw-orch-interchain"