From b825cfb649e3b3592d20191d44bdc79cf125a285 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 2 Nov 2023 12:29:05 +0100 Subject: [PATCH 01/22] Added IBC implementation --- Cargo.lock | 443 ++++++++- Cargo.toml | 25 +- src/app.rs | 145 ++- src/bank.rs | 85 ++ src/contracts.rs | 289 +++++- src/ibc.rs | 12 - src/ibc/addresses.rs | 53 ++ src/ibc/api.rs | 110 +++ src/ibc/mod.rs | 46 + src/ibc/relayer.rs | 425 +++++++++ src/ibc/simple_ibc.rs | 1128 +++++++++++++++++++++++ src/ibc/state.rs | 55 ++ src/ibc/test.rs | 154 ++++ src/ibc/test/bank.rs | 259 ++++++ src/ibc/test/polytone.rs | 291 ++++++ src/ibc/types.rs | 231 +++++ src/lib.rs | 2 +- src/module.rs | 77 +- src/wasm.rs | 305 ++++++ tests/mod.rs | 1 + tests/test_app_builder/test_with_ibc.rs | 9 +- 21 files changed, 4084 insertions(+), 61 deletions(-) delete mode 100644 src/ibc.rs create mode 100644 src/ibc/addresses.rs create mode 100644 src/ibc/api.rs create mode 100644 src/ibc/mod.rs create mode 100644 src/ibc/relayer.rs create mode 100644 src/ibc/simple_ibc.rs create mode 100644 src/ibc/state.rs create mode 100644 src/ibc/test.rs create mode 100644 src/ibc/test/bank.rs create mode 100644 src/ibc/test/polytone.rs create mode 100644 src/ibc/types.rs diff --git a/Cargo.lock b/Cargo.lock index 54c1c8c7..a9f7927f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,37 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "abstract-cw-multi-test" +version = "0.17.0" +dependencies = [ + "anyhow", + "bech32", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw20-ics20", + "derivative", + "ecdsa", + "env_logger", + "hex", + "itertools", + "log", + "once_cell", + "polytone", + "polytone-note", + "polytone-proxy", + "polytone-voice", + "prost", + "schemars", + "serde", + "serde_json", + "sha2 0.10.8", + "sha256", + "thiserror", +] + [[package]] name = "addr2line" version = "0.21.0" @@ -19,15 +50,24 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ "getrandom", "once_cell", "version_check", ] +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.75" @@ -37,6 +77,17 @@ dependencies = [ "backtrace", ] +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -60,9 +111,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -76,6 +127,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "block-buffer" version = "0.9.0" @@ -158,9 +215,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef683a9c1c4eabd6d31515719d0d2cc66952c4c87f7eb192bfc90384517dc34" +checksum = "0df41ea55f2946b6b43579659eec048cc2f66e8c8e2e3652fc5e5e476f673856" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -171,9 +228,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9567025acbb4c0c008178393eb53b3ac3c2e492c25949d3bf415b9cbe80772d8" +checksum = "43609e92ce1b9368aa951b334dd354a2d0dd4d484931a5f83ae10e12a26c8ba9" dependencies = [ "proc-macro2", "quote", @@ -204,9 +261,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -247,22 +304,17 @@ dependencies = [ ] [[package]] -name = "cw-multi-test" -version = "0.17.0" +name = "cw-controllers" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b129ca74fa41111fd2e1727426532556dc63973420343b659f5c072b85d789" dependencies = [ - "anyhow", - "bech32", + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw-utils", - "derivative", - "ecdsa", - "itertools", - "once_cell", - "prost", "schemars", "serde", - "sha2 0.10.8", "thiserror", ] @@ -306,6 +358,38 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw20" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786e9da5e937f473cecd2463e81384c1af65d0f6398bbd851be7655487c55492" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "schemars", + "serde", +] + +[[package]] +name = "cw20-ics20" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3392c2c3b0f6afe736e2f236d73c6895c3d6e87804bba76e0d9de2e8f9f51f55" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-controllers", + "cw-storage-plus", + "cw-utils", + "cw2", + "cw20", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "der" version = "0.7.8" @@ -408,6 +492,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "ff" version = "0.13.0" @@ -472,6 +579,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + [[package]] name = "hex" version = "0.4.3" @@ -487,6 +600,23 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + [[package]] name = "itertools" version = "0.11.0" @@ -522,6 +652,18 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "memchr" version = "2.6.4" @@ -558,6 +700,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + [[package]] name = "pkcs8" version = "0.10.2" @@ -568,6 +716,65 @@ dependencies = [ "spki", ] +[[package]] +name = "polytone" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f16d20da9144fdf0658e785fc9108b86cecee517335ff531745029dd56088" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "thiserror", +] + +[[package]] +name = "polytone-note" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc85da4f72e5a3837b5fbc6a494702e2b7e7b5f695cef56a223216fbf9de3c9" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw2", + "polytone", + "thiserror", +] + +[[package]] +name = "polytone-proxy" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b373674cd2345c0f646c11bccfbeed78214959a56cd3224d80618b07e73b1b4" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw2", + "polytone", + "thiserror", +] + +[[package]] +name = "polytone-voice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34e4ce7a7816207baacb9137cfd5f102c2e6160641d6699fe5d798dc513e6b09" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw2", + "polytone", + "polytone-proxy", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "proc-macro2" version = "1.0.69" @@ -624,6 +831,35 @@ dependencies = [ "getrandom", ] +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rfc6979" version = "0.4.0" @@ -640,6 +876,19 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustix" +version = "0.38.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.15" @@ -686,15 +935,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] @@ -710,9 +959,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", @@ -732,9 +981,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -765,6 +1014,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha256" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2 0.10.8", + "tokio", +] + [[package]] name = "signature" version = "2.1.0" @@ -819,26 +1081,46 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "pin-project-lite", +] + [[package]] name = "typenum" version = "1.17.0" @@ -863,6 +1145,103 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index d671e26b..a47d8d2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,20 +1,21 @@ [package] -name = "cw-multi-test" + +name = "abstract-cw-multi-test" version = "0.17.0" authors = ["Ethan Frey "] edition = "2021" -description = "Test helpers for multi-contract interactions" +description = "Test helpers for multi-contract interactions with ibc capabilities" license = "Apache-2.0" repository = "https://github.com/CosmWasm/cw-multi-test" homepage = "https://cosmwasm.com" [features] -default = ["iterator", "staking", "cosmwasm_1_1"] +default = ["iterator", "staking", "cosmwasm_1_2"] iterator = ["cosmwasm-std/iterator"] stargate = ["cosmwasm-std/stargate"] staking = ["cosmwasm-std/staking"] backtrace = ["anyhow/backtrace"] -cosmwasm_1_1 = ["cosmwasm-std/cosmwasm_1_1"] +cosmwasm_1_1 = ["cosmwasm-std/cosmwasm_1_1", "cosmwasm-std/ibc3"] cosmwasm_1_2 = ["cosmwasm_1_1", "cosmwasm-std/cosmwasm_1_2"] cosmwasm_1_3 = ["cosmwasm_1_2", "cosmwasm-std/cosmwasm_1_3"] cosmwasm_1_4 = ["cosmwasm_1_3", "cosmwasm-std/cosmwasm_1_4"] @@ -32,11 +33,25 @@ serde = { version = "1.0.188", default-features = false, features = ["derive"] } sha2 = "0.10.8" thiserror = "1.0.49" +cosmwasm-schema = "1.3.3" +log = "0.4.20" +cw20-ics20 = "1.1.0" +hex = "0.4.3" +serde_json = "1.0.40" + +sha256 = "1.4.0" +bech32 = "0.9.1" + [dev-dependencies] bech32 = "0.9.1" once_cell = "1.18.0" -sha2 = "0.10.8" # We don't use these dependencies directly, # we tighten versions that builds with `-Zminimal-versions` work. ecdsa = "0.16.8" + +env_logger = "0.10.0" +polytone-note = {version="1.0.0" } +polytone-voice = {version="1.0.0" } +polytone-proxy = {version="1.0.0" } +polytone = {version="1.0.0" } diff --git a/src/app.rs b/src/app.rs index 9e3ed072..35739078 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,7 +3,8 @@ use crate::contracts::Contract; use crate::error::{bail, AnyResult}; use crate::executor::{AppResponse, Executor}; use crate::gov::Gov; -use crate::ibc::Ibc; +use crate::ibc::types::MockIbcQuery; +use crate::ibc::{types::IbcResponse, Ibc, IbcModuleMsg, IbcPacketRelayingMsg as IbcSudo}; use crate::module::{FailingModule, Module}; use crate::staking::{Distribution, DistributionKeeper, StakeKeeper, Staking, StakingSudo}; use crate::transactions::transactional; @@ -261,7 +262,7 @@ where /// /// ``` /// use cosmwasm_std::Addr; - /// use cw_multi_test::App; + /// use abstract_cw_multi_test::App; /// /// // contract implementation /// mod echo { @@ -269,7 +270,7 @@ where /// # use std::todo; /// # use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError, SubMsg, WasmMsg}; /// # use serde::{Deserialize, Serialize}; - /// # use cw_multi_test::{Contract, ContractWrapper}; + /// # use abstract_cw_multi_test::{Contract, ContractWrapper}; /// # /// # fn instantiate(_: DepsMut, _: Env, _: MessageInfo, _: Empty) -> Result { /// # todo!() @@ -403,6 +404,20 @@ where }) } + /// Queries the IBC module + pub fn ibc_query(&self, query: MockIbcQuery) -> AnyResult { + let Self { + block, + router, + api, + storage, + } = self; + + let querier = router.querier(api, storage, block); + + router.ibc.query(api, storage, &querier, block, query) + } + /// Runs arbitrary SudoMsg. /// This will create a cache before the execution, so no state changes are persisted if this /// returns an error, but all are persisted on success. @@ -472,6 +487,7 @@ pub enum SudoMsg { Custom(Empty), Staking(StakingSudo), Wasm(WasmSudo), + Ibc(IbcSudo), } impl From for SudoMsg { @@ -492,6 +508,19 @@ impl From for SudoMsg { } } +/// We use it to allow calling into modules from the ibc module. This is used for receiving packets +pub struct IbcRouterMsg { + pub module: IbcModule, + pub msg: IbcModuleMsg, +} + +#[cosmwasm_schema::cw_serde] +pub enum IbcModule { + Wasm(Addr), // The wasm module needs to contain the wasm contract address (usually decoded from the port) + Bank, + Staking, +} + pub trait CosmosRouter { type ExecC; type QueryC: CustomQuery; @@ -520,6 +549,14 @@ pub trait CosmosRouter { block: &BlockInfo, msg: SudoMsg, ) -> AnyResult; + + fn ibc( + &self, + api: &dyn Api, + storage: &mut dyn Storage, + block: &BlockInfo, + msg: IbcRouterMsg, + ) -> AnyResult; } impl CosmosRouter @@ -576,7 +613,7 @@ where QueryRequest::Bank(req) => self.bank.query(api, storage, &querier, block, req), QueryRequest::Custom(req) => self.custom.query(api, storage, &querier, block, req), QueryRequest::Staking(req) => self.staking.query(api, storage, &querier, block, req), - QueryRequest::Ibc(req) => self.ibc.query(api, storage, &querier, block, req), + QueryRequest::Ibc(req) => self.ibc.query(api, storage, &querier, block, req.into()), _ => unimplemented!(), } } @@ -596,6 +633,96 @@ where SudoMsg::Bank(msg) => self.bank.sudo(api, storage, self, block, msg), SudoMsg::Staking(msg) => self.staking.sudo(api, storage, self, block, msg), SudoMsg::Custom(_) => unimplemented!(), + SudoMsg::Ibc(msg) => self.ibc.sudo(api, storage, self, block, msg), + } + } + + fn ibc( + &self, + api: &dyn Api, + storage: &mut dyn Storage, + block: &BlockInfo, + msg: IbcRouterMsg, + ) -> AnyResult { + match msg.module { + IbcModule::Bank => match msg.msg { + IbcModuleMsg::ChannelOpen(m) => self + .bank + .ibc_channel_open(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::ChannelConnect(m) => self + .bank + .ibc_channel_connect(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::ChannelClose(m) => self + .bank + .ibc_channel_close(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::PacketReceive(m) => self + .bank + .ibc_packet_receive(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::PacketAcknowledgement(m) => self + .bank + .ibc_packet_acknowledge(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::PacketTimeout(m) => self + .bank + .ibc_packet_timeout(api, storage, self, block, m) + .map(Into::into), + }, + IbcModule::Staking => match msg.msg { + IbcModuleMsg::ChannelOpen(m) => self + .staking + .ibc_channel_open(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::ChannelConnect(m) => self + .staking + .ibc_channel_connect(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::ChannelClose(m) => self + .staking + .ibc_channel_close(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::PacketReceive(m) => self + .staking + .ibc_packet_receive(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::PacketAcknowledgement(m) => self + .staking + .ibc_packet_acknowledge(api, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::PacketTimeout(m) => self + .staking + .ibc_packet_timeout(api, storage, self, block, m) + .map(Into::into), + }, + IbcModule::Wasm(contract_addr) => match msg.msg { + IbcModuleMsg::ChannelOpen(m) => self + .wasm + .ibc_channel_open(api, contract_addr, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::ChannelConnect(m) => self + .wasm + .ibc_channel_connect(api, contract_addr, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::ChannelClose(m) => self + .wasm + .ibc_channel_close(api, contract_addr, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::PacketReceive(m) => self + .wasm + .ibc_packet_receive(api, contract_addr, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::PacketAcknowledgement(m) => self + .wasm + .ibc_packet_acknowledge(api, contract_addr, storage, self, block, m) + .map(Into::into), + IbcModuleMsg::PacketTimeout(m) => self + .wasm + .ibc_packet_timeout(api, contract_addr, storage, self, block, m) + .map(Into::into), + }, } } } @@ -654,6 +781,16 @@ where ) -> AnyResult { panic!("Cannot sudo MockRouters"); } + + fn ibc( + &self, + _api: &dyn Api, + _storage: &mut dyn Storage, + _block: &BlockInfo, + _msg: IbcRouterMsg, + ) -> AnyResult { + panic!("Cannot ibc MockRouters"); + } } pub struct RouterQuerier<'a, ExecC, QueryC> { diff --git a/src/bank.rs b/src/bank.rs index b578bf9d..e4476ba3 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -1,6 +1,7 @@ use crate::app::CosmosRouter; use crate::error::{bail, AnyResult}; use crate::executor::AppResponse; +use crate::ibc::types::{AppIbcBasicResponse, AppIbcReceiveResponse}; use crate::module::Module; use crate::prefixed_storage::{prefixed, prefixed_read}; use cosmwasm_std::{ @@ -12,9 +13,13 @@ use cw_utils::NativeBalance; use itertools::Itertools; use schemars::JsonSchema; +use cosmwasm_std::{coins, from_json, IbcPacketAckMsg, IbcPacketReceiveMsg}; +use cw20_ics20::ibc::Ics20Packet; + const BALANCES: Map<&Addr, NativeBalance> = Map::new("balances"); pub const NAMESPACE_BANK: &[u8] = b"bank"; +pub const IBC_LOCK_MODULE_ADDRESS: &str = "ibc_bank_lock_module"; #[derive(Clone, std::fmt::Debug, PartialEq, Eq, JsonSchema)] pub enum BankSudo { @@ -229,6 +234,86 @@ impl Module for BankKeeper { q => bail!("Unsupported bank query: {:?}", q), } } + + fn ibc_packet_receive( + &self, + api: &dyn Api, + storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + request: IbcPacketReceiveMsg, + ) -> AnyResult { + // When receiving a packet, one simply needs to unpack the amount and send that to the the receiver + let packet: Ics20Packet = from_json(&request.packet.data)?; + + let mut bank_storage = prefixed(storage, NAMESPACE_BANK); + + // If the denom is exactly a denom that was sent through this channel, we can mint it directly without denom changes + // This can be verified by checking the ibc_module mock balance + let balances = + self.get_balance(&bank_storage, &Addr::unchecked(IBC_LOCK_MODULE_ADDRESS))?; + let locked_amount = balances.iter().find(|b| b.denom == packet.denom); + + if let Some(locked_amount) = locked_amount { + assert!( + locked_amount.amount >= packet.amount, + "The ibc locked amount is lower than the packet amount" + ); + // We send tokens from the IBC_LOCK_MODULE + self.send( + &mut bank_storage, + Addr::unchecked(IBC_LOCK_MODULE_ADDRESS), + api.addr_validate(&packet.receiver)?, + coins(packet.amount.u128(), packet.denom), + )?; + } else { + // Else, we receive the denom with prefixes + self.mint( + &mut bank_storage, + api.addr_validate(&packet.receiver)?, + coins( + packet.amount.u128(), + wrap_ibc_denom(request.packet.dest.channel_id, packet.denom), + ), + )?; + } + + // No acknowledgment needed + Ok(AppIbcReceiveResponse::default()) + } + + fn ibc_packet_acknowledge( + &self, + _api: &dyn Api, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcPacketAckMsg, + ) -> AnyResult { + // Acknowledgment can't fail, so no need for ack response parsing + Ok(AppIbcBasicResponse::default()) + } +} + +pub fn wrap_ibc_denom(channel_id: String, denom: String) -> String { + format!("ibc/{}/{}", channel_id, denom) +} + +pub fn optional_unwrap_ibc_denom(denom: String, expected_channel_id: String) -> String { + let split: Vec<_> = denom.splitn(3, '/').collect(); + if split.len() != 3 { + return denom; + } + + if split[0] != "ibc" { + return denom; + } + + if split[1] != expected_channel_id { + return denom; + } + + split[2].to_string() } #[cfg(test)] diff --git a/src/contracts.rs b/src/contracts.rs index 52a189be..2f7bd4f5 100644 --- a/src/contracts.rs +++ b/src/contracts.rs @@ -9,6 +9,12 @@ use std::error::Error; use std::fmt::{Debug, Display}; use std::ops::Deref; +use cosmwasm_std::{ + IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, + IbcChannelOpenResponse, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, + IbcReceiveResponse, +}; + /// Interface to call into a `Contract`. pub trait Contract where @@ -38,6 +44,66 @@ where fn reply(&self, deps: DepsMut, env: Env, msg: Reply) -> AnyResult>; fn migrate(&self, deps: DepsMut, env: Env, msg: Vec) -> AnyResult>; + + #[allow(unused)] + fn ibc_channel_open( + &self, + deps: DepsMut, + env: Env, + msg: IbcChannelOpenMsg, + ) -> AnyResult { + bail!("No Ibc capabilities on this contract") + } + + #[allow(unused)] + fn ibc_channel_connect( + &self, + deps: DepsMut, + env: Env, + msg: IbcChannelConnectMsg, + ) -> AnyResult> { + bail!("No Ibc capabilities on this contract") + } + + #[allow(unused)] + fn ibc_channel_close( + &self, + deps: DepsMut, + env: Env, + msg: IbcChannelCloseMsg, + ) -> AnyResult> { + bail!("No Ibc capabilities on this contract") + } + + #[allow(unused)] + fn ibc_packet_receive( + &self, + deps: DepsMut, + env: Env, + msg: IbcPacketReceiveMsg, + ) -> AnyResult> { + bail!("No Ibc capabilities on this contract") + } + + #[allow(unused)] + fn ibc_packet_acknowledge( + &self, + deps: DepsMut, + env: Env, + msg: IbcPacketAckMsg, + ) -> AnyResult> { + bail!("No Ibc capabilities on this contract") + } + + #[allow(unused)] + fn ibc_packet_timeout( + &self, + deps: DepsMut, + env: Env, + msg: IbcPacketTimeoutMsg, + ) -> AnyResult> { + bail!("No Ibc capabilities on this contract") + } } type ContractFn = @@ -46,6 +112,8 @@ type PermissionedFn = fn(deps: DepsMut, env: Env, msg: T) -> Resu type ReplyFn = fn(deps: DepsMut, env: Env, msg: Reply) -> Result, E>; type QueryFn = fn(deps: Deps, env: Env, msg: T) -> Result; +type IbcFn = fn(deps: DepsMut, env: Env, msg: T) -> Result; + type ContractClosure = Box, Env, MessageInfo, T) -> Result, E>>; type PermissionedClosure = Box, Env, T) -> Result, E>>; @@ -68,6 +136,12 @@ pub struct ContractWrapper< E5 = AnyError, T6 = Empty, E6 = AnyError, + E7 = AnyError, + E8 = AnyError, + E9 = AnyError, + E10 = AnyError, + E11 = AnyError, + E12 = AnyError, > where T1: DeserializeOwned + Debug, T2: DeserializeOwned, @@ -80,6 +154,12 @@ pub struct ContractWrapper< E4: Display + Debug + Send + Sync + 'static, E5: Display + Debug + Send + Sync + 'static, E6: Display + Debug + Send + Sync + 'static, + E7: Display + Debug + Send + Sync + 'static, + E8: Display + Debug + Send + Sync + 'static, + E9: Display + Debug + Send + Sync + 'static, + E10: Display + Debug + Send + Sync + 'static, + E11: Display + Debug + Send + Sync + 'static, + E12: Display + Debug + Send + Sync + 'static, C: Clone + Debug + PartialEq + JsonSchema, Q: CustomQuery + DeserializeOwned + 'static, { @@ -89,6 +169,14 @@ pub struct ContractWrapper< sudo_fn: Option>, reply_fn: Option>, migrate_fn: Option>, + + channel_open_fn: Option>, + channel_connect_fn: Option, E8, Q>>, + channel_close_fn: Option, E9, Q>>, + + ibc_packet_receive_fn: Option, E10, Q>>, + ibc_packet_ack_fn: Option, E11, Q>>, + ibc_packet_timeout_fn: Option, E12, Q>>, } impl ContractWrapper @@ -114,6 +202,14 @@ where sudo_fn: None, reply_fn: None, migrate_fn: None, + + channel_open_fn: None, + channel_connect_fn: None, + channel_close_fn: None, + + ibc_packet_receive_fn: None, + ibc_packet_ack_fn: None, + ibc_packet_timeout_fn: None, } } @@ -131,6 +227,14 @@ where sudo_fn: None, reply_fn: None, migrate_fn: None, + + channel_open_fn: None, + channel_connect_fn: None, + channel_close_fn: None, + + ibc_packet_receive_fn: None, + ibc_packet_ack_fn: None, + ibc_packet_timeout_fn: None, } } } @@ -167,6 +271,14 @@ where sudo_fn: Some(Box::new(sudo_fn)), reply_fn: self.reply_fn, migrate_fn: self.migrate_fn, + + channel_open_fn: self.channel_open_fn, + channel_connect_fn: self.channel_connect_fn, + channel_close_fn: self.channel_close_fn, + + ibc_packet_receive_fn: self.ibc_packet_receive_fn, + ibc_packet_ack_fn: self.ibc_packet_ack_fn, + ibc_packet_timeout_fn: self.ibc_packet_timeout_fn, } } @@ -185,6 +297,14 @@ where sudo_fn: Some(customize_permissioned_fn(sudo_fn)), reply_fn: self.reply_fn, migrate_fn: self.migrate_fn, + + channel_open_fn: self.channel_open_fn, + channel_connect_fn: self.channel_connect_fn, + channel_close_fn: self.channel_close_fn, + + ibc_packet_receive_fn: self.ibc_packet_receive_fn, + ibc_packet_ack_fn: self.ibc_packet_ack_fn, + ibc_packet_timeout_fn: self.ibc_packet_timeout_fn, } } @@ -202,6 +322,14 @@ where sudo_fn: self.sudo_fn, reply_fn: Some(Box::new(reply_fn)), migrate_fn: self.migrate_fn, + + channel_open_fn: self.channel_open_fn, + channel_connect_fn: self.channel_connect_fn, + channel_close_fn: self.channel_close_fn, + + ibc_packet_receive_fn: self.ibc_packet_receive_fn, + ibc_packet_ack_fn: self.ibc_packet_ack_fn, + ibc_packet_timeout_fn: self.ibc_packet_timeout_fn, } } @@ -220,6 +348,14 @@ where sudo_fn: self.sudo_fn, reply_fn: Some(customize_permissioned_fn(reply_fn)), migrate_fn: self.migrate_fn, + + channel_open_fn: self.channel_open_fn, + channel_connect_fn: self.channel_connect_fn, + channel_close_fn: self.channel_close_fn, + + ibc_packet_receive_fn: self.ibc_packet_receive_fn, + ibc_packet_ack_fn: self.ibc_packet_ack_fn, + ibc_packet_timeout_fn: self.ibc_packet_timeout_fn, } } @@ -238,6 +374,14 @@ where sudo_fn: self.sudo_fn, reply_fn: self.reply_fn, migrate_fn: Some(Box::new(migrate_fn)), + + channel_open_fn: self.channel_open_fn, + channel_connect_fn: self.channel_connect_fn, + channel_close_fn: self.channel_close_fn, + + ibc_packet_receive_fn: self.ibc_packet_receive_fn, + ibc_packet_ack_fn: self.ibc_packet_ack_fn, + ibc_packet_timeout_fn: self.ibc_packet_timeout_fn, } } @@ -256,6 +400,71 @@ where sudo_fn: self.sudo_fn, reply_fn: self.reply_fn, migrate_fn: Some(customize_permissioned_fn(migrate_fn)), + + channel_open_fn: self.channel_open_fn, + channel_connect_fn: self.channel_connect_fn, + channel_close_fn: self.channel_close_fn, + + ibc_packet_receive_fn: self.ibc_packet_receive_fn, + ibc_packet_ack_fn: self.ibc_packet_ack_fn, + ibc_packet_timeout_fn: self.ibc_packet_timeout_fn, + } + } + + // Adding IBC endpoint capabilities + pub fn with_ibc( + self, + channel_open_fn: IbcFn, + channel_connect_fn: IbcFn, E8A, Q>, + channel_close_fn: IbcFn, E9A, Q>, + + ibc_packet_receive_fn: IbcFn, E10A, Q>, + ibc_packet_ack_fn: IbcFn, E11A, Q>, + ibc_packet_timeout_fn: IbcFn, E12A, Q>, + ) -> ContractWrapper< + T1, + T2, + T3, + E1, + E2, + E3, + C, + Q, + T4, + E4, + E5, + T6, + E6, + E7A, + E8A, + E9A, + E10A, + E11A, + E12A, + > + where + E7A: Display + Debug + Send + Sync + 'static, + E8A: Display + Debug + Send + Sync + 'static, + E9A: Display + Debug + Send + Sync + 'static, + E10A: Display + Debug + Send + Sync + 'static, + E11A: Display + Debug + Send + Sync + 'static, + E12A: Display + Debug + Send + Sync + 'static, + { + ContractWrapper { + execute_fn: self.execute_fn, + instantiate_fn: self.instantiate_fn, + query_fn: self.query_fn, + sudo_fn: self.sudo_fn, + reply_fn: self.reply_fn, + migrate_fn: self.migrate_fn, + + channel_open_fn: Some(channel_open_fn), + channel_connect_fn: Some(channel_connect_fn), + channel_close_fn: Some(channel_close_fn), + + ibc_packet_receive_fn: Some(ibc_packet_receive_fn), + ibc_packet_ack_fn: Some(ibc_packet_ack_fn), + ibc_packet_timeout_fn: Some(ibc_packet_timeout_fn), } } } @@ -363,8 +572,8 @@ where } } -impl Contract - for ContractWrapper +impl Contract + for ContractWrapper where T1: DeserializeOwned + Debug + Clone, T2: DeserializeOwned + Debug + Clone, @@ -377,8 +586,14 @@ where E4: Display + Debug + Send + Sync + 'static, E5: Display + Debug + Send + Sync + 'static, E6: Display + Debug + Send + Sync + 'static, + E7: Display + Debug + Send + Sync + 'static, + E8: Display + Debug + Send + Sync + 'static, + E9: Display + Debug + Send + Sync + 'static, + E10: Display + Debug + Send + Sync + 'static, + E11: Display + Debug + Send + Sync + 'static, + E12: Display + Debug + Send + Sync + 'static, C: Clone + Debug + PartialEq + JsonSchema, - Q: CustomQuery + DeserializeOwned, + Q: CustomQuery + DeserializeOwned + 'static, { fn execute( &self, @@ -432,4 +647,72 @@ where None => bail!("migrate not implemented for contract"), } } + + fn ibc_channel_open( + &self, + deps: DepsMut, + env: Env, + msg: IbcChannelOpenMsg, + ) -> AnyResult { + match &self.channel_open_fn { + Some(channel_open) => channel_open(deps, env, msg).map_err(|err| anyhow!(err)), + None => bail!("channel open not implemented for contract"), + } + } + fn ibc_channel_connect( + &self, + deps: DepsMut, + env: Env, + msg: IbcChannelConnectMsg, + ) -> AnyResult> { + match &self.channel_connect_fn { + Some(channel_connect) => channel_connect(deps, env, msg).map_err(|err| anyhow!(err)), + None => bail!("channel connect not implemented for contract"), + } + } + fn ibc_channel_close( + &self, + deps: DepsMut, + env: Env, + msg: IbcChannelCloseMsg, + ) -> AnyResult> { + match &self.channel_close_fn { + Some(channel_close) => channel_close(deps, env, msg).map_err(|err| anyhow!(err)), + None => bail!("channel close not implemented for contract"), + } + } + + fn ibc_packet_receive( + &self, + deps: DepsMut, + env: Env, + msg: IbcPacketReceiveMsg, + ) -> AnyResult> { + match &self.ibc_packet_receive_fn { + Some(packet_receive) => packet_receive(deps, env, msg).map_err(|err| anyhow!(err)), + None => bail!("packet receive not implemented for contract"), + } + } + fn ibc_packet_acknowledge( + &self, + deps: DepsMut, + env: Env, + msg: IbcPacketAckMsg, + ) -> AnyResult> { + match &self.ibc_packet_ack_fn { + Some(packet_ack) => packet_ack(deps, env, msg).map_err(|err| anyhow!(err)), + None => bail!("packet ack not implemented for contract"), + } + } + fn ibc_packet_timeout( + &self, + deps: DepsMut, + env: Env, + msg: IbcPacketTimeoutMsg, + ) -> AnyResult> { + match &self.ibc_packet_timeout_fn { + Some(packet_timeout) => packet_timeout(deps, env, msg).map_err(|err| anyhow!(err)), + None => bail!("packet timeout not implemented for contract"), + } + } } diff --git a/src/ibc.rs b/src/ibc.rs deleted file mode 100644 index 97e5b026..00000000 --- a/src/ibc.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::{AcceptingModule, FailingModule, Module}; -use cosmwasm_std::{Empty, IbcMsg, IbcQuery}; - -pub trait Ibc: Module {} - -pub type IbcAcceptingModule = AcceptingModule; - -impl Ibc for IbcAcceptingModule {} - -pub type IbcFailingModule = FailingModule; - -impl Ibc for IbcFailingModule {} diff --git a/src/ibc/addresses.rs b/src/ibc/addresses.rs new file mode 100644 index 00000000..9c701310 --- /dev/null +++ b/src/ibc/addresses.rs @@ -0,0 +1,53 @@ +use crate::error::AnyResult; +use crate::AddressGenerator; + +use cosmwasm_std::{instantiate2_address, Addr, Api, CanonicalAddr, Storage}; +use sha2::{digest::Update, Digest, Sha256}; + +#[derive(Default)] +pub struct MockAddressGenerator; + +impl AddressGenerator for MockAddressGenerator { + fn contract_address( + &self, + api: &dyn Api, + _storage: &mut dyn Storage, + code_id: u64, + instance_id: u64, + ) -> AnyResult { + let canonical_addr = Self::instantiate_address(code_id, instance_id); + Ok(Addr::unchecked(api.addr_humanize(&canonical_addr)?)) + } + + fn predictable_contract_address( + &self, + api: &dyn Api, + _storage: &mut dyn Storage, + _code_id: u64, + _instance_id: u64, + checksum: &[u8], + creator: &CanonicalAddr, + salt: &[u8], + ) -> AnyResult { + let canonical_addr = instantiate2_address(checksum, creator, salt)?; + Ok(Addr::unchecked(api.addr_humanize(&canonical_addr)?)) + } +} + +impl MockAddressGenerator { + // non-predictable contract address generator, see `BuildContractAddressClassic` + // implementation in wasmd: https://github.com/CosmWasm/wasmd/blob/main/x/wasm/keeper/addresses.go#L35-L42 + fn instantiate_address(code_id: u64, instance_id: u64) -> CanonicalAddr { + let mut key = Vec::::new(); + key.extend_from_slice(b"wasm\0"); + key.extend_from_slice(&code_id.to_be_bytes()); + key.extend_from_slice(&instance_id.to_be_bytes()); + let module = Sha256::digest("module".as_bytes()); + Sha256::new() + .chain(module) + .chain(key) + .finalize() + .to_vec() + .into() + } +} diff --git a/src/ibc/api.rs b/src/ibc/api.rs new file mode 100644 index 00000000..193a647b --- /dev/null +++ b/src/ibc/api.rs @@ -0,0 +1,110 @@ +use bech32::{decode, encode, FromBase32, ToBase32, Variant}; +use cosmwasm_std::{ + Addr, Api, CanonicalAddr, RecoverPubkeyError, StdError, StdResult, VerificationError, +}; + +use sha2::{Digest, Sha256}; + +pub struct MockApiBech32 { + prefix: &'static str, +} + +impl MockApiBech32 { + pub fn new(prefix: &'static str) -> Self { + Self { prefix } + } +} + +impl Api for MockApiBech32 { + fn addr_validate(&self, input: &str) -> StdResult { + let canonical = self.addr_canonicalize(input)?; + let normalized = self.addr_humanize(&canonical)?; + if input != normalized { + Err(StdError::generic_err( + "Invalid input: address not normalized", + )) + } else { + Ok(Addr::unchecked(input)) + } + } + + fn addr_canonicalize(&self, input: &str) -> StdResult { + match decode(input) { + Ok((prefix, decoded, Variant::Bech32)) => { + if prefix == self.prefix { + if let Ok(bytes) = Vec::::from_base32(&decoded) { + return Ok(bytes.into()); + } + } + Err(StdError::generic_err("Decoded but wrong base32")) + } + Err(e) => Err(StdError::generic_err(format!( + "Invalid address input : {:?}", + e + ))), + _ => Err(StdError::generic_err("Wrong decode variant")), + } + } + + fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult { + if let Ok(encoded) = encode( + self.prefix, + canonical.as_slice().to_base32(), + Variant::Bech32, + ) { + Ok(Addr::unchecked(encoded)) + } else { + Err(StdError::generic_err("Invalid canonical address")) + } + } + + fn secp256k1_verify( + &self, + _message_hash: &[u8], + _signature: &[u8], + _public_key: &[u8], + ) -> Result { + unimplemented!() + } + + fn secp256k1_recover_pubkey( + &self, + _message_hash: &[u8], + _signature: &[u8], + _recovery_param: u8, + ) -> Result, RecoverPubkeyError> { + unimplemented!() + } + + fn ed25519_verify( + &self, + _message: &[u8], + _signature: &[u8], + _public_key: &[u8], + ) -> Result { + unimplemented!() + } + + fn ed25519_batch_verify( + &self, + _messages: &[&[u8]], + _signatures: &[&[u8]], + _public_keys: &[&[u8]], + ) -> Result { + unimplemented!() + } + + fn debug(&self, _message: &str) { + unimplemented!() + } +} + +impl MockApiBech32 { + pub fn addr_make(&self, input: &str) -> Addr { + let digest = Sha256::digest(input).to_vec(); + match encode(self.prefix, digest.to_base32(), Variant::Bech32) { + Ok(address) => Addr::unchecked(address), + Err(reason) => panic!("Generating address failed with reason: {reason}"), + } + } +} diff --git a/src/ibc/mod.rs b/src/ibc/mod.rs new file mode 100644 index 00000000..1b8bb49c --- /dev/null +++ b/src/ibc/mod.rs @@ -0,0 +1,46 @@ +use cosmwasm_std::{ + IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcMsg, IbcPacketAckMsg, + IbcPacketReceiveMsg, IbcPacketTimeoutMsg, +}; + +use crate::{AcceptingModule, FailingModule, Module}; + +pub mod addresses; +pub mod api; +pub mod relayer; +mod simple_ibc; +mod state; +pub mod types; + +pub use self::types::IbcPacketRelayingMsg; +use self::types::MockIbcQuery; +pub use simple_ibc::IbcSimpleModule; + +/// This is added for modules to implement actions upon ibc actions. +/// This kind of execution flow is copied from the WASM way of doing things and is not 100% completetely compatible with the IBC standard +/// Those messages should only be called by the Ibc module. +/// For additional Modules, the packet endpoints should be implemented +/// The Channel endpoints are usually not implemented besides storing the channel ids +#[cosmwasm_schema::cw_serde] +pub enum IbcModuleMsg { + ChannelOpen(IbcChannelOpenMsg), + ChannelConnect(IbcChannelConnectMsg), + ChannelClose(IbcChannelCloseMsg), + + PacketReceive(IbcPacketReceiveMsg), + PacketAcknowledgement(IbcPacketAckMsg), + PacketTimeout(IbcPacketTimeoutMsg), +} + +pub trait Ibc: Module {} + +pub type IbcAcceptingModule = AcceptingModule; + +impl Ibc for IbcAcceptingModule {} + +pub type IbcFailingModule = FailingModule; + +impl Ibc for IbcFailingModule {} + +#[cfg(test)] +mod test; diff --git a/src/ibc/relayer.rs b/src/ibc/relayer.rs new file mode 100644 index 00000000..3b33b2ee --- /dev/null +++ b/src/ibc/relayer.rs @@ -0,0 +1,425 @@ +use std::fmt; + +use anyhow::Result as AnyResult; +use cosmwasm_std::{ + from_json, Api, Binary, CustomQuery, IbcEndpoint, IbcOrder, StdError, StdResult, Storage, +}; +use schemars::JsonSchema; +use serde::de::DeserializeOwned; + +use crate::{ + ibc::types::Connection, App, AppResponse, Bank, Distribution, Gov, Ibc, Module, Staking, + SudoMsg, Wasm, +}; + +use super::{ + types::{IbcPacketData, MockIbcQuery}, + IbcPacketRelayingMsg, +}; + +#[derive(Debug)] +pub struct ChannelCreationResult { + pub init: AppResponse, + pub r#try: AppResponse, + pub ack: AppResponse, + pub confirm: AppResponse, + pub src_channel: String, + pub dst_channel: String, +} + +pub fn create_connection< + BankT1, + ApiT1, + StorageT1, + CustomT1, + WasmT1, + StakingT1, + DistrT1, + IbcT1, + GovT1, + BankT2, + ApiT2, + StorageT2, + CustomT2, + WasmT2, + StakingT2, + DistrT2, + IbcT2, + GovT2, +>( + src_app: &mut App, + dst_app: &mut App, +) -> AnyResult<(String, String)> +where + CustomT1::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static, + CustomT1::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT1: Wasm, + BankT1: Bank, + ApiT1: Api, + StorageT1: Storage, + CustomT1: Module, + StakingT1: Staking, + DistrT1: Distribution, + IbcT1: Ibc, + GovT1: Gov, + + CustomT2::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static, + CustomT2::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT2: Wasm, + BankT2: Bank, + ApiT2: Api, + StorageT2: Storage, + CustomT2: Module, + StakingT2: Staking, + DistrT2: Distribution, + IbcT2: Ibc, + GovT2: Gov, +{ + let src_connection_msg = IbcPacketRelayingMsg::CreateConnection { + remote_chain_id: dst_app.block_info().chain_id, + connection_id: None, + counterparty_connection_id: None, + }; + let src_create_response = src_app.sudo(crate::SudoMsg::Ibc(src_connection_msg))?; + let src_connection = + get_event_attr_value(&src_create_response, "connection_open", "connection_id")?; + + let dst_connection_msg = IbcPacketRelayingMsg::CreateConnection { + remote_chain_id: src_app.block_info().chain_id, + connection_id: None, + counterparty_connection_id: Some(src_connection.clone()), + }; + let dst_create_response = dst_app.sudo(crate::SudoMsg::Ibc(dst_connection_msg))?; + let dst_connection = + get_event_attr_value(&dst_create_response, "connection_open", "connection_id")?; + + let src_connection_msg = IbcPacketRelayingMsg::CreateConnection { + remote_chain_id: dst_app.block_info().chain_id, + connection_id: Some(src_connection.clone()), + counterparty_connection_id: Some(dst_connection.clone()), + }; + src_app.sudo(crate::SudoMsg::Ibc(src_connection_msg))?; + + Ok((src_connection, dst_connection)) +} +pub fn create_channel< + BankT1, + ApiT1, + StorageT1, + CustomT1, + WasmT1, + StakingT1, + DistrT1, + IbcT1, + GovT1, + BankT2, + ApiT2, + StorageT2, + CustomT2, + WasmT2, + StakingT2, + DistrT2, + IbcT2, + GovT2, +>( + src_app: &mut App, + dst_app: &mut App, + src_connection_id: String, + src_port: String, + dst_port: String, + version: String, + order: IbcOrder, +) -> AnyResult +where + CustomT1::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static, + CustomT1::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT1: Wasm, + BankT1: Bank, + ApiT1: Api, + StorageT1: Storage, + CustomT1: Module, + StakingT1: Staking, + DistrT1: Distribution, + IbcT1: Ibc, + GovT1: Gov, + + CustomT2::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static, + CustomT2::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT2: Wasm, + BankT2: Bank, + ApiT2: Api, + StorageT2: Storage, + CustomT2: Module, + StakingT2: Staking, + DistrT2: Distribution, + IbcT2: Ibc, + GovT2: Gov, +{ + let ibc_init_msg = IbcPacketRelayingMsg::OpenChannel { + local_connection_id: src_connection_id.clone(), + local_port: src_port.clone(), + version: version.clone(), + order: order.clone(), + counterparty_version: None, + counterparty_endpoint: IbcEndpoint { + port_id: dst_port.clone(), + channel_id: "".to_string(), + }, + }; + + let init_response = src_app.sudo(crate::SudoMsg::Ibc(ibc_init_msg))?; + + log::debug!("Channel init {:?}", init_response); + + // Get the returned version + let new_version = get_event_attr_value(&init_response, "channel_open_init", "version")?; + // Get the returned channel id + let src_channel = get_event_attr_value(&init_response, "channel_open_init", "channel_id")?; + + let counterparty: Connection = from_json(src_app.ibc_query(MockIbcQuery::ConnectedChain { + connection_id: src_connection_id, + })?)?; + + let ibc_try_msg = IbcPacketRelayingMsg::OpenChannel { + local_connection_id: counterparty.counterparty_connection_id.unwrap(), + local_port: dst_port.clone(), + version: version.clone(), + order, + counterparty_version: Some(new_version), + counterparty_endpoint: IbcEndpoint { + port_id: src_port.clone(), + channel_id: src_channel.clone(), + }, + }; + + let try_response: crate::AppResponse = dst_app.sudo(crate::SudoMsg::Ibc(ibc_try_msg))?; + log::debug!("Channel try {:?}", try_response); + + // Get the returned version + let new_version = get_event_attr_value(&try_response, "channel_open_try", "version")?; + // Get the returned channel id + let dst_channel = get_event_attr_value(&try_response, "channel_open_try", "channel_id")?; + + let ibc_ack_msg = IbcPacketRelayingMsg::ConnectChannel { + port_id: src_port.clone(), + channel_id: src_channel.clone(), + counterparty_version: Some(new_version.clone()), + counterparty_endpoint: IbcEndpoint { + port_id: dst_port.clone(), + channel_id: dst_channel.clone(), + }, + }; + + let ack_response: crate::AppResponse = src_app.sudo(crate::SudoMsg::Ibc(ibc_ack_msg))?; + log::debug!("Channel ack {:?}", ack_response); + + let ibc_ack_msg = IbcPacketRelayingMsg::ConnectChannel { + port_id: dst_port.clone(), + channel_id: dst_channel.clone(), + counterparty_version: Some(new_version), + counterparty_endpoint: IbcEndpoint { + port_id: src_port.clone(), + channel_id: src_channel.clone(), + }, + }; + + let confirm_response: crate::AppResponse = dst_app.sudo(crate::SudoMsg::Ibc(ibc_ack_msg))?; + log::debug!("Channel confirm {:?}", confirm_response); + + Ok(ChannelCreationResult { + init: init_response, + r#try: try_response, + ack: ack_response, + confirm: confirm_response, + src_channel, + dst_channel, + }) +} + +pub fn relay_packets_in_tx< + BankT1, + ApiT1, + StorageT1, + CustomT1, + WasmT1, + StakingT1, + DistrT1, + IbcT1, + GovT1, + BankT2, + ApiT2, + StorageT2, + CustomT2, + WasmT2, + StakingT2, + DistrT2, + IbcT2, + GovT2, +>( + app1: &mut App, + app2: &mut App, + app1_tx_response: AppResponse, +) -> AnyResult> +where + CustomT1::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static, + CustomT1::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT1: Wasm, + BankT1: Bank, + ApiT1: Api, + StorageT1: Storage, + CustomT1: Module, + StakingT1: Staking, + DistrT1: Distribution, + IbcT1: Ibc, + GovT1: Gov, + + CustomT2::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static, + CustomT2::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT2: Wasm, + BankT2: Bank, + ApiT2: Api, + StorageT2: Storage, + CustomT2: Module, + StakingT2: Staking, + DistrT2: Distribution, + IbcT2: Ibc, + GovT2: Gov, +{ + // Find all packets and their data + let packets = get_all_event_attr_value(&app1_tx_response, "send_packet", "packet_sequence"); + let channels = get_all_event_attr_value(&app1_tx_response, "send_packet", "packet_src_channel"); + let ports = get_all_event_attr_value(&app1_tx_response, "send_packet", "packet_src_port"); + + // For all packets, query the packetdata and relay them + + let mut packet_forwarding = vec![]; + + for i in 0..packets.len() { + let (rcv_response, ack_response, ack) = relay_packet( + app1, + app2, + ports[i].clone(), + channels[i].clone(), + packets[i].parse()?, + )?; + + packet_forwarding.push((rcv_response, ack_response, ack)); + } + + Ok(packet_forwarding) +} + +/// Relays (rcv + ack) any pending packet between 2 chains +pub fn relay_packet< + BankT1, + ApiT1, + StorageT1, + CustomT1, + WasmT1, + StakingT1, + DistrT1, + IbcT1, + GovT1, + BankT2, + ApiT2, + StorageT2, + CustomT2, + WasmT2, + StakingT2, + DistrT2, + IbcT2, + GovT2, +>( + app1: &mut App, + app2: &mut App, + src_port_id: String, + src_channel_id: String, + sequence: u64, +) -> AnyResult<(AppResponse, AppResponse, Binary)> +where + CustomT1::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static, + CustomT1::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT1: Wasm, + BankT1: Bank, + ApiT1: Api, + StorageT1: Storage, + CustomT1: Module, + StakingT1: Staking, + DistrT1: Distribution, + IbcT1: Ibc, + GovT1: Gov, + + CustomT2::ExecT: Clone + fmt::Debug + PartialEq + JsonSchema + DeserializeOwned + 'static, + CustomT2::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT2: Wasm, + BankT2: Bank, + ApiT2: Api, + StorageT2: Storage, + CustomT2: Module, + StakingT2: Staking, + DistrT2: Distribution, + IbcT2: Ibc, + GovT2: Gov, +{ + let packet: IbcPacketData = from_json(app1.ibc_query(MockIbcQuery::SendPacket { + channel_id: src_channel_id.clone(), + port_id: src_port_id.clone(), + sequence, + })?)?; + + // First we start by sending the packet on chain 2 + let receive_response = app2.sudo(SudoMsg::Ibc(IbcPacketRelayingMsg::Receive { + packet: packet.clone(), + }))?; + + let hex_ack = + get_event_attr_value(&receive_response, "write_acknowledgement", "packet_ack_hex")?; + + let ack = Binary::from(hex::decode(hex_ack)?); + + // Then we query the packet ack to deliver the response on chain 1 + let ack_response = app1.sudo(SudoMsg::Ibc(IbcPacketRelayingMsg::Acknowledge { + packet, + ack: ack.clone(), + }))?; + + Ok((receive_response, ack_response, ack)) +} + +pub fn get_event_attr_value( + response: &AppResponse, + event_type: &str, + attr_key: &str, +) -> StdResult { + for event in &response.events { + if event.ty == event_type { + for attr in &event.attributes { + if attr.key == attr_key { + return Ok(attr.value.clone()); + } + } + } + } + + Err(StdError::generic_err(format!( + "event of type {event_type} does not have a value at key {attr_key}" + ))) +} + +pub fn get_all_event_attr_value( + response: &AppResponse, + event: &str, + attribute: &str, +) -> Vec { + response + .events + .iter() + .filter(|e| e.ty.eq(event)) + .flat_map(|e| { + e.attributes + .iter() + .filter(|a| a.key.eq(attribute)) + .map(|a| a.value.clone()) + }) + .collect() +} diff --git a/src/ibc/simple_ibc.rs b/src/ibc/simple_ibc.rs new file mode 100644 index 00000000..daaa3528 --- /dev/null +++ b/src/ibc/simple_ibc.rs @@ -0,0 +1,1128 @@ +use anyhow::{anyhow, bail}; +use cosmwasm_std::{ + ensure_eq, to_json_binary, Addr, BankMsg, Binary, ChannelResponse, Coin, Event, + IbcAcknowledgement, IbcChannel, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcEndpoint, IbcMsg, + IbcOrder, IbcPacket, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcQuery, + IbcTimeout, IbcTimeoutBlock, ListChannelsResponse, Order, Storage, +}; +use cw20_ics20::ibc::Ics20Packet; + +use crate::{ + app::IbcRouterMsg, + bank::{optional_unwrap_ibc_denom, IBC_LOCK_MODULE_ADDRESS}, + ibc::types::Connection, + prefixed_storage::{prefixed, prefixed_read}, + transactions::transactional, + AppResponse, Ibc, Module, +}; +use anyhow::Result as AnyResult; + +#[derive(Default)] +pub struct IbcSimpleModule; + +use super::{ + state::{ + ibc_connections, load_port_info, ACK_PACKET_MAP, CHANNEL_HANDSHAKE_INFO, CHANNEL_INFO, + NAMESPACE_IBC, PORT_INFO, RECEIVE_PACKET_MAP, SEND_PACKET_MAP, TIMEOUT_PACKET_MAP, + }, + types::{ + ChannelHandshakeInfo, ChannelHandshakeState, ChannelInfo, IbcPacketAck, IbcPacketData, + IbcPacketRelayingMsg, IbcResponse, MockIbcPort, MockIbcQuery, + }, +}; + +pub const RELAYER_ADDR: &str = "relayer"; + +fn packet_from_data_and_channel(packet: &IbcPacketData, channel_info: &ChannelInfo) -> IbcPacket { + IbcPacket::new( + packet.data.clone(), + IbcEndpoint { + port_id: packet.src_port_id.clone(), + channel_id: packet.src_channel_id.clone(), + }, + IbcEndpoint { + port_id: channel_info.info.counterparty_endpoint.port_id.to_string(), + channel_id: packet.dst_channel_id.clone(), + }, + packet.sequence, + packet.timeout.clone(), + ) +} + +impl IbcSimpleModule { + fn create_connection( + &self, + storage: &mut dyn Storage, + remote_chain_id: String, + connection_id: Option, + counterparty_connection_id: Option, + ) -> AnyResult { + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + + // First we get the data (from storage or create it) + let (connection_id, mut data) = if let Some(connection_id) = connection_id { + ( + connection_id.clone(), + ibc_connections().load(&ibc_storage, &connection_id)?, + ) + } else { + let connection_count = ibc_connections() + .range(&ibc_storage, None, None, Order::Ascending) + .count(); + let connection_id = format!("connection-{}", connection_count); + ( + connection_id, + Connection { + counterparty_connection_id: None, + counterparty_chain_id: remote_chain_id.clone(), + }, + ) + }; + + // We make sure we're not doing weird things + ensure_eq!( + remote_chain_id, + data.counterparty_chain_id, + anyhow!( + "Wrong chain id already registered with this connection {}, {}!={}", + connection_id.clone(), + data.counterparty_chain_id, + remote_chain_id + ) + ); + + // We eventually save the counterparty_chain_id + if let Some(counterparty_connection_id) = counterparty_connection_id { + data.counterparty_connection_id = Some(counterparty_connection_id); + } + + // The tx will return the connection id + ibc_connections().save(&mut ibc_storage, &connection_id, &data)?; + + let event = Event::new("connection_open").add_attribute("connection_id", &connection_id); + + Ok(AppResponse { + data: None, + events: vec![event], + }) + } + + #[allow(clippy::too_many_arguments)] + fn open_channel( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn Storage, + router: &dyn crate::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + local_connection_id: String, + local_port: String, + version: String, + order: IbcOrder, + + counterparty_endpoint: IbcEndpoint, + counterparty_version: Option, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + + // We verify the connection_id exists locally + if !ibc_connections().has(&ibc_storage, &local_connection_id) { + bail!( + "connection {local_connection_id} doesn't exist on chain {}", + block.chain_id + ) + }; + + // Here we just verify that the port exists locally. + let port: MockIbcPort = local_port.parse()?; + + // We create a new channel id + let mut port_info = load_port_info(&ibc_storage, local_port.clone())?; + + let channel_id = format!("channel-{}", port_info.next_channel_id); + port_info.next_channel_id += 1; + + PORT_INFO.save(&mut ibc_storage, local_port.clone(), &port_info)?; + + let local_endpoint = IbcEndpoint { + port_id: local_port.clone(), + channel_id: channel_id.clone(), + }; + + let mut handshake_object = ChannelHandshakeInfo { + local_endpoint: local_endpoint.clone(), + remote_endpoint: counterparty_endpoint.clone(), + state: ChannelHandshakeState::Init, + version: version.clone(), + port: port.clone(), + order: order.clone(), + connection_id: local_connection_id.clone(), + }; + + let channel = IbcChannel::new( + local_endpoint.clone(), + counterparty_endpoint.clone(), + order.clone(), + version.clone(), + local_connection_id.clone(), + ); + + let (open_request, mut ibc_event) = if let Some(counterparty_version) = counterparty_version + { + handshake_object.state = ChannelHandshakeState::Try; + + let event = Event::new("channel_open_try"); + + ( + IbcChannelOpenMsg::OpenTry { + channel, + counterparty_version, + }, + event, + ) + } else { + let event = Event::new("channel_open_init"); + + (IbcChannelOpenMsg::OpenInit { channel }, event) + }; + + ibc_event = ibc_event + .add_attribute("port_id", local_endpoint.port_id) + .add_attribute("channel_id", local_endpoint.channel_id) + .add_attribute( + "counterparty_port_id", + counterparty_endpoint.clone().port_id, + ) + .add_attribute("counterparty_channel_id", "".to_string()) + .add_attribute("connection_id", local_connection_id); + + // First we send an ibc message on the wasm module in cache + let res = transactional(storage, |write_cache, _| { + router.ibc( + api, + write_cache, + block, + IbcRouterMsg { + module: port.into(), + msg: super::IbcModuleMsg::ChannelOpen(open_request), + }, + ) + })?; + + // Then, we store the acknowledgement and collect events + match res { + IbcResponse::OpenResponse(r) => { + // The channel version may be changed here + let version = r.map(|r| r.version).unwrap_or(version); + handshake_object.version = version.clone(); + ibc_event = ibc_event.add_attribute("version", version); + // This is repeated to avoid multiple mutable borrows + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + // We save the channel handshake status + CHANNEL_HANDSHAKE_INFO.save( + &mut ibc_storage, + (local_port, channel_id), + &handshake_object, + )?; + } + _ => panic!("Only an open response was expected when receiving a packet"), + }; + + let events = vec![ibc_event]; + + Ok(AppResponse { data: None, events }) + } + + fn connect_channel( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn Storage, + router: &dyn crate::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + port_id: String, + channel_id: String, + + counterparty_endpoint: IbcEndpoint, + counterparty_version: Option, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + + // We load the channel handshake info (second step) + let mut channel_handshake = + CHANNEL_HANDSHAKE_INFO.load(&ibc_storage, (port_id.clone(), channel_id.clone()))?; + + // We update the remote endpoint + channel_handshake.remote_endpoint = counterparty_endpoint; + + let channel = IbcChannel::new( + channel_handshake.local_endpoint.clone(), + channel_handshake.remote_endpoint.clone(), + channel_handshake.order.clone(), + channel_handshake.version.clone(), + channel_handshake.connection_id.to_string(), + ); + + let (connect_request, mut ibc_event) = + if channel_handshake.state == ChannelHandshakeState::Try { + channel_handshake.state = ChannelHandshakeState::Confirm; + + let event = Event::new("channel_open_confirm"); + + (IbcChannelConnectMsg::OpenConfirm { channel }, event) + } else if channel_handshake.state == ChannelHandshakeState::Init { + // If we were in the init state, now we need to ack the channel creation + + channel_handshake.state = ChannelHandshakeState::Ack; + + let event = Event::new("channel_open_ack"); + + ( + IbcChannelConnectMsg::OpenAck { + channel, + counterparty_version: counterparty_version.clone().unwrap(), // This should be set in case of an ack + }, + event, + ) + } else { + bail!("This is unreachable, configuration error"); + }; + + ibc_event = ibc_event + .add_attribute("port_id", channel_handshake.local_endpoint.port_id.clone()) + .add_attribute( + "channel_id", + channel_handshake.local_endpoint.channel_id.clone(), + ) + .add_attribute( + "counterparty_port_id", + channel_handshake.remote_endpoint.port_id.clone(), + ) + .add_attribute( + "counterparty_channel_id", + channel_handshake.remote_endpoint.channel_id.clone(), + ) + .add_attribute("connection_id", channel_handshake.connection_id.clone()); + + // Remove handshake, add channel + CHANNEL_HANDSHAKE_INFO.remove(&mut ibc_storage, (port_id.clone(), channel_id.clone())); + CHANNEL_INFO.save( + &mut ibc_storage, + (port_id.clone(), channel_id.clone()), + &ChannelInfo { + next_packet_id: 1, + last_packet_relayed: 1, + info: IbcChannel::new( + IbcEndpoint { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }, + IbcEndpoint { + port_id: channel_handshake.remote_endpoint.port_id.clone(), + channel_id: channel_handshake.remote_endpoint.channel_id.clone(), + }, + channel_handshake.order, + counterparty_version.unwrap(), + channel_handshake.connection_id, + ), + }, + )?; + + // First we send an ibc message on the wasm module in cache + let res = transactional(storage, |write_cache, _| { + router.ibc( + api, + write_cache, + block, + IbcRouterMsg { + module: channel_handshake.port.into(), + msg: super::IbcModuleMsg::ChannelConnect(connect_request), + }, + ) + })?; + + // Then, we store the acknowledgement and collect events + let mut events = match res { + IbcResponse::BasicResponse(r) => r.events, + _ => panic!("Only an open response was expected when receiving a packet"), + }; + + events.push(ibc_event); + + Ok(AppResponse { data: None, events }) + } + + fn send_packet( + &self, + storage: &mut dyn Storage, + port_id: String, + channel_id: String, + data: Binary, + timeout: IbcTimeout, + ) -> AnyResult { + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + + // On this storage, we need to get the id of the transfer packet + // Get the last packet index + + let mut channel_info = + CHANNEL_INFO.load(&ibc_storage, (port_id.clone(), channel_id.clone()))?; + let packet = IbcPacketData { + ack: None, + src_channel_id: channel_id.clone(), + src_port_id: channel_info.info.endpoint.port_id.to_string(), + dst_channel_id: channel_info.info.counterparty_endpoint.channel_id.clone(), + dst_port_id: channel_info.info.counterparty_endpoint.port_id.clone(), + sequence: channel_info.next_packet_id, + data, + timeout, + }; + // Saving this packet for relaying purposes + SEND_PACKET_MAP.save( + &mut ibc_storage, + ( + port_id.clone(), + channel_id.clone(), + channel_info.next_packet_id, + ), + &packet.clone(), + )?; + + // Incrementing the packet sequence + channel_info.next_packet_id += 1; + CHANNEL_INFO.save(&mut ibc_storage, (port_id, channel_id), &channel_info)?; + + // We add custom packet sending events + let timeout_height = packet.timeout.block().unwrap_or(IbcTimeoutBlock { + revision: 0, + height: 0, + }); + let timeout_timestamp = packet.timeout.timestamp().map(|t| t.nanos()).unwrap_or(0); + + let send_event = Event::new("send_packet") + .add_attribute( + "packet_data", + String::from_utf8_lossy(packet.data.as_slice()), + ) + .add_attribute("packet_data_hex", hex::encode(packet.data.0.clone())) + .add_attribute( + "packet_timeout_height", + format!("{}-{}", timeout_height.revision, timeout_height.height), + ) + .add_attribute("packet_timeout_timestamp", timeout_timestamp.to_string()) + .add_attribute("packet_sequence", packet.sequence.to_string()) + .add_attribute("packet_src_port", packet.src_port_id.clone()) + .add_attribute("packet_src_channel", packet.src_channel_id.clone()) + .add_attribute("packet_dst_port", packet.dst_port_id.clone()) + .add_attribute("packet_dst_channel", packet.dst_channel_id.clone()) + .add_attribute( + "packet_channel_ordering", + serde_json::to_value(channel_info.info.order)?.to_string(), + ) + .add_attribute("packet_connection", channel_info.info.connection_id); + + let events = vec![send_event]; + Ok(AppResponse { data: None, events }) + } + + fn receive_packet( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn Storage, + router: &dyn crate::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + packet: IbcPacketData, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + + // First we get the channel info to get the port out of it + let channel_info: ChannelInfo = CHANNEL_INFO.load( + &ibc_storage, + (packet.dst_port_id.clone(), packet.dst_channel_id.clone()), + )?; + + // First we verify it's not already in storage. If its is, we error, not possible to receive the same packet twice + if RECEIVE_PACKET_MAP + .load( + &ibc_storage, + ( + packet.dst_port_id.clone(), + packet.dst_channel_id.clone(), + packet.sequence, + ), + ) + .is_ok() + { + bail!("You can't receive the same packet twice on the chain") + } + + // We save it into storage (for tracking purposes and making sure we don't broadcast the message twice) + RECEIVE_PACKET_MAP.save( + &mut ibc_storage, + ( + packet.dst_port_id.clone(), + packet.dst_channel_id.clone(), + packet.sequence, + ), + &packet, + )?; + + let packet_msg = packet_from_data_and_channel(&packet, &channel_info); + + #[cfg(not(feature = "cosmwasm_1_1"))] + let receive_msg = IbcPacketReceiveMsg::new(packet_msg); + #[cfg(feature = "cosmwasm_1_1")] + let receive_msg = IbcPacketReceiveMsg::new(packet_msg, Addr::unchecked(RELAYER_ADDR)); + + // First we send an ibc message on the wasm module in cache + let port: MockIbcPort = channel_info.info.endpoint.port_id.parse()?; + + let res = transactional(storage, |write_cache, _| { + router.ibc( + api, + write_cache, + block, + IbcRouterMsg { + module: port.into(), + msg: super::IbcModuleMsg::PacketReceive(receive_msg), + }, + ) + })?; + + // This is repeated to avoid multiple mutable borrows + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + let acknowledgement; + // Then, we store the acknowledgement and collect events + let mut events = match res { + IbcResponse::ReceiveResponse(r) => { + // We save the acknowledgment in the structure + acknowledgement = r.acknowledgement.clone(); + ACK_PACKET_MAP.save( + &mut ibc_storage, + ( + packet.dst_port_id.clone(), + packet.dst_channel_id.clone(), + packet.sequence, + ), + &IbcPacketAck { + ack: r.acknowledgement, + }, + )?; + r.events + } + _ => panic!("Only a receive response was expected when receiving a packet"), + }; + + let timeout_height = packet.timeout.block().unwrap_or(IbcTimeoutBlock { + revision: 0, + height: 0, + }); + let timeout_timestamp = packet.timeout.timestamp().map(|t| t.nanos()).unwrap_or(0); + + let recv_event = Event::new("recv_packet") + .add_attribute( + "packet_data", + String::from_utf8_lossy(packet.data.as_slice()), + ) + .add_attribute("packet_data_hex", hex::encode(packet.data.0.clone())) + .add_attribute( + "packet_timeout_height", + format!("{}-{}", timeout_height.revision, timeout_height.height), + ) + .add_attribute("packet_timeout_timestamp", timeout_timestamp.to_string()) + .add_attribute("packet_sequence", packet.sequence.to_string()) + .add_attribute("packet_src_port", packet.src_port_id.clone()) + .add_attribute("packet_src_channel", packet.src_channel_id.clone()) + .add_attribute("packet_dst_port", packet.dst_port_id.clone()) + .add_attribute("packet_dst_channel", packet.dst_channel_id.clone()) + .add_attribute( + "packet_channel_ordering", + serde_json::to_value(channel_info.info.order)?.to_string(), + ) + .add_attribute("packet_connection", channel_info.info.connection_id); + + let ack_event = Event::new("write_acknowledgement") + .add_attribute( + "packet_data", + serde_json::to_value(&packet.data)?.to_string(), + ) + .add_attribute("packet_data_hex", hex::encode(packet.data.0)) + .add_attribute( + "packet_timeout_height", + format!("{}-{}", timeout_height.revision, timeout_height.height), + ) + .add_attribute("packet_timeout_timestamp", timeout_timestamp.to_string()) + .add_attribute("packet_sequence", packet.sequence.to_string()) + .add_attribute("packet_src_port", packet.src_port_id) + .add_attribute("packet_src_channel", packet.src_channel_id) + .add_attribute("packet_dst_port", packet.dst_port_id) + .add_attribute("packet_dst_channel", packet.dst_channel_id) + .add_attribute( + "packet_ack", + String::from_utf8_lossy(acknowledgement.as_slice()), + ) + .add_attribute("packet_ack_hex", hex::encode(acknowledgement.0)); + + events.push(recv_event); + events.push(ack_event); + + Ok(AppResponse { data: None, events }) + } + + fn acknowledge_packet( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn Storage, + router: &dyn crate::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + packet: IbcPacketData, + ack: Binary, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + + // First we get the channel info to get the port out of it + let channel_info = CHANNEL_INFO.load( + &ibc_storage, + (packet.src_port_id.clone(), packet.src_channel_id.clone()), + )?; + + // First we verify the packet exists and the acknowledgement is not received yet + let mut packet_data: IbcPacketData = SEND_PACKET_MAP.load( + &ibc_storage, + ( + packet.src_port_id.clone(), + packet.src_channel_id.clone(), + packet.sequence, + ), + )?; + if packet_data.ack.is_some() { + bail!("You can't ack the same packet twice on the chain") + } + + if TIMEOUT_PACKET_MAP.has( + &ibc_storage, + ( + packet.src_port_id.clone(), + packet.src_channel_id.clone(), + packet.sequence, + ), + ) { + bail!("Packet has timed_out, can't acknowledge"); + } + + // We save the ack into storage + packet_data.ack = Some(ack.clone()); + SEND_PACKET_MAP.save( + &mut ibc_storage, + ( + packet.src_port_id.clone(), + packet.src_channel_id.clone(), + packet.sequence, + ), + &packet_data, + )?; + + let acknowledgement = IbcAcknowledgement::new(ack); + let original_packet = packet_from_data_and_channel(&packet_data, &channel_info); + + #[cfg(not(feature = "cosmwasm_1_1"))] + let ack_message = IbcPacketAckMsg::new(acknowledgement, original_packet); + #[cfg(feature = "cosmwasm_1_1")] + let ack_message = IbcPacketAckMsg::new( + acknowledgement, + original_packet, + Addr::unchecked(RELAYER_ADDR), + ); + + let port: MockIbcPort = channel_info.info.endpoint.port_id.parse()?; + let res = transactional(storage, |write_cache, _| { + router.ibc( + api, + write_cache, + block, + IbcRouterMsg { + module: port.into(), + msg: super::IbcModuleMsg::PacketAcknowledgement(ack_message), + }, + ) + })?; + + let mut events = match res { + // Only type allowed as an ack response + IbcResponse::BasicResponse(r) => r.events, + _ => panic!("Only a basic response was expected when ack a packet"), + }; + + // We add custom packet ack events + let timeout_height = packet.timeout.block().unwrap_or(IbcTimeoutBlock { + revision: 0, + height: 0, + }); + let timeout_timestamp = packet.timeout.timestamp().map(|t| t.nanos()).unwrap_or(0); + + let ack_event = Event::new("recv_packet") + .add_attribute( + "packet_timeout_height", + format!("{}-{}", timeout_height.revision, timeout_height.height), + ) + .add_attribute("packet_timeout_timestamp", timeout_timestamp.to_string()) + .add_attribute("packet_sequence", packet.sequence.to_string()) + .add_attribute("packet_src_port", packet.src_port_id.clone()) + .add_attribute("packet_src_channel", packet.src_channel_id.clone()) + .add_attribute("packet_dst_port", packet.dst_port_id.clone()) + .add_attribute("packet_dst_channel", packet.dst_channel_id.clone()) + .add_attribute( + "packet_channel_ordering", + serde_json::to_value(channel_info.info.order)?.to_string(), + ) + .add_attribute("packet_connection", channel_info.info.connection_id); + + events.push(ack_event); + + Ok(AppResponse { data: None, events }) + } + + fn timeout_packet( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn Storage, + router: &dyn crate::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + packet: IbcPacketData, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + + // First we get the channel info to get the port out of it + let channel_info = CHANNEL_INFO.load( + &ibc_storage, + (packet.src_port_id.clone(), packet.src_channel_id.clone()), + )?; + + // We verify the timeout is indeed passed on the packet + let packet_data: IbcPacketData = SEND_PACKET_MAP.load( + &ibc_storage, + ( + packet.src_port_id.clone(), + packet.src_channel_id.clone(), + packet.sequence, + ), + )?; + + // If the packet was already aknowledge, no timeout possible + if packet_data.ack.is_some() { + bail!("You can't timeout an acked packet") + } + + if TIMEOUT_PACKET_MAP + .may_load( + &ibc_storage, + ( + packet.src_port_id.clone(), + packet.src_channel_id.clone(), + packet.sequence, + ), + )? + .is_some() + { + bail!("You can't timeout an packet twice") + } + + // If there is a block timeout + let mut has_timedout = false; + if let Some(block_timeout) = packet_data.timeout.block() { + if block.height >= block_timeout.height { + has_timedout = true; + } + } + if let Some(timeout) = packet_data.timeout.timestamp() { + if block.time >= timeout { + has_timedout = true; + } + } + + if !has_timedout { + bail!("Packet hasn't timedout"); + } + + TIMEOUT_PACKET_MAP.save( + &mut ibc_storage, + ( + packet.src_port_id.clone(), + packet.src_channel_id.clone(), + packet.sequence, + ), + &true, + )?; + + let original_packet = packet_from_data_and_channel(&packet_data, &channel_info); + + #[cfg(not(feature = "cosmwasm_1_1"))] + let timeout_message = IbcPacketTimeoutMsg::new(original_packet); + #[cfg(feature = "cosmwasm_1_1")] + let timeout_message = + IbcPacketTimeoutMsg::new(original_packet, Addr::unchecked(RELAYER_ADDR)); + + // First we send an ibc message on the module in cache + let port: MockIbcPort = channel_info.info.endpoint.port_id.parse()?; + let res = transactional(storage, |write_cache, _| { + router.ibc( + api, + write_cache, + block, + IbcRouterMsg { + module: port.into(), + msg: super::IbcModuleMsg::PacketTimeout(timeout_message), + }, + ) + })?; + + // Then we collect events + let mut events = match res { + // Only type allowed as an timeout response + IbcResponse::BasicResponse(r) => r.events, + _ => panic!("Only a basic response was expected when timeout a packet"), + }; + + // We add custom packet timeout events + let timeout_height = packet.timeout.block().unwrap_or(IbcTimeoutBlock { + revision: 0, + height: 0, + }); + let timeout_timestamp = packet.timeout.timestamp().map(|t| t.nanos()).unwrap_or(0); + + let timeout_event = Event::new("timeout_packet") + .add_attribute( + "packet_timeout_height", + format!("{}-{}", timeout_height.revision, timeout_height.height), + ) + .add_attribute("packet_timeout_timestamp", timeout_timestamp.to_string()) + .add_attribute("packet_sequence", packet.sequence.to_string()) + .add_attribute("packet_src_port", packet.src_port_id.clone()) + .add_attribute("packet_src_channel", packet.src_channel_id.clone()) + .add_attribute("packet_dst_port", packet.dst_port_id.clone()) + .add_attribute("packet_dst_channel", packet.dst_channel_id.clone()) + .add_attribute( + "packet_channel_ordering", + serde_json::to_value(channel_info.info.order)?.to_string(), + ); + + events.push(timeout_event); + + Ok(AppResponse { data: None, events }) + } + + // Applications + fn transfer( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn crate::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: Addr, + channel_id: String, + to_address: String, + amount: Coin, + timeout: IbcTimeout, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + // Transfer is : + // 1. Lock user funds into the port balance. We send from the sender to the lock address + let msg: cosmwasm_std::CosmosMsg = BankMsg::Send { + to_address: IBC_LOCK_MODULE_ADDRESS.to_string(), + amount: vec![amount.clone()], + } + .into(); + router.execute(api, storage, block, sender.clone(), msg)?; + + // We unwrap the denom if the funds were received on this specific channel + let denom = optional_unwrap_ibc_denom(amount.denom, channel_id.clone()); + + // 2. Send an ICS20 Packet to the remote chain + let packet_formed = Ics20Packet { + amount: amount.amount, + denom, + receiver: to_address, + sender: sender.to_string(), + memo: None, + }; + + self.send_packet( + storage, + "transfer".to_string(), + channel_id, + to_json_binary(&packet_formed)?, + timeout, + ) + } + + pub fn close_channel( + &self, + _storage: &mut dyn Storage, + _channel_id: String, + ) -> AnyResult { + bail!("Close channel not implemented in cw-multi-test"); + } +} + +impl Module for IbcSimpleModule { + type ExecT = IbcMsg; + type QueryT = MockIbcQuery; + type SudoT = IbcPacketRelayingMsg; + + fn execute( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn crate::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: cosmwasm_std::Addr, + msg: Self::ExecT, + ) -> anyhow::Result + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + match msg { + IbcMsg::Transfer { + channel_id, + to_address, + amount, + timeout, + } => self.transfer( + api, storage, router, block, sender, channel_id, to_address, amount, timeout, + ), + IbcMsg::SendPacket { + channel_id, + data, + timeout, + } => { + // This should come from a contract. So the port_id is always the same format + // If you want to send a packet form a module use the sudo Send Packet msg + let port_id = format!("wasm.{}", sender); + self.send_packet(storage, port_id, channel_id, data, timeout) + } + IbcMsg::CloseChannel { channel_id } => self.close_channel(storage, channel_id), + _ => bail!("Not implemented on the ibc module"), + } + } + + fn sudo( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn crate::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + msg: Self::SudoT, + ) -> anyhow::Result + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + let response = match msg { + IbcPacketRelayingMsg::CreateConnection { + connection_id, + remote_chain_id, + counterparty_connection_id, + } => self.create_connection( + storage, + remote_chain_id, + connection_id, + counterparty_connection_id, + ), + + IbcPacketRelayingMsg::OpenChannel { + local_connection_id, + local_port, + version, + order, + counterparty_version, + counterparty_endpoint, + } => self.open_channel( + api, + storage, + router, + block, + local_connection_id, + local_port, + version, + order, + counterparty_endpoint, + counterparty_version, + ), + IbcPacketRelayingMsg::ConnectChannel { + counterparty_version, + counterparty_endpoint, + port_id, + channel_id, + } => self.connect_channel( + api, + storage, + router, + block, + port_id, + channel_id, + counterparty_endpoint, + counterparty_version, + ), + IbcPacketRelayingMsg::CloseChannel {} => { + panic!("Can't close a channel in cw-multi-test") + } + + IbcPacketRelayingMsg::Send { + port_id, + channel_id, + data, + timeout, + } => self.send_packet(storage, port_id, channel_id, data, timeout), + IbcPacketRelayingMsg::Receive { packet } => { + self.receive_packet(api, storage, router, block, packet) + } + IbcPacketRelayingMsg::Acknowledge { packet, ack } => { + self.acknowledge_packet(api, storage, router, block, packet, ack) + } + IbcPacketRelayingMsg::Timeout { packet } => { + self.timeout_packet(api, storage, router, block, packet) + } + }?; + + Ok(response) + } + + fn query( + &self, + _api: &dyn cosmwasm_std::Api, + storage: &dyn cosmwasm_std::Storage, + _querier: &dyn cosmwasm_std::Querier, + _block: &cosmwasm_std::BlockInfo, + request: Self::QueryT, + ) -> anyhow::Result { + let ibc_storage = prefixed_read(storage, NAMESPACE_IBC); + match request { + MockIbcQuery::CosmWasm(m) => { + match m { + IbcQuery::Channel { + channel_id, + port_id, + } => { + // Port id has to be specificed unfortunately here + let port_id = port_id.unwrap(); + // We load the channel of the port + let channel_info = + CHANNEL_INFO.may_load(&ibc_storage, (port_id, channel_id.clone()))?; + + Ok(to_json_binary(&ChannelResponse { + channel: channel_info.map(|c| c.info), + })?) + } + IbcQuery::ListChannels { port_id } => { + // Port_id has to be specified here, unfortunately we can't access the contract address + let port_id = port_id.unwrap(); + + let channels = CHANNEL_INFO + .prefix(port_id) + .range(&ibc_storage, None, None, Order::Ascending) + .collect::, _>>()?; + + Ok(to_json_binary(&ListChannelsResponse { + channels: channels.iter().map(|c| c.1.info.clone()).collect(), + })?) + } + _ => bail!("Query not available"), + } + } + MockIbcQuery::SendPacket { + channel_id, + port_id, + sequence, + } => { + let packet_data = + SEND_PACKET_MAP.load(&ibc_storage, (port_id, channel_id, sequence))?; + + Ok(to_json_binary(&packet_data)?) + } + MockIbcQuery::ConnectedChain { connection_id } => { + let chain_id = ibc_connections().load(&ibc_storage, &connection_id)?; + + Ok(to_json_binary(&chain_id)?) + } + MockIbcQuery::ChainConnections { chain_id } => { + let connections = ibc_connections() + .idx + .chain_id + .prefix(chain_id) + .range(&ibc_storage, None, None, Order::Descending) + .collect::, _>>()?; + + Ok(to_json_binary(&connections)?) + } + } + } + + //Ibc endpoints are not available on the IBC module. This module is only a fix for receiving IBC messages. The IBC module doesn't and will never have ports opened to other blockchains +} + +impl Ibc for IbcSimpleModule {} + +#[cfg(test)] +mod test {} diff --git a/src/ibc/state.rs b/src/ibc/state.rs new file mode 100644 index 00000000..5b2be47c --- /dev/null +++ b/src/ibc/state.rs @@ -0,0 +1,55 @@ +use cosmwasm_std::Storage; +use cw_storage_plus::{Index, IndexList, IndexedMap, Map, MultiIndex}; + +use super::types::*; + +use anyhow::Result as AnyResult; + +pub const NAMESPACE_IBC: &[u8] = b"ibc-namespace"; + +/// This maps a connection id to a remote chain id +pub struct ConnectionIndexes<'a> { + // chain_id, Connection info, connection_id + pub chain_id: MultiIndex<'a, String, Connection, String>, +} + +impl<'a> IndexList for ConnectionIndexes<'a> { + fn get_indexes(&'_ self) -> Box> + '_> { + let v: Vec<&dyn Index> = vec![&self.chain_id]; + Box::new(v.into_iter()) + } +} + +pub fn ibc_connections<'a>() -> IndexedMap<'a, &'a str, Connection, ConnectionIndexes<'a>> { + let indexes = ConnectionIndexes { + chain_id: MultiIndex::new( + |_, d: &Connection| d.counterparty_chain_id.clone(), + "connections", + "connections_chain_id", + ), + }; + IndexedMap::new("tokens", indexes) +} + +pub const PORT_INFO: Map = Map::new("port_info"); + +pub const CHANNEL_HANDSHAKE_INFO: Map<(String, String), ChannelHandshakeInfo> = + Map::new("channel_handshake_info"); +pub const CHANNEL_INFO: Map<(String, String), ChannelInfo> = Map::new("channel_info"); + +// channel id, packet_id ==> Packet data +pub const SEND_PACKET_MAP: Map<(String, String, u64), IbcPacketData> = Map::new("send_packet"); + +// channel id, packet_id ==> Packet data +pub const RECEIVE_PACKET_MAP: Map<(String, String, u64), IbcPacketData> = + Map::new("receive_packet"); + +// channel id, packet_id ==> Packet data +pub const ACK_PACKET_MAP: Map<(String, String, u64), IbcPacketAck> = Map::new("ack_packet"); + +// channel id, packet_id ==> Packet data +pub const TIMEOUT_PACKET_MAP: Map<(String, String, u64), bool> = Map::new("timeout_packet"); + +pub fn load_port_info(storage: &dyn Storage, port_id: String) -> AnyResult { + Ok(PORT_INFO.may_load(storage, port_id)?.unwrap_or_default()) +} diff --git a/src/ibc/test.rs b/src/ibc/test.rs new file mode 100644 index 00000000..ffe37c2e --- /dev/null +++ b/src/ibc/test.rs @@ -0,0 +1,154 @@ +use cosmwasm_std::{ + from_json, to_json_binary, ChannelResponse, Empty, IbcChannel, IbcEndpoint, IbcOrder, IbcQuery, + Querier, QueryRequest, +}; + +use crate::{ + ibc::relayer::{create_connection, ChannelCreationResult}, + AppBuilder, +}; + +use super::{relayer::create_channel, simple_ibc::IbcSimpleModule}; + +mod bank; +mod polytone; + +fn init() { + //let _ = env_logger::builder().is_test(true).try_init(); + env_logger::init(); +} + +#[test] +fn channel_creation() -> anyhow::Result<()> { + init(); + // Here we want to create a channel between 2 bank modules to make sure that we are able to create a channel correctly + // This is a tracking test for all channel creation + let mut app1 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|_, _, _| {}); + let mut app2 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|_, _, _| {}); + + app1.update_block(|block| block.chain_id = "mock_app_1".to_string()); + app2.update_block(|block| block.chain_id = "mock_app_2".to_string()); + + let src_port = "transfer".to_string(); + let dst_port = "transfer".to_string(); + let order = IbcOrder::Unordered; + let version = "ics20-1".to_string(); + + let (src_connection_id, _) = create_connection(&mut app1, &mut app2)?; + + let ChannelCreationResult { + src_channel, + dst_channel, + .. + } = create_channel( + &mut app1, + &mut app2, + src_connection_id, + src_port.clone(), + dst_port.clone(), + version.clone(), + order.clone(), + )?; + + let channel_query = app1 + .raw_query( + to_json_binary(&QueryRequest::::Ibc(IbcQuery::Channel { + channel_id: src_channel.clone(), + port_id: Some(src_port.clone()), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + + let channel: ChannelResponse = from_json(channel_query)?; + + assert_eq!( + channel, + ChannelResponse { + channel: Some(IbcChannel::new( + IbcEndpoint { + port_id: src_port.clone(), + channel_id: src_channel.clone() + }, + IbcEndpoint { + port_id: dst_port.clone(), + channel_id: dst_channel.clone() + }, + order.clone(), + version.clone(), + "connection-0" + )) + } + ); + + let channel_query = app2 + .raw_query( + to_json_binary(&QueryRequest::::Ibc(IbcQuery::Channel { + channel_id: dst_channel.clone(), + port_id: Some(dst_port.clone()), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + + let channel: ChannelResponse = from_json(channel_query)?; + + assert_eq!( + channel, + ChannelResponse { + channel: Some(IbcChannel::new( + IbcEndpoint { + port_id: dst_port.clone(), + channel_id: dst_channel.clone() + }, + IbcEndpoint { + port_id: src_port.clone(), + channel_id: src_channel.clone() + }, + order, + version, + "connection-0" + )) + } + ); + + Ok(()) +} + +#[test] +fn channel_unknown_port() -> anyhow::Result<()> { + init(); + // Here we want to create a channel between 2 bank modules to make sure that we are able to create a channel correctly + // This is a tracking test for all channel creation + + let mut app1 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|_, _, _| {}); + let mut app2 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|_, _, _| {}); + + let port1 = "other-bad-port".to_string(); + let port2 = "bad-port".to_string(); + + let (src_connection_id, _) = create_connection(&mut app1, &mut app2)?; + + create_channel( + &mut app1, + &mut app2, + src_connection_id, + port1, + port2, + "ics20-1".to_string(), + IbcOrder::Ordered, + ) + .unwrap_err(); + + Ok(()) +} diff --git a/src/ibc/test/bank.rs b/src/ibc/test/bank.rs new file mode 100644 index 00000000..19f26eb0 --- /dev/null +++ b/src/ibc/test/bank.rs @@ -0,0 +1,259 @@ +use cosmwasm_std::{ + coin, from_json, to_json_binary, Addr, AllBalanceResponse, BankQuery, CosmosMsg, Empty, IbcMsg, + IbcOrder, IbcTimeout, IbcTimeoutBlock, Querier, QueryRequest, +}; + +use crate::{ + bank::IBC_LOCK_MODULE_ADDRESS, + ibc::{ + relayer::{create_channel, create_connection, relay_packets_in_tx, ChannelCreationResult}, + simple_ibc::IbcSimpleModule, + test::init, + }, + AppBuilder, Executor, +}; + +/// In this module, we are testing the bank module ibc capabilities +/// We try in the implementation to stay simple but as close as the real deal as possible + +#[test] +fn simple_transfer() -> anyhow::Result<()> { + init(); + + let funds = coin(100_000, "ufund"); + let fund_owner = "owner"; + let fund_recipient = "recipient"; + + // We mint some funds to the owner + let mut app1 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|router, api, storage| { + router + .bank + .init_balance( + storage, + &api.addr_validate(fund_owner).unwrap(), + vec![funds.clone()], + ) + .unwrap(); + }); + let mut app2 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|_, _, _| {}); + + let port1 = "transfer".to_string(); + let port2 = "transfer".to_string(); + + let (src_connection_id, _) = create_connection(&mut app1, &mut app2)?; + + // We start by creating channels + let ChannelCreationResult { + src_channel, + dst_channel, + .. + } = create_channel( + &mut app1, + &mut app2, + src_connection_id, + port1.clone(), + port2, + "ics20-1".to_string(), + IbcOrder::Ordered, + )?; + + // We send an IBC transfer Cosmos Msg on app 1 + let send_response = app1.execute( + Addr::unchecked(fund_owner), + CosmosMsg::Ibc(IbcMsg::Transfer { + channel_id: src_channel, + to_address: fund_recipient.to_string(), + amount: funds.clone(), + timeout: IbcTimeout::with_block(IbcTimeoutBlock { + revision: 1, + height: app1.block_info().height, + }), + }), + )?; + + // We relaying all packets found in the transaction + relay_packets_in_tx(&mut app1, &mut app2, send_response)?; + + // We make sure the balance of the reciepient has changed + let balances = app2 + .raw_query( + to_json_binary(&QueryRequest::::Bank(BankQuery::AllBalances { + address: fund_recipient.to_string(), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + let balances: AllBalanceResponse = from_json(balances)?; + + // The recipient has received exactly what they needs + assert_eq!(balances.amount.len(), 1); + assert_eq!(balances.amount[0].amount, funds.amount); + assert_eq!( + balances.amount[0].denom, + format!("ibc/{}/{}", dst_channel, funds.denom) + ); + + // We make sure the balance of the sender has changed as well + let balances = app1 + .raw_query( + to_json_binary(&QueryRequest::::Bank(BankQuery::AllBalances { + address: fund_owner.to_string(), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + let balances: AllBalanceResponse = from_json(balances)?; + assert!(balances.amount.is_empty()); + + Ok(()) +} + +#[test] +fn transfer_and_back() -> anyhow::Result<()> { + init(); + + let funds = coin(100_000, "ufund"); + let fund_owner = "owner"; + let fund_recipient = "recipient"; + + let port1 = "transfer".to_string(); + let port2 = "transfer".to_string(); + + // We mint some funds to the owner + let mut app1 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|router, api, storage| { + router + .bank + .init_balance( + storage, + &api.addr_validate(fund_owner).unwrap(), + vec![funds.clone()], + ) + .unwrap(); + }); + let mut app2 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|_, _, _| {}); + + let (src_connection_id, _) = create_connection(&mut app1, &mut app2)?; + + // We start by creating channels + let ChannelCreationResult { + src_channel, + dst_channel, + .. + } = create_channel( + &mut app1, + &mut app2, + src_connection_id, + port1.clone(), + port2, + "ics20-1".to_string(), + IbcOrder::Ordered, + )?; + + // We send an IBC transfer Cosmos Msg on app 1 + let send_response = app1.execute( + Addr::unchecked(fund_owner), + CosmosMsg::Ibc(IbcMsg::Transfer { + channel_id: src_channel, + to_address: fund_recipient.to_string(), + amount: funds.clone(), + timeout: IbcTimeout::with_block(IbcTimeoutBlock { + revision: 1, + height: app1.block_info().height, + }), + }), + )?; + + // We relaying all packets found in the transaction + relay_packets_in_tx(&mut app1, &mut app2, send_response)?; + + // We verify the funds are locked + let balances = app1 + .raw_query( + to_json_binary(&QueryRequest::::Bank(BankQuery::AllBalances { + address: IBC_LOCK_MODULE_ADDRESS.to_string(), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + let balances: AllBalanceResponse = from_json(balances)?; + assert_eq!(balances.amount.len(), 1); + assert_eq!(balances.amount[0].amount, funds.amount); + assert_eq!(balances.amount[0].denom, funds.denom); + + let chain2_funds = coin( + funds.amount.u128(), + format!("ibc/{}/{}", dst_channel, funds.denom), + ); + // We send an IBC transfer back from app2 + let send_back_response = app2.execute( + Addr::unchecked(fund_recipient), + CosmosMsg::Ibc(IbcMsg::Transfer { + channel_id: dst_channel, + to_address: fund_owner.to_string(), + amount: chain2_funds.clone(), + timeout: IbcTimeout::with_block(IbcTimeoutBlock { + revision: 1, + height: app2.block_info().height + 100, + }), + }), + )?; + + // We relaying all packets found in the transaction + relay_packets_in_tx(&mut app2, &mut app1, send_back_response)?; + + // We make sure the balance of the reciepient has changed + let balances = app2 + .raw_query( + to_json_binary(&QueryRequest::::Bank(BankQuery::AllBalances { + address: fund_recipient.to_string(), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + let balances: AllBalanceResponse = from_json(balances)?; + assert!(balances.amount.is_empty()); + + // We make sure the balance of the sender has changed as well + let balances = app1 + .raw_query( + to_json_binary(&QueryRequest::::Bank(BankQuery::AllBalances { + address: fund_owner.to_string(), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + let balances: AllBalanceResponse = from_json(balances)?; + + // The owner has back exactly what they need + assert_eq!(balances.amount.len(), 1); + assert_eq!(balances.amount[0].amount, funds.amount); + assert_eq!(balances.amount[0].denom, funds.denom); + + // Same for ibc lock address + let balances = app1 + .raw_query( + to_json_binary(&QueryRequest::::Bank(BankQuery::AllBalances { + address: IBC_LOCK_MODULE_ADDRESS.to_string(), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + let balances: AllBalanceResponse = from_json(balances)?; + assert_eq!(balances.amount.len(), 0); + + Ok(()) +} diff --git a/src/ibc/test/polytone.rs b/src/ibc/test/polytone.rs new file mode 100644 index 00000000..20b68d36 --- /dev/null +++ b/src/ibc/test/polytone.rs @@ -0,0 +1,291 @@ +// We create Polytone Contract Wrappers + +use cosmwasm_std::{Addr, ContractInfoResponse, Empty, Never, QueryRequest, WasmQuery}; + +use crate::{ + ibc::{ + addresses::MockAddressGenerator, + api::MockApiBech32, + relayer::{create_channel, create_connection, get_event_attr_value, relay_packets_in_tx}, + simple_ibc::IbcSimpleModule, + test::init, + }, + AppBuilder, ContractWrapper, Executor, FailingModule, WasmKeeper, +}; + +use anyhow::Result as AnyResult; +use cosmwasm_std::{Api, IbcOrder, Storage}; + +use crate::{App, Bank, Distribution, Gov, Ibc, Staking}; + +use polytone::callbacks::{Callback, ExecutionResponse}; + +type PolytoneNoteType = ContractWrapper< + polytone_note::msg::ExecuteMsg, + polytone_note::msg::InstantiateMsg, + polytone_note::msg::QueryMsg, + polytone_note::error::ContractError, + polytone_note::error::ContractError, + cosmwasm_std::StdError, + cosmwasm_std::Empty, + cosmwasm_std::Empty, + cosmwasm_std::Empty, + anyhow::Error, + polytone_note::error::ContractError, + cosmwasm_std::Empty, + anyhow::Error, + polytone_note::error::ContractError, + polytone_note::error::ContractError, + polytone_note::error::ContractError, + Never, + polytone_note::error::ContractError, + polytone_note::error::ContractError, +>; + +type PolytoneVoiceType = ContractWrapper< + polytone_voice::msg::ExecuteMsg, + polytone_voice::msg::InstantiateMsg, + polytone_voice::msg::QueryMsg, + polytone_voice::error::ContractError, + polytone_voice::error::ContractError, + cosmwasm_std::StdError, + cosmwasm_std::Empty, + cosmwasm_std::Empty, + cosmwasm_std::Empty, + anyhow::Error, + polytone_voice::error::ContractError, + cosmwasm_std::Empty, + anyhow::Error, + polytone_voice::error::ContractError, + polytone_voice::error::ContractError, + polytone_voice::error::ContractError, + Never, + polytone_voice::error::ContractError, + polytone_voice::error::ContractError, +>; + +type PolytoneProxyType = ContractWrapper< + polytone_proxy::msg::ExecuteMsg, + polytone_proxy::msg::InstantiateMsg, + polytone_proxy::msg::QueryMsg, + polytone_proxy::error::ContractError, + polytone_proxy::error::ContractError, + cosmwasm_std::StdError, + cosmwasm_std::Empty, + cosmwasm_std::Empty, + cosmwasm_std::Empty, + anyhow::Error, + polytone_proxy::error::ContractError, +>; + +fn polytone_note() -> PolytoneNoteType { + ContractWrapper::new( + polytone_note::contract::execute, + polytone_note::contract::instantiate, + polytone_note::contract::query, + ) + .with_reply(polytone_note::ibc::reply) + .with_ibc( + polytone_note::ibc::ibc_channel_open, + polytone_note::ibc::ibc_channel_connect, + polytone_note::ibc::ibc_channel_close, + polytone_note::ibc::ibc_packet_receive, + polytone_note::ibc::ibc_packet_ack, + polytone_note::ibc::ibc_packet_timeout, + ) +} + +fn polytone_voice() -> PolytoneVoiceType { + ContractWrapper::new( + polytone_voice::contract::execute, + polytone_voice::contract::instantiate, + polytone_voice::contract::query, + ) + .with_reply(polytone_voice::ibc::reply) + .with_ibc( + polytone_voice::ibc::ibc_channel_open, + polytone_voice::ibc::ibc_channel_connect, + polytone_voice::ibc::ibc_channel_close, + polytone_voice::ibc::ibc_packet_receive, + polytone_voice::ibc::ibc_packet_ack, + polytone_voice::ibc::ibc_packet_timeout, + ) +} + +fn polytone_proxy() -> PolytoneProxyType { + ContractWrapper::new( + polytone_proxy::contract::execute, + polytone_proxy::contract::instantiate, + polytone_proxy::contract::query, + ) + .with_reply(polytone_proxy::contract::reply) +} + +pub const MAX_BLOCK_GAS: u64 = 100_000_000; +pub const ADMIN: &str = "admin"; + +pub type AppType = App< + BankT1, + ApiT1, + StorageT1, + FailingModule, + WasmKeeper, + StakingT1, + DistrT1, + IbcT1, + GovT1, +>; + +pub type DeployReturn = AnyResult<( + AppType, + Addr, + Addr, + u64, +)>; + +pub fn deploy_polytone( + mut app: AppType, +) -> DeployReturn +where + BankT1: Bank, + ApiT1: Api, + StorageT1: Storage, + StakingT1: Staking, + DistrT1: Distribution, + IbcT1: Ibc, + GovT1: Gov, +{ + let admin = Addr::unchecked(ADMIN); + let note = Box::new(polytone_note()); + let voice = Box::new(polytone_voice()); + let proxy = Box::new(polytone_proxy()); + let note_code_id = app.store_code(note); + let voice_code_id = app.store_code(voice); + let proxy_code_id = app.store_code(proxy); + + let note_address = app.instantiate_contract( + note_code_id, + admin.clone(), + &polytone_note::msg::InstantiateMsg { + pair: None, + block_max_gas: MAX_BLOCK_GAS.into(), + }, + &[], + "note_1".to_string(), + None, + )?; + + let voice_address = app.instantiate_contract( + voice_code_id, + admin, + &polytone_voice::msg::InstantiateMsg { + block_max_gas: MAX_BLOCK_GAS.into(), + proxy_code_id: proxy_code_id.into(), + }, + &[], + "note_1".to_string(), + None, + )?; + + Ok((app, note_address, voice_address, proxy_code_id)) +} + +#[test] +fn polytone() -> anyhow::Result<()> { + init(); + + let sender = Addr::unchecked("sender"); + + // prepare wasm module with custom address generator + let wasm_keeper_1: WasmKeeper = + WasmKeeper::new().with_address_generator(MockAddressGenerator); + let wasm_keeper_2: WasmKeeper = + WasmKeeper::new().with_address_generator(MockAddressGenerator); + // We mint some funds to the owner + let mut app1 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .with_api(MockApiBech32::new("local")) + .with_wasm(wasm_keeper_1) + .build(|_, _, _| {}); + let mut app2 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .with_api(MockApiBech32::new("remote")) + .with_wasm(wasm_keeper_2) + .build(|_, _, _| {}); + + // We start by uploading the contracts and instantiating them + let note1: Addr; + let _note2: Addr; + let _voice1: Addr; + let voice2: Addr; + let _proxy_code_id1: u64; + let proxy_code_id2: u64; + (app1, note1, _voice1, _proxy_code_id1) = deploy_polytone(app1)?; + (app2, _note2, voice2, proxy_code_id2) = deploy_polytone(app2)?; + + // Now, we create a channel between the 2 contracts + + let port1 = format!("wasm.{}", note1); + let port2 = format!("wasm.{}", voice2); + + let (src_connection_id, _) = create_connection(&mut app1, &mut app2)?; + + // We start by creating channels + create_channel( + &mut app1, + &mut app2, + src_connection_id, + port1.clone(), + port2, + "polytone-1".to_string(), + IbcOrder::Unordered, + )?; + + // We send a simple empty execute message to the note + let send_response = app1.execute_contract( + sender, + note1, + &polytone_note::msg::ExecuteMsg::Execute { + msgs: vec![], + callback: None, + timeout_seconds: 100_000_000u64.into(), + }, + &[], + )?; + + // We relaying all packets found in the transaction + let packet_txs = relay_packets_in_tx(&mut app1, &mut app2, send_response)?; + + assert_eq!(packet_txs.len(), 1); + + println!("{:?}", packet_txs); + let contract_addr = get_event_attr_value(&packet_txs[0].0, "instantiate", "_contract_address")?; + + // We test if the proxy is instantiated on app2 + let test: ContractInfoResponse = + app2.wrap() + .query(&QueryRequest::Wasm(WasmQuery::ContractInfo { + contract_addr: contract_addr.clone(), + }))?; + assert_eq!(test.code_id, proxy_code_id2); + + // Assert the polytone result (executed_by field of the ack) + let ack: Callback = serde_json::from_str(&get_event_attr_value( + &packet_txs[0].0, + "write_acknowledgement", + "packet_ack", + )?)?; + + match ack { + Callback::Execute(Ok(ExecutionResponse { + executed_by, + result, + })) => { + assert_eq!(executed_by, contract_addr); + assert!(result.is_empty()); + } + _ => panic!("Wrong acknowledgement, {:?}", ack), + } + + Ok(()) +} diff --git a/src/ibc/types.rs b/src/ibc/types.rs new file mode 100644 index 00000000..528d3bf8 --- /dev/null +++ b/src/ibc/types.rs @@ -0,0 +1,231 @@ +use std::str::FromStr; + +use anyhow::bail; +use cosmwasm_std::{ + Addr, Binary, Event, IbcChannel, IbcChannelOpenResponse, IbcEndpoint, IbcOrder, IbcQuery, + IbcTimeout, +}; + +use crate::app::IbcModule; + +#[cosmwasm_schema::cw_serde] +pub struct Connection { + pub counterparty_connection_id: Option, + pub counterparty_chain_id: String, +} + +#[cosmwasm_schema::cw_serde] +#[derive(Default)] +pub struct PortInfo { + pub next_channel_id: u64, +} + +#[cosmwasm_schema::cw_serde] +pub struct ChannelHandshakeInfo { + pub connection_id: String, + pub port: MockIbcPort, + pub local_endpoint: IbcEndpoint, + pub remote_endpoint: IbcEndpoint, + pub state: ChannelHandshakeState, + pub order: IbcOrder, + pub version: String, +} + +#[cosmwasm_schema::cw_serde] +pub enum ChannelHandshakeState { + Init, + Try, + Ack, + Confirm, +} + +#[cosmwasm_schema::cw_serde] +pub struct ChannelInfo { + pub next_packet_id: u64, + pub last_packet_relayed: u64, + + pub info: IbcChannel, +} + +#[cosmwasm_schema::cw_serde] +pub enum MockIbcPort { + Wasm(String), // A wasm port is simply a wasm contract address + Bank, // The bank port simply talks to the bank module + Staking, // The staking port simply talks to the staking module +} + +impl From for IbcModule { + fn from(port: MockIbcPort) -> IbcModule { + match port { + MockIbcPort::Bank => IbcModule::Bank, + MockIbcPort::Staking => IbcModule::Staking, + MockIbcPort::Wasm(contract) => IbcModule::Wasm(Addr::unchecked(contract)), + } + } +} + +pub const BANK_MODULE_PORT: &str = "transfer"; + +impl ToString for MockIbcPort { + fn to_string(&self) -> String { + match self { + MockIbcPort::Wasm(c) => format!("wasm.{}", c), + MockIbcPort::Bank => BANK_MODULE_PORT.to_string(), + MockIbcPort::Staking => panic!("No ibc port for the staking module"), + } + } +} + +impl FromStr for MockIbcPort { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + // For the bank module + if s.eq(BANK_MODULE_PORT) { + return Ok(MockIbcPort::Bank); + } + + // For the wasm module + let wasm = s.split('.').collect::>(); + if wasm.len() == 2 && wasm[0] == "wasm" { + return Ok(MockIbcPort::Wasm(wasm[1].to_string())); + } + // Error + bail!( + "The ibc port {} can't be linked to an mock ibc implementation", + s + ) + } +} + +#[cosmwasm_schema::cw_serde] +pub struct IbcPacketData { + pub ack: Option, + /// This also tells us whether this packet was already sent on the other chain or not + pub src_port_id: String, + pub src_channel_id: String, + pub dst_port_id: String, + pub dst_channel_id: String, + pub sequence: u64, + pub data: Binary, + pub timeout: IbcTimeout, +} + +#[cosmwasm_schema::cw_serde] +pub struct IbcPacketAck { + pub ack: Binary, +} + +/// This is a custom msg that is used for executing actions on the IBC module +/// We trust all packets that are relayed. Remember, this is a test environement +#[cosmwasm_schema::cw_serde] +pub enum IbcPacketRelayingMsg { + CreateConnection { + remote_chain_id: String, + // And in the case we need to register the counterparty id as well + connection_id: Option, + counterparty_connection_id: Option, + }, + + OpenChannel { + local_connection_id: String, + local_port: String, + version: String, + order: IbcOrder, + + counterparty_version: Option, + counterparty_endpoint: IbcEndpoint, + }, + ConnectChannel { + port_id: String, + channel_id: String, + + counterparty_version: Option, + counterparty_endpoint: IbcEndpoint, + }, + CloseChannel {}, + + Send { + port_id: String, + channel_id: String, + data: Binary, + timeout: IbcTimeout, + }, + Receive { + packet: IbcPacketData, + }, + Acknowledge { + packet: IbcPacketData, + ack: Binary, + }, + Timeout { + packet: IbcPacketData, + }, +} + +// This type allows to wrap the ibc response to return from the Router +#[cosmwasm_schema::cw_serde] +pub enum IbcResponse { + OpenResponse(IbcChannelOpenResponse), + BasicResponse(AppIbcBasicResponse), + ReceiveResponse(AppIbcReceiveResponse), +} + +#[cosmwasm_schema::cw_serde] +#[derive(Default)] +pub struct AppIbcBasicResponse { + pub events: Vec, +} + +#[cosmwasm_schema::cw_serde] +#[derive(Default)] +pub struct AppIbcReceiveResponse { + pub events: Vec, + pub acknowledgement: Binary, +} + +impl From for IbcResponse { + fn from(c: IbcChannelOpenResponse) -> IbcResponse { + IbcResponse::OpenResponse(c) + } +} + +impl From for IbcResponse { + fn from(c: AppIbcBasicResponse) -> IbcResponse { + IbcResponse::BasicResponse(c) + } +} + +impl From for IbcResponse { + fn from(c: AppIbcReceiveResponse) -> IbcResponse { + IbcResponse::ReceiveResponse(c) + } +} + +#[cosmwasm_schema::cw_serde] +// This extends the cosmwasm std IBC query type with internal tools needed +pub enum MockIbcQuery { + CosmWasm(IbcQuery), + /// Only used inside cw-multi-test + /// Queries a packet that was sent on the chain + /// Returns `IbcPacketData` + SendPacket { + channel_id: String, + port_id: String, + sequence: u64, + }, + /// This is used to get the chain_id of the connected chain + ConnectedChain { + connection_id: String, + }, + /// Gets all the connections with a chain + ChainConnections { + chain_id: String, + }, +} + +impl From for MockIbcQuery { + fn from(value: IbcQuery) -> Self { + MockIbcQuery::CosmWasm(value) + } +} diff --git a/src/lib.rs b/src/lib.rs index dfcbbdf9..07fced80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ pub mod custom_handler; pub mod error; mod executor; mod gov; -mod ibc; +pub mod ibc; mod module; mod prefixed_storage; mod staking; diff --git a/src/module.rs b/src/module.rs index 3bbe452c..36bb2aa0 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,5 +1,5 @@ use crate::app::CosmosRouter; -use crate::error::{bail, AnyResult}; +use crate::ibc::types::{AppIbcBasicResponse, AppIbcReceiveResponse}; use crate::AppResponse; use cosmwasm_std::{Addr, Api, Binary, BlockInfo, CustomQuery, Querier, Storage}; use schemars::JsonSchema; @@ -7,6 +7,12 @@ use serde::de::DeserializeOwned; use std::fmt::Debug; use std::marker::PhantomData; +use crate::error::{bail, AnyResult}; +use cosmwasm_std::{ + IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, + IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, +}; + /// Module interface. pub trait Module { type ExecT; @@ -55,6 +61,75 @@ pub trait Module { where ExecC: Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static, QueryC: CustomQuery + DeserializeOwned + 'static; + + // The following ibc endpoints can only be used by the ibc module. + // For channels + fn ibc_channel_open( + &self, + _api: &dyn Api, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcChannelOpenMsg, + ) -> AnyResult { + Ok(IbcChannelOpenResponse::None) + } + + fn ibc_channel_connect( + &self, + _api: &dyn Api, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcChannelConnectMsg, + ) -> AnyResult { + Ok(AppIbcBasicResponse::default()) + } + + fn ibc_channel_close( + &self, + _api: &dyn Api, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcChannelCloseMsg, + ) -> AnyResult { + Ok(AppIbcBasicResponse::default()) + } + + // For packet operations + fn ibc_packet_receive( + &self, + _api: &dyn Api, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcPacketReceiveMsg, + ) -> AnyResult { + panic!("No ibc packet receive implemented"); + } + + fn ibc_packet_acknowledge( + &self, + _api: &dyn Api, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcPacketAckMsg, + ) -> AnyResult { + panic!("No ibc packet acknowledgement implemented"); + } + + fn ibc_packet_timeout( + &self, + _api: &dyn Api, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcPacketTimeoutMsg, + ) -> AnyResult { + panic!("No ibc packet timeout implemented"); + } } pub struct FailingModule(PhantomData<(ExecT, QueryT, SudoT)>); diff --git a/src/wasm.rs b/src/wasm.rs index 9e621109..2da28d1a 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -1,9 +1,11 @@ use crate::addresses::{AddressGenerator, SimpleAddressGenerator}; + use crate::app::{CosmosRouter, RouterQuerier}; use crate::checksums::{ChecksumGenerator, SimpleChecksumGenerator}; use crate::contracts::Contract; use crate::error::{bail, AnyContext, AnyError, AnyResult, Error}; use crate::executor::AppResponse; +use crate::ibc::types::{AppIbcBasicResponse, AppIbcReceiveResponse}; use crate::prefixed_storage::{prefixed, prefixed_read, PrefixedStorage, ReadonlyPrefixedStorage}; use crate::transactions::transactional; use cosmwasm_std::testing::mock_wasmd_attr; @@ -13,6 +15,11 @@ use cosmwasm_std::{ Querier, QuerierWrapper, Record, Reply, ReplyOn, Response, StdResult, Storage, SubMsg, SubMsgResponse, SubMsgResult, TransactionInfo, WasmMsg, WasmQuery, }; +use cosmwasm_std::{ + IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, + IbcChannelOpenResponse, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, + IbcReceiveResponse, +}; use cw_storage_plus::Map; use prost::Message; use schemars::JsonSchema; @@ -117,6 +124,81 @@ pub trait Wasm { /// Returns a raw state dump of all key-values held by a contract with specified address. fn dump_wasm_raw(&self, storage: &dyn Storage, address: &Addr) -> Vec; + + // The following ibc endpoints can only be used by the ibc module. + // For channels + fn ibc_channel_open( + &self, + _api: &dyn Api, + _contract_addr: Addr, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcChannelOpenMsg, + ) -> AnyResult { + panic!("No ibc channel open implemented"); + } + + fn ibc_channel_connect( + &self, + _api: &dyn Api, + _contract_addr: Addr, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcChannelConnectMsg, + ) -> AnyResult { + panic!("No ibc channel connect implemented"); + } + + fn ibc_channel_close( + &self, + _api: &dyn Api, + _contract_addr: Addr, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcChannelCloseMsg, + ) -> AnyResult { + panic!("No ibc channel close implemented"); + } + + // For packet operations + fn ibc_packet_receive( + &self, + _api: &dyn Api, + _contract_addr: Addr, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcPacketReceiveMsg, + ) -> AnyResult { + panic!("No ibc packet receive implemented"); + } + + fn ibc_packet_acknowledge( + &self, + _api: &dyn Api, + _contract_addr: Addr, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcPacketAckMsg, + ) -> AnyResult { + panic!("No ibc packet acknowledgement implemented"); + } + + fn ibc_packet_timeout( + &self, + _api: &dyn Api, + _contract_addr: Addr, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _request: IbcPacketTimeoutMsg, + ) -> AnyResult { + panic!("No ibc packet timeout implemented"); + } } pub struct WasmKeeper { @@ -221,6 +303,134 @@ where self.process_response(api, router, storage, block, contract, res, msgs) } + // The following ibc endpoints can only be used by the ibc module. + // For channels + fn ibc_channel_open( + &self, + api: &dyn Api, + contract: Addr, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + request: IbcChannelOpenMsg, + ) -> AnyResult { + // For channel open, we simply return the result directly to the ibc module + let contract_response = self.with_storage( + api, + storage, + router, + block, + contract.clone(), + |contract, deps, env| contract.ibc_channel_open(deps, env, request), + )?; + + Ok(contract_response) + } + + fn ibc_channel_connect( + &self, + api: &dyn Api, + contract_addr: Addr, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + request: IbcChannelConnectMsg, + ) -> AnyResult { + let res = Self::verify_ibc_response(self.with_storage( + api, + storage, + router, + block, + contract_addr.clone(), + |contract, deps, env| contract.ibc_channel_connect(deps, env, request), + )?)?; + + self.process_ibc_response(api, contract_addr, storage, router, block, res) + } + fn ibc_channel_close( + &self, + api: &dyn Api, + contract_addr: Addr, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + request: IbcChannelCloseMsg, + ) -> AnyResult { + let res = Self::verify_ibc_response(self.with_storage( + api, + storage, + router, + block, + contract_addr.clone(), + |contract, deps, env| contract.ibc_channel_close(deps, env, request), + )?)?; + + self.process_ibc_response(api, contract_addr, storage, router, block, res) + } + + fn ibc_packet_receive( + &self, + api: &dyn Api, + contract_addr: Addr, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + request: IbcPacketReceiveMsg, + ) -> AnyResult { + let res = Self::verify_packet_response(self.with_storage( + api, + storage, + router, + block, + contract_addr.clone(), + |contract, deps, env| contract.ibc_packet_receive(deps, env, request), + )?)?; + + self.process_ibc_receive_response(api, contract_addr, storage, router, block, res) + } + + fn ibc_packet_acknowledge( + &self, + api: &dyn Api, + contract_addr: Addr, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + request: IbcPacketAckMsg, + ) -> AnyResult { + let res = Self::verify_ibc_response(self.with_storage( + api, + storage, + router, + block, + contract_addr.clone(), + |contract, deps, env| contract.ibc_packet_acknowledge(deps, env, request), + )?)?; + + self.process_ibc_response(api, contract_addr, storage, router, block, res) + } + + fn ibc_packet_timeout( + &self, + api: &dyn Api, + contract_addr: Addr, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + request: IbcPacketTimeoutMsg, + ) -> AnyResult { + let res = Self::verify_ibc_response(self.with_storage( + api, + storage, + router, + block, + contract_addr.clone(), + |contract, deps, env| contract.ibc_packet_timeout(deps, env, request), + )?)?; + + self.process_ibc_response(api, contract_addr, storage, router, block, res) + } + /// Stores the contract's code in the in-memory lookup table. /// Returns an identifier of the stored contract code. fn store_code(&mut self, creator: Addr, code: Box>) -> u64 { @@ -348,6 +558,42 @@ impl WasmKeeper { Ok(response) } + + fn verify_ibc_response(response: IbcBasicResponse) -> AnyResult> + where + T: Clone + std::fmt::Debug + PartialEq + JsonSchema, + { + Self::verify_attributes(&response.attributes)?; + + for event in &response.events { + Self::verify_attributes(&event.attributes)?; + let ty = event.ty.trim(); + if ty.len() < 2 { + bail!(Error::event_type_too_short(ty)); + } + } + + Ok(response) + } + + fn verify_packet_response( + response: IbcReceiveResponse, + ) -> AnyResult> + where + T: Clone + std::fmt::Debug + PartialEq + JsonSchema, + { + Self::verify_attributes(&response.attributes)?; + + for event in &response.events { + Self::verify_attributes(&event.attributes)?; + let ty = event.ty.trim(); + if ty.len() < 2 { + bail!(Error::event_type_too_short(ty)); + } + } + + Ok(response) + } } impl WasmKeeper @@ -821,11 +1067,70 @@ where Ok(AppResponse { events, data }) } + fn process_ibc_response( + &self, + api: &dyn Api, + contract: Addr, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + res: IbcBasicResponse, + ) -> AnyResult { + // We format the events correctly because we are executing wasm + let contract_response = Response::new() + .add_submessages(res.messages) + .add_attributes(res.attributes) + .add_events(res.events); + + let (res, msgs) = self.build_app_response(&contract, Event::new("ibc"), contract_response); + + // We process eventual messages that were sent out with the response + let res = self.process_response(api, router, storage, block, contract, res, msgs)?; + + // We transfer back to an IbcBasicResponse + Ok(AppIbcBasicResponse { events: res.events }) + } + + fn process_ibc_receive_response( + &self, + api: &dyn Api, + contract: Addr, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + original_res: IbcReceiveResponse, + ) -> AnyResult { + // We format the events correctly because we are executing wasm + let contract_response = Response::new() + .add_submessages(original_res.messages) + .add_attributes(original_res.attributes) + .add_events(original_res.events); + + let (res, msgs) = self.build_app_response(&contract, Event::new("ibc"), contract_response); + + // We process eventual messages that were sent out with the response + let res = self.process_response(api, router, storage, block, contract, res, msgs)?; + + // If the data field was overwritten by the response propagation, we replace the ibc ack + let ack = if let Some(new_ack) = res.data { + new_ack + } else { + original_res.acknowledgement + }; + + // We transfer back to an IbcBasicResponse + Ok(AppIbcReceiveResponse { + events: res.events, + acknowledgement: ack, + }) + } + /// Creates a contract address and empty storage instance. /// Returns the new contract address. /// /// You have to call init after this to set up the contract properly. /// These two steps are separated to have cleaner return values. + #[allow(clippy::too_many_arguments)] pub fn register_contract( &self, api: &dyn Api, diff --git a/tests/mod.rs b/tests/mod.rs index e794b49e..7a0b3bce 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -5,6 +5,7 @@ use cosmwasm_std::{ instantiate2_address, Addr, Api, CanonicalAddr, RecoverPubkeyError, StdError, StdResult, Storage, VerificationError, }; +pub extern crate abstract_cw_multi_test as cw_multi_test; use cw_multi_test::error::AnyResult; use cw_multi_test::AddressGenerator; use cw_storage_plus::Item; diff --git a/tests/test_app_builder/test_with_ibc.rs b/tests/test_app_builder/test_with_ibc.rs index 4e4e8a91..e949d067 100644 --- a/tests/test_app_builder/test_with_ibc.rs +++ b/tests/test_app_builder/test_with_ibc.rs @@ -1,8 +1,11 @@ use crate::test_app_builder::{MyKeeper, NO_MESSAGE}; -use cosmwasm_std::{Addr, Empty, IbcMsg, IbcQuery, QueryRequest}; -use cw_multi_test::{AppBuilder, Executor, Ibc}; +use cosmwasm_std::{Addr, IbcMsg, IbcQuery, QueryRequest}; +use cw_multi_test::{ + ibc::{types::MockIbcQuery, IbcPacketRelayingMsg}, + AppBuilder, Executor, Ibc, +}; -type MyIbcKeeper = MyKeeper; +type MyIbcKeeper = MyKeeper; impl Ibc for MyIbcKeeper {} From 77deabc1f73bd909d6315c84744371dfecef667d Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 2 Nov 2023 12:39:10 +0100 Subject: [PATCH 02/22] Perfected diff --- Cargo.lock | 216 ------------------------ Cargo.toml | 1 - src/app.rs | 10 +- src/contracts.rs | 2 +- src/ibc/test.rs | 7 - src/ibc/test/bank.rs | 5 - src/ibc/test/polytone.rs | 3 - src/module.rs | 4 +- src/wasm.rs | 2 - tests/test_app_builder/test_with_ibc.rs | 6 +- 10 files changed, 11 insertions(+), 245 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9f7927f..aa0f7d2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,6 @@ dependencies = [ "cw20-ics20", "derivative", "ecdsa", - "env_logger", "hex", "itertools", "log", @@ -59,15 +58,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - [[package]] name = "anyhow" version = "1.0.75" @@ -127,12 +117,6 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - [[package]] name = "block-buffer" version = "0.9.0" @@ -492,29 +476,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "errno" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" -dependencies = [ - "libc", - "windows-sys", -] - [[package]] name = "ff" version = "0.13.0" @@ -579,12 +540,6 @@ dependencies = [ "ahash", ] -[[package]] -name = "hermit-abi" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" - [[package]] name = "hex" version = "0.4.3" @@ -600,23 +555,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys", -] - [[package]] name = "itertools" version = "0.11.0" @@ -652,12 +590,6 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" -[[package]] -name = "linux-raw-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" - [[package]] name = "log" version = "0.4.20" @@ -831,35 +763,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - [[package]] name = "rfc6979" version = "0.4.0" @@ -876,19 +779,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustix" -version = "0.38.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - [[package]] name = "ryu" version = "1.0.15" @@ -1081,15 +971,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "termcolor" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "1.0.50" @@ -1145,103 +1026,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index a47d8d2d..ce754746 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ once_cell = "1.18.0" # we tighten versions that builds with `-Zminimal-versions` work. ecdsa = "0.16.8" -env_logger = "0.10.0" polytone-note = {version="1.0.0" } polytone-voice = {version="1.0.0" } polytone-proxy = {version="1.0.0" } diff --git a/src/app.rs b/src/app.rs index 35739078..41c15cde 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,8 +3,10 @@ use crate::contracts::Contract; use crate::error::{bail, AnyResult}; use crate::executor::{AppResponse, Executor}; use crate::gov::Gov; -use crate::ibc::types::MockIbcQuery; -use crate::ibc::{types::IbcResponse, Ibc, IbcModuleMsg, IbcPacketRelayingMsg as IbcSudo}; +use crate::ibc::Ibc; +use crate::ibc::{ + types::IbcResponse, types::MockIbcQuery, IbcModuleMsg, IbcPacketRelayingMsg as IbcSudo, +}; use crate::module::{FailingModule, Module}; use crate::staking::{Distribution, DistributionKeeper, StakeKeeper, Staking, StakingSudo}; use crate::transactions::transactional; @@ -262,7 +264,7 @@ where /// /// ``` /// use cosmwasm_std::Addr; - /// use abstract_cw_multi_test::App; + /// use cw_multi_test::App; /// /// // contract implementation /// mod echo { @@ -270,7 +272,7 @@ where /// # use std::todo; /// # use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError, SubMsg, WasmMsg}; /// # use serde::{Deserialize, Serialize}; - /// # use abstract_cw_multi_test::{Contract, ContractWrapper}; + /// # use cw_multi_test::{Contract, ContractWrapper}; /// # /// # fn instantiate(_: DepsMut, _: Env, _: MessageInfo, _: Empty) -> Result { /// # todo!() diff --git a/src/contracts.rs b/src/contracts.rs index 2f7bd4f5..b29a82dd 100644 --- a/src/contracts.rs +++ b/src/contracts.rs @@ -593,7 +593,7 @@ where E11: Display + Debug + Send + Sync + 'static, E12: Display + Debug + Send + Sync + 'static, C: Clone + Debug + PartialEq + JsonSchema, - Q: CustomQuery + DeserializeOwned + 'static, + Q: CustomQuery + DeserializeOwned, { fn execute( &self, diff --git a/src/ibc/test.rs b/src/ibc/test.rs index ffe37c2e..a0e5baee 100644 --- a/src/ibc/test.rs +++ b/src/ibc/test.rs @@ -13,14 +13,8 @@ use super::{relayer::create_channel, simple_ibc::IbcSimpleModule}; mod bank; mod polytone; -fn init() { - //let _ = env_logger::builder().is_test(true).try_init(); - env_logger::init(); -} - #[test] fn channel_creation() -> anyhow::Result<()> { - init(); // Here we want to create a channel between 2 bank modules to make sure that we are able to create a channel correctly // This is a tracking test for all channel creation let mut app1 = AppBuilder::default() @@ -123,7 +117,6 @@ fn channel_creation() -> anyhow::Result<()> { #[test] fn channel_unknown_port() -> anyhow::Result<()> { - init(); // Here we want to create a channel between 2 bank modules to make sure that we are able to create a channel correctly // This is a tracking test for all channel creation diff --git a/src/ibc/test/bank.rs b/src/ibc/test/bank.rs index 19f26eb0..eb137d30 100644 --- a/src/ibc/test/bank.rs +++ b/src/ibc/test/bank.rs @@ -8,7 +8,6 @@ use crate::{ ibc::{ relayer::{create_channel, create_connection, relay_packets_in_tx, ChannelCreationResult}, simple_ibc::IbcSimpleModule, - test::init, }, AppBuilder, Executor, }; @@ -18,8 +17,6 @@ use crate::{ #[test] fn simple_transfer() -> anyhow::Result<()> { - init(); - let funds = coin(100_000, "ufund"); let fund_owner = "owner"; let fund_recipient = "recipient"; @@ -116,8 +113,6 @@ fn simple_transfer() -> anyhow::Result<()> { #[test] fn transfer_and_back() -> anyhow::Result<()> { - init(); - let funds = coin(100_000, "ufund"); let fund_owner = "owner"; let fund_recipient = "recipient"; diff --git a/src/ibc/test/polytone.rs b/src/ibc/test/polytone.rs index 20b68d36..7d70dfbb 100644 --- a/src/ibc/test/polytone.rs +++ b/src/ibc/test/polytone.rs @@ -8,7 +8,6 @@ use crate::{ api::MockApiBech32, relayer::{create_channel, create_connection, get_event_attr_value, relay_packets_in_tx}, simple_ibc::IbcSimpleModule, - test::init, }, AppBuilder, ContractWrapper, Executor, FailingModule, WasmKeeper, }; @@ -192,8 +191,6 @@ where #[test] fn polytone() -> anyhow::Result<()> { - init(); - let sender = Addr::unchecked("sender"); // prepare wasm module with custom address generator diff --git a/src/module.rs b/src/module.rs index 36bb2aa0..f51d47a5 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,5 +1,5 @@ use crate::app::CosmosRouter; -use crate::ibc::types::{AppIbcBasicResponse, AppIbcReceiveResponse}; +use crate::error::{bail, AnyResult}; use crate::AppResponse; use cosmwasm_std::{Addr, Api, Binary, BlockInfo, CustomQuery, Querier, Storage}; use schemars::JsonSchema; @@ -7,7 +7,7 @@ use serde::de::DeserializeOwned; use std::fmt::Debug; use std::marker::PhantomData; -use crate::error::{bail, AnyResult}; +use crate::ibc::types::{AppIbcBasicResponse, AppIbcReceiveResponse}; use cosmwasm_std::{ IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, diff --git a/src/wasm.rs b/src/wasm.rs index 2da28d1a..37299ebb 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -1,5 +1,4 @@ use crate::addresses::{AddressGenerator, SimpleAddressGenerator}; - use crate::app::{CosmosRouter, RouterQuerier}; use crate::checksums::{ChecksumGenerator, SimpleChecksumGenerator}; use crate::contracts::Contract; @@ -1130,7 +1129,6 @@ where /// /// You have to call init after this to set up the contract properly. /// These two steps are separated to have cleaner return values. - #[allow(clippy::too_many_arguments)] pub fn register_contract( &self, api: &dyn Api, diff --git a/tests/test_app_builder/test_with_ibc.rs b/tests/test_app_builder/test_with_ibc.rs index e949d067..0958ad51 100644 --- a/tests/test_app_builder/test_with_ibc.rs +++ b/tests/test_app_builder/test_with_ibc.rs @@ -1,9 +1,7 @@ use crate::test_app_builder::{MyKeeper, NO_MESSAGE}; use cosmwasm_std::{Addr, IbcMsg, IbcQuery, QueryRequest}; -use cw_multi_test::{ - ibc::{types::MockIbcQuery, IbcPacketRelayingMsg}, - AppBuilder, Executor, Ibc, -}; +use cw_multi_test::ibc::{types::MockIbcQuery, IbcPacketRelayingMsg}; +use cw_multi_test::{AppBuilder, Executor, Ibc}; type MyIbcKeeper = MyKeeper; From a08fd82470597147bf96ca63d215516faa3156bf Mon Sep 17 00:00:00 2001 From: Kayanski Date: Wed, 22 Nov 2023 16:26:42 +0100 Subject: [PATCH 03/22] cw-multi-test ready for publishing --- src/addresses.rs | 4 ++-- src/app.rs | 4 ++-- src/app_builder.rs | 2 +- src/wasm.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/addresses.rs b/src/addresses.rs index 3a0b1669..aeb10bea 100644 --- a/src/addresses.rs +++ b/src/addresses.rs @@ -41,7 +41,7 @@ pub trait AddressGenerator { /// /// ``` /// # use cosmwasm_std::testing::{MockApi, MockStorage}; - /// # use cw_multi_test::{AddressGenerator, SimpleAddressGenerator}; + /// # use abstract_cw_multi_test::{AddressGenerator, SimpleAddressGenerator}; /// # let api = MockApi::default(); /// # let mut storage = MockStorage::default(); /// struct MyAddressGenerator; @@ -84,7 +84,7 @@ pub trait AddressGenerator { /// ``` /// # use cosmwasm_std::Api; /// # use cosmwasm_std::testing::{MockApi, MockStorage}; - /// # use cw_multi_test::{AddressGenerator, SimpleAddressGenerator}; + /// # use abstract_cw_multi_test::{AddressGenerator, SimpleAddressGenerator}; /// # let api = MockApi::default(); /// # let mut storage = MockStorage::default(); /// # let creator = api.addr_canonicalize("creator").unwrap(); diff --git a/src/app.rs b/src/app.rs index 41c15cde..d01439aa 100644 --- a/src/app.rs +++ b/src/app.rs @@ -264,7 +264,7 @@ where /// /// ``` /// use cosmwasm_std::Addr; - /// use cw_multi_test::App; + /// use abstract_cw_multi_test::App; /// /// // contract implementation /// mod echo { @@ -272,7 +272,7 @@ where /// # use std::todo; /// # use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError, SubMsg, WasmMsg}; /// # use serde::{Deserialize, Serialize}; - /// # use cw_multi_test::{Contract, ContractWrapper}; + /// # use abstract_cw_multi_test::{Contract, ContractWrapper}; /// # /// # fn instantiate(_: DepsMut, _: Env, _: MessageInfo, _: Empty) -> Result { /// # todo!() diff --git a/src/app_builder.rs b/src/app_builder.rs index 884a5569..99db3249 100644 --- a/src/app_builder.rs +++ b/src/app_builder.rs @@ -16,7 +16,7 @@ use std::fmt::Debug; /// /// ``` /// # use cosmwasm_std::Empty; -/// # use cw_multi_test::{BasicAppBuilder, FailingModule, Module}; +/// # use abstract_cw_multi_test::{BasicAppBuilder, FailingModule, Module}; /// # type MyHandler = FailingModule; /// # type MyExecC = Empty; /// # type MyQueryC = Empty; diff --git a/src/wasm.rs b/src/wasm.rs index 37299ebb..59d88106 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -1548,7 +1548,7 @@ mod test { // Default error message from router when not found assert_eq!( - StdError::not_found("cw_multi_test::wasm::ContractData"), + StdError::not_found("abstract_cw_multi_test::wasm::ContractData"), err.downcast().unwrap() ); } From d788c49b91990749b5d10666d1bc2a6efd851ace Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Tue, 28 Nov 2023 14:44:40 +0100 Subject: [PATCH 04/22] fix multi-index map namespace for connections --- src/ibc/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ibc/state.rs b/src/ibc/state.rs index 5b2be47c..1f69ceea 100644 --- a/src/ibc/state.rs +++ b/src/ibc/state.rs @@ -28,7 +28,7 @@ pub fn ibc_connections<'a>() -> IndexedMap<'a, &'a str, Connection, ConnectionIn "connections_chain_id", ), }; - IndexedMap::new("tokens", indexes) + IndexedMap::new("connections", indexes) } pub const PORT_INFO: Map = Map::new("port_info"); From 5f01510d0dd1fcde0fd0202551b279d8755ec1d1 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Mon, 4 Dec 2023 18:17:27 +0100 Subject: [PATCH 05/22] Added cargo --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e087e30b..9ad54e6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/CosmWasm/cw-multi-test" homepage = "https://cosmwasm.com" [features] -default = [] +default = ["cosmwasm_1_3"] backtrace = ["anyhow/backtrace"] cosmwasm_1_1 = ["cosmwasm-std/cosmwasm_1_1", "cosmwasm-std/ibc3"] cosmwasm_1_2 = ["cosmwasm_1_1", "cosmwasm-std/cosmwasm_1_2"] @@ -48,3 +48,4 @@ polytone-note = { version = "1.0.0" } polytone-voice = { version = "1.0.0" } polytone-proxy = { version = "1.0.0" } polytone = { version = "1.0.0" } +once_cell = "1.18.0" From 3e827cc10b0d1027adc670d2b5cf7e1a5ab206d2 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Mon, 4 Dec 2023 18:20:30 +0100 Subject: [PATCH 06/22] Readed ibc logic in ibc.rs file --- src/{ibc/mod.rs => ibc.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{ibc/mod.rs => ibc.rs} (100%) diff --git a/src/ibc/mod.rs b/src/ibc.rs similarity index 100% rename from src/ibc/mod.rs rename to src/ibc.rs From 581f40e84528193d02e69c88577add1bfcaff51b Mon Sep 17 00:00:00 2001 From: Kayanski Date: Fri, 12 Jan 2024 15:27:34 +0100 Subject: [PATCH 07/22] Cleaned repo --- src/ibc.rs | 1 - src/ibc/addresses.rs | 53 ---------------------------------------- src/ibc/test/polytone.rs | 4 +-- 3 files changed, 2 insertions(+), 56 deletions(-) delete mode 100644 src/ibc/addresses.rs diff --git a/src/ibc.rs b/src/ibc.rs index 0b3afea6..fc65b534 100644 --- a/src/ibc.rs +++ b/src/ibc.rs @@ -7,7 +7,6 @@ use cosmwasm_std::{ use crate::{AcceptingModule, FailingModule, Module}; -pub mod addresses; pub mod relayer; mod simple_ibc; mod state; diff --git a/src/ibc/addresses.rs b/src/ibc/addresses.rs deleted file mode 100644 index 9c701310..00000000 --- a/src/ibc/addresses.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::error::AnyResult; -use crate::AddressGenerator; - -use cosmwasm_std::{instantiate2_address, Addr, Api, CanonicalAddr, Storage}; -use sha2::{digest::Update, Digest, Sha256}; - -#[derive(Default)] -pub struct MockAddressGenerator; - -impl AddressGenerator for MockAddressGenerator { - fn contract_address( - &self, - api: &dyn Api, - _storage: &mut dyn Storage, - code_id: u64, - instance_id: u64, - ) -> AnyResult { - let canonical_addr = Self::instantiate_address(code_id, instance_id); - Ok(Addr::unchecked(api.addr_humanize(&canonical_addr)?)) - } - - fn predictable_contract_address( - &self, - api: &dyn Api, - _storage: &mut dyn Storage, - _code_id: u64, - _instance_id: u64, - checksum: &[u8], - creator: &CanonicalAddr, - salt: &[u8], - ) -> AnyResult { - let canonical_addr = instantiate2_address(checksum, creator, salt)?; - Ok(Addr::unchecked(api.addr_humanize(&canonical_addr)?)) - } -} - -impl MockAddressGenerator { - // non-predictable contract address generator, see `BuildContractAddressClassic` - // implementation in wasmd: https://github.com/CosmWasm/wasmd/blob/main/x/wasm/keeper/addresses.go#L35-L42 - fn instantiate_address(code_id: u64, instance_id: u64) -> CanonicalAddr { - let mut key = Vec::::new(); - key.extend_from_slice(b"wasm\0"); - key.extend_from_slice(&code_id.to_be_bytes()); - key.extend_from_slice(&instance_id.to_be_bytes()); - let module = Sha256::digest("module".as_bytes()); - Sha256::new() - .chain(module) - .chain(key) - .finalize() - .to_vec() - .into() - } -} diff --git a/src/ibc/test/polytone.rs b/src/ibc/test/polytone.rs index 8eaefb05..c7101d52 100644 --- a/src/ibc/test/polytone.rs +++ b/src/ibc/test/polytone.rs @@ -3,12 +3,12 @@ use cosmwasm_std::{Addr, ContractInfoResponse, Empty, Never, QueryRequest, WasmQuery}; use crate::{ + addons::{MockAddressGenerator, MockApiBech32}, ibc::{ - addresses::MockAddressGenerator, relayer::{create_channel, create_connection, get_event_attr_value, relay_packets_in_tx}, simple_ibc::IbcSimpleModule, }, - AppBuilder, ContractWrapper, Executor, FailingModule, WasmKeeper, addons::MockApiBech32, + AppBuilder, ContractWrapper, Executor, FailingModule, WasmKeeper, }; use anyhow::Result as AnyResult; From ba22aef13e3fb59edd36001a8bcbeaf880fabeef Mon Sep 17 00:00:00 2001 From: Kayanski Date: Wed, 17 Jan 2024 12:16:22 +0100 Subject: [PATCH 08/22] Added ibc simple module as default --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/app.rs | 12 ++++---- src/app_builder.rs | 16 +++++----- src/ibc.rs | 1 + tests/test_app_builder/test_with_ibc.rs | 39 ++++++++++++++++++++++++- 6 files changed, 55 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec61672d..ba234514 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "abstract-cw-multi-test" -version = "0.20.0" +version = "0.20.1" dependencies = [ "anyhow", "bech32", diff --git a/Cargo.toml b/Cargo.toml index b564f443..73727c27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "abstract-cw-multi-test" -version = "0.20.0" +version = "0.20.1" authors = ["Ethan Frey "] edition = "2021" description = "Testing tools for multi-contract interactions" diff --git a/src/app.rs b/src/app.rs index 61843c1b..85592edb 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,16 +3,16 @@ use crate::contracts::Contract; use crate::error::{bail, AnyResult}; use crate::executor::{AppResponse, Executor}; use crate::gov::Gov; -use crate::ibc::Ibc; use crate::ibc::{ types::IbcResponse, types::MockIbcQuery, IbcModuleMsg, IbcPacketRelayingMsg as IbcSudo, }; +use crate::ibc::{Ibc, IbcSimpleModule}; use crate::module::{FailingModule, Module}; use crate::staking::{Distribution, DistributionKeeper, StakeKeeper, Staking, StakingSudo}; use crate::stargate::{Stargate, StargateFailing}; use crate::transactions::transactional; use crate::wasm::{ContractData, Wasm, WasmKeeper, WasmSudo}; -use crate::{AppBuilder, GovFailingModule, IbcFailingModule}; +use crate::{AppBuilder, GovFailingModule}; use cosmwasm_std::testing::{MockApi, MockStorage}; use cosmwasm_std::{ from_json, to_json_binary, Addr, Api, Binary, BlockInfo, ContractResult, CosmosMsg, CustomMsg, @@ -40,7 +40,7 @@ pub type BasicApp = App< WasmKeeper, StakeKeeper, DistributionKeeper, - IbcFailingModule, + IbcSimpleModule, GovFailingModule, StargateFailing, >; @@ -57,7 +57,7 @@ pub struct App< Wasm = WasmKeeper, Staking = StakeKeeper, Distr = DistributionKeeper, - Ibc = IbcFailingModule, + Ibc = IbcSimpleModule, Gov = GovFailingModule, Stargate = StargateFailing, > { @@ -93,7 +93,7 @@ impl BasicApp { WasmKeeper, StakeKeeper, DistributionKeeper, - IbcFailingModule, + IbcSimpleModule, GovFailingModule, StargateFailing, >, @@ -118,7 +118,7 @@ where WasmKeeper, StakeKeeper, DistributionKeeper, - IbcFailingModule, + IbcSimpleModule, GovFailingModule, StargateFailing, >, diff --git a/src/app_builder.rs b/src/app_builder.rs index 290159fb..9700082d 100644 --- a/src/app_builder.rs +++ b/src/app_builder.rs @@ -1,9 +1,9 @@ //!AppBuilder helps you set up your test blockchain environment step by step [App]. +use crate::ibc::IbcSimpleModule; use crate::{ App, Bank, BankKeeper, Distribution, DistributionKeeper, FailingModule, Gov, GovFailingModule, - Ibc, IbcFailingModule, Module, Router, StakeKeeper, Staking, Stargate, StargateFailing, Wasm, - WasmKeeper, + Ibc, Module, Router, StakeKeeper, Staking, Stargate, StargateFailing, Wasm, WasmKeeper, }; use cosmwasm_std::testing::{mock_env, MockApi, MockStorage}; use cosmwasm_std::{Api, BlockInfo, CustomMsg, CustomQuery, Empty, Storage}; @@ -36,7 +36,7 @@ pub type BasicAppBuilder = AppBuilder< WasmKeeper, StakeKeeper, DistributionKeeper, - IbcFailingModule, + IbcSimpleModule, GovFailingModule, StargateFailing, >; @@ -66,7 +66,7 @@ impl Default WasmKeeper, StakeKeeper, DistributionKeeper, - IbcFailingModule, + IbcSimpleModule, GovFailingModule, StargateFailing, > @@ -85,7 +85,7 @@ impl WasmKeeper, StakeKeeper, DistributionKeeper, - IbcFailingModule, + IbcSimpleModule, GovFailingModule, StargateFailing, > @@ -101,7 +101,7 @@ impl custom: FailingModule::new(), staking: StakeKeeper::new(), distribution: DistributionKeeper::new(), - ibc: IbcFailingModule::new(), + ibc: IbcSimpleModule, gov: GovFailingModule::new(), stargate: StargateFailing, } @@ -117,7 +117,7 @@ impl WasmKeeper, StakeKeeper, DistributionKeeper, - IbcFailingModule, + IbcSimpleModule, GovFailingModule, StargateFailing, > @@ -137,7 +137,7 @@ where custom: FailingModule::new(), staking: StakeKeeper::new(), distribution: DistributionKeeper::new(), - ibc: IbcFailingModule::new(), + ibc: IbcSimpleModule, gov: GovFailingModule::new(), stargate: StargateFailing, } diff --git a/src/ibc.rs b/src/ibc.rs index fc65b534..b3643512 100644 --- a/src/ibc.rs +++ b/src/ibc.rs @@ -14,6 +14,7 @@ pub mod types; pub use self::types::IbcPacketRelayingMsg; use self::types::MockIbcQuery; +pub use simple_ibc::IbcSimpleModule; /// This is added for modules to implement actions upon ibc actions. /// This kind of execution flow is copied from the WASM way of doing things and is not 100% completetely compatible with the IBC standard diff --git a/tests/test_app_builder/test_with_ibc.rs b/tests/test_app_builder/test_with_ibc.rs index 7178d03c..120f930a 100644 --- a/tests/test_app_builder/test_with_ibc.rs +++ b/tests/test_app_builder/test_with_ibc.rs @@ -1,7 +1,9 @@ use crate::test_app_builder::{MyKeeper, NO_MESSAGE}; +use anyhow::Result as AnyResult; use cosmwasm_std::{Addr, IbcMsg, IbcQuery, QueryRequest}; +use cw_multi_test::ibc::relayer::{create_channel, create_connection}; use cw_multi_test::ibc::{types::MockIbcQuery, IbcPacketRelayingMsg}; -use cw_multi_test::{no_init, AppBuilder, Executor, Ibc}; +use cw_multi_test::{no_init, AppBuilder, BasicApp, Executor, Ibc}; type MyIbcKeeper = MyKeeper; @@ -44,3 +46,38 @@ fn building_app_with_custom_ibc_should_work() { .to_string() ); } + +#[test] +fn create_channel_should_work_with_basic_app() -> AnyResult<()> { + let mut app1 = BasicApp::new(no_init); + let mut app2 = BasicApp::new(no_init); + + let (src_connection_id, _dst_connection) = create_connection(&mut app1, &mut app2)?; + + create_channel( + &mut app1, + &mut app2, + src_connection_id, + "transfer".to_string(), + "transfer".to_string(), + "ics20-1".to_string(), + cosmwasm_std::IbcOrder::Unordered, + )?; + + Ok(()) +} + +#[test] +fn create_channel_should_work_with_failing_keeper() -> AnyResult<()> { + // build custom ibc keeper (no sudo handling for ibc) + let ibc_keeper1 = MyIbcKeeper::new(EXECUTE_MSG, QUERY_MSG, NO_MESSAGE); + let ibc_keeper2 = MyIbcKeeper::new(EXECUTE_MSG, QUERY_MSG, NO_MESSAGE); + + // build the application with custom ibc keeper + let mut app1 = AppBuilder::default().with_ibc(ibc_keeper1).build(no_init); + let mut app2 = AppBuilder::default().with_ibc(ibc_keeper2).build(no_init); + + create_connection(&mut app1, &mut app2).unwrap_err(); + + Ok(()) +} From 33b9da8bc716fdf058e478503b0f97645269ae0c Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 23 Jan 2024 14:56:12 +0100 Subject: [PATCH 09/22] Added sender in instantiate2 address --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/addresses.rs | 7 ++++--- src/tests/test_app.rs | 2 +- src/wasm.rs | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba234514..d8872c5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "abstract-cw-multi-test" -version = "0.20.1" +version = "0.20.2" dependencies = [ "anyhow", "bech32", diff --git a/Cargo.toml b/Cargo.toml index 73727c27..5f1d1cdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "abstract-cw-multi-test" -version = "0.20.1" +version = "0.20.2" authors = ["Ethan Frey "] edition = "2021" description = "Testing tools for multi-contract interactions" diff --git a/src/addresses.rs b/src/addresses.rs index d3d76aca..0879b255 100644 --- a/src/addresses.rs +++ b/src/addresses.rs @@ -112,16 +112,17 @@ pub trait AddressGenerator { /// ``` fn predictable_contract_address( &self, - _api: &dyn Api, + api: &dyn Api, _storage: &mut dyn Storage, _code_id: u64, _instance_id: u64, _checksum: &[u8], - _creator: &CanonicalAddr, + creator: &CanonicalAddr, salt: &[u8], ) -> AnyResult { Ok(Addr::unchecked(format!( - "contract{}", + "contract/{}/{}", + api.addr_humanize(creator)?, HexBinary::from(salt).to_hex() ))) } diff --git a/src/tests/test_app.rs b/src/tests/test_app.rs index 46f33db2..015a8081 100644 --- a/src/tests/test_app.rs +++ b/src/tests/test_app.rs @@ -1585,7 +1585,7 @@ mod contract_instantiation { // assert contract's address is exactly the predicted one, // in default address generator, this is like `contract` + salt in hex - assert_eq!(parsed.contract_address, "contract010203040506"); + assert_eq!(parsed.contract_address, "contract/sender/010203040506"); } } diff --git a/src/wasm.rs b/src/wasm.rs index 10fc7691..8fd33cbb 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -2238,7 +2238,7 @@ mod test { .unwrap(); assert_eq!( - contract_addr, "contract01c0ffee", + contract_addr, "contract/foobar/01c0ffee", "default address generator returned incorrect address" ); } From abcf5f23355593863889083b97830c73a51b7758 Mon Sep 17 00:00:00 2001 From: Buckram Date: Wed, 14 Feb 2024 18:56:10 +0200 Subject: [PATCH 10/22] remove tokio from multi-test --- Cargo.lock | 42 ------------------------------------------ Cargo.toml | 2 -- 2 files changed, 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8872c5b..3df5dd3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,7 +29,6 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.8", - "sha256", "thiserror", ] @@ -68,17 +67,6 @@ dependencies = [ "backtrace", ] -[[package]] -name = "async-trait" -version = "0.1.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "backtrace" version = "0.3.69" @@ -678,12 +666,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - [[package]] name = "pkcs8" version = "0.10.2" @@ -950,19 +932,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha256" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" -dependencies = [ - "async-trait", - "bytes", - "hex", - "sha2 0.10.8", - "tokio", -] - [[package]] name = "signature" version = "2.2.0" @@ -1037,17 +1006,6 @@ dependencies = [ "syn 2.0.48", ] -[[package]] -name = "tokio" -version = "1.35.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" -dependencies = [ - "backtrace", - "bytes", - "pin-project-lite", -] - [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index 5f1d1cdd..005bf292 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,8 +41,6 @@ log = "0.4.20" cw20-ics20 = "1.1.0" hex = "0.4.3" -sha256 = "1.4.0" - [dev-dependencies] From e66357bb8fde62ea1f164d097739102e3982bcfb Mon Sep 17 00:00:00 2001 From: Kayanski <44806566+Kayanski@users.noreply.github.com> Date: Wed, 28 Feb 2024 10:37:06 +0100 Subject: [PATCH 11/22] IBC - add timeout possiblity (#4) * Added timeout tx * Added automatic timeout when relaying packets * Added channel closure in case of ordered channel * Fixed timeout with channel ordering * Updated version --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/bank.rs | 40 ++++ src/ibc.rs | 2 +- src/ibc/events.rs | 9 + src/ibc/{relayer.rs => relayer/channel.rs} | 205 +---------------- src/ibc/relayer/mod.rs | 56 +++++ src/ibc/relayer/packet.rs | 217 ++++++++++++++++++ src/ibc/simple_ibc.rs | 252 +++++++++++++++++---- src/ibc/state.rs | 2 +- src/ibc/test.rs | 1 + src/ibc/test/bank.rs | 4 +- src/ibc/test/polytone.rs | 11 +- src/ibc/test/timeout.rs | 230 +++++++++++++++++++ src/ibc/types.rs | 21 +- 15 files changed, 803 insertions(+), 251 deletions(-) create mode 100644 src/ibc/events.rs rename src/ibc/{relayer.rs => relayer/channel.rs} (56%) create mode 100644 src/ibc/relayer/mod.rs create mode 100644 src/ibc/relayer/packet.rs create mode 100644 src/ibc/test/timeout.rs diff --git a/Cargo.lock b/Cargo.lock index 3df5dd3a..95f1af76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "abstract-cw-multi-test" -version = "0.20.2" +version = "0.21.0" dependencies = [ "anyhow", "bech32", diff --git a/Cargo.toml b/Cargo.toml index 005bf292..7b38349d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "abstract-cw-multi-test" -version = "0.20.2" +version = "0.21.0" authors = ["Ethan Frey "] edition = "2021" description = "Testing tools for multi-contract interactions" diff --git a/src/bank.rs b/src/bank.rs index fead3b3c..4ccd6bac 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -349,6 +349,46 @@ impl Module for BankKeeper { // Acknowledgment can't fail, so no need for ack response parsing Ok(AppIbcBasicResponse::default()) } + + fn ibc_packet_timeout( + &self, + api: &dyn Api, + storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + request: cosmwasm_std::IbcPacketTimeoutMsg, + ) -> AnyResult { + // On timeout, we unpack the amount and sent that back to the receiverwe give the funds back to the sender of the packet + + // When receiving a packet, one simply needs to unpack the amount and send that to the the receiver + let packet: Ics20Packet = from_json(request.packet.data)?; + + let mut bank_storage = prefixed(storage, NAMESPACE_BANK); + + // We verify the denom is exactly a denom that was sent through this channel + // This can be verified by checking the ibc_module mock balance + let balances = + self.get_balance(&bank_storage, &Addr::unchecked(IBC_LOCK_MODULE_ADDRESS))?; + let locked_amount = balances.iter().find(|b| b.denom == packet.denom); + + if let Some(locked_amount) = locked_amount { + assert!( + locked_amount.amount >= packet.amount, + "The ibc locked amount is lower than the packet amount" + ); + // We send tokens from the IBC_LOCK_MODULE + self.send( + &mut bank_storage, + Addr::unchecked(IBC_LOCK_MODULE_ADDRESS), + api.addr_validate(&packet.sender)?, + coins(packet.amount.u128(), packet.denom), + )?; + } else { + bail!("Funds refund after a timeout, can't timeout a transfer that was not initiated") + } + + Ok(AppIbcBasicResponse::default()) + } } pub fn wrap_ibc_denom(channel_id: String, denom: String) -> String { diff --git a/src/ibc.rs b/src/ibc.rs index b3643512..3c81a9c3 100644 --- a/src/ibc.rs +++ b/src/ibc.rs @@ -7,11 +7,11 @@ use cosmwasm_std::{ use crate::{AcceptingModule, FailingModule, Module}; +pub mod events; pub mod relayer; mod simple_ibc; mod state; pub mod types; - pub use self::types::IbcPacketRelayingMsg; use self::types::MockIbcQuery; pub use simple_ibc::IbcSimpleModule; diff --git a/src/ibc/events.rs b/src/ibc/events.rs new file mode 100644 index 00000000..a08f439c --- /dev/null +++ b/src/ibc/events.rs @@ -0,0 +1,9 @@ +pub const SEND_PACKET_EVENT: &str = "send_packet"; +pub const RECEIVE_PACKET_EVENT: &str = "recv_packet"; +pub const WRITE_ACK_EVENT: &str = "write_acknowledgement"; +pub const ACK_PACKET_EVENT: &str = "acknowledge_packet"; +pub const TIMEOUT_RECEIVE_PACKET_EVENT: &str = "timeout_received_packet"; +pub const TIMEOUT_PACKET_EVENT: &str = "timeout_packet"; + +pub const CHANNEL_CLOSE_INIT_EVENT: &str = "channel_close_init"; +pub const CHANNEL_CLOSE_CONFIRM_EVENT: &str = "channel_close_confirm"; diff --git a/src/ibc/relayer.rs b/src/ibc/relayer/channel.rs similarity index 56% rename from src/ibc/relayer.rs rename to src/ibc/relayer/channel.rs index e8f086e7..d348ad4d 100644 --- a/src/ibc/relayer.rs +++ b/src/ibc/relayer/channel.rs @@ -1,19 +1,16 @@ use anyhow::Result as AnyResult; -use cosmwasm_std::{ - from_json, Api, Binary, CustomMsg, CustomQuery, IbcEndpoint, IbcOrder, StdError, StdResult, - Storage, -}; +use cosmwasm_std::{from_json, Api, CustomMsg, CustomQuery, IbcEndpoint, IbcOrder, Storage}; use serde::de::DeserializeOwned; use crate::{ - ibc::types::Connection, App, AppResponse, Bank, Distribution, Gov, Ibc, Module, Staking, - SudoMsg, Wasm, + ibc::{ + types::{Connection, MockIbcQuery}, + IbcPacketRelayingMsg, + }, + App, AppResponse, Bank, Distribution, Gov, Ibc, Module, Staking, Wasm, }; -use super::{ - types::{IbcPacketData, MockIbcQuery}, - IbcPacketRelayingMsg, -}; +use super::get_event_attr_value; #[derive(Debug)] pub struct ChannelCreationResult { @@ -233,191 +230,3 @@ where dst_channel, }) } - -pub fn relay_packets_in_tx< - BankT1, - ApiT1, - StorageT1, - CustomT1, - WasmT1, - StakingT1, - DistrT1, - IbcT1, - GovT1, - BankT2, - ApiT2, - StorageT2, - CustomT2, - WasmT2, - StakingT2, - DistrT2, - IbcT2, - GovT2, ->( - app1: &mut App, - app2: &mut App, - app1_tx_response: AppResponse, -) -> AnyResult> -where - CustomT1::ExecT: CustomMsg + DeserializeOwned + 'static, - CustomT1::QueryT: CustomQuery + DeserializeOwned + 'static, - WasmT1: Wasm, - BankT1: Bank, - ApiT1: Api, - StorageT1: Storage, - CustomT1: Module, - StakingT1: Staking, - DistrT1: Distribution, - IbcT1: Ibc, - GovT1: Gov, - - CustomT2::ExecT: CustomMsg + DeserializeOwned + 'static, - CustomT2::QueryT: CustomQuery + DeserializeOwned + 'static, - WasmT2: Wasm, - BankT2: Bank, - ApiT2: Api, - StorageT2: Storage, - CustomT2: Module, - StakingT2: Staking, - DistrT2: Distribution, - IbcT2: Ibc, - GovT2: Gov, -{ - // Find all packets and their data - let packets = get_all_event_attr_value(&app1_tx_response, "send_packet", "packet_sequence"); - let channels = get_all_event_attr_value(&app1_tx_response, "send_packet", "packet_src_channel"); - let ports = get_all_event_attr_value(&app1_tx_response, "send_packet", "packet_src_port"); - - // For all packets, query the packetdata and relay them - - let mut packet_forwarding = vec![]; - - for i in 0..packets.len() { - let (rcv_response, ack_response, ack) = relay_packet( - app1, - app2, - ports[i].clone(), - channels[i].clone(), - packets[i].parse()?, - )?; - - packet_forwarding.push((rcv_response, ack_response, ack)); - } - - Ok(packet_forwarding) -} - -/// Relays (rcv + ack) any pending packet between 2 chains -pub fn relay_packet< - BankT1, - ApiT1, - StorageT1, - CustomT1, - WasmT1, - StakingT1, - DistrT1, - IbcT1, - GovT1, - BankT2, - ApiT2, - StorageT2, - CustomT2, - WasmT2, - StakingT2, - DistrT2, - IbcT2, - GovT2, ->( - app1: &mut App, - app2: &mut App, - src_port_id: String, - src_channel_id: String, - sequence: u64, -) -> AnyResult<(AppResponse, AppResponse, Binary)> -where - CustomT1::ExecT: CustomMsg + DeserializeOwned + 'static, - CustomT1::QueryT: CustomQuery + DeserializeOwned + 'static, - WasmT1: Wasm, - BankT1: Bank, - ApiT1: Api, - StorageT1: Storage, - CustomT1: Module, - StakingT1: Staking, - DistrT1: Distribution, - IbcT1: Ibc, - GovT1: Gov, - - CustomT2::ExecT: CustomMsg + DeserializeOwned + 'static, - CustomT2::QueryT: CustomQuery + DeserializeOwned + 'static, - WasmT2: Wasm, - BankT2: Bank, - ApiT2: Api, - StorageT2: Storage, - CustomT2: Module, - StakingT2: Staking, - DistrT2: Distribution, - IbcT2: Ibc, - GovT2: Gov, -{ - let packet: IbcPacketData = from_json(app1.ibc_query(MockIbcQuery::SendPacket { - channel_id: src_channel_id.clone(), - port_id: src_port_id.clone(), - sequence, - })?)?; - - // First we start by sending the packet on chain 2 - let receive_response = app2.sudo(SudoMsg::Ibc(IbcPacketRelayingMsg::Receive { - packet: packet.clone(), - }))?; - - let hex_ack = - get_event_attr_value(&receive_response, "write_acknowledgement", "packet_ack_hex")?; - - let ack = Binary::from(hex::decode(hex_ack)?); - - // Then we query the packet ack to deliver the response on chain 1 - let ack_response = app1.sudo(SudoMsg::Ibc(IbcPacketRelayingMsg::Acknowledge { - packet, - ack: ack.clone(), - }))?; - - Ok((receive_response, ack_response, ack)) -} - -pub fn get_event_attr_value( - response: &AppResponse, - event_type: &str, - attr_key: &str, -) -> StdResult { - for event in &response.events { - if event.ty == event_type { - for attr in &event.attributes { - if attr.key == attr_key { - return Ok(attr.value.clone()); - } - } - } - } - - Err(StdError::generic_err(format!( - "event of type {event_type} does not have a value at key {attr_key}" - ))) -} - -pub fn get_all_event_attr_value( - response: &AppResponse, - event: &str, - attribute: &str, -) -> Vec { - response - .events - .iter() - .filter(|e| e.ty.eq(event)) - .flat_map(|e| { - e.attributes - .iter() - .filter(|a| a.key.eq(attribute)) - .map(|a| a.value.clone()) - }) - .collect() -} diff --git a/src/ibc/relayer/mod.rs b/src/ibc/relayer/mod.rs new file mode 100644 index 00000000..8c9f03ff --- /dev/null +++ b/src/ibc/relayer/mod.rs @@ -0,0 +1,56 @@ +use cosmwasm_std::{StdError, StdResult}; + +use crate::AppResponse; + +mod channel; +mod packet; + +pub use channel::{create_channel, create_connection, ChannelCreationResult}; +pub use packet::{relay_packet, relay_packets_in_tx, RelayPacketResult, RelayingResult}; + +pub fn get_event_attr_value( + response: &AppResponse, + event_type: &str, + attr_key: &str, +) -> StdResult { + for event in &response.events { + if event.ty == event_type { + for attr in &event.attributes { + if attr.key == attr_key { + return Ok(attr.value.clone()); + } + } + } + } + + Err(StdError::generic_err(format!( + "event of type {event_type} does not have a value at key {attr_key}" + ))) +} + +pub fn has_event(response: &AppResponse, event_type: &str) -> bool { + for event in &response.events { + if event.ty == event_type { + return true; + } + } + false +} + +pub fn get_all_event_attr_value( + response: &AppResponse, + event: &str, + attribute: &str, +) -> Vec { + response + .events + .iter() + .filter(|e| e.ty.eq(event)) + .flat_map(|e| { + e.attributes + .iter() + .filter(|a| a.key.eq(attribute)) + .map(|a| a.value.clone()) + }) + .collect() +} diff --git a/src/ibc/relayer/packet.rs b/src/ibc/relayer/packet.rs new file mode 100644 index 00000000..cb230e13 --- /dev/null +++ b/src/ibc/relayer/packet.rs @@ -0,0 +1,217 @@ +use anyhow::Result as AnyResult; +use cosmwasm_std::{from_json, Api, Binary, CustomMsg, CustomQuery, Storage}; +use serde::de::DeserializeOwned; + +use crate::{ + ibc::{ + events::{ + CHANNEL_CLOSE_INIT_EVENT, SEND_PACKET_EVENT, TIMEOUT_RECEIVE_PACKET_EVENT, + WRITE_ACK_EVENT, + }, + types::{IbcPacketData, MockIbcQuery}, + IbcPacketRelayingMsg, + }, + App, AppResponse, Bank, Distribution, Gov, Ibc, Module, Staking, SudoMsg, Wasm, +}; + +use super::{get_all_event_attr_value, get_event_attr_value, has_event}; + +#[derive(Debug, Clone)] +pub struct RelayPacketResult { + pub receive_tx: AppResponse, + pub result: RelayingResult, +} + +#[derive(Debug, Clone)] +pub enum RelayingResult { + Timeout { + timeout_tx: AppResponse, + close_channel_confirm: Option, + }, + Acknowledgement { + tx: AppResponse, + ack: Binary, + }, +} + +pub fn relay_packets_in_tx< + BankT1, + ApiT1, + StorageT1, + CustomT1, + WasmT1, + StakingT1, + DistrT1, + IbcT1, + GovT1, + BankT2, + ApiT2, + StorageT2, + CustomT2, + WasmT2, + StakingT2, + DistrT2, + IbcT2, + GovT2, +>( + app1: &mut App, + app2: &mut App, + app1_tx_response: AppResponse, +) -> AnyResult> +where + CustomT1::ExecT: CustomMsg + DeserializeOwned + 'static, + CustomT1::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT1: Wasm, + BankT1: Bank, + ApiT1: Api, + StorageT1: Storage, + CustomT1: Module, + StakingT1: Staking, + DistrT1: Distribution, + IbcT1: Ibc, + GovT1: Gov, + + CustomT2::ExecT: CustomMsg + DeserializeOwned + 'static, + CustomT2::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT2: Wasm, + BankT2: Bank, + ApiT2: Api, + StorageT2: Storage, + CustomT2: Module, + StakingT2: Staking, + DistrT2: Distribution, + IbcT2: Ibc, + GovT2: Gov, +{ + // Find all packets and their data + let packets = get_all_event_attr_value(&app1_tx_response, SEND_PACKET_EVENT, "packet_sequence"); + let channels = + get_all_event_attr_value(&app1_tx_response, SEND_PACKET_EVENT, "packet_src_channel"); + let ports = get_all_event_attr_value(&app1_tx_response, SEND_PACKET_EVENT, "packet_src_port"); + + // For all packets, query the packetdata and relay them + + let mut packet_forwarding = vec![]; + + for i in 0..packets.len() { + let relay_response = relay_packet( + app1, + app2, + ports[i].clone(), + channels[i].clone(), + packets[i].parse()?, + )?; + + packet_forwarding.push(relay_response); + } + + Ok(packet_forwarding) +} + +/// Relays (rcv + ack) any pending packet between 2 chains +pub fn relay_packet< + BankT1, + ApiT1, + StorageT1, + CustomT1, + WasmT1, + StakingT1, + DistrT1, + IbcT1, + GovT1, + BankT2, + ApiT2, + StorageT2, + CustomT2, + WasmT2, + StakingT2, + DistrT2, + IbcT2, + GovT2, +>( + app1: &mut App, + app2: &mut App, + src_port_id: String, + src_channel_id: String, + sequence: u64, +) -> AnyResult +where + CustomT1::ExecT: CustomMsg + DeserializeOwned + 'static, + CustomT1::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT1: Wasm, + BankT1: Bank, + ApiT1: Api, + StorageT1: Storage, + CustomT1: Module, + StakingT1: Staking, + DistrT1: Distribution, + IbcT1: Ibc, + GovT1: Gov, + + CustomT2::ExecT: CustomMsg + DeserializeOwned + 'static, + CustomT2::QueryT: CustomQuery + DeserializeOwned + 'static, + WasmT2: Wasm, + BankT2: Bank, + ApiT2: Api, + StorageT2: Storage, + CustomT2: Module, + StakingT2: Staking, + DistrT2: Distribution, + IbcT2: Ibc, + GovT2: Gov, +{ + let packet: IbcPacketData = from_json(app1.ibc_query(MockIbcQuery::SendPacket { + channel_id: src_channel_id.clone(), + port_id: src_port_id.clone(), + sequence, + })?)?; + + // First we start by sending the packet on chain 2 + let receive_response = app2.sudo(SudoMsg::Ibc(IbcPacketRelayingMsg::Receive { + packet: packet.clone(), + }))?; + + // We start by verifying that we have an acknowledgment and not a timeout + if has_event(&receive_response, TIMEOUT_RECEIVE_PACKET_EVENT) { + // If there was a timeout, we timeout the packet on the sending chain + // TODO: We don't handle the chain closure in here for now in case of ordered channels + let timeout_response = app1.sudo(SudoMsg::Ibc(IbcPacketRelayingMsg::Timeout { packet }))?; + + // We close the channel on the sending chain if it's request by the receiving chain + let close_confirm_response = if has_event(&receive_response, CHANNEL_CLOSE_INIT_EVENT) { + Some(app1.sudo(SudoMsg::Ibc(IbcPacketRelayingMsg::CloseChannel { + port_id: src_port_id, + channel_id: src_channel_id, + init: false, + }))?) + } else { + None + }; + + return Ok(RelayPacketResult { + receive_tx: receive_response, + result: RelayingResult::Timeout { + timeout_tx: timeout_response, + close_channel_confirm: close_confirm_response, + }, + }); + } + + // Then we query the packet ack to deliver the response on chain 1 + let hex_ack = get_event_attr_value(&receive_response, WRITE_ACK_EVENT, "packet_ack_hex")?; + + let ack = Binary::from(hex::decode(hex_ack)?); + + let ack_response = app1.sudo(SudoMsg::Ibc(IbcPacketRelayingMsg::Acknowledge { + packet, + ack: ack.clone(), + }))?; + + Ok(RelayPacketResult { + receive_tx: receive_response, + result: RelayingResult::Acknowledgement { + tx: ack_response, + ack, + }, + }) +} diff --git a/src/ibc/simple_ibc.rs b/src/ibc/simple_ibc.rs index 8aee9e39..bb6684c8 100644 --- a/src/ibc/simple_ibc.rs +++ b/src/ibc/simple_ibc.rs @@ -1,9 +1,10 @@ use anyhow::{anyhow, bail}; use cosmwasm_std::{ ensure_eq, to_json_binary, Addr, BankMsg, Binary, ChannelResponse, Coin, Event, - IbcAcknowledgement, IbcChannel, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcEndpoint, IbcMsg, - IbcOrder, IbcPacket, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcQuery, - IbcTimeout, IbcTimeoutBlock, ListChannelsResponse, Order, Storage, + IbcAcknowledgement, IbcChannel, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, + IbcEndpoint, IbcMsg, IbcOrder, IbcPacket, IbcPacketAckMsg, IbcPacketReceiveMsg, + IbcPacketTimeoutMsg, IbcQuery, IbcTimeout, IbcTimeoutBlock, ListChannelsResponse, Order, + Storage, }; use cw20_ics20::ibc::Ics20Packet; @@ -13,7 +14,7 @@ use crate::{ ibc::types::Connection, prefixed_storage::{prefixed, prefixed_read}, transactions::transactional, - AppResponse, Ibc, Module, + AppResponse, Ibc, Module, SudoMsg, }; use anyhow::Result as AnyResult; @@ -21,13 +22,18 @@ use anyhow::Result as AnyResult; pub struct IbcSimpleModule; use super::{ + events::{ + ACK_PACKET_EVENT, CHANNEL_CLOSE_CONFIRM_EVENT, CHANNEL_CLOSE_INIT_EVENT, + RECEIVE_PACKET_EVENT, SEND_PACKET_EVENT, TIMEOUT_PACKET_EVENT, + TIMEOUT_RECEIVE_PACKET_EVENT, WRITE_ACK_EVENT, + }, state::{ ibc_connections, load_port_info, ACK_PACKET_MAP, CHANNEL_HANDSHAKE_INFO, CHANNEL_INFO, NAMESPACE_IBC, PORT_INFO, RECEIVE_PACKET_MAP, SEND_PACKET_MAP, TIMEOUT_PACKET_MAP, }, types::{ ChannelHandshakeInfo, ChannelHandshakeState, ChannelInfo, IbcPacketAck, IbcPacketData, - IbcPacketRelayingMsg, IbcResponse, MockIbcPort, MockIbcQuery, + IbcPacketReceived, IbcPacketRelayingMsg, IbcResponse, MockIbcPort, MockIbcQuery, }, }; @@ -341,6 +347,7 @@ impl IbcSimpleModule { counterparty_version.unwrap(), channel_handshake.connection_id, ), + open: true, }, )?; @@ -368,6 +375,99 @@ impl IbcSimpleModule { Ok(AppResponse { data: None, events }) } + /// Closes an already fully established channel + /// This doesn't handle closing half opened channels + fn close_channel( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn Storage, + router: &dyn crate::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + port_id: String, + channel_id: String, + init: bool, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); + + // We pass the channel status to closed + let channel_info = CHANNEL_INFO.update( + &mut ibc_storage, + (port_id.clone(), channel_id.clone()), + |channel| match channel { + None => bail!( + "No channel exists with this port and channel id : {}:{}", + port_id, + channel_id + ), + Some(mut channel) => { + channel.open = false; + Ok(channel) + } + }, + )?; + + let (close_request, mut ibc_event) = if init { + ( + IbcChannelCloseMsg::CloseInit { + channel: channel_info.info.clone(), + }, + Event::new(CHANNEL_CLOSE_INIT_EVENT), + ) + } else { + ( + IbcChannelCloseMsg::CloseConfirm { + channel: channel_info.info.clone(), + }, + Event::new(CHANNEL_CLOSE_CONFIRM_EVENT), + ) + }; + + ibc_event = ibc_event + .add_attribute("port_id", port_id.clone()) + .add_attribute("channel_id", channel_id.clone()) + .add_attribute( + "counterparty_port_id", + channel_info.info.counterparty_endpoint.port_id.clone(), + ) + .add_attribute( + "counterparty_channel_id", + channel_info.info.counterparty_endpoint.channel_id.clone(), + ) + .add_attribute("connection_id", channel_info.info.connection_id.clone()); + + // Then we send an ibc message on the corresponding module in cache + let res = transactional(storage, |write_cache, _| { + router.ibc( + api, + write_cache, + block, + IbcRouterMsg { + module: port_id.parse::()?.into(), + msg: super::IbcModuleMsg::ChannelClose(close_request), + }, + ) + })?; + + // Then, we store the close events + let mut events = match res { + IbcResponse::Basic(r) => r.events, + _ => panic!("Only an basic response was expected when closing a channel"), + }; + + events.push(ibc_event); + + Ok(AppResponse { data: None, events }) + } + fn send_packet( &self, storage: &mut dyn Storage, @@ -415,7 +515,7 @@ impl IbcSimpleModule { }); let timeout_timestamp = packet.timeout.timestamp().map(|t| t.nanos()).unwrap_or(0); - let send_event = Event::new("send_packet") + let send_event = Event::new(SEND_PACKET_EVENT) .add_attribute( "packet_data", String::from_utf8_lossy(packet.data.as_slice()), @@ -481,6 +581,22 @@ impl IbcSimpleModule { bail!("You can't receive the same packet twice on the chain") } + // We take a look at the timeout status of the packet + let timeout = packet.timeout.clone(); + let mut has_timeout = false; + if let Some(packet_block) = timeout.block() { + // We verify the block indicated is not passed + if block.height >= packet_block.height { + has_timeout = true; + } + } + if let Some(packet_timestamp) = timeout.timestamp() { + // We verify the timestamp indicated is not passed + if block.time >= packet_timestamp { + has_timeout = true; + } + } + // We save it into storage (for tracking purposes and making sure we don't broadcast the message twice) RECEIVE_PACKET_MAP.save( &mut ibc_storage, @@ -489,9 +605,71 @@ impl IbcSimpleModule { packet.dst_channel_id.clone(), packet.sequence, ), - &packet, + &IbcPacketReceived { + data: packet.clone(), + timeout: has_timeout, + }, )?; + // If the packet has timeout on an ordered channel, we need to return an appropriate response AND close the channel + if has_timeout { + let res = if channel_info.info.order == IbcOrder::Ordered { + // We send a close channel response + transactional(storage, |write_cache, _| { + router.sudo( + api, + write_cache, + block, + SudoMsg::Ibc(IbcPacketRelayingMsg::CloseChannel { + port_id: packet.dst_port_id.clone(), + channel_id: packet.dst_channel_id.clone(), + init: true, + }), + ) + })? + } else { + AppResponse { + events: vec![], + data: None, + } + }; + + // We add timeout events + let timeout_height = packet.timeout.block().unwrap_or(IbcTimeoutBlock { + revision: 0, + height: 0, + }); + let timeout_timestamp = packet.timeout.timestamp().map(|t| t.nanos()).unwrap_or(0); + let timeout_event = Event::new(TIMEOUT_RECEIVE_PACKET_EVENT) + .add_attribute( + "packet_data", + String::from_utf8_lossy(packet.data.as_slice()), + ) + .add_attribute("packet_data_hex", hex::encode(packet.data.0.clone())) + .add_attribute( + "packet_timeout_height", + format!("{}-{}", timeout_height.revision, timeout_height.height), + ) + .add_attribute("packet_timeout_timestamp", timeout_timestamp.to_string()) + .add_attribute("packet_sequence", packet.sequence.to_string()) + .add_attribute("packet_src_port", packet.src_port_id.clone()) + .add_attribute("packet_src_channel", packet.src_channel_id.clone()) + .add_attribute("packet_dst_port", packet.dst_port_id.clone()) + .add_attribute("packet_dst_channel", packet.dst_channel_id.clone()) + .add_attribute( + "packet_channel_ordering", + serde_json::to_value(channel_info.info.order)?.to_string(), + ) + .add_attribute("packet_connection", channel_info.info.connection_id); + + let mut events = res.events; + events.push(timeout_event); + return Ok(AppResponse { + events, + data: res.data, + }); + } + let packet_msg = packet_from_data_and_channel(&packet, &channel_info); #[cfg(not(feature = "cosmwasm_1_1"))] @@ -499,7 +677,7 @@ impl IbcSimpleModule { #[cfg(feature = "cosmwasm_1_1")] let receive_msg = IbcPacketReceiveMsg::new(packet_msg, Addr::unchecked(RELAYER_ADDR)); - // First we send an ibc message on the wasm module in cache + // First we send an ibc message on the corresponding module let port: MockIbcPort = channel_info.info.endpoint.port_id.parse()?; let res = transactional(storage, |write_cache, _| { @@ -514,7 +692,6 @@ impl IbcSimpleModule { ) })?; - // This is repeated to avoid multiple mutable borrows let mut ibc_storage = prefixed(storage, NAMESPACE_IBC); let acknowledgement; // Then, we store the acknowledgement and collect events @@ -544,7 +721,7 @@ impl IbcSimpleModule { }); let timeout_timestamp = packet.timeout.timestamp().map(|t| t.nanos()).unwrap_or(0); - let recv_event = Event::new("recv_packet") + let recv_event = Event::new(RECEIVE_PACKET_EVENT) .add_attribute( "packet_data", String::from_utf8_lossy(packet.data.as_slice()), @@ -566,7 +743,7 @@ impl IbcSimpleModule { ) .add_attribute("packet_connection", channel_info.info.connection_id); - let ack_event = Event::new("write_acknowledgement") + let ack_event = Event::new(WRITE_ACK_EVENT) .add_attribute( "packet_data", serde_json::to_value(&packet.data)?.to_string(), @@ -694,7 +871,7 @@ impl IbcSimpleModule { }); let timeout_timestamp = packet.timeout.timestamp().map(|t| t.nanos()).unwrap_or(0); - let ack_event = Event::new("recv_packet") + let ack_event = Event::new(ACK_PACKET_EVENT) .add_attribute( "packet_timeout_height", format!("{}-{}", timeout_height.revision, timeout_height.height), @@ -770,22 +947,7 @@ impl IbcSimpleModule { bail!("You can't timeout an packet twice") } - // If there is a block timeout - let mut has_timedout = false; - if let Some(block_timeout) = packet_data.timeout.block() { - if block.height >= block_timeout.height { - has_timedout = true; - } - } - if let Some(timeout) = packet_data.timeout.timestamp() { - if block.time >= timeout { - has_timedout = true; - } - } - - if !has_timedout { - bail!("Packet hasn't timedout"); - } + // We don't check timeout conditions here, because when calling this function, we assume the counterparty chain has received the packet after the timeout TIMEOUT_PACKET_MAP.save( &mut ibc_storage, @@ -833,7 +995,7 @@ impl IbcSimpleModule { }); let timeout_timestamp = packet.timeout.timestamp().map(|t| t.nanos()).unwrap_or(0); - let timeout_event = Event::new("timeout_packet") + let timeout_event = Event::new(TIMEOUT_PACKET_EVENT) .add_attribute( "packet_timeout_height", format!("{}-{}", timeout_height.revision, timeout_height.height), @@ -905,14 +1067,6 @@ impl IbcSimpleModule { timeout, ) } - - pub fn close_channel( - &self, - _storage: &mut dyn Storage, - _channel_id: String, - ) -> AnyResult { - bail!("Close channel not implemented in cw-multi-test"); - } } impl Module for IbcSimpleModule { @@ -954,10 +1108,14 @@ impl Module for IbcSimpleModule { } => { // This should come from a contract. So the port_id is always the same format // If you want to send a packet form a module use the sudo Send Packet msg - let port_id = format!("wasm.{}", sender); + let port_id: String = format!("wasm.{}", sender); self.send_packet(storage, port_id, channel_id, data, timeout) } - IbcMsg::CloseChannel { channel_id } => self.close_channel(storage, channel_id), + IbcMsg::CloseChannel { channel_id } => { + let port_id: String = format!("wasm.{}", sender); + // This message correspond to init closing a channel + self.close_channel(api, storage, router, block, port_id, channel_id, true) + } _ => bail!("Not implemented on the ibc module"), } } @@ -1025,9 +1183,11 @@ impl Module for IbcSimpleModule { counterparty_endpoint, counterparty_version, ), - IbcPacketRelayingMsg::CloseChannel {} => { - panic!("Can't close a channel in cw-multi-test") - } + IbcPacketRelayingMsg::CloseChannel { + port_id, + channel_id, + init, + } => self.close_channel(api, storage, router, block, port_id, channel_id, init), IbcPacketRelayingMsg::Send { port_id, @@ -1116,6 +1276,14 @@ impl Module for IbcSimpleModule { Ok(to_json_binary(&connections)?) } + MockIbcQuery::ChannelInfo { + port_id, + channel_id, + } => { + let channel_info = CHANNEL_INFO.load(&ibc_storage, (port_id, channel_id))?; + + Ok(to_json_binary(&channel_info)?) + } } } diff --git a/src/ibc/state.rs b/src/ibc/state.rs index 1f69ceea..69ecbcca 100644 --- a/src/ibc/state.rs +++ b/src/ibc/state.rs @@ -41,7 +41,7 @@ pub const CHANNEL_INFO: Map<(String, String), ChannelInfo> = Map::new("channel_i pub const SEND_PACKET_MAP: Map<(String, String, u64), IbcPacketData> = Map::new("send_packet"); // channel id, packet_id ==> Packet data -pub const RECEIVE_PACKET_MAP: Map<(String, String, u64), IbcPacketData> = +pub const RECEIVE_PACKET_MAP: Map<(String, String, u64), IbcPacketReceived> = Map::new("receive_packet"); // channel id, packet_id ==> Packet data diff --git a/src/ibc/test.rs b/src/ibc/test.rs index a0e5baee..2f212c5f 100644 --- a/src/ibc/test.rs +++ b/src/ibc/test.rs @@ -12,6 +12,7 @@ use super::{relayer::create_channel, simple_ibc::IbcSimpleModule}; mod bank; mod polytone; +mod timeout; #[test] fn channel_creation() -> anyhow::Result<()> { diff --git a/src/ibc/test/bank.rs b/src/ibc/test/bank.rs index eb137d30..3fe58c9f 100644 --- a/src/ibc/test/bank.rs +++ b/src/ibc/test/bank.rs @@ -67,7 +67,7 @@ fn simple_transfer() -> anyhow::Result<()> { amount: funds.clone(), timeout: IbcTimeout::with_block(IbcTimeoutBlock { revision: 1, - height: app1.block_info().height, + height: app2.block_info().height + 1, }), }), )?; @@ -163,7 +163,7 @@ fn transfer_and_back() -> anyhow::Result<()> { amount: funds.clone(), timeout: IbcTimeout::with_block(IbcTimeoutBlock { revision: 1, - height: app1.block_info().height, + height: app2.block_info().height + 1, }), }), )?; diff --git a/src/ibc/test/polytone.rs b/src/ibc/test/polytone.rs index c7101d52..9df83f91 100644 --- a/src/ibc/test/polytone.rs +++ b/src/ibc/test/polytone.rs @@ -5,6 +5,7 @@ use cosmwasm_std::{Addr, ContractInfoResponse, Empty, Never, QueryRequest, WasmQ use crate::{ addons::{MockAddressGenerator, MockApiBech32}, ibc::{ + events::WRITE_ACK_EVENT, relayer::{create_channel, create_connection, get_event_attr_value, relay_packets_in_tx}, simple_ibc::IbcSimpleModule, }, @@ -255,7 +256,11 @@ fn polytone() -> anyhow::Result<()> { assert_eq!(packet_txs.len(), 1); println!("{:?}", packet_txs); - let contract_addr = get_event_attr_value(&packet_txs[0].0, "instantiate", "_contract_address")?; + let contract_addr = get_event_attr_value( + &packet_txs[0].receive_tx, + "instantiate", + "_contract_address", + )?; // We test if the proxy is instantiated on app2 let test: ContractInfoResponse = @@ -267,8 +272,8 @@ fn polytone() -> anyhow::Result<()> { // Assert the polytone result (executed_by field of the ack) let ack: Callback = serde_json::from_str(&get_event_attr_value( - &packet_txs[0].0, - "write_acknowledgement", + &packet_txs[0].receive_tx, + WRITE_ACK_EVENT, "packet_ack", )?)?; diff --git a/src/ibc/test/timeout.rs b/src/ibc/test/timeout.rs new file mode 100644 index 00000000..f5eb7691 --- /dev/null +++ b/src/ibc/test/timeout.rs @@ -0,0 +1,230 @@ +use cosmwasm_std::{ + coin, from_json, to_json_binary, Addr, AllBalanceResponse, BankQuery, CosmosMsg, Empty, IbcMsg, + IbcOrder, IbcTimeout, IbcTimeoutBlock, Querier, QueryRequest, +}; + +use crate::{ + ibc::{ + events::TIMEOUT_RECEIVE_PACKET_EVENT, + relayer::{ + create_channel, create_connection, has_event, relay_packets_in_tx, + ChannelCreationResult, RelayingResult, + }, + simple_ibc::IbcSimpleModule, + types::{ChannelInfo, MockIbcQuery}, + }, + AppBuilder, Executor, +}; + +#[test] +fn simple_transfer_timeout() -> anyhow::Result<()> { + let funds = coin(100_000, "ufund"); + let fund_owner = "owner"; + let fund_recipient = "recipient"; + + // We mint some funds to the owner + let mut app1 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|router, api, storage| { + router + .bank + .init_balance( + storage, + &api.addr_validate(fund_owner).unwrap(), + vec![funds.clone()], + ) + .unwrap(); + }); + let mut app2 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|_, _, _| {}); + + let port1 = "transfer".to_string(); + let port2 = "transfer".to_string(); + + let (src_connection_id, _) = create_connection(&mut app1, &mut app2)?; + + // We start by creating channels + let ChannelCreationResult { src_channel, .. } = create_channel( + &mut app1, + &mut app2, + src_connection_id, + port1.clone(), + port2, + "ics20-1".to_string(), + IbcOrder::Ordered, + )?; + + // We send an IBC transfer Cosmos Msg on app 1 + let send_response = app1.execute( + Addr::unchecked(fund_owner), + CosmosMsg::Ibc(IbcMsg::Transfer { + channel_id: src_channel, + to_address: fund_recipient.to_string(), + amount: funds.clone(), + timeout: IbcTimeout::with_block(IbcTimeoutBlock { + revision: 1, + height: app2.block_info().height, // this will have the effect of a timeout when relaying the packets + }), + }), + )?; + + // We assert the sender balance is empty ! + + // We make sure the balance of the sender hasn't changed in the process + let balances = app1 + .raw_query( + to_json_binary(&QueryRequest::::Bank(BankQuery::AllBalances { + address: fund_owner.to_string(), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + let balances: AllBalanceResponse = from_json(balances)?; + assert!(balances.amount.is_empty()); + + // We relaying all packets found in the transaction + let resp = relay_packets_in_tx(&mut app1, &mut app2, send_response)?; + + // We make sure the response contains a timeout + assert_eq!(resp.len(), 1); + if let RelayingResult::Acknowledgement { .. } = resp[0].result { + panic!("Expected a timeout"); + } + assert!(has_event(&resp[0].receive_tx, TIMEOUT_RECEIVE_PACKET_EVENT)); + + // We make sure the balance of the recipient has not changed + let balances = app2 + .raw_query( + to_json_binary(&QueryRequest::::Bank(BankQuery::AllBalances { + address: fund_recipient.to_string(), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + let balances: AllBalanceResponse = from_json(balances)?; + + // The recipient has exactly no balance, because it has timed out + assert_eq!(balances.amount.len(), 0); + + // We make sure the balance of the sender hasn't changed in the process + let balances = app1 + .raw_query( + to_json_binary(&QueryRequest::::Bank(BankQuery::AllBalances { + address: fund_owner.to_string(), + }))? + .as_slice(), + ) + .into_result()? + .unwrap(); + let balances: AllBalanceResponse = from_json(balances)?; + println!("{:?}", balances); + assert_eq!(balances.amount.len(), 1); + assert_eq!(balances.amount[0].amount, funds.amount); + assert_eq!(balances.amount[0].denom, funds.denom); + Ok(()) +} + +#[test] +fn simple_transfer_timeout_closes_channel() -> anyhow::Result<()> { + let funds = coin(100_000, "ufund"); + let fund_owner = "owner"; + let fund_recipient = "recipient"; + + // We mint some funds to the owner + let mut app1 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|router, api, storage| { + router + .bank + .init_balance( + storage, + &api.addr_validate(fund_owner).unwrap(), + vec![funds.clone()], + ) + .unwrap(); + }); + let mut app2 = AppBuilder::default() + .with_ibc(IbcSimpleModule) + .build(|_, _, _| {}); + + let port1 = "transfer".to_string(); + let port2 = "transfer".to_string(); + + let (src_connection_id, _) = create_connection(&mut app1, &mut app2)?; + + // We start by creating channels + let ChannelCreationResult { + src_channel, + dst_channel, + .. + } = create_channel( + &mut app1, + &mut app2, + src_connection_id, + port1.clone(), + port2.clone(), + "ics20-1".to_string(), + IbcOrder::Ordered, + )?; + + // We send an IBC transfer Cosmos Msg on app 1 + let send_response = app1.execute( + Addr::unchecked(fund_owner), + CosmosMsg::Ibc(IbcMsg::Transfer { + channel_id: src_channel.clone(), + to_address: fund_recipient.to_string(), + amount: funds.clone(), + timeout: IbcTimeout::with_block(IbcTimeoutBlock { + revision: 1, + height: app2.block_info().height, // this will have the effect of a timeout when relaying the packets + }), + }), + )?; + + // We make sure the channel is open + let channel_info: ChannelInfo = from_json(app1.ibc_query(MockIbcQuery::ChannelInfo { + port_id: port1.clone(), + channel_id: src_channel.clone(), + })?)?; + assert!(channel_info.open); + // We make sure the channel is open + let channel_info: ChannelInfo = from_json(app2.ibc_query(MockIbcQuery::ChannelInfo { + port_id: port2.clone(), + channel_id: dst_channel.clone(), + })?)?; + assert!(channel_info.open); + + // We relaying all packets found in the transaction + let resp = relay_packets_in_tx(&mut app1, &mut app2, send_response)?; + + // We make sure the response contains a timeout + assert_eq!(resp.len(), 1); + match resp[0].result.clone() { + RelayingResult::Acknowledgement { .. } => panic!("Expected a timeout"), + RelayingResult::Timeout { + close_channel_confirm, + .. + } => { + // We make sure the confirm close transaction was executed + assert!(close_channel_confirm.is_some()) + } + } + + // We make sure the channel is closed + let channel_info: ChannelInfo = from_json(app1.ibc_query(MockIbcQuery::ChannelInfo { + port_id: port1, + channel_id: src_channel, + })?)?; + assert!(!channel_info.open); + // We make sure the channel is closed + let channel_info: ChannelInfo = from_json(app2.ibc_query(MockIbcQuery::ChannelInfo { + port_id: port2.clone(), + channel_id: dst_channel.clone(), + })?)?; + assert!(!channel_info.open); + + Ok(()) +} diff --git a/src/ibc/types.rs b/src/ibc/types.rs index 8e981334..069668dc 100644 --- a/src/ibc/types.rs +++ b/src/ibc/types.rs @@ -49,6 +49,8 @@ pub struct ChannelInfo { pub last_packet_relayed: u64, pub info: IbcChannel, + + pub open: bool, } #[cosmwasm_schema::cw_serde] @@ -115,6 +117,13 @@ pub struct IbcPacketData { pub timeout: IbcTimeout, } +#[cosmwasm_schema::cw_serde] +pub struct IbcPacketReceived { + pub data: IbcPacketData, + /// Indicates wether the packet was received with a timeout + pub timeout: bool, +} + #[cosmwasm_schema::cw_serde] pub struct IbcPacketAck { pub ack: Binary, @@ -147,8 +156,11 @@ pub enum IbcPacketRelayingMsg { counterparty_version: Option, counterparty_endpoint: IbcEndpoint, }, - CloseChannel {}, - + CloseChannel { + port_id: String, + channel_id: String, + init: bool, + }, Send { port_id: String, channel_id: String, @@ -226,6 +238,11 @@ pub enum MockIbcQuery { ChainConnections { chain_id: String, }, + /// Gets information on a channel + ChannelInfo { + port_id: String, + channel_id: String, + }, } impl From for MockIbcQuery { From 7f3be95150b58d71c0893b019191c2d1ee72254b Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Wed, 20 Mar 2024 13:45:01 +0100 Subject: [PATCH 12/22] Prepared a branch for releases 1.0.x --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d59020aa..89ffc432 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,7 +251,7 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "1.0.0-rc.2" +version = "1.0.0" dependencies = [ "anyhow", "bech32 0.11.0", diff --git a/Cargo.toml b/Cargo.toml index d9337791..77e0610e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-multi-test" -version = "1.0.0-rc.2" +version = "1.0.0" authors = ["Ethan Frey "] description = "Testing tools for multi-contract interactions" repository = "https://github.com/CosmWasm/cw-multi-test" From 217e3750e0fe07969b90a2ef2d5bb9e6975a961c Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Thu, 21 Mar 2024 09:47:41 +0100 Subject: [PATCH 13/22] Reverted version number. --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89ffc432..d59020aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,7 +251,7 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "1.0.0" +version = "1.0.0-rc.2" dependencies = [ "anyhow", "bech32 0.11.0", diff --git a/Cargo.toml b/Cargo.toml index 77e0610e..d9337791 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-multi-test" -version = "1.0.0" +version = "1.0.0-rc.2" authors = ["Ethan Frey "] description = "Testing tools for multi-contract interactions" repository = "https://github.com/CosmWasm/cw-multi-test" From 67428f65d83e0332193c423b30acbfc332d34481 Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Thu, 21 Mar 2024 09:51:20 +0100 Subject: [PATCH 14/22] Bumped version number. --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d59020aa..89ffc432 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,7 +251,7 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "1.0.0-rc.2" +version = "1.0.0" dependencies = [ "anyhow", "bech32 0.11.0", diff --git a/Cargo.toml b/Cargo.toml index d9337791..77e0610e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-multi-test" -version = "1.0.0-rc.2" +version = "1.0.0" authors = ["Ethan Frey "] description = "Testing tools for multi-contract interactions" repository = "https://github.com/CosmWasm/cw-multi-test" From 29ce60c6e0705790d714691013e69f0216cea4c1 Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Thu, 21 Mar 2024 09:58:49 +0100 Subject: [PATCH 15/22] Added co-author. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 77e0610e..b1e85b47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cw-multi-test" version = "1.0.0" -authors = ["Ethan Frey "] +authors = ["Ethan Frey ", "Dariusz Depta "] description = "Testing tools for multi-contract interactions" repository = "https://github.com/CosmWasm/cw-multi-test" homepage = "https://cosmwasm.com" From 05200979df606252eebcfa01cbdc9c9632079770 Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Thu, 21 Mar 2024 10:51:44 +0100 Subject: [PATCH 16/22] Updated CHANGELOG. --- CHANGELOG.md | 76 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75fc5d19..2b840438 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,37 @@ # Changelog +## [v1.0.0](https://github.com/CosmWasm/cw-multi-test/tree/v1.0.0) (2024-03-22) + +[Full Changelog](https://github.com/CosmWasm/cw-multi-test/compare/v0.20.1...v1.0.0) + +**Closed issues:** + +- Forward port: Fixing contract wrapper [\#145](https://github.com/CosmWasm/cw-multi-test/issues/145) +- Implement `store_code_with_id` helper [\#22](https://github.com/CosmWasm/cw-multi-test/issues/22) +- New `App::store_code` function definition [\#69](https://github.com/CosmWasm/cw-multi-test/issues/69) (wontfix) +- Make `App::store_code_with_creator` deprecated [\#70](https://github.com/CosmWasm/cw-multi-test/issues/70) (wontfix) +- Remove function `next_address` from `AddressGenerator` trait [\#90](https://github.com/CosmWasm/cw-multi-test/issues/90) +- Remove `new_with_custom_address_generator` function from `WasmKeeper` [\#91](https://github.com/CosmWasm/cw-multi-test/issues/91) + +**Merged pull requests:** + +- Refactored contract wrapper [\#149](https://github.com/CosmWasm/cw-multi-test/pull/149) ([DariuszDepta](https://github.com/DariuszDepta)) +- Fixed contract wrapper [\#148](https://github.com/CosmWasm/cw-multi-test/pull/148) ([DariuszDepta](https://github.com/DariuszDepta)) +- Remove `Addr::unchecked` where possible [\#141](https://github.com/CosmWasm/cw-multi-test/pull/141) ([DariuszDepta](https://github.com/DariuszDepta)) +- Refactored wasm trait [\#139](https://github.com/CosmWasm/cw-multi-test/pull/139) ([DariuszDepta](https://github.com/DariuszDepta)) +- Added `IntoAddr` trait [\#138](https://github.com/CosmWasm/cw-multi-test/pull/138) ([DariuszDepta](https://github.com/DariuszDepta)) +- Removed `new_with_custom_address_generator` function [\#135](https://github.com/CosmWasm/cw-multi-test/pull/135) ([DariuszDepta](https://github.com/DariuszDepta)) +- Removed `next_address` function [\#134](https://github.com/CosmWasm/cw-multi-test/pull/134) ([DariuszDepta](https://github.com/DariuszDepta)) +- Added `store_code_with_id` function to `App` [\#117](https://github.com/CosmWasm/cw-multi-test/pull/117) ([DariuszDepta](https://github.com/DariuszDepta)) + +## [v0.20.1](https://github.com/CosmWasm/cw-multi-test/tree/v0.20.1) (2024-03-15) + +[Full Changelog](https://github.com/CosmWasm/cw-multi-test/compare/v0.20.0...v0.20.1) + +**Merged pull requests:** + +- Fixed contract wrapper [\#147](https://github.com/CosmWasm/cw-multi-test/pull/147) ([DariuszDepta](https://github.com/DariuszDepta)) + ## [v0.20.0](https://github.com/CosmWasm/cw-multi-test/tree/v0.20.0) (2023-12-06) [Full Changelog](https://github.com/CosmWasm/cw-multi-test/compare/v0.19.0...v0.20.0) @@ -85,6 +117,15 @@ - Adds BankQuery::Supply support [\#51](https://github.com/CosmWasm/cw-multi-test/pull/51) ([JakeHartnell](https://github.com/JakeHartnell)) - Remove direct k256 dependencies [\#47](https://github.com/CosmWasm/cw-multi-test/pull/47) ([webmaster128](https://github.com/webmaster128)) +## [v0.16.6](https://github.com/CosmWasm/cw-multi-test/tree/v0.16.6) (2024-03-15) + +[Full Changelog](https://github.com/CosmWasm/cw-multi-test/compare/v0.16.5...v0.16.6) + +**Merged pull requests:** + +- Fixed contract + wrapper [\#143](https://github.com/CosmWasm/cw-multi-test/pull/143) ([DariuszDepta](https://github.com/DariuszDepta)) + ## [v0.16.5](https://github.com/CosmWasm/cw-multi-test/tree/v0.16.5) (2023-06-07) [Full Changelog](https://github.com/CosmWasm/cw-multi-test/compare/v0.16.4...v0.16.5) @@ -148,6 +189,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. ## [v0.16.1](https://github.com/CosmWasm/cw-plus/tree/v0.16.1) (2022-11-23) [Full Changelog](https://github.com/CosmWasm/cw-plus/compare/v0.16.0...v0.16.1) + - Modules for Stargate (IBC and Gov) messages - failing by default, but possible to exchange ## [v0.16.0](https://github.com/CosmWasm/cw-plus/tree/v0.16.0) (2022-10-14) @@ -346,7 +388,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - Update changelog add upcoming [\#675](https://github.com/CosmWasm/cw-plus/pull/675) ([maurolacy](https://github.com/maurolacy)) - Reject proposals early [\#668](https://github.com/CosmWasm/cw-plus/pull/668) ([Callum-A](https://github.com/Callum-A)) - cw20-base: validate addresses are unique in initial balances [\#659](https://github.com/CosmWasm/cw-plus/pull/659) ([harryscholes](https://github.com/harryscholes)) -- New SECURITY.md refering to wasmd [\#624](https://github.com/CosmWasm/cw-plus/pull/624) ([ethanfrey](https://github.com/ethanfrey)) +- New SECURITY.md referring to wasmd [\#624](https://github.com/CosmWasm/cw-plus/pull/624) ([ethanfrey](https://github.com/ethanfrey)) ## [v0.13.0](https://github.com/CosmWasm/cw-plus/tree/v0.13.0) (2022-03-09) @@ -406,7 +448,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. **Merged pull requests:** - Prepare release v0.12.0 [\#654](https://github.com/CosmWasm/cw-plus/pull/654) ([uint](https://github.com/uint)) -- Ics20 same ack handling as ibctransfer [\#653](https://github.com/CosmWasm/cw-plus/pull/653) ([ethanfrey](https://github.com/ethanfrey)) +- Ics20 same ack handling as IBC transfer [\#653](https://github.com/CosmWasm/cw-plus/pull/653) ([ethanfrey](https://github.com/ethanfrey)) - packages: support custom queries [\#652](https://github.com/CosmWasm/cw-plus/pull/652) ([uint](https://github.com/uint)) - CW20 - Fix Docs URL [\#649](https://github.com/CosmWasm/cw-plus/pull/649) ([entrancedjames](https://github.com/entrancedjames)) - CW3: Add proposal\_id field to VoteInfo structure [\#648](https://github.com/CosmWasm/cw-plus/pull/648) ([ueco-jb](https://github.com/ueco-jb)) @@ -556,7 +598,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - ics20: Handle send errors with reply [\#520](https://github.com/CosmWasm/cw-plus/pull/520) ([ethanfrey](https://github.com/ethanfrey)) - Proper execute responses [\#519](https://github.com/CosmWasm/cw-plus/pull/519) ([ethanfrey](https://github.com/ethanfrey)) - Publish MsgInstantiate / Execute responses [\#518](https://github.com/CosmWasm/cw-plus/pull/518) ([maurolacy](https://github.com/maurolacy)) -- Fix instaniate reply data [\#517](https://github.com/CosmWasm/cw-plus/pull/517) ([ethanfrey](https://github.com/ethanfrey)) +- Fix instantiate reply data [\#517](https://github.com/CosmWasm/cw-plus/pull/517) ([ethanfrey](https://github.com/ethanfrey)) - Use protobuf de helpers [\#515](https://github.com/CosmWasm/cw-plus/pull/515) ([maurolacy](https://github.com/maurolacy)) - Add tests for the claims controller [\#514](https://github.com/CosmWasm/cw-plus/pull/514) ([sgoya](https://github.com/sgoya)) - Implement cw3-flex-multisig helper [\#479](https://github.com/CosmWasm/cw-plus/pull/479) ([orkunkl](https://github.com/orkunkl)) @@ -575,9 +617,9 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - Prepare 0.10.1 release [\#513](https://github.com/CosmWasm/cw-plus/pull/513) ([ethanfrey](https://github.com/ethanfrey)) - Added cw1-whitelist-ng to CI [\#512](https://github.com/CosmWasm/cw-plus/pull/512) ([hashedone](https://github.com/hashedone)) -- cw1-subkeys-ng: Additional follow up improvements [\#506](https://github.com/CosmWasm/cw-plus/pull/506) ([hashedone](https://github.com/hashedone)) +- cw1-subkeys-ng: Additional follow-up improvements [\#506](https://github.com/CosmWasm/cw-plus/pull/506) ([hashedone](https://github.com/hashedone)) - Parse reply helpers [\#502](https://github.com/CosmWasm/cw-plus/pull/502) ([maurolacy](https://github.com/maurolacy)) -- cw1-whitelist-ng: Contract implementation in terms of semantical structures [\#499](https://github.com/CosmWasm/cw-plus/pull/499) ([hashedone](https://github.com/hashedone)) +- cw1-whitelist-ng: Contract implementation in terms of semantic structures [\#499](https://github.com/CosmWasm/cw-plus/pull/499) ([hashedone](https://github.com/hashedone)) - range\_de for IndexMap [\#498](https://github.com/CosmWasm/cw-plus/pull/498) ([uint](https://github.com/uint)) - Implement range\_de for SnapshotMap [\#497](https://github.com/CosmWasm/cw-plus/pull/497) ([uint](https://github.com/uint)) - Fix publish script [\#486](https://github.com/CosmWasm/cw-plus/pull/486) ([ethanfrey](https://github.com/ethanfrey)) @@ -625,7 +667,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - Release v0.10.0-soon4 [\#477](https://github.com/CosmWasm/cw-plus/pull/477) ([ethanfrey](https://github.com/ethanfrey)) - Update to CosmWasm 1.0.0-soon2 [\#475](https://github.com/CosmWasm/cw-plus/pull/475) ([ethanfrey](https://github.com/ethanfrey)) - Allow error type conversions in ensure! and ensure\_eq! [\#474](https://github.com/CosmWasm/cw-plus/pull/474) ([webmaster128](https://github.com/webmaster128)) -- Improve error handling / remove FIXMEs [\#470](https://github.com/CosmWasm/cw-plus/pull/470) ([maurolacy](https://github.com/maurolacy)) +- Improve error handling / remove FIXME markers [\#470](https://github.com/CosmWasm/cw-plus/pull/470) ([maurolacy](https://github.com/maurolacy)) - Add ensure [\#469](https://github.com/CosmWasm/cw-plus/pull/469) ([ethanfrey](https://github.com/ethanfrey)) - Key deserializer improvements [\#467](https://github.com/CosmWasm/cw-plus/pull/467) ([maurolacy](https://github.com/maurolacy)) - Upgrade to cosmwasm/workspace-optimizer:0.12.3 [\#465](https://github.com/CosmWasm/cw-plus/pull/465) ([webmaster128](https://github.com/webmaster128)) @@ -676,7 +718,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - storage-plus: Improve in-code documentation of map primitives, in particular `MultiIndex` [\#407](https://github.com/CosmWasm/cw-plus/issues/407) - Remove use of dyn in multitest Router [\#404](https://github.com/CosmWasm/cw-plus/issues/404) - Define generic multitest module [\#387](https://github.com/CosmWasm/cw-plus/issues/387) -- Cw20 state key compatibity with previous versions [\#346](https://github.com/CosmWasm/cw-plus/issues/346) +- Cw20 state key compatibility with previous versions [\#346](https://github.com/CosmWasm/cw-plus/issues/346) - Refactor cw20-base to use controller pattern [\#205](https://github.com/CosmWasm/cw-plus/issues/205) **Merged pull requests:** @@ -732,7 +774,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - Snapshot item [\#409](https://github.com/CosmWasm/cw-plus/pull/409) ([maurolacy](https://github.com/maurolacy)) - cw20-base: upgrade helper.ts to cosmjs 0.26.0 [\#406](https://github.com/CosmWasm/cw-plus/pull/406) ([spacepotahto](https://github.com/spacepotahto)) - CW1-whitelist execute multitest [\#402](https://github.com/CosmWasm/cw-plus/pull/402) ([ueco-jb](https://github.com/ueco-jb)) -- Implementing all messages handling in mutlitest App [\#398](https://github.com/CosmWasm/cw-plus/pull/398) ([hashedone](https://github.com/hashedone)) +- Implementing all messages handling in MultiTest App [\#398](https://github.com/CosmWasm/cw-plus/pull/398) ([hashedone](https://github.com/hashedone)) - Make it easier to assert events on reply statements [\#395](https://github.com/CosmWasm/cw-plus/pull/395) ([ethanfrey](https://github.com/ethanfrey)) - Add helpers to check events [\#392](https://github.com/CosmWasm/cw-plus/pull/392) ([ethanfrey](https://github.com/ethanfrey)) - Switching from String to anyhow::Error for error type in multi-test [\#389](https://github.com/CosmWasm/cw-plus/pull/389) ([hashedone](https://github.com/hashedone)) @@ -805,7 +847,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - Responses validation in multi-test [\#373](https://github.com/CosmWasm/cw-plus/pull/373) ([hashedone](https://github.com/hashedone)) - Cw20 logo spec [\#370](https://github.com/CosmWasm/cw-plus/pull/370) ([ethanfrey](https://github.com/ethanfrey)) - Properly handling data in submessages in multi-test [\#369](https://github.com/CosmWasm/cw-plus/pull/369) ([hashedone](https://github.com/hashedone)) -- Abstracting API out of tests internals so it is clearly owned by `App` [\#368](https://github.com/CosmWasm/cw-plus/pull/368) ([hashedone](https://github.com/hashedone)) +- Abstracting API out of tests internals, so it is clearly owned by `App` [\#368](https://github.com/CosmWasm/cw-plus/pull/368) ([hashedone](https://github.com/hashedone)) - Storage plus doc correction [\#367](https://github.com/CosmWasm/cw-plus/pull/367) ([hashedone](https://github.com/hashedone)) - Multitest migrate support [\#366](https://github.com/CosmWasm/cw-plus/pull/366) ([ethanfrey](https://github.com/ethanfrey)) - Reorganizations of contracts in `multi-test::test_utils` [\#365](https://github.com/CosmWasm/cw-plus/pull/365) ([hashedone](https://github.com/hashedone)) @@ -858,7 +900,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - Proper event/data handling on reply in multitest [\#326](https://github.com/CosmWasm/cw-plus/issues/326) - Messages differ for cw20 & cw20\_base [\#320](https://github.com/CosmWasm/cw-plus/issues/320) - Upgrade cw20-staking to cw 15 [\#312](https://github.com/CosmWasm/cw-plus/issues/312) -- Uprade cw20-ics20 to cw 0.15 [\#311](https://github.com/CosmWasm/cw-plus/issues/311) +- Upgrade cw20-ics20 to cw 0.15 [\#311](https://github.com/CosmWasm/cw-plus/issues/311) - Upgrade cw20-escrow to 0.15 [\#309](https://github.com/CosmWasm/cw-plus/issues/309) - Upgrade cw20-bonding to 0.15 [\#307](https://github.com/CosmWasm/cw-plus/issues/307) - cw1-subkeys [\#305](https://github.com/CosmWasm/cw-plus/issues/305) @@ -928,7 +970,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. **Merged pull requests:** - Clarify index\_key\(\) range\(\) vs prefix\(\) behaviour [\#291](https://github.com/CosmWasm/cw-plus/pull/291) ([maurolacy](https://github.com/maurolacy)) -- Pkowned to vec u8 [\#290](https://github.com/CosmWasm/cw-plus/pull/290) ([maurolacy](https://github.com/maurolacy)) +- PkOwned to vec u8 [\#290](https://github.com/CosmWasm/cw-plus/pull/290) ([maurolacy](https://github.com/maurolacy)) - Update to CosmWasm v0.14.0 [\#289](https://github.com/CosmWasm/cw-plus/pull/289) ([ethanfrey](https://github.com/ethanfrey)) - Primary key / index key helpers [\#288](https://github.com/CosmWasm/cw-plus/pull/288) ([maurolacy](https://github.com/maurolacy)) @@ -978,7 +1020,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. **Merged pull requests:** -- Bump dependency to cosmasm v0.14.0-beta3 [\#269](https://github.com/CosmWasm/cw-plus/pull/269) ([ethanfrey](https://github.com/ethanfrey)) +- Bump dependency to cosmwasm v0.14.0-beta3 [\#269](https://github.com/CosmWasm/cw-plus/pull/269) ([ethanfrey](https://github.com/ethanfrey)) - Remove unused PrimaryKey::parse\_key [\#267](https://github.com/CosmWasm/cw-plus/pull/267) ([webmaster128](https://github.com/webmaster128)) - Use workspace-optimizer:0.11.0 [\#262](https://github.com/CosmWasm/cw-plus/pull/262) ([webmaster128](https://github.com/webmaster128)) - Update cosmwasm-std [\#260](https://github.com/CosmWasm/cw-plus/pull/260) ([yihuang](https://github.com/yihuang)) @@ -1089,7 +1131,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - Don't use hooks for snapshotting on cw3-cw4 interface [\#162](https://github.com/CosmWasm/cw-plus/issues/162) - Refactor snapshotting into reusable module [\#161](https://github.com/CosmWasm/cw-plus/issues/161) - Distinguish between weight 0 and not member in cw3 queries [\#154](https://github.com/CosmWasm/cw-plus/issues/154) -- Migrate strorage-plus to v0.12.0 [\#149](https://github.com/CosmWasm/cw-plus/issues/149) +- Migrate storage-plus to v0.12.0 [\#149](https://github.com/CosmWasm/cw-plus/issues/149) - Asymmetries between query and execute in CW1 \(subkeys\) [\#145](https://github.com/CosmWasm/cw-plus/issues/145) - Add token-weighted group [\#142](https://github.com/CosmWasm/cw-plus/issues/142) - Multisig handles changes to group membership [\#141](https://github.com/CosmWasm/cw-plus/issues/141) @@ -1197,7 +1239,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. **Closed issues:** -- Migration to 0.11: errors of shared functions accross contracts [\#103](https://github.com/CosmWasm/cw-plus/issues/103) +- Migration to 0.11: errors of shared functions across contracts [\#103](https://github.com/CosmWasm/cw-plus/issues/103) - Look at serde\(flatten\) to simplify return value composition [\#57](https://github.com/CosmWasm/cw-plus/issues/57) **Merged pull requests:** @@ -1239,7 +1281,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. **Closed issues:** - Implement Copy for Coin / Vec\ [\#77](https://github.com/CosmWasm/cw-plus/issues/77) -- Why does not cw20 pass the received native token? [\#74](https://github.com/CosmWasm/cw-plus/issues/74) +- Why cw20 does not pass the received native token? [\#74](https://github.com/CosmWasm/cw-plus/issues/74) - Cw20Coin duplication [\#73](https://github.com/CosmWasm/cw-plus/issues/73) - Fix docker run script in all contract README [\#69](https://github.com/CosmWasm/cw-plus/issues/69) - Add cw20 support to atomic swap contract [\#27](https://github.com/CosmWasm/cw-plus/issues/27) @@ -1292,7 +1334,7 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - Bump all CosmWasm dependencies to 0.10.1 [\#56](https://github.com/CosmWasm/cw-plus/pull/56) ([ethanfrey](https://github.com/ethanfrey)) - Add new query to return all allowances on subkeys [\#54](https://github.com/CosmWasm/cw-plus/pull/54) ([ethanfrey](https://github.com/ethanfrey)) - Add CanSend query to the cw1 spec [\#53](https://github.com/CosmWasm/cw-plus/pull/53) ([ethanfrey](https://github.com/ethanfrey)) -- Add Expration to cw0 [\#51](https://github.com/CosmWasm/cw-plus/pull/51) ([ethanfrey](https://github.com/ethanfrey)) +- Add expiration to cw0 [\#51](https://github.com/CosmWasm/cw-plus/pull/51) ([ethanfrey](https://github.com/ethanfrey)) - Nft 721 spec [\#50](https://github.com/CosmWasm/cw-plus/pull/50) ([ethanfrey](https://github.com/ethanfrey)) - Add Subkeys helper [\#49](https://github.com/CosmWasm/cw-plus/pull/49) ([ethanfrey](https://github.com/ethanfrey)) - Add helpers to cw20-base [\#46](https://github.com/CosmWasm/cw-plus/pull/46) ([ethanfrey](https://github.com/ethanfrey)) @@ -1348,6 +1390,4 @@ changelog will be noisy - not everything is relevant to `cw-multi-test` there. - Define all Message and Query types [\#11](https://github.com/CosmWasm/cw-plus/pull/11) ([ethanfrey](https://github.com/ethanfrey)) - Set up basic CI script [\#10](https://github.com/CosmWasm/cw-plus/pull/10) ([ethanfrey](https://github.com/ethanfrey)) - - \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* From e2da4b7d24b7fc53dedab63c6ad0d77af940bfe1 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Mon, 8 Apr 2024 19:34:26 +0000 Subject: [PATCH 17/22] Nits --- Cargo.toml | 7 +++---- src/addresses.rs | 4 ++-- src/app.rs | 4 ++-- src/app_builder.rs | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 028b7967..cf0e785a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ serde = "1.0.197" sha2 = "0.10.8" thiserror = "1.0.58" - serde_json = "1.0.40" cosmwasm-schema = "1.3.3" log = "0.4.20" @@ -45,11 +44,11 @@ cw20-ics20 = "1.1.0" hex = "0.4.3" [dev-dependencies] - +hex = "0.4.3" +hex-literal = "0.4.1" +once_cell = "1.19.0" polytone-note = { version = "1.0.0" } polytone-voice = { version = "1.0.0" } polytone-proxy = { version = "1.0.0" } polytone = { version = "1.0.0" } -hex-literal = "0.4.1" -once_cell = "1.19.0" diff --git a/src/addresses.rs b/src/addresses.rs index 5a7d62f4..9cc0542f 100644 --- a/src/addresses.rs +++ b/src/addresses.rs @@ -92,7 +92,7 @@ pub trait AddressGenerator { /// /// ``` /// # use cosmwasm_std::testing::{MockApi, MockStorage}; - /// # use abstract_cw_multi_test::{AddressGenerator, SimpleAddressGenerator}; + /// # use cw_multi_test::{AddressGenerator, SimpleAddressGenerator}; /// # let api = MockApi::default(); /// # let mut storage = MockStorage::default(); /// struct MyAddressGenerator; @@ -136,7 +136,7 @@ pub trait AddressGenerator { /// ``` /// # use cosmwasm_std::Api; /// # use cosmwasm_std::testing::{MockApi, MockStorage}; - /// # use abstract_cw_multi_test::{AddressGenerator, SimpleAddressGenerator}; + /// # use cw_multi_test::{AddressGenerator, SimpleAddressGenerator}; /// # let api = MockApi::default(); /// # let mut storage = MockStorage::default(); /// struct MyAddressGenerator; diff --git a/src/app.rs b/src/app.rs index 64283fd4..fdea634b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -293,7 +293,7 @@ where /// /// ``` /// use cosmwasm_std::Addr; - /// use abstract_cw_multi_test::App; + /// use cw_multi_test::App; /// /// // contract implementation /// mod echo { @@ -301,7 +301,7 @@ where /// # use std::todo; /// # use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError, SubMsg, WasmMsg}; /// # use serde::{Deserialize, Serialize}; - /// # use abstract_cw_multi_test::{Contract, ContractWrapper}; + /// # use cw_multi_test::{Contract, ContractWrapper}; /// # /// # fn instantiate(_: DepsMut, _: Env, _: MessageInfo, _: Empty) -> Result { /// # todo!() diff --git a/src/app_builder.rs b/src/app_builder.rs index 3cc41db3..e272464e 100644 --- a/src/app_builder.rs +++ b/src/app_builder.rs @@ -16,7 +16,7 @@ use std::fmt::Debug; /// /// ``` /// # use cosmwasm_std::Empty; -/// # use abstract_cw_multi_test::{BasicAppBuilder, FailingModule, Module}; +/// # use cw_multi_test::{BasicAppBuilder, FailingModule, Module, no_init}; /// # type MyHandler = FailingModule; /// # type MyExecC = Empty; /// # type MyQueryC = Empty; From dbf511c8404b6a065f923c8589151e15fcd45481 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Tue, 14 May 2024 18:37:05 +0000 Subject: [PATCH 18/22] Compiles but tests --- src/ibc/simple_ibc.rs | 43 +++++++++++++++--------------- src/ibc/state.rs | 2 +- src/ibc/test.rs | 56 +++++++++++++++++++--------------------- src/ibc/test/bank.rs | 3 +++ src/ibc/test/polytone.rs | 6 ++--- src/ibc/test/timeout.rs | 2 ++ src/ibc/types.rs | 2 +- 7 files changed, 58 insertions(+), 56 deletions(-) diff --git a/src/ibc/simple_ibc.rs b/src/ibc/simple_ibc.rs index dd087773..6ff8d09a 100644 --- a/src/ibc/simple_ibc.rs +++ b/src/ibc/simple_ibc.rs @@ -505,7 +505,7 @@ impl IbcSimpleModule { "packet_data", String::from_utf8_lossy(packet.data.as_slice()), ) - .add_attribute("packet_data_hex", hex::encode(packet.data.0.clone())) + .add_attribute("packet_data_hex", hex::encode(packet.data.as_slice())) .add_attribute( "packet_timeout_height", format!("{}-{}", timeout_height.revision, timeout_height.height), @@ -625,7 +625,7 @@ impl IbcSimpleModule { "packet_data", String::from_utf8_lossy(packet.data.as_slice()), ) - .add_attribute("packet_data_hex", hex::encode(packet.data.0.clone())) + .add_attribute("packet_data_hex", hex::encode(packet.data.as_slice())) .add_attribute( "packet_timeout_height", format!("{}-{}", timeout_height.revision, timeout_height.height), @@ -652,9 +652,6 @@ impl IbcSimpleModule { let packet_msg = packet_from_data_and_channel(&packet, &channel_info); - #[cfg(not(feature = "cosmwasm_1_1"))] - let receive_msg = IbcPacketReceiveMsg::new(packet_msg); - #[cfg(feature = "cosmwasm_1_1")] let receive_msg = IbcPacketReceiveMsg::new(packet_msg, Addr::unchecked(RELAYER_ADDR)); // First we send an ibc message on the corresponding module @@ -706,7 +703,7 @@ impl IbcSimpleModule { "packet_data", String::from_utf8_lossy(packet.data.as_slice()), ) - .add_attribute("packet_data_hex", hex::encode(packet.data.0.clone())) + .add_attribute("packet_data_hex", hex::encode(packet.data.as_slice())) .add_attribute( "packet_timeout_height", format!("{}-{}", timeout_height.revision, timeout_height.height), @@ -728,7 +725,7 @@ impl IbcSimpleModule { "packet_data", serde_json::to_value(&packet.data)?.to_string(), ) - .add_attribute("packet_data_hex", hex::encode(packet.data.0)) + .add_attribute("packet_data_hex", hex::encode(packet.data.as_slice())) .add_attribute( "packet_timeout_height", format!("{}-{}", timeout_height.revision, timeout_height.height), @@ -741,9 +738,18 @@ impl IbcSimpleModule { .add_attribute("packet_dst_channel", packet.dst_channel_id) .add_attribute( "packet_ack", - String::from_utf8_lossy(acknowledgement.as_slice()), + acknowledgement + .as_ref() + .map(|a| String::from_utf8_lossy(a.as_slice()).to_string()) + .unwrap_or("".to_string()), ) - .add_attribute("packet_ack_hex", hex::encode(acknowledgement.0)); + .add_attribute( + "packet_ack_hex", + acknowledgement + .as_ref() + .map(|a| hex::encode(a.as_slice())) + .unwrap_or("".to_string()), + ); events.push(recv_event); events.push(ack_event); @@ -811,9 +817,6 @@ impl IbcSimpleModule { let acknowledgement = IbcAcknowledgement::new(ack); let original_packet = packet_from_data_and_channel(&packet_data, &channel_info); - #[cfg(not(feature = "cosmwasm_1_1"))] - let ack_message = IbcPacketAckMsg::new(acknowledgement, original_packet); - #[cfg(feature = "cosmwasm_1_1")] let ack_message = IbcPacketAckMsg::new( acknowledgement, original_packet, @@ -931,9 +934,6 @@ impl IbcSimpleModule { let original_packet = packet_from_data_and_channel(&packet_data, &channel_info); - #[cfg(not(feature = "cosmwasm_1_1"))] - let timeout_message = IbcPacketTimeoutMsg::new(original_packet); - #[cfg(feature = "cosmwasm_1_1")] let timeout_message = IbcPacketTimeoutMsg::new(original_packet, Addr::unchecked(RELAYER_ADDR)); @@ -1058,6 +1058,7 @@ impl Module for IbcSimpleModule { to_address, amount, timeout, + memo, } => self.transfer( api, storage, router, block, sender, channel_id, to_address, amount, timeout, ), @@ -1186,9 +1187,9 @@ impl Module for IbcSimpleModule { let channel_info = CHANNEL_INFO.may_load(&ibc_storage, (port_id, channel_id.clone()))?; - Ok(to_json_binary(&ChannelResponse { - channel: channel_info.map(|c| c.info), - })?) + Ok(to_json_binary(&ChannelResponse::new( + channel_info.map(|c| c.info), + ))?) } IbcQuery::ListChannels { port_id } => { // Port_id has to be specified here, unfortunately we can't access the contract address @@ -1199,9 +1200,9 @@ impl Module for IbcSimpleModule { .range(&ibc_storage, None, None, Order::Ascending) .collect::, _>>()?; - Ok(to_json_binary(&ListChannelsResponse { - channels: channels.iter().map(|c| c.1.info.clone()).collect(), - })?) + Ok(to_json_binary(&ListChannelsResponse::new( + channels.iter().map(|c| c.1.info.clone()).collect(), + ))?) } _ => bail!("Query not available"), } diff --git a/src/ibc/state.rs b/src/ibc/state.rs index 69ecbcca..e1f868f6 100644 --- a/src/ibc/state.rs +++ b/src/ibc/state.rs @@ -20,7 +20,7 @@ impl<'a> IndexList for ConnectionIndexes<'a> { } } -pub fn ibc_connections<'a>() -> IndexedMap<'a, &'a str, Connection, ConnectionIndexes<'a>> { +pub fn ibc_connections<'a>() -> IndexedMap<&'a str, Connection, ConnectionIndexes<'a>> { let indexes = ConnectionIndexes { chain_id: MultiIndex::new( |_, d: &Connection| d.counterparty_chain_id.clone(), diff --git a/src/ibc/test.rs b/src/ibc/test.rs index 2f212c5f..6faa8711 100644 --- a/src/ibc/test.rs +++ b/src/ibc/test.rs @@ -64,21 +64,19 @@ fn channel_creation() -> anyhow::Result<()> { assert_eq!( channel, - ChannelResponse { - channel: Some(IbcChannel::new( - IbcEndpoint { - port_id: src_port.clone(), - channel_id: src_channel.clone() - }, - IbcEndpoint { - port_id: dst_port.clone(), - channel_id: dst_channel.clone() - }, - order.clone(), - version.clone(), - "connection-0" - )) - } + ChannelResponse::new(Some(IbcChannel::new( + IbcEndpoint { + port_id: src_port.clone(), + channel_id: src_channel.clone() + }, + IbcEndpoint { + port_id: dst_port.clone(), + channel_id: dst_channel.clone() + }, + order.clone(), + version.clone(), + "connection-0" + ))) ); let channel_query = app2 @@ -96,21 +94,19 @@ fn channel_creation() -> anyhow::Result<()> { assert_eq!( channel, - ChannelResponse { - channel: Some(IbcChannel::new( - IbcEndpoint { - port_id: dst_port.clone(), - channel_id: dst_channel.clone() - }, - IbcEndpoint { - port_id: src_port.clone(), - channel_id: src_channel.clone() - }, - order, - version, - "connection-0" - )) - } + ChannelResponse::new(Some(IbcChannel::new( + IbcEndpoint { + port_id: dst_port.clone(), + channel_id: dst_channel.clone() + }, + IbcEndpoint { + port_id: src_port.clone(), + channel_id: src_channel.clone() + }, + order, + version, + "connection-0" + ))) ); Ok(()) diff --git a/src/ibc/test/bank.rs b/src/ibc/test/bank.rs index 3fe58c9f..531ccdf7 100644 --- a/src/ibc/test/bank.rs +++ b/src/ibc/test/bank.rs @@ -69,6 +69,7 @@ fn simple_transfer() -> anyhow::Result<()> { revision: 1, height: app2.block_info().height + 1, }), + memo: None, }), )?; @@ -165,6 +166,7 @@ fn transfer_and_back() -> anyhow::Result<()> { revision: 1, height: app2.block_info().height + 1, }), + memo: None, }), )?; @@ -201,6 +203,7 @@ fn transfer_and_back() -> anyhow::Result<()> { revision: 1, height: app2.block_info().height + 100, }), + memo: None, }), )?; diff --git a/src/ibc/test/polytone.rs b/src/ibc/test/polytone.rs index 071d542a..9173f005 100644 --- a/src/ibc/test/polytone.rs +++ b/src/ibc/test/polytone.rs @@ -8,7 +8,7 @@ use crate::{ relayer::{create_channel, create_connection, get_event_attr_value, relay_packets_in_tx}, simple_ibc::IbcSimpleModule, }, - AppBuilder, ContractWrapper, Executor, FailingModule, MockAddressGenerator, MockApiBech32, + AppBuilder, ContractWrapper, Executor, FailingModule, MockApiBech32, SimpleAddressGenerator, WasmKeeper, }; @@ -195,9 +195,9 @@ fn polytone() -> anyhow::Result<()> { // prepare wasm module with custom address generator let wasm_keeper_1: WasmKeeper = - WasmKeeper::new().with_address_generator(MockAddressGenerator); + WasmKeeper::new().with_address_generator(SimpleAddressGenerator); let wasm_keeper_2: WasmKeeper = - WasmKeeper::new().with_address_generator(MockAddressGenerator); + WasmKeeper::new().with_address_generator(SimpleAddressGenerator); // We mint some funds to the owner let mut app1 = AppBuilder::default() .with_ibc(IbcSimpleModule) diff --git a/src/ibc/test/timeout.rs b/src/ibc/test/timeout.rs index f5eb7691..4479a5a0 100644 --- a/src/ibc/test/timeout.rs +++ b/src/ibc/test/timeout.rs @@ -66,6 +66,7 @@ fn simple_transfer_timeout() -> anyhow::Result<()> { revision: 1, height: app2.block_info().height, // this will have the effect of a timeout when relaying the packets }), + memo: None, }), )?; @@ -181,6 +182,7 @@ fn simple_transfer_timeout_closes_channel() -> anyhow::Result<()> { revision: 1, height: app2.block_info().height, // this will have the effect of a timeout when relaying the packets }), + memo: None, }), )?; diff --git a/src/ibc/types.rs b/src/ibc/types.rs index a2b87177..2ab3f114 100644 --- a/src/ibc/types.rs +++ b/src/ibc/types.rs @@ -126,7 +126,7 @@ pub struct IbcPacketReceived { #[cosmwasm_schema::cw_serde] pub struct IbcPacketAck { - pub ack: Binary, + pub ack: Option, } /// This is a custom msg that is used for executing actions on the IBC module From 8e6642b7810b9070fde721e854f59665e8992c45 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 13 Jun 2024 14:12:34 +0000 Subject: [PATCH 19/22] Nits --- CHANGELOG.md | 3 - Cargo.lock | 244 ++++++--------------------------------------------- Cargo.toml | 7 +- 3 files changed, 29 insertions(+), 225 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01695cad..c2758683 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,5 @@ # Changelog -<<<<<<< HEAD -======= ## [v2.0.1](https://github.com/CosmWasm/cw-multi-test/tree/v2.0.1) (2024-04-22) [Full Changelog](https://github.com/CosmWasm/cw-multi-test/compare/v2.0.0...v2.0.1) @@ -31,7 +29,6 @@ - V2: upgrading dependencies and refactoring [\#128](https://github.com/CosmWasm/cw-multi-test/pull/128) ([DariuszDepta](https://github.com/DariuszDepta)) ->>>>>>> cosmwasm/main ## [v1.0.0](https://github.com/CosmWasm/cw-multi-test/tree/v1.0.0) (2024-03-22) [Full Changelog](https://github.com/CosmWasm/cw-multi-test/compare/v0.20.1...v1.0.0) diff --git a/Cargo.lock b/Cargo.lock index 1b0ce9a1..90b5556f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,12 +100,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bnum" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" - [[package]] name = "bnum" version = "0.10.0" @@ -142,20 +136,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "cosmwasm-crypto" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6aa9f904de106fa16443ad14ec2abe75e94ba003bb61c681c0e43d4c58d2a" -dependencies = [ - "digest 0.10.7", - "ecdsa", - "ed25519-zebra", - "k256", - "rand_core 0.6.4", - "thiserror", -] - [[package]] name = "cosmwasm-crypto" version = "2.0.4" @@ -169,15 +149,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cosmwasm-derive" -version = "1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "242e98e7a231c122e08f300d9db3262d1007b51758a8732cd6210b3e9faa4f3a" -dependencies = [ - "syn 1.0.109", -] - [[package]] name = "cosmwasm-derive" version = "2.0.4" @@ -187,43 +158,19 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "cosmwasm-schema" -version = "1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7879036156092ad1c22fe0d7316efc5a5eceec2bc3906462a2560215f2a2f929" -dependencies = [ - "cosmwasm-schema-derive 1.5.5", - "schemars", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "cosmwasm-schema" version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d403dea1175a5b20fd2d29dda180fa9f1391dd46f354a8639391d1e549a99e5e" dependencies = [ - "cosmwasm-schema-derive 2.0.3", + "cosmwasm-schema-derive", "schemars", "serde", "serde_json", "thiserror", ] -[[package]] -name = "cosmwasm-schema-derive" -version = "1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb57855fbfc83327f8445ae0d413b1a05ac0d68c396ab4d122b2abd7bb82cb6" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "cosmwasm-schema-derive" version = "2.0.3" @@ -235,28 +182,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "cosmwasm-std" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad011ae7447188e26e4a7dbca2fcd0fc186aa21ae5c86df0503ea44c78f9e469" -dependencies = [ - "base64", - "bech32 0.9.1", - "bnum 0.8.1", - "cosmwasm-crypto 1.5.2", - "cosmwasm-derive 1.5.5", - "derivative", - "forward_ref", - "hex", - "schemars", - "serde", - "serde-json-wasm 0.5.2", - "sha2 0.10.8", - "static_assertions", - "thiserror", -] - [[package]] name = "cosmwasm-std" version = "2.0.4" @@ -265,15 +190,15 @@ checksum = "ded932165de44cd0717979c34fc3b84d8e8066b8dde4f5bd78f96a643b090f90" dependencies = [ "base64", "bech32 0.9.1", - "bnum 0.10.0", - "cosmwasm-crypto 2.0.4", - "cosmwasm-derive 2.0.4", + "bnum", + "cosmwasm-crypto", + "cosmwasm-derive", "derivative", "forward_ref", "hex", "schemars", "serde", - "serde-json-wasm 1.0.1", + "serde-json-wasm", "sha2 0.10.8", "static_assertions", "thiserror", @@ -329,10 +254,10 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50c1804013d21060b994dea28a080f9eab78a3bcb6b617f05e7634b0600bf7b1" dependencies = [ - "cosmwasm-schema 2.0.3", - "cosmwasm-std 2.0.4", - "cw-storage-plus 2.0.0", - "cw-utils 2.0.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", "schemars", "serde", "thiserror", @@ -340,14 +265,14 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "bech32 0.11.0", - "cosmwasm-schema 2.0.3", - "cosmwasm-std 2.0.4", - "cw-storage-plus 2.0.0", - "cw-utils 2.0.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", "cw20-ics20", "derivative", "hex", @@ -355,10 +280,6 @@ dependencies = [ "itertools 0.13.0", "log", "once_cell", - "polytone", - "polytone-note", - "polytone-proxy", - "polytone-voice", "prost", "schemars", "serde", @@ -367,41 +288,15 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cw-storage-plus" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" -dependencies = [ - "cosmwasm-std 1.5.2", - "schemars", - "serde", -] - [[package]] name = "cw-storage-plus" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f13360e9007f51998d42b1bc6b7fa0141f74feae61ed5fd1e5b0a89eec7b5de1" dependencies = [ - "cosmwasm-std 2.0.4", - "schemars", - "serde", -] - -[[package]] -name = "cw-utils" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" -dependencies = [ - "cosmwasm-schema 1.5.5", - "cosmwasm-std 1.5.2", - "cw2 1.1.2", + "cosmwasm-std", "schemars", - "semver", "serde", - "thiserror", ] [[package]] @@ -410,37 +305,22 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07dfee7f12f802431a856984a32bce1cb7da1e6c006b5409e3981035ce562dec" dependencies = [ - "cosmwasm-schema 2.0.3", - "cosmwasm-std 2.0.4", + "cosmwasm-schema", + "cosmwasm-std", "schemars", "serde", "thiserror", ] -[[package]] -name = "cw2" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" -dependencies = [ - "cosmwasm-schema 1.5.5", - "cosmwasm-std 1.5.2", - "cw-storage-plus 1.2.0", - "schemars", - "semver", - "serde", - "thiserror", -] - [[package]] name = "cw2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b04852cd38f044c0751259d5f78255d07590d136b8a86d4e09efdd7666bd6d27" dependencies = [ - "cosmwasm-schema 2.0.3", - "cosmwasm-std 2.0.4", - "cw-storage-plus 2.0.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", "schemars", "semver", "serde", @@ -453,9 +333,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a42212b6bf29bbdda693743697c621894723f35d3db0d5df930be22903d0e27c" dependencies = [ - "cosmwasm-schema 2.0.3", - "cosmwasm-std 2.0.4", - "cw-utils 2.0.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", "schemars", "serde", ] @@ -466,12 +346,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80a9e377dbbd1ffb3b6a8a2dbf9128609a6458a3292f88f99e0b6840a7e9762e" dependencies = [ - "cosmwasm-schema 2.0.3", - "cosmwasm-std 2.0.4", + "cosmwasm-schema", + "cosmwasm-std", "cw-controllers", - "cw-storage-plus 2.0.0", - "cw-utils 2.0.0", - "cw2 2.0.0", + "cw-storage-plus", + "cw-utils", + "cw2", "cw20", "schemars", "semver", @@ -762,65 +642,6 @@ dependencies = [ "spki", ] -[[package]] -name = "polytone" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f16d20da9144fdf0658e785fc9108b86cecee517335ff531745029dd56088" -dependencies = [ - "cosmwasm-schema 1.5.5", - "cosmwasm-std 1.5.2", - "cw-storage-plus 1.2.0", - "thiserror", -] - -[[package]] -name = "polytone-note" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc85da4f72e5a3837b5fbc6a494702e2b7e7b5f695cef56a223216fbf9de3c9" -dependencies = [ - "cosmwasm-schema 1.5.5", - "cosmwasm-std 1.5.2", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "polytone", - "thiserror", -] - -[[package]] -name = "polytone-proxy" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b373674cd2345c0f646c11bccfbeed78214959a56cd3224d80618b07e73b1b4" -dependencies = [ - "cosmwasm-schema 1.5.5", - "cosmwasm-std 1.5.2", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "polytone", - "thiserror", -] - -[[package]] -name = "polytone-voice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34e4ce7a7816207baacb9137cfd5f102c2e6160641d6699fe5d798dc513e6b09" -dependencies = [ - "cosmwasm-schema 1.5.5", - "cosmwasm-std 1.5.2", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", - "polytone", - "polytone-proxy", - "sha2 0.10.8", - "thiserror", -] - [[package]] name = "proc-macro2" version = "1.0.81" @@ -952,15 +773,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-json-wasm" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9213a07d53faa0b8dd81e767a54a8188a242fdb9be99ab75ec576a774bfdd7" -dependencies = [ - "serde", -] - [[package]] name = "serde-json-wasm" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 1590efc0..ec60a139 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-multi-test" -version = "2.0.0" +version = "2.0.1" authors = [ "Ethan Frey ", "Dariusz Depta ", @@ -44,8 +44,3 @@ hex = "0.4.3" hex = "0.4.3" hex-literal = "0.4.1" once_cell = "1.19.0" - -polytone-note = { version = "1.0.0" } -polytone-voice = { version = "1.0.0" } -polytone-proxy = { version = "1.0.0" } -polytone = { version = "1.0.0" } From b3adddc4cda1fc16573954f6977cbbd98e7e632c Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 13 Jun 2024 14:19:20 +0000 Subject: [PATCH 20/22] Nits clippy --- src/ibc/relayer/channel.rs | 6 +++--- src/ibc/simple_ibc.rs | 23 ++++++++++------------- src/wasm.rs | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/ibc/relayer/channel.rs b/src/ibc/relayer/channel.rs index d348ad4d..8639f967 100644 --- a/src/ibc/relayer/channel.rs +++ b/src/ibc/relayer/channel.rs @@ -178,7 +178,7 @@ where let ibc_try_msg = IbcPacketRelayingMsg::OpenChannel { local_connection_id: counterparty.counterparty_connection_id.unwrap(), local_port: dst_port.clone(), - version: version.clone(), + version, order, counterparty_version: Some(new_version), counterparty_endpoint: IbcEndpoint { @@ -209,11 +209,11 @@ where log::debug!("Channel ack {:?}", ack_response); let ibc_ack_msg = IbcPacketRelayingMsg::ConnectChannel { - port_id: dst_port.clone(), + port_id: dst_port, channel_id: dst_channel.clone(), counterparty_version: Some(new_version), counterparty_endpoint: IbcEndpoint { - port_id: src_port.clone(), + port_id: src_port, channel_id: src_channel.clone(), }, }; diff --git a/src/ibc/simple_ibc.rs b/src/ibc/simple_ibc.rs index 481edd6e..3613d653 100644 --- a/src/ibc/simple_ibc.rs +++ b/src/ibc/simple_ibc.rs @@ -171,7 +171,7 @@ impl IbcSimpleModule { let channel = IbcChannel::new( local_endpoint.clone(), counterparty_endpoint.clone(), - order.clone(), + order, version.clone(), local_connection_id.clone(), ); @@ -198,11 +198,8 @@ impl IbcSimpleModule { ibc_event = ibc_event .add_attribute("port_id", local_endpoint.port_id) .add_attribute("channel_id", local_endpoint.channel_id) - .add_attribute( - "counterparty_port_id", - counterparty_endpoint.clone().port_id, - ) - .add_attribute("counterparty_channel_id", "".to_string()) + .add_attribute("counterparty_port_id", counterparty_endpoint.port_id) + .add_attribute("counterparty_channel_id", "") .add_attribute("connection_id", local_connection_id); // First we send an ibc message on the wasm module in cache @@ -326,8 +323,8 @@ impl IbcSimpleModule { last_packet_relayed: 1, info: IbcChannel::new( IbcEndpoint { - port_id: port_id.clone(), - channel_id: channel_id.clone(), + port_id, + channel_id, }, IbcEndpoint { port_id: channel_handshake.remote_endpoint.port_id.clone(), @@ -427,7 +424,7 @@ impl IbcSimpleModule { "counterparty_channel_id", channel_info.info.counterparty_endpoint.channel_id.clone(), ) - .add_attribute("connection_id", channel_info.info.connection_id.clone()); + .add_attribute("connection_id", channel_info.info.connection_id); // Then we send an ibc message on the corresponding module in cache let res = transactional(storage, |write_cache, _| { @@ -486,7 +483,7 @@ impl IbcSimpleModule { channel_id.clone(), channel_info.next_packet_id, ), - &packet.clone(), + &packet, )?; // Incrementing the packet sequence @@ -859,7 +856,7 @@ impl IbcSimpleModule { .add_attribute("packet_src_port", packet.src_port_id.clone()) .add_attribute("packet_src_channel", packet.src_channel_id.clone()) .add_attribute("packet_dst_port", packet.dst_port_id.clone()) - .add_attribute("packet_dst_channel", packet.dst_channel_id.clone()) + .add_attribute("packet_dst_channel", packet.dst_channel_id) .add_attribute( "packet_channel_ordering", serde_json::to_value(channel_info.info.order)?.to_string(), @@ -975,7 +972,7 @@ impl IbcSimpleModule { .add_attribute("packet_src_port", packet.src_port_id.clone()) .add_attribute("packet_src_channel", packet.src_channel_id.clone()) .add_attribute("packet_dst_port", packet.dst_port_id.clone()) - .add_attribute("packet_dst_channel", packet.dst_channel_id.clone()) + .add_attribute("packet_dst_channel", packet.dst_channel_id) .add_attribute( "packet_channel_ordering", serde_json::to_value(channel_info.info.order)?.to_string(), @@ -1186,7 +1183,7 @@ impl Module for IbcSimpleModule { let port_id = port_id.unwrap(); // We load the channel of the port let channel_info = - CHANNEL_INFO.may_load(&ibc_storage, (port_id, channel_id.clone()))?; + CHANNEL_INFO.may_load(&ibc_storage, (port_id, channel_id))?; Ok(to_json_binary(&ChannelResponse::new( channel_info.map(|c| c.info), diff --git a/src/wasm.rs b/src/wasm.rs index 3f6a5251..ef5eb876 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -380,7 +380,7 @@ where storage, router, block, - contract.clone(), + contract, |contract, deps, env| contract.ibc_channel_open(deps, env, request), )?; From a7e01922910e628e7a304912c958ca8098c979f4 Mon Sep 17 00:00:00 2001 From: Kayanski Date: Thu, 13 Jun 2024 14:22:09 +0000 Subject: [PATCH 21/22] Nits clippy --- tests/test_ibc/bank.rs | 6 +++--- tests/test_ibc/mod.rs | 8 ++++---- tests/test_ibc/timeout.rs | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_ibc/bank.rs b/tests/test_ibc/bank.rs index 7536ae87..31e0c2a9 100644 --- a/tests/test_ibc/bank.rs +++ b/tests/test_ibc/bank.rs @@ -52,7 +52,7 @@ fn simple_transfer() -> anyhow::Result<()> { &mut app1, &mut app2, src_connection_id, - port1.clone(), + port1, port2, "ics20-1".to_string(), IbcOrder::Ordered, @@ -150,7 +150,7 @@ fn transfer_and_back() -> anyhow::Result<()> { &mut app1, &mut app2, src_connection_id, - port1.clone(), + port1, port2, "ics20-1".to_string(), IbcOrder::Ordered, @@ -199,7 +199,7 @@ fn transfer_and_back() -> anyhow::Result<()> { CosmosMsg::Ibc(IbcMsg::Transfer { channel_id: dst_channel, to_address: fund_owner.to_string(), - amount: chain2_funds.clone(), + amount: chain2_funds, timeout: IbcTimeout::with_block(IbcTimeoutBlock { revision: 1, height: app2.block_info().height + 100, diff --git a/tests/test_ibc/mod.rs b/tests/test_ibc/mod.rs index a376057b..9fce6769 100644 --- a/tests/test_ibc/mod.rs +++ b/tests/test_ibc/mod.rs @@ -96,12 +96,12 @@ fn channel_creation() -> anyhow::Result<()> { channel, ChannelResponse::new(Some(IbcChannel::new( IbcEndpoint { - port_id: dst_port.clone(), - channel_id: dst_channel.clone() + port_id: dst_port, + channel_id: dst_channel }, IbcEndpoint { - port_id: src_port.clone(), - channel_id: src_channel.clone() + port_id: src_port, + channel_id: src_channel }, order, version, diff --git a/tests/test_ibc/timeout.rs b/tests/test_ibc/timeout.rs index ba9641c3..b15af3b7 100644 --- a/tests/test_ibc/timeout.rs +++ b/tests/test_ibc/timeout.rs @@ -50,7 +50,7 @@ fn simple_transfer_timeout() -> anyhow::Result<()> { &mut app1, &mut app2, src_connection_id, - port1.clone(), + port1, port2, "ics20-1".to_string(), IbcOrder::Ordered, @@ -225,8 +225,8 @@ fn simple_transfer_timeout_closes_channel() -> anyhow::Result<()> { assert!(!channel_info.open); // We make sure the channel is closed let channel_info: ChannelInfo = from_json(app2.ibc_query(MockIbcQuery::ChannelInfo { - port_id: port2.clone(), - channel_id: dst_channel.clone(), + port_id: port2, + channel_id: dst_channel, })?)?; assert!(!channel_info.open); From 00f5f76c37b72cfcedda1e8e7530ba23e57c3d54 Mon Sep 17 00:00:00 2001 From: Kayanski <44806566+Kayanski@users.noreply.github.com> Date: Tue, 18 Jun 2024 11:45:14 +0200 Subject: [PATCH 22/22] Update src/ibc.rs Co-authored-by: Christoph Otter --- src/ibc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ibc.rs b/src/ibc.rs index 83811efc..bdbf321e 100644 --- a/src/ibc.rs +++ b/src/ibc.rs @@ -17,7 +17,7 @@ use self::types::MockIbcQuery; pub use simple_ibc::IbcSimpleModule; /// This is added for modules to implement actions upon ibc actions. -/// This kind of execution flow is copied from the WASM way of doing things and is not 100% completetely compatible with the IBC standard +/// This kind of execution flow is copied from the WASM way of doing things and is not 100% completely compatible with the IBC standard /// Those messages should only be called by the Ibc module. /// For additional Modules, the packet endpoints should be implemented /// The Channel endpoints are usually not implemented besides storing the channel ids