From 104040e3c05a55ebe0a96659911bd1af6413d1b5 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sat, 21 Oct 2023 21:40:30 +0300 Subject: [PATCH 01/46] feat(owner): generated new contract from template --- testing/contracts/cw-ica-owner/.cargo/config | 4 + testing/contracts/cw-ica-owner/.editorconfig | 11 + testing/contracts/cw-ica-owner/.gitignore | 20 + testing/contracts/cw-ica-owner/Cargo.lock | 938 ++++++++++++++++++ testing/contracts/cw-ica-owner/Cargo.toml | 56 ++ .../contracts/cw-ica-owner/src/bin/schema.rs | 11 + .../contracts/cw-ica-owner/src/contract.rs | 41 + testing/contracts/cw-ica-owner/src/error.rs | 13 + testing/contracts/cw-ica-owner/src/helpers.rs | 27 + testing/contracts/cw-ica-owner/src/lib.rs | 7 + testing/contracts/cw-ica-owner/src/msg.rs | 11 + testing/contracts/cw-ica-owner/src/state.rs | 1 + 12 files changed, 1140 insertions(+) create mode 100644 testing/contracts/cw-ica-owner/.cargo/config create mode 100644 testing/contracts/cw-ica-owner/.editorconfig create mode 100644 testing/contracts/cw-ica-owner/.gitignore create mode 100644 testing/contracts/cw-ica-owner/Cargo.lock create mode 100644 testing/contracts/cw-ica-owner/Cargo.toml create mode 100644 testing/contracts/cw-ica-owner/src/bin/schema.rs create mode 100644 testing/contracts/cw-ica-owner/src/contract.rs create mode 100644 testing/contracts/cw-ica-owner/src/error.rs create mode 100644 testing/contracts/cw-ica-owner/src/helpers.rs create mode 100644 testing/contracts/cw-ica-owner/src/lib.rs create mode 100644 testing/contracts/cw-ica-owner/src/msg.rs create mode 100644 testing/contracts/cw-ica-owner/src/state.rs diff --git a/testing/contracts/cw-ica-owner/.cargo/config b/testing/contracts/cw-ica-owner/.cargo/config new file mode 100644 index 00000000..af5698e5 --- /dev/null +++ b/testing/contracts/cw-ica-owner/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema" diff --git a/testing/contracts/cw-ica-owner/.editorconfig b/testing/contracts/cw-ica-owner/.editorconfig new file mode 100644 index 00000000..3d36f20b --- /dev/null +++ b/testing/contracts/cw-ica-owner/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.rs] +indent_size = 4 diff --git a/testing/contracts/cw-ica-owner/.gitignore b/testing/contracts/cw-ica-owner/.gitignore new file mode 100644 index 00000000..9929cc18 --- /dev/null +++ b/testing/contracts/cw-ica-owner/.gitignore @@ -0,0 +1,20 @@ +# Build results +/target +/schema +/artifacts + +# Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) +.cargo-ok + +# Text file backups +**/*.rs.bk + +# macOS +.DS_Store + +# IDEs +*.iml +.idea + +# e2e tests +vendor diff --git a/testing/contracts/cw-ica-owner/Cargo.lock b/testing/contracts/cw-ica-owner/Cargo.lock new file mode 100644 index 00000000..7e237457 --- /dev/null +++ b/testing/contracts/cw-ica-owner/Cargo.lock @@ -0,0 +1,938 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bnum" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128a44527fc0d6abf05f9eda748b9027536e12dff93f5acc8449f51583309350" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "cosmwasm-crypto" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6fb22494cf7d23d0c348740e06e5c742070b2991fd41db77bba0bcfbae1a723" +dependencies = [ + "digest 0.10.7", + "ed25519-zebra", + "k256 0.13.1", + "rand_core 0.6.4", + "thiserror", +] + +[[package]] +name = "cosmwasm-derive" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e199424486ea97d6b211db6387fd72e26b4a439d40cc23140b2d8305728055b" +dependencies = [ + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-schema" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef683a9c1c4eabd6d31515719d0d2cc66952c4c87f7eb192bfc90384517dc34" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9567025acbb4c0c008178393eb53b3ac3c2e492c25949d3bf415b9cbe80772d8" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-std" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d89d680fb60439b7c5947b15f9c84b961b88d1f8a3b20c4bd178a3f87db8bae" +dependencies = [ + "base64", + "bnum", + "cosmwasm-crypto", + "cosmwasm-derive", + "derivative", + "forward_ref", + "hex", + "schemars", + "serde", + "serde-json-wasm", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "cpufeatures" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cw-ica-owner" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "cw-storage-plus", + "cw2", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-multi-test" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "127c7bb95853b8e828bdab97065c81cb5ddc20f7339180b61b2300565aaa99d1" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "derivative", + "itertools", + "k256 0.11.6", + "prost", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-storage-plus" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "cw-utils" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9f351a4e4d81ef7c890e44d903f8c0bdcdc00f094fd3a181eaf70c0eec7a3a" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9431d14f64f49e41c6ef5561ed11a5391c417d0cb16455dea8cdcb9037a8d197" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.6", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek", + "hashbrown", + "hex", + "rand_core 0.6.4", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array", + "group 0.12.1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.3", + "digest 0.10.7", + "ff 0.13.0", + "generic-array", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa 0.16.8", + "elliptic-curve 0.13.6", + "once_cell", + "sha2 0.10.8", + "signature 2.1.0", +] + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki 0.7.2", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schemars" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.8", + "generic-array", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.8", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/testing/contracts/cw-ica-owner/Cargo.toml b/testing/contracts/cw-ica-owner/Cargo.toml new file mode 100644 index 00000000..48d5ab6d --- /dev/null +++ b/testing/contracts/cw-ica-owner/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "cw-ica-owner" +version = "0.1.0" +authors = ["srdtrk "] +edition = "2021" + +exclude = [ + # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. + "contract.wasm", + "hash.txt", +] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +opt-level = 3 +debug = false +rpath = false +lto = true +debug-assertions = false +codegen-units = 1 +panic = 'abort' +incremental = false +overflow-checks = true + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[package.metadata.scripts] +optimize = """docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.14.0 +""" + +[dependencies] +cosmwasm-schema = "1.4.0" +cosmwasm-std = { version = "1.4.0", features = [ + "cosmwasm_1_3", + # Enable this if you only deploy to chains that have CosmWasm 1.4 or higher + # "cosmwasm_1_4", +] } +cw-storage-plus = "1.1.0" +cw2 = "1.1.0" +schemars = "0.8.10" +serde = { version = "1.0.145", default-features = false, features = ["derive"] } +thiserror = { version = "1.0.31" } + +[dev-dependencies] +cw-multi-test = "0.16.5" diff --git a/testing/contracts/cw-ica-owner/src/bin/schema.rs b/testing/contracts/cw-ica-owner/src/bin/schema.rs new file mode 100644 index 00000000..45fae17d --- /dev/null +++ b/testing/contracts/cw-ica-owner/src/bin/schema.rs @@ -0,0 +1,11 @@ +use cosmwasm_schema::write_api; + +use cw_ica_owner::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg, + } +} diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs new file mode 100644 index 00000000..5f7ad994 --- /dev/null +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -0,0 +1,41 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +// use cw2::set_contract_version; + +use crate::error::ContractError; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +/* +// version info for migration info +const CONTRACT_NAME: &str = "crates.io:cw-ica-owner"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +*/ + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: InstantiateMsg, +) -> Result { + unimplemented!() +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: ExecuteMsg, +) -> Result { + unimplemented!() +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { + unimplemented!() +} + +#[cfg(test)] +mod tests {} diff --git a/testing/contracts/cw-ica-owner/src/error.rs b/testing/contracts/cw-ica-owner/src/error.rs new file mode 100644 index 00000000..4a69d8ff --- /dev/null +++ b/testing/contracts/cw-ica-owner/src/error.rs @@ -0,0 +1,13 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Unauthorized")] + Unauthorized {}, + // Add any other custom errors you like here. + // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. +} diff --git a/testing/contracts/cw-ica-owner/src/helpers.rs b/testing/contracts/cw-ica-owner/src/helpers.rs new file mode 100644 index 00000000..a39b1558 --- /dev/null +++ b/testing/contracts/cw-ica-owner/src/helpers.rs @@ -0,0 +1,27 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; + +use crate::msg::ExecuteMsg; + +/// CwTemplateContract is a wrapper around Addr that provides a lot of helpers +/// for working with this. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct CwTemplateContract(pub Addr); + +impl CwTemplateContract { + pub fn addr(&self) -> Addr { + self.0.clone() + } + + pub fn call>(&self, msg: T) -> StdResult { + let msg = to_binary(&msg.into())?; + Ok(WasmMsg::Execute { + contract_addr: self.addr().into(), + msg, + funds: vec![], + } + .into()) + } +} diff --git a/testing/contracts/cw-ica-owner/src/lib.rs b/testing/contracts/cw-ica-owner/src/lib.rs new file mode 100644 index 00000000..233dbf55 --- /dev/null +++ b/testing/contracts/cw-ica-owner/src/lib.rs @@ -0,0 +1,7 @@ +pub mod contract; +mod error; +pub mod helpers; +pub mod msg; +pub mod state; + +pub use crate::error::ContractError; diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs new file mode 100644 index 00000000..3b6b9b27 --- /dev/null +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -0,0 +1,11 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; + +#[cw_serde] +pub struct InstantiateMsg {} + +#[cw_serde] +pub enum ExecuteMsg {} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg {} diff --git a/testing/contracts/cw-ica-owner/src/state.rs b/testing/contracts/cw-ica-owner/src/state.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/testing/contracts/cw-ica-owner/src/state.rs @@ -0,0 +1 @@ + From efb202952e21dc9a5ebe816e4aea81338ebdcedb Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 22 Oct 2023 01:24:52 +0300 Subject: [PATCH 02/46] deps(owner): imported local pin of 'cw-ica-controller' --- testing/contracts/cw-ica-owner/Cargo.lock | 185 +++++++++++++++++++++- testing/contracts/cw-ica-owner/Cargo.toml | 1 + 2 files changed, 184 insertions(+), 2 deletions(-) diff --git a/testing/contracts/cw-ica-owner/Cargo.lock b/testing/contracts/cw-ica-owner/Cargo.lock index 7e237457..ca343bdf 100644 --- a/testing/contracts/cw-ica-owner/Cargo.lock +++ b/testing/contracts/cw-ica-owner/Cargo.lock @@ -19,6 +19,12 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "base16ct" version = "0.1.1" @@ -78,6 +84,9 @@ name = "bytes" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde", +] [[package]] name = "cfg-if" @@ -91,6 +100,17 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +[[package]] +name = "cosmos-sdk-proto" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237" +dependencies = [ + "prost 0.12.1", + "prost-types", + "tendermint-proto", +] + [[package]] name = "cosmwasm-crypto" version = "1.4.1" @@ -213,12 +233,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cw-ica-controller" +version = "0.1.1" +dependencies = [ + "cosmos-sdk-proto", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw2", + "schemars", + "serde", + "serde-json-wasm", + "thiserror", +] + [[package]] name = "cw-ica-owner" version = "0.1.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", + "cw-ica-controller", "cw-multi-test", "cw-storage-plus", "cw2", @@ -240,7 +276,7 @@ dependencies = [ "derivative", "itertools", "k256 0.11.6", - "prost", + "prost 0.9.0", "schemars", "serde", "thiserror", @@ -306,6 +342,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -450,6 +495,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "flex-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" +dependencies = [ + "paste", +] + [[package]] name = "forward_ref" version = "1.0.0" @@ -571,6 +625,26 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -583,6 +657,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pkcs8" version = "0.9.0" @@ -603,6 +683,12 @@ dependencies = [ "spki 0.7.2", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.69" @@ -619,7 +705,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.9.0", +] + +[[package]] +name = "prost" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" +dependencies = [ + "bytes", + "prost-derive 0.12.1", ] [[package]] @@ -635,6 +731,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "prost-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" +dependencies = [ + "prost 0.12.1", +] + [[package]] name = "quote" version = "1.0.33" @@ -762,6 +880,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.189" @@ -865,6 +992,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + [[package]] name = "syn" version = "1.0.109" @@ -887,6 +1023,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tendermint-proto" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc728a4f9e891d71adf66af6ecaece146f9c7a11312288a3107b3e1d6979aaf" +dependencies = [ + "bytes", + "flex-error", + "num-derive", + "num-traits", + "prost 0.12.1", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + [[package]] name = "thiserror" version = "1.0.50" @@ -907,6 +1061,33 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "powerfmt", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + [[package]] name = "typenum" version = "1.17.0" diff --git a/testing/contracts/cw-ica-owner/Cargo.toml b/testing/contracts/cw-ica-owner/Cargo.toml index 48d5ab6d..66bb7724 100644 --- a/testing/contracts/cw-ica-owner/Cargo.toml +++ b/testing/contracts/cw-ica-owner/Cargo.toml @@ -48,6 +48,7 @@ cosmwasm-std = { version = "1.4.0", features = [ ] } cw-storage-plus = "1.1.0" cw2 = "1.1.0" +cw-ica-controller = { path = "../../.." } schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } thiserror = { version = "1.0.31" } From c840ef1aa947fa5aa0edc27bbac70699cfdbc5d3 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 22 Oct 2023 01:25:39 +0300 Subject: [PATCH 03/46] feat(owner): added initialization logic and state --- .../contracts/cw-ica-owner/src/contract.rs | 16 +++++++--- testing/contracts/cw-ica-owner/src/msg.rs | 5 +++- testing/contracts/cw-ica-owner/src/state.rs | 30 +++++++++++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index 5f7ad994..7f15c84e 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -5,6 +5,7 @@ use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult} use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::{STATE, ContractState}; /* // version info for migration info @@ -14,12 +15,19 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( - _deps: DepsMut, + deps: DepsMut, _env: Env, - _info: MessageInfo, - _msg: InstantiateMsg, + info: MessageInfo, + msg: InstantiateMsg, ) -> Result { - unimplemented!() + let admin = if let Some(admin) = msg.admin { + deps.api.addr_validate(&admin)? + } else { + info.sender + }; + + STATE.save(deps.storage, &ContractState::new(admin, msg.ica_controller_code_id))?; + Ok(Response::default()) } #[cfg_attr(not(feature = "library"), entry_point)] diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index 3b6b9b27..a28ab793 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -1,7 +1,10 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; #[cw_serde] -pub struct InstantiateMsg {} +pub struct InstantiateMsg { + pub admin: Option, + pub ica_controller_code_id: u32 +} #[cw_serde] pub enum ExecuteMsg {} diff --git a/testing/contracts/cw-ica-owner/src/state.rs b/testing/contracts/cw-ica-owner/src/state.rs index 8b137891..141e6f84 100644 --- a/testing/contracts/cw-ica-owner/src/state.rs +++ b/testing/contracts/cw-ica-owner/src/state.rs @@ -1 +1,31 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Addr; +use cw_storage_plus::Item; +pub use contract::ContractState; + +/// The item used to store the state of the IBC application. +pub const STATE: Item = Item::new("state"); + +mod contract { + use super::*; + + /// ContractState is the state of the IBC application. + #[cw_serde] + pub struct ContractState { + /// The admin of this contract. + pub admin: Addr, + /// The code ID of the cw-ica-controller contract. + pub ica_controller_code_id: u32, + } + + impl ContractState { + /// Creates a new ContractState. + pub fn new(admin: Addr, ica_controller_code_id: u32) -> Self { + Self { + admin, + ica_controller_code_id, + } + } + } +} From 762729b545877c6839c590c8ce1b8af74d404fd0 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 22 Oct 2023 11:19:20 +0300 Subject: [PATCH 04/46] imp(testing/owner): added state --- .../contracts/cw-ica-owner/src/contract.rs | 7 ++- testing/contracts/cw-ica-owner/src/msg.rs | 2 +- testing/contracts/cw-ica-owner/src/state.rs | 54 ++++++++++++++++++- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index 7f15c84e..80f637bb 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -5,7 +5,7 @@ use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult} use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::state::{STATE, ContractState}; +use crate::state::{ContractState, STATE}; /* // version info for migration info @@ -26,7 +26,10 @@ pub fn instantiate( info.sender }; - STATE.save(deps.storage, &ContractState::new(admin, msg.ica_controller_code_id))?; + STATE.save( + deps.storage, + &ContractState::new(admin, msg.ica_controller_code_id), + )?; Ok(Response::default()) } diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index a28ab793..49024af7 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -3,7 +3,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; #[cw_serde] pub struct InstantiateMsg { pub admin: Option, - pub ica_controller_code_id: u32 + pub ica_controller_code_id: u32, } #[cw_serde] diff --git a/testing/contracts/cw-ica-owner/src/state.rs b/testing/contracts/cw-ica-owner/src/state.rs index 141e6f84..f8ee4808 100644 --- a/testing/contracts/cw-ica-owner/src/state.rs +++ b/testing/contracts/cw-ica-owner/src/state.rs @@ -1,12 +1,15 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::Addr; -use cw_storage_plus::Item; +use cw_storage_plus::{Item, Map}; pub use contract::ContractState; +pub use ica::{IcaState, IcaContractState}; /// The item used to store the state of the IBC application. pub const STATE: Item = Item::new("state"); +pub const ICA_STATES: Map = Map::new("ica_states"); + mod contract { use super::*; @@ -29,3 +32,52 @@ mod contract { } } } + +mod ica { + use cw_ica_controller::{ibc::types::metadata::TxEncoding, types::state::ChannelState}; + + use super::*; + + /// IcaContractState is the state of the cw-ica-controller contract. + #[cw_serde] + pub struct IcaContractState { + pub contract_addr: Addr, + pub ica_state: Option, + } + + /// IcaState is the state of the ICA. + #[cw_serde] + pub struct IcaState { + pub ica_id: u32, + pub ica_addr: Addr, + pub tx_encoding: TxEncoding, + pub channel_state: ChannelState, + } + + impl IcaContractState { + /// Creates a new [`IcaContractState`]. + pub fn new(contract_addr: Addr) -> Self { + Self { + contract_addr, + ica_state: None, + } + } + } + + impl IcaState { + /// Creates a new [`IcaState`]. + pub fn new( + ica_id: u32, + ica_addr: Addr, + tx_encoding: TxEncoding, + channel_state: ChannelState, + ) -> Self { + Self { + ica_id, + ica_addr, + tx_encoding, + channel_state, + } + } + } +} From 2ba1bf122950eee4505ed66f415733fb4aae762e Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 22 Oct 2023 11:30:51 +0300 Subject: [PATCH 05/46] imp(testing/owner): added 'CreateIcaContract' execute message --- testing/contracts/cw-ica-owner/src/contract.rs | 6 ++++-- testing/contracts/cw-ica-owner/src/msg.rs | 4 +++- testing/contracts/cw-ica-owner/src/state.rs | 6 ++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index 80f637bb..3e602cff 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -38,9 +38,11 @@ pub fn execute( _deps: DepsMut, _env: Env, _info: MessageInfo, - _msg: ExecuteMsg, + msg: ExecuteMsg, ) -> Result { - unimplemented!() + match msg { + ExecuteMsg::CreateIcaContract {} => unimplemented!(), + } } #[cfg_attr(not(feature = "library"), entry_point)] diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index 49024af7..eb5df88c 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -7,7 +7,9 @@ pub struct InstantiateMsg { } #[cw_serde] -pub enum ExecuteMsg {} +pub enum ExecuteMsg { + CreateIcaContract {}, +} #[cw_serde] #[derive(QueryResponses)] diff --git a/testing/contracts/cw-ica-owner/src/state.rs b/testing/contracts/cw-ica-owner/src/state.rs index f8ee4808..8eb55ec1 100644 --- a/testing/contracts/cw-ica-owner/src/state.rs +++ b/testing/contracts/cw-ica-owner/src/state.rs @@ -3,12 +3,14 @@ use cosmwasm_std::Addr; use cw_storage_plus::{Item, Map}; pub use contract::ContractState; -pub use ica::{IcaState, IcaContractState}; +pub use ica::{IcaContractState, IcaState}; /// The item used to store the state of the IBC application. pub const STATE: Item = Item::new("state"); - +/// The map used to store the state of the cw-ica-controller contracts. pub const ICA_STATES: Map = Map::new("ica_states"); +/// The item used to store the count of the cw-ica-controller contracts. +pub const ICA_COUNT: Item = Item::new("ica_count"); mod contract { use super::*; From 3622099fce4573b878e1a6219e9adfe515154549 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 22 Oct 2023 18:29:04 +0300 Subject: [PATCH 06/46] deps(testing/owner): local pin updated --- testing/contracts/cw-ica-owner/Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/contracts/cw-ica-owner/Cargo.lock b/testing/contracts/cw-ica-owner/Cargo.lock index ca343bdf..49c9708a 100644 --- a/testing/contracts/cw-ica-owner/Cargo.lock +++ b/testing/contracts/cw-ica-owner/Cargo.lock @@ -243,6 +243,7 @@ dependencies = [ "cw-storage-plus", "cw2", "schemars", + "semver", "serde", "serde-json-wasm", "thiserror", From 848439e8b9db76fd0e55146c66adbfa6058012fe Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 22 Oct 2023 18:29:30 +0300 Subject: [PATCH 07/46] fix(testing/owner): code_id is a u64 --- testing/contracts/cw-ica-owner/src/msg.rs | 2 +- testing/contracts/cw-ica-owner/src/state.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index eb5df88c..d817b936 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -3,7 +3,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; #[cw_serde] pub struct InstantiateMsg { pub admin: Option, - pub ica_controller_code_id: u32, + pub ica_controller_code_id: u64, } #[cw_serde] diff --git a/testing/contracts/cw-ica-owner/src/state.rs b/testing/contracts/cw-ica-owner/src/state.rs index 8eb55ec1..397ff048 100644 --- a/testing/contracts/cw-ica-owner/src/state.rs +++ b/testing/contracts/cw-ica-owner/src/state.rs @@ -21,12 +21,12 @@ mod contract { /// The admin of this contract. pub admin: Addr, /// The code ID of the cw-ica-controller contract. - pub ica_controller_code_id: u32, + pub ica_controller_code_id: u64, } impl ContractState { /// Creates a new ContractState. - pub fn new(admin: Addr, ica_controller_code_id: u32) -> Self { + pub fn new(admin: Addr, ica_controller_code_id: u64) -> Self { Self { admin, ica_controller_code_id, From 1b2bb76726ecd11b4e25f0dbaf5fb72be0983025 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 24 Oct 2023 22:48:21 +0300 Subject: [PATCH 08/46] feat(testing/owner): added initial implementation of instantiate2 --- .../contracts/cw-ica-owner/src/contract.rs | 72 +++++++++++++++++-- testing/contracts/cw-ica-owner/src/error.rs | 7 +- testing/contracts/cw-ica-owner/src/msg.rs | 2 +- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index 3e602cff..90eb88fe 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -35,13 +35,15 @@ pub fn instantiate( #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( - _deps: DepsMut, - _env: Env, - _info: MessageInfo, + deps: DepsMut, + env: Env, + info: MessageInfo, msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::CreateIcaContract {} => unimplemented!(), + ExecuteMsg::CreateIcaContract { salt } => { + execute::create_ica_contract(deps, env, info, salt) + } } } @@ -50,5 +52,67 @@ pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { unimplemented!() } +mod execute { + use cosmwasm_std::instantiate2_address; + use cw_ica_controller::helpers::CwIcaControllerCode; + + use crate::state::{self, ICA_COUNT, ICA_STATES}; + + use super::*; + + pub fn create_ica_contract( + deps: DepsMut, + env: Env, + info: MessageInfo, + salt: Option, + ) -> Result { + let state = STATE.load(deps.storage)?; + if state.admin != info.sender { + return Err(ContractError::Unauthorized {}); + } + + let ica_code = CwIcaControllerCode::new(state.ica_controller_code_id); + + let instantiate_msg = cw_ica_controller::types::msg::InstantiateMsg { + admin: Some(env.contract.address.to_string()), + }; + + let ica_count = ICA_COUNT.load(deps.storage).unwrap_or(0); + + let salt = salt.unwrap_or(format!( + "{}-{}", + env.contract.address.to_string(), + env.block.time.nanos() + )); + let label = format!("icacontroller-{}-{}", env.contract.address, ica_count); + + let cosmos_msg = ica_code.instantiate2( + instantiate_msg, + label, + Some(env.contract.address.to_string()), + salt.as_bytes(), + )?; + + let code_info = deps + .querier + .query_wasm_code_info(state.ica_controller_code_id)?; + let code_creator_cannonical = deps.api.addr_canonicalize(&code_info.creator)?; + + let contract_addr = deps.api.addr_humanize(&instantiate2_address( + &code_info.checksum, + &code_creator_cannonical, + salt.as_bytes(), + )?)?; + + let initial_state = state::IcaContractState::new(contract_addr); + + ICA_STATES.save(deps.storage, ica_count, &initial_state)?; + + ICA_COUNT.save(deps.storage, &(ica_count + 1))?; + + Ok(Response::new().add_message(cosmos_msg)) + } +} + #[cfg(test)] mod tests {} diff --git a/testing/contracts/cw-ica-owner/src/error.rs b/testing/contracts/cw-ica-owner/src/error.rs index 4a69d8ff..ddbaf2cc 100644 --- a/testing/contracts/cw-ica-owner/src/error.rs +++ b/testing/contracts/cw-ica-owner/src/error.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::StdError; +use cosmwasm_std::{Instantiate2AddressError, StdError}; use thiserror::Error; #[derive(Error, Debug)] @@ -6,7 +6,10 @@ pub enum ContractError { #[error("{0}")] Std(#[from] StdError), - #[error("Unauthorized")] + #[error("error when computing the instantiate2 address")] + Instantiate2AddressError(#[from] Instantiate2AddressError), + + #[error("unauthorized")] Unauthorized {}, // Add any other custom errors you like here. // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index d817b936..5ff96e4e 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -8,7 +8,7 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { - CreateIcaContract {}, + CreateIcaContract { salt: Option }, } #[cw_serde] From d50b931e4288e3a2caf8ac75716cfedf00c55817 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 24 Oct 2023 23:04:06 +0300 Subject: [PATCH 09/46] imp(testing/owner): added basic state queries --- .../contracts/cw-ica-owner/src/contract.rs | 31 +++++++++++++++++-- testing/contracts/cw-ica-owner/src/msg.rs | 12 ++++++- testing/contracts/cw-ica-owner/src/state.rs | 4 +-- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index 90eb88fe..07dbaa7d 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -1,6 +1,6 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; // use cw2::set_contract_version; use crate::error::ContractError; @@ -48,8 +48,12 @@ pub fn execute( } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { - unimplemented!() +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::GetContractState {} => to_binary(&query::state(deps)?), + QueryMsg::GetIcaContractState { ica_id } => to_binary(&query::ica_state(deps, ica_id)?), + QueryMsg::GetIcaCount {} => to_binary(&query::ica_count(deps)?), + } } mod execute { @@ -114,5 +118,26 @@ mod execute { } } +mod query { + use crate::state::{IcaContractState, ICA_COUNT, ICA_STATES}; + + use super::*; + + /// Returns the saved contract state. + pub fn state(deps: Deps) -> StdResult { + STATE.load(deps.storage) + } + + /// Returns the saved ICA state for the given ICA ID. + pub fn ica_state(deps: Deps, ica_id: u64) -> StdResult { + ICA_STATES.load(deps.storage, ica_id) + } + + /// Returns the saved ICA count. + pub fn ica_count(deps: Deps) -> StdResult { + ICA_COUNT.load(deps.storage) + } +} + #[cfg(test)] mod tests {} diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index 5ff96e4e..53645f62 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -13,4 +13,14 @@ pub enum ExecuteMsg { #[cw_serde] #[derive(QueryResponses)] -pub enum QueryMsg {} +pub enum QueryMsg { + /// GetContractState returns the contact's state. + #[returns(crate::state::ContractState)] + GetContractState {}, + /// GetIcaState returns the ICA state for the given ICA ID. + #[returns(crate::state::IcaContractState)] + GetIcaContractState { ica_id: u64 }, + /// GetIcaCount returns the number of ICAs. + #[returns(u64)] + GetIcaCount {}, +} diff --git a/testing/contracts/cw-ica-owner/src/state.rs b/testing/contracts/cw-ica-owner/src/state.rs index 397ff048..76d7ed9a 100644 --- a/testing/contracts/cw-ica-owner/src/state.rs +++ b/testing/contracts/cw-ica-owner/src/state.rs @@ -8,9 +8,9 @@ pub use ica::{IcaContractState, IcaState}; /// The item used to store the state of the IBC application. pub const STATE: Item = Item::new("state"); /// The map used to store the state of the cw-ica-controller contracts. -pub const ICA_STATES: Map = Map::new("ica_states"); +pub const ICA_STATES: Map = Map::new("ica_states"); /// The item used to store the count of the cw-ica-controller contracts. -pub const ICA_COUNT: Item = Item::new("ica_count"); +pub const ICA_COUNT: Item = Item::new("ica_count"); mod contract { use super::*; From 3bc4136c4467f21be012b0046e9e9897fb79db71 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 25 Oct 2023 19:14:01 +0300 Subject: [PATCH 10/46] deps: ran 'cargo clippy' --- testing/contracts/cw-ica-owner/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/contracts/cw-ica-owner/Cargo.lock b/testing/contracts/cw-ica-owner/Cargo.lock index 49c9708a..39b2a6eb 100644 --- a/testing/contracts/cw-ica-owner/Cargo.lock +++ b/testing/contracts/cw-ica-owner/Cargo.lock @@ -235,7 +235,7 @@ dependencies = [ [[package]] name = "cw-ica-controller" -version = "0.1.1" +version = "0.1.2" dependencies = [ "cosmos-sdk-proto", "cosmwasm-schema", From 7f7e880f4f4311e9bc95caf5d0b0d186da608615 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 25 Oct 2023 21:09:07 +0300 Subject: [PATCH 11/46] deps: using library feature --- testing/contracts/cw-ica-owner/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/contracts/cw-ica-owner/Cargo.toml b/testing/contracts/cw-ica-owner/Cargo.toml index 66bb7724..ae6041cc 100644 --- a/testing/contracts/cw-ica-owner/Cargo.toml +++ b/testing/contracts/cw-ica-owner/Cargo.toml @@ -48,7 +48,7 @@ cosmwasm-std = { version = "1.4.0", features = [ ] } cw-storage-plus = "1.1.0" cw2 = "1.1.0" -cw-ica-controller = { path = "../../.." } +cw-ica-controller = { path = "../../..", features = ["library"] } schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } thiserror = { version = "1.0.31" } From db57c2a8322a1c5c079654a3c4caab14155d5d4b Mon Sep 17 00:00:00 2001 From: srdtrk Date: Thu, 26 Oct 2023 00:53:48 +0300 Subject: [PATCH 12/46] feat(e2e): refactor types, and create IcaContract type --- e2e/interchaintest/contract_test.go | 8 +- e2e/interchaintest/types/contract.go | 69 +-------------- e2e/interchaintest/types/contract_query.go | 57 ------------ e2e/interchaintest/types/cosmwasm.go | 8 ++ e2e/interchaintest/types/export_test.go | 37 ++++++++ e2e/interchaintest/types/ica_contract.go | 88 +++++++++++++++++++ .../types/{contract_msg.go => ica_msg.go} | 12 +-- e2e/interchaintest/types/ica_query.go | 51 +++++++++++ .../types/{contract_state.go => ica_state.go} | 18 ++-- 9 files changed, 205 insertions(+), 143 deletions(-) delete mode 100644 e2e/interchaintest/types/contract_query.go create mode 100644 e2e/interchaintest/types/export_test.go create mode 100644 e2e/interchaintest/types/ica_contract.go rename e2e/interchaintest/types/{contract_msg.go => ica_msg.go} (78%) create mode 100644 e2e/interchaintest/types/ica_query.go rename e2e/interchaintest/types/{contract_state.go => ica_state.go} (56%) diff --git a/e2e/interchaintest/contract_test.go b/e2e/interchaintest/contract_test.go index 9e350f97..ff338d00 100644 --- a/e2e/interchaintest/contract_test.go +++ b/e2e/interchaintest/contract_test.go @@ -30,7 +30,7 @@ import ( type ContractTestSuite struct { mysuite.TestSuite - Contract *types.Contract + Contract *types.IcaContract IcaAddress string } @@ -40,11 +40,12 @@ func (s *ContractTestSuite) SetupContractTestSuite(ctx context.Context, encoding // This starts the chains, relayer, creates the user accounts, and creates the ibc clients and connections. s.SetupSuite(ctx, chainSpecs) - var err error // Upload and Instantiate the contract on wasmd: - s.Contract, err = types.StoreAndInstantiateNewContract(ctx, s.ChainA, s.UserA.KeyName(), "../../artifacts/cw_ica_controller.wasm") + contract, err := types.StoreAndInstantiateNewContract(ctx, s.ChainA, s.UserA.KeyName(), "../../artifacts/cw_ica_controller.wasm") s.Require().NoError(err) + s.Contract = types.NewIcaContract(*contract) + version := fmt.Sprintf(`{"version":"%s","controller_connection_id":"%s","host_connection_id":"%s","address":"","encoding":"%s","tx_type":"%s"}`, icatypes.Version, s.ChainAConnID, s.ChainBConnID, encoding, icatypes.TxTypeSDKMultiMsg) err = s.Relayer.CreateChannel(ctx, s.ExecRep, s.PathName, ibc.CreateChannelOptions{ SourcePortName: s.Contract.Port(), @@ -62,6 +63,7 @@ func (s *ContractTestSuite) SetupContractTestSuite(ctx context.Context, encoding contractState, err := s.Contract.QueryContractState(ctx) s.Require().NoError(err) s.IcaAddress = contractState.IcaInfo.IcaAddress + s.Contract.SetIcaAddress(s.IcaAddress) } func TestWithContractTestSuite(t *testing.T) { diff --git a/e2e/interchaintest/types/contract.go b/e2e/interchaintest/types/contract.go index a68ea952..4794bf2b 100644 --- a/e2e/interchaintest/types/contract.go +++ b/e2e/interchaintest/types/contract.go @@ -3,8 +3,6 @@ package types import ( "context" - "github.com/cosmos/gogoproto/proto" - "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" ) @@ -25,7 +23,7 @@ func StoreAndInstantiateNewContract( return nil, err } - contractAddr, err := chain.InstantiateContract(ctx, callerKeyName, codeId, NewInstantiateMsg(nil), true) + contractAddr, err := chain.InstantiateContract(ctx, callerKeyName, codeId, newInstantiateMsg(nil), true) if err != nil { return nil, err } @@ -41,72 +39,7 @@ func (c *Contract) Port() string { return "wasm." + c.Address } -// ExecCustomMessages invokes the contract's `CustomIcaMessages` message as the caller -func (c *Contract) ExecCustomIcaMessages( - ctx context.Context, callerKeyName string, - messages []proto.Message, encoding string, - memo *string, timeout *uint64, -) error { - customMsg := NewSendCustomIcaMessagesMsg(c.chain.Config().EncodingConfig.Codec, messages, encoding, memo, timeout) - err := c.Execute(ctx, callerKeyName, customMsg) - return err -} - -// ExecPredefinedAction executes the contract's predefined action message as the caller -func (c *Contract) ExecPredefinedAction(ctx context.Context, callerKeyName string, toAddress string) error { - err := c.Execute(ctx, callerKeyName, NewSendPredefinedActionMsg(toAddress)) - return err -} - func (c *Contract) Execute(ctx context.Context, callerKeyName string, execMsg string) error { _, err := c.chain.ExecuteContract(ctx, callerKeyName, c.Address, execMsg) return err } - -// QueryContractState queries the contract's state -func (c *Contract) QueryContractState(ctx context.Context) (*ContractState, error) { - queryResp := QueryResponse{} - err := c.chain.QueryContract(ctx, c.Address, NewGetContractStateQueryMsg(), &queryResp) - if err != nil { - return nil, err - } - - contractState, err := queryResp.GetContractState() - if err != nil { - return nil, err - } - - return &contractState, nil -} - -// QueryChannelState queries the channel state stored in the contract -func (c *Contract) QueryChannelState(ctx context.Context) (*ContractChannelState, error) { - queryResp := QueryResponse{} - err := c.chain.QueryContract(ctx, c.Address, NewGetChannelQueryMsg(), &queryResp) - if err != nil { - return nil, err - } - - channelState, err := queryResp.GetChannelState() - if err != nil { - return nil, err - } - - return &channelState, nil -} - -// QueryCallbackCounter queries the callback counter stored in the contract -func (c *Contract) QueryCallbackCounter(ctx context.Context) (*ContractCallbackCounter, error) { - queryResp := QueryResponse{} - err := c.chain.QueryContract(ctx, c.Address, NewGetCallbackCounterQueryMsg(), &queryResp) - if err != nil { - return nil, err - } - - callbackCounter, err := queryResp.GetCallbackCounter() - if err != nil { - return nil, err - } - - return &callbackCounter, nil -} diff --git a/e2e/interchaintest/types/contract_query.go b/e2e/interchaintest/types/contract_query.go deleted file mode 100644 index 094af7c2..00000000 --- a/e2e/interchaintest/types/contract_query.go +++ /dev/null @@ -1,57 +0,0 @@ -package types - -import "encoding/json" - -// NewGetChannelQueryMsg creates a new GetChannelQueryMsg. -// This function returns a map[string]interface{} instead of []byte -// because interchaintest uses json.Marshal to convert the map to a string -func NewGetChannelQueryMsg() map[string]interface{} { - return map[string]interface{}{ - "get_channel": struct{}{}, - } -} - -// NewGetContractStateQueryMsg creates a new GetContractStateQueryMsg. -// This function returns a map[string]interface{} instead of []byte -// because interchaintest uses json.Marshal to convert the map to a string -func NewGetContractStateQueryMsg() map[string]interface{} { - return map[string]interface{}{ - "get_contract_state": struct{}{}, - } -} - -// NewGetCallbackCounterQueryMsg creates a new GetCallbackCounterQueryMsg. -// This function returns a map[string]interface{} instead of []byte -// because interchaintest uses json.Marshal to convert the map to a string -func NewGetCallbackCounterQueryMsg() map[string]interface{} { - return map[string]interface{}{ - "get_callback_counter": struct{}{}, - } -} - -// QueryResponse is used to represent the response of a query. -// It may contain different types of data, so we need to unmarshal it -type QueryResponse struct { - Response json.RawMessage `json:"data"` -} - -// GetChannelState unmarshals the response to a ContractChannelState -func (qr QueryResponse) GetChannelState() (ContractChannelState, error) { - var channelState ContractChannelState - err := json.Unmarshal(qr.Response, &channelState) - return channelState, err -} - -// GetContractState unmarshals the response to a ContractState -func (qr QueryResponse) GetContractState() (ContractState, error) { - var contractState ContractState - err := json.Unmarshal(qr.Response, &contractState) - return contractState, err -} - -// GetCallbackCounter unmarshals the response to a ContractCallbackCounter -func (qr QueryResponse) GetCallbackCounter() (ContractCallbackCounter, error) { - var callbackCounter ContractCallbackCounter - err := json.Unmarshal(qr.Response, &callbackCounter) - return callbackCounter, err -} diff --git a/e2e/interchaintest/types/cosmwasm.go b/e2e/interchaintest/types/cosmwasm.go index 86754f3a..aa4c2dfe 100644 --- a/e2e/interchaintest/types/cosmwasm.go +++ b/e2e/interchaintest/types/cosmwasm.go @@ -1,5 +1,13 @@ package types +import "encoding/json" + +// QueryResponse is used to represent the response of a query. +// It may contain different types of data, so we need to unmarshal it +type QueryResponse struct { + Response json.RawMessage `json:"data"` +} + // CwIbcEndpoint is the endpoint of a channel defined in CosmWasm type CwIbcEndpoint struct { PortID string `json:"port_id"` diff --git a/e2e/interchaintest/types/export_test.go b/e2e/interchaintest/types/export_test.go new file mode 100644 index 00000000..9457ad69 --- /dev/null +++ b/e2e/interchaintest/types/export_test.go @@ -0,0 +1,37 @@ +package types + +import ( + "github.com/cosmos/gogoproto/proto" + + codec "github.com/cosmos/cosmos-sdk/codec" +) + +// NewInstantiateMsg is a wrapper for newInstantiateMsg for internal testing +func NewInstantiateMsg(admin *string) string { + return newInstantiateMsg(admin) +} + +// NewSendCustomIcaMessagesMsg is a wrapper for newSendCustomIcaMessagesMsg for internal testing +func NewSendCustomIcaMessagesMsg(cdc codec.BinaryCodec, msgs []proto.Message, encoding string, memo *string, timeout *uint64) string { + return newSendCustomIcaMessagesMsg(cdc, msgs, encoding, memo, timeout) +} + +// NewSendPredefinedActionMsg is a wrapper for newSendPredefinedActionMsg for internal testing +func NewSendPredefinedActionMsg(to_address string) string { + return newSendPredefinedActionMsg(to_address) +} + +// NewGetChannelQueryMsg is a wrapper for newGetChannelQueryMsg for internal testing +func NewGetChannelQueryMsg() map[string]interface{} { + return newGetCallbackCounterQueryMsg() +} + +// NewGetContractStateQueryMsg is a wrapper for newGetContractStateQueryMsg for internal testing +func NewGetContractStateQueryMsg() map[string]interface{} { + return newGetContractStateQueryMsg() +} + +// NewGetCallbackCounterQueryMsg is a wrapper for newGetCallbackCounterQueryMsg for internal testing +func NewGetCallbackCounterQueryMsg() map[string]interface{} { + return newGetCallbackCounterQueryMsg() +} diff --git a/e2e/interchaintest/types/ica_contract.go b/e2e/interchaintest/types/ica_contract.go new file mode 100644 index 00000000..d6c8ed20 --- /dev/null +++ b/e2e/interchaintest/types/ica_contract.go @@ -0,0 +1,88 @@ +package types + +import ( + "context" + + "github.com/cosmos/gogoproto/proto" +) + +type IcaContract struct { + Contract + IcaAddress string +} + +func NewIcaContract(contract Contract) *IcaContract { + return &IcaContract{ + Contract: contract, + IcaAddress: "", + } +} + +func (c *IcaContract) SetIcaAddress(icaAddress string) { + c.IcaAddress = icaAddress +} + +// ExecCustomMessages invokes the contract's `CustomIcaMessages` message as the caller +func (c *IcaContract) ExecCustomIcaMessages( + ctx context.Context, callerKeyName string, + messages []proto.Message, encoding string, + memo *string, timeout *uint64, +) error { + customMsg := newSendCustomIcaMessagesMsg(c.chain.Config().EncodingConfig.Codec, messages, encoding, memo, timeout) + err := c.Execute(ctx, callerKeyName, customMsg) + return err +} + +// ExecPredefinedAction executes the contract's predefined action message as the caller +func (c *IcaContract) ExecPredefinedAction(ctx context.Context, callerKeyName string, toAddress string) error { + err := c.Execute(ctx, callerKeyName, newSendPredefinedActionMsg(toAddress)) + return err +} + +// QueryContractState queries the contract's state +func (c *IcaContract) QueryContractState(ctx context.Context) (*IcaContractState, error) { + queryResp := QueryResponse{} + err := c.chain.QueryContract(ctx, c.Address, newGetContractStateQueryMsg(), &queryResp) + if err != nil { + return nil, err + } + + contractState, err := queryResp.getContractState() + if err != nil { + return nil, err + } + + return &contractState, nil +} + +// QueryChannelState queries the channel state stored in the contract +func (c *IcaContract) QueryChannelState(ctx context.Context) (*IcaContractChannelState, error) { + queryResp := QueryResponse{} + err := c.chain.QueryContract(ctx, c.Address, newGetChannelQueryMsg(), &queryResp) + if err != nil { + return nil, err + } + + channelState, err := queryResp.getChannelState() + if err != nil { + return nil, err + } + + return &channelState, nil +} + +// QueryCallbackCounter queries the callback counter stored in the contract +func (c *IcaContract) QueryCallbackCounter(ctx context.Context) (*IcaContractCallbackCounter, error) { + queryResp := QueryResponse{} + err := c.chain.QueryContract(ctx, c.Address, newGetCallbackCounterQueryMsg(), &queryResp) + if err != nil { + return nil, err + } + + callbackCounter, err := queryResp.getCallbackCounter() + if err != nil { + return nil, err + } + + return &callbackCounter, nil +} diff --git a/e2e/interchaintest/types/contract_msg.go b/e2e/interchaintest/types/ica_msg.go similarity index 78% rename from e2e/interchaintest/types/contract_msg.go rename to e2e/interchaintest/types/ica_msg.go index 9e5fb721..59db717c 100644 --- a/e2e/interchaintest/types/contract_msg.go +++ b/e2e/interchaintest/types/ica_msg.go @@ -12,8 +12,8 @@ import ( icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" ) -// NewInstantiateMsg creates a new InstantiateMsg. -func NewInstantiateMsg(admin *string) string { +// newInstantiateMsg creates a new InstantiateMsg. +func newInstantiateMsg(admin *string) string { if admin == nil { return `{}` } else { @@ -21,13 +21,13 @@ func NewInstantiateMsg(admin *string) string { } } -// NewSendPredefinedActionMsg creates a new SendPredefinedActionMsg. -func NewSendPredefinedActionMsg(to_address string) string { +// newSendPredefinedActionMsg creates a new SendPredefinedActionMsg. +func newSendPredefinedActionMsg(to_address string) string { return fmt.Sprintf(`{"send_predefined_action":{"to_address":"%s"}}`, to_address) } -// NewSendCustomIcaMessagesMsg creates a new SendCustomIcaMessagesMsg. -func NewSendCustomIcaMessagesMsg(cdc codec.BinaryCodec, msgs []proto.Message, encoding string, memo *string, timeout *uint64) string { +// newSendCustomIcaMessagesMsg creates a new SendCustomIcaMessagesMsg. +func newSendCustomIcaMessagesMsg(cdc codec.BinaryCodec, msgs []proto.Message, encoding string, memo *string, timeout *uint64) string { type SendCustomIcaMessagesMsg struct { Messages string `json:"messages"` PacketMemo *string `json:"packet_memo,omitempty"` diff --git a/e2e/interchaintest/types/ica_query.go b/e2e/interchaintest/types/ica_query.go new file mode 100644 index 00000000..f98b4058 --- /dev/null +++ b/e2e/interchaintest/types/ica_query.go @@ -0,0 +1,51 @@ +package types + +import "encoding/json" + +// newGetChannelQueryMsg creates a new GetChannelQueryMsg. +// This function returns a map[string]interface{} instead of []byte +// because interchaintest uses json.Marshal to convert the map to a string +func newGetChannelQueryMsg() map[string]interface{} { + return map[string]interface{}{ + "get_channel": struct{}{}, + } +} + +// newGetContractStateQueryMsg creates a new GetContractStateQueryMsg. +// This function returns a map[string]interface{} instead of []byte +// because interchaintest uses json.Marshal to convert the map to a string +func newGetContractStateQueryMsg() map[string]interface{} { + return map[string]interface{}{ + "get_contract_state": struct{}{}, + } +} + +// newGetCallbackCounterQueryMsg creates a new GetCallbackCounterQueryMsg. +// This function returns a map[string]interface{} instead of []byte +// because interchaintest uses json.Marshal to convert the map to a string +func newGetCallbackCounterQueryMsg() map[string]interface{} { + return map[string]interface{}{ + "get_callback_counter": struct{}{}, + } +} + +// getChannelState unmarshals the response to a ContractChannelState +func (qr QueryResponse) getChannelState() (IcaContractChannelState, error) { + var channelState IcaContractChannelState + err := json.Unmarshal(qr.Response, &channelState) + return channelState, err +} + +// getContractState unmarshals the response to a ContractState +func (qr QueryResponse) getContractState() (IcaContractState, error) { + var contractState IcaContractState + err := json.Unmarshal(qr.Response, &contractState) + return contractState, err +} + +// getCallbackCounter unmarshals the response to a ContractCallbackCounter +func (qr QueryResponse) getCallbackCounter() (IcaContractCallbackCounter, error) { + var callbackCounter IcaContractCallbackCounter + err := json.Unmarshal(qr.Response, &callbackCounter) + return callbackCounter, err +} diff --git a/e2e/interchaintest/types/contract_state.go b/e2e/interchaintest/types/ica_state.go similarity index 56% rename from e2e/interchaintest/types/contract_state.go rename to e2e/interchaintest/types/ica_state.go index 539d1e98..72c37ce7 100644 --- a/e2e/interchaintest/types/contract_state.go +++ b/e2e/interchaintest/types/ica_state.go @@ -1,31 +1,31 @@ package types -// ContractState is used to represent its state in Contract's storage -type ContractState struct { - Admin string `json:"admin"` - IcaInfo ContractIcaInfo `json:"ica_info"` +// IcaContractState is used to represent its state in Contract's storage +type IcaContractState struct { + Admin string `json:"admin"` + IcaInfo IcaContractIcaInfo `json:"ica_info"` } -// ContractIcaInfo is used to represent the ICA info in the contract's state -type ContractIcaInfo struct { +// IcaContractIcaInfo is used to represent the ICA info in the contract's state +type IcaContractIcaInfo struct { IcaAddress string `json:"ica_address"` ChannelID string `json:"channel_id"` } // ContractCallbackCounter is used to represent the callback counter in the contract's storage -type ContractCallbackCounter struct { +type IcaContractCallbackCounter struct { Success uint64 `json:"success"` Error uint64 `json:"error"` Timeout uint64 `json:"timeout"` } // ContractChannelState is used to represent the channel state in the contract's storage -type ContractChannelState struct { +type IcaContractChannelState struct { Channel CwIbcChannel `json:"channel"` ChannelStatus string `json:"channel_status"` } // IsOpen returns true if the channel is open -func (c *ContractChannelState) IsOpen() bool { +func (c *IcaContractChannelState) IsOpen() bool { return c.ChannelStatus == "STATE_OPEN" } From 5cfbb368fc376ecfdb9e978465fc86e4b1636810 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Thu, 26 Oct 2023 21:28:56 +0300 Subject: [PATCH 13/46] imp: added types/owner_state.go --- e2e/interchaintest/types/owner_state.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 e2e/interchaintest/types/owner_state.go diff --git a/e2e/interchaintest/types/owner_state.go b/e2e/interchaintest/types/owner_state.go new file mode 100644 index 00000000..a8cfe9c0 --- /dev/null +++ b/e2e/interchaintest/types/owner_state.go @@ -0,0 +1,21 @@ +package types + +// OwnerContractState is used to represent its state in Contract's storage +type OwnerContractState struct { + Admin string `json:"admin"` + IcaControllerCodeID uint64 `json:"ica_controller_code_id"` +} + +// OwnerIcaContractState is used to represent its state in Contract's storage +type OwnerIcaContractState struct { + ContractAddr string `json:"contract_addr"` + IcaState *OwnerIcaState `json:"ica_state"` +} + +// OwnerIcaState is the state of the ICA. +type OwnerIcaState struct { + IcaID uint32 `json:"ica_id"` + IcaAddr string `json:"ica_addr"` + TxEncoding string `json:"tx_encoding"` + ChannelState *IcaContractChannelState `json:"channel_state"` +} From b3a286271907e5ce54c76d77f77da976ab3fe4ef Mon Sep 17 00:00:00 2001 From: srdtrk Date: Thu, 26 Oct 2023 21:49:01 +0300 Subject: [PATCH 14/46] imp(e2e): refactored queries to use generics --- e2e/interchaintest/types/cosmwasm.go | 9 ++++++++- e2e/interchaintest/types/ica_contract.go | 12 ++++++------ e2e/interchaintest/types/ica_query.go | 23 ----------------------- 3 files changed, 14 insertions(+), 30 deletions(-) diff --git a/e2e/interchaintest/types/cosmwasm.go b/e2e/interchaintest/types/cosmwasm.go index aa4c2dfe..f800e411 100644 --- a/e2e/interchaintest/types/cosmwasm.go +++ b/e2e/interchaintest/types/cosmwasm.go @@ -4,7 +4,7 @@ import "encoding/json" // QueryResponse is used to represent the response of a query. // It may contain different types of data, so we need to unmarshal it -type QueryResponse struct { +type QueryResponse[T any] struct { Response json.RawMessage `json:"data"` } @@ -23,3 +23,10 @@ type CwIbcChannel struct { Version string `json:"version"` ConnectionID string `json:"connection_id"` } + +// GetResp unmarshals the response to a T +func (qr QueryResponse[T]) GetResp() (T, error) { + var resp T + err := json.Unmarshal(qr.Response, &resp) + return resp, err +} diff --git a/e2e/interchaintest/types/ica_contract.go b/e2e/interchaintest/types/ica_contract.go index d6c8ed20..6d3c9caa 100644 --- a/e2e/interchaintest/types/ica_contract.go +++ b/e2e/interchaintest/types/ica_contract.go @@ -41,13 +41,13 @@ func (c *IcaContract) ExecPredefinedAction(ctx context.Context, callerKeyName st // QueryContractState queries the contract's state func (c *IcaContract) QueryContractState(ctx context.Context) (*IcaContractState, error) { - queryResp := QueryResponse{} + queryResp := QueryResponse[IcaContractState]{} err := c.chain.QueryContract(ctx, c.Address, newGetContractStateQueryMsg(), &queryResp) if err != nil { return nil, err } - contractState, err := queryResp.getContractState() + contractState, err := queryResp.GetResp() if err != nil { return nil, err } @@ -57,13 +57,13 @@ func (c *IcaContract) QueryContractState(ctx context.Context) (*IcaContractState // QueryChannelState queries the channel state stored in the contract func (c *IcaContract) QueryChannelState(ctx context.Context) (*IcaContractChannelState, error) { - queryResp := QueryResponse{} + queryResp := QueryResponse[IcaContractChannelState]{} err := c.chain.QueryContract(ctx, c.Address, newGetChannelQueryMsg(), &queryResp) if err != nil { return nil, err } - channelState, err := queryResp.getChannelState() + channelState, err := queryResp.GetResp() if err != nil { return nil, err } @@ -73,13 +73,13 @@ func (c *IcaContract) QueryChannelState(ctx context.Context) (*IcaContractChanne // QueryCallbackCounter queries the callback counter stored in the contract func (c *IcaContract) QueryCallbackCounter(ctx context.Context) (*IcaContractCallbackCounter, error) { - queryResp := QueryResponse{} + queryResp := QueryResponse[IcaContractCallbackCounter]{} err := c.chain.QueryContract(ctx, c.Address, newGetCallbackCounterQueryMsg(), &queryResp) if err != nil { return nil, err } - callbackCounter, err := queryResp.getCallbackCounter() + callbackCounter, err := queryResp.GetResp() if err != nil { return nil, err } diff --git a/e2e/interchaintest/types/ica_query.go b/e2e/interchaintest/types/ica_query.go index f98b4058..a03b8006 100644 --- a/e2e/interchaintest/types/ica_query.go +++ b/e2e/interchaintest/types/ica_query.go @@ -1,7 +1,5 @@ package types -import "encoding/json" - // newGetChannelQueryMsg creates a new GetChannelQueryMsg. // This function returns a map[string]interface{} instead of []byte // because interchaintest uses json.Marshal to convert the map to a string @@ -28,24 +26,3 @@ func newGetCallbackCounterQueryMsg() map[string]interface{} { "get_callback_counter": struct{}{}, } } - -// getChannelState unmarshals the response to a ContractChannelState -func (qr QueryResponse) getChannelState() (IcaContractChannelState, error) { - var channelState IcaContractChannelState - err := json.Unmarshal(qr.Response, &channelState) - return channelState, err -} - -// getContractState unmarshals the response to a ContractState -func (qr QueryResponse) getContractState() (IcaContractState, error) { - var contractState IcaContractState - err := json.Unmarshal(qr.Response, &contractState) - return contractState, err -} - -// getCallbackCounter unmarshals the response to a ContractCallbackCounter -func (qr QueryResponse) getCallbackCounter() (IcaContractCallbackCounter, error) { - var callbackCounter IcaContractCallbackCounter - err := json.Unmarshal(qr.Response, &callbackCounter) - return callbackCounter, err -} From 9f85e27fb1d6163a4f65bcf572423d22d6d396ee Mon Sep 17 00:00:00 2001 From: srdtrk Date: Thu, 26 Oct 2023 22:23:27 +0300 Subject: [PATCH 15/46] style: renamed 'StoreAndInstantiateNewContract' to 'StoreAndInstantiateNewIcaContract' --- e2e/interchaintest/contract_test.go | 5 ++--- e2e/interchaintest/types/contract.go | 23 -------------------- e2e/interchaintest/types/ica_contract.go | 27 ++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/e2e/interchaintest/contract_test.go b/e2e/interchaintest/contract_test.go index ff338d00..15a474dc 100644 --- a/e2e/interchaintest/contract_test.go +++ b/e2e/interchaintest/contract_test.go @@ -40,12 +40,11 @@ func (s *ContractTestSuite) SetupContractTestSuite(ctx context.Context, encoding // This starts the chains, relayer, creates the user accounts, and creates the ibc clients and connections. s.SetupSuite(ctx, chainSpecs) + var err error // Upload and Instantiate the contract on wasmd: - contract, err := types.StoreAndInstantiateNewContract(ctx, s.ChainA, s.UserA.KeyName(), "../../artifacts/cw_ica_controller.wasm") + s.Contract, err = types.StoreAndInstantiateNewIcaContract(ctx, s.ChainA, s.UserA.KeyName(), "../../artifacts/cw_ica_controller.wasm") s.Require().NoError(err) - s.Contract = types.NewIcaContract(*contract) - version := fmt.Sprintf(`{"version":"%s","controller_connection_id":"%s","host_connection_id":"%s","address":"","encoding":"%s","tx_type":"%s"}`, icatypes.Version, s.ChainAConnID, s.ChainBConnID, encoding, icatypes.TxTypeSDKMultiMsg) err = s.Relayer.CreateChannel(ctx, s.ExecRep, s.PathName, ibc.CreateChannelOptions{ SourcePortName: s.Contract.Port(), diff --git a/e2e/interchaintest/types/contract.go b/e2e/interchaintest/types/contract.go index 4794bf2b..315aa119 100644 --- a/e2e/interchaintest/types/contract.go +++ b/e2e/interchaintest/types/contract.go @@ -12,29 +12,6 @@ type Contract struct { chain *cosmos.CosmosChain } -// StoreAndInstantiateNewContract stores the contract code and instantiates a new contract as the caller. -// Returns a new Contract instance. -func StoreAndInstantiateNewContract( - ctx context.Context, chain *cosmos.CosmosChain, - callerKeyName, fileName string, -) (*Contract, error) { - codeId, err := chain.StoreContract(ctx, callerKeyName, fileName) - if err != nil { - return nil, err - } - - contractAddr, err := chain.InstantiateContract(ctx, callerKeyName, codeId, newInstantiateMsg(nil), true) - if err != nil { - return nil, err - } - - return &Contract{ - Address: contractAddr, - CodeID: codeId, - chain: chain, - }, nil -} - func (c *Contract) Port() string { return "wasm." + c.Address } diff --git a/e2e/interchaintest/types/ica_contract.go b/e2e/interchaintest/types/ica_contract.go index 6d3c9caa..f0e72187 100644 --- a/e2e/interchaintest/types/ica_contract.go +++ b/e2e/interchaintest/types/ica_contract.go @@ -4,6 +4,8 @@ import ( "context" "github.com/cosmos/gogoproto/proto" + + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" ) type IcaContract struct { @@ -22,6 +24,31 @@ func (c *IcaContract) SetIcaAddress(icaAddress string) { c.IcaAddress = icaAddress } +// StoreAndInstantiateNewIcaContract stores the contract code and instantiates a new contract as the caller. +// Returns a new Contract instance. +func StoreAndInstantiateNewIcaContract( + ctx context.Context, chain *cosmos.CosmosChain, + callerKeyName, fileName string, +) (*IcaContract, error) { + codeId, err := chain.StoreContract(ctx, callerKeyName, fileName) + if err != nil { + return nil, err + } + + contractAddr, err := chain.InstantiateContract(ctx, callerKeyName, codeId, newInstantiateMsg(nil), true) + if err != nil { + return nil, err + } + + contract := Contract{ + Address: contractAddr, + CodeID: codeId, + chain: chain, + } + + return NewIcaContract(contract), nil +} + // ExecCustomMessages invokes the contract's `CustomIcaMessages` message as the caller func (c *IcaContract) ExecCustomIcaMessages( ctx context.Context, callerKeyName string, From 802bd237de0acaa0873838216b87c7ba42c67cd2 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sat, 28 Oct 2023 20:33:07 +0300 Subject: [PATCH 16/46] imp: added basic test helpers --- e2e/interchaintest/types/owner_contract.go | 11 ++++++++ e2e/interchaintest/types/owner_msg.go | 21 +++++++++++++++ e2e/interchaintest/types/owner_query.go | 30 ++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 e2e/interchaintest/types/owner_contract.go create mode 100644 e2e/interchaintest/types/owner_msg.go create mode 100644 e2e/interchaintest/types/owner_query.go diff --git a/e2e/interchaintest/types/owner_contract.go b/e2e/interchaintest/types/owner_contract.go new file mode 100644 index 00000000..d6edd528 --- /dev/null +++ b/e2e/interchaintest/types/owner_contract.go @@ -0,0 +1,11 @@ +package types + +type OwnerContract struct { + Contract +} + +func NewOwnerContract(contract Contract) *OwnerContract { + return &OwnerContract{ + Contract: contract, + } +} diff --git a/e2e/interchaintest/types/owner_msg.go b/e2e/interchaintest/types/owner_msg.go new file mode 100644 index 00000000..4100b23d --- /dev/null +++ b/e2e/interchaintest/types/owner_msg.go @@ -0,0 +1,21 @@ +package types + +import "fmt" + +// newOwnerInstantiateMsg creates a new InstantiateMsg. +func newOwnerInstantiateMsg(admin *string, icaControllerCodeId uint64) string { + if admin == nil { + return fmt.Sprintf(`{"ica_controller_code_id":%d}`, icaControllerCodeId) + } else { + return fmt.Sprintf(`{"admin":"%s","ica_controller_code_id":%d}`, *admin, icaControllerCodeId) + } +} + +// newOwnerCreateIcaContractMsg creates a new CreateIcaContractMsg. +func newOwnerCreateIcaContractMsg(salt *string) string { + if salt == nil { + return `{"create_ica_contract":{}}` + } else { + return fmt.Sprintf(`{"create_ica_contract":{"salt":"%s"}}`, *salt) + } +} diff --git a/e2e/interchaintest/types/owner_query.go b/e2e/interchaintest/types/owner_query.go new file mode 100644 index 00000000..d574802e --- /dev/null +++ b/e2e/interchaintest/types/owner_query.go @@ -0,0 +1,30 @@ +package types + +// newOwnerGetContractStateQueryMsg creates a new GetContractStateQueryMsg. +// This function returns a map[string]interface{} instead of []byte +// because interchaintest uses json.Marshal to convert the map to a string +func newOwnerGetContractStateQueryMsg() map[string]interface{} { + return map[string]interface{}{ + "get_contract_state": struct{}{}, + } +} + +// newOwnerGetIcaContractStateQueryMsg creates a new GetIcaContractStateQueryMsg. +// This function returns a map[string]interface{} instead of []byte +// because interchaintest uses json.Marshal to convert the map to a string +func newOwnerGetIcaContractStateQueryMsg(icaID uint64) map[string]interface{} { + return map[string]interface{}{ + "get_ica_contract_state": map[string]interface{}{ + "ica_id": icaID, + }, + } +} + +// newOwnerGetIcaCountQueryMsg creates a new GetIcaCountQueryMsg. +// This function returns a map[string]interface{} instead of []byte +// because interchaintest uses json.Marshal to convert the map to a string +func newOwnerGetIcaCountQueryMsg() map[string]interface{} { + return map[string]interface{}{ + "get_ica_count": struct{}{}, + } +} From a44c161dd2f00f7e6c95fb3ca37c018420334b4a Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 1 Nov 2023 22:19:52 +0300 Subject: [PATCH 17/46] imp: updated ica_owner --- testing/contracts/cw-ica-owner/Cargo.lock | 2 +- testing/contracts/cw-ica-owner/src/contract.rs | 13 +++++++++---- testing/contracts/cw-ica-owner/src/msg.rs | 6 +++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/testing/contracts/cw-ica-owner/Cargo.lock b/testing/contracts/cw-ica-owner/Cargo.lock index 39b2a6eb..bb4d4c22 100644 --- a/testing/contracts/cw-ica-owner/Cargo.lock +++ b/testing/contracts/cw-ica-owner/Cargo.lock @@ -235,7 +235,7 @@ dependencies = [ [[package]] name = "cw-ica-controller" -version = "0.1.2" +version = "0.1.3" dependencies = [ "cosmos-sdk-proto", "cosmwasm-schema", diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index 07dbaa7d..84ad69ac 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -41,9 +41,10 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::CreateIcaContract { salt } => { - execute::create_ica_contract(deps, env, info, salt) - } + ExecuteMsg::CreateIcaContract { + salt, + channel_open_init_options, + } => execute::create_ica_contract(deps, env, info, salt, channel_open_init_options), } } @@ -58,7 +59,9 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { mod execute { use cosmwasm_std::instantiate2_address; - use cw_ica_controller::helpers::CwIcaControllerCode; + use cw_ica_controller::{ + helpers::CwIcaControllerCode, types::msg::options::ChannelOpenInitOptions, + }; use crate::state::{self, ICA_COUNT, ICA_STATES}; @@ -69,6 +72,7 @@ mod execute { env: Env, info: MessageInfo, salt: Option, + channel_open_init_options: Option, ) -> Result { let state = STATE.load(deps.storage)?; if state.admin != info.sender { @@ -79,6 +83,7 @@ mod execute { let instantiate_msg = cw_ica_controller::types::msg::InstantiateMsg { admin: Some(env.contract.address.to_string()), + channel_open_init_options, }; let ica_count = ICA_COUNT.load(deps.storage).unwrap_or(0); diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index 53645f62..3a3c073e 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -1,4 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cw_ica_controller::types::msg::options::ChannelOpenInitOptions; #[cw_serde] pub struct InstantiateMsg { @@ -8,7 +9,10 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { - CreateIcaContract { salt: Option }, + CreateIcaContract { + salt: Option, + channel_open_init_options: Option, + }, } #[cw_serde] From dd2abf59e83aa42bd38f6b93a92fa938232cc752 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 1 Nov 2023 22:58:17 +0300 Subject: [PATCH 18/46] e2e: minor imp --- e2e/interchaintest/types/owner_contract.go | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/e2e/interchaintest/types/owner_contract.go b/e2e/interchaintest/types/owner_contract.go index d6edd528..1c2908b6 100644 --- a/e2e/interchaintest/types/owner_contract.go +++ b/e2e/interchaintest/types/owner_contract.go @@ -1,5 +1,11 @@ package types +import ( + "context" + + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" +) + type OwnerContract struct { Contract } @@ -9,3 +15,28 @@ func NewOwnerContract(contract Contract) *OwnerContract { Contract: contract, } } + +// StoreAndInstantiateNewOwnerContract stores the contract code and instantiates a new contract as the caller. +// Returns a new OwnerContract instance. +func StoreAndInstantiateNewOwnerContract( + ctx context.Context, chain *cosmos.CosmosChain, + callerKeyName, fileName string, icaCodeId uint64, +) (*OwnerContract, error) { + codeId, err := chain.StoreContract(ctx, callerKeyName, fileName) + if err != nil { + return nil, err + } + + contractAddr, err := chain.InstantiateContract(ctx, callerKeyName, codeId, newOwnerInstantiateMsg(nil, icaCodeId), true) + if err != nil { + return nil, err + } + + contract := Contract{ + Address: contractAddr, + CodeID: codeId, + chain: chain, + } + + return NewOwnerContract(contract), nil +} From 81e1e818fc67596fb8ffeeac85a6e150f12af2f2 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sat, 4 Nov 2023 15:37:05 +0300 Subject: [PATCH 19/46] fix(testing/owner): fixed instantiate2 --- testing/contracts/cw-ica-owner/src/contract.rs | 10 +++++----- testing/contracts/cw-ica-owner/src/error.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index 84ad69ac..a51198dd 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -89,9 +89,9 @@ mod execute { let ica_count = ICA_COUNT.load(deps.storage).unwrap_or(0); let salt = salt.unwrap_or(format!( - "{}-{}", - env.contract.address.to_string(), - env.block.time.nanos() + // "test", + "{}", + env.block.time.seconds() )); let label = format!("icacontroller-{}-{}", env.contract.address, ica_count); @@ -105,11 +105,11 @@ mod execute { let code_info = deps .querier .query_wasm_code_info(state.ica_controller_code_id)?; - let code_creator_cannonical = deps.api.addr_canonicalize(&code_info.creator)?; + let creator_cannonical = deps.api.addr_canonicalize(env.contract.address.as_str())?; let contract_addr = deps.api.addr_humanize(&instantiate2_address( &code_info.checksum, - &code_creator_cannonical, + &creator_cannonical, salt.as_bytes(), )?)?; diff --git a/testing/contracts/cw-ica-owner/src/error.rs b/testing/contracts/cw-ica-owner/src/error.rs index ddbaf2cc..2412e16c 100644 --- a/testing/contracts/cw-ica-owner/src/error.rs +++ b/testing/contracts/cw-ica-owner/src/error.rs @@ -6,7 +6,7 @@ pub enum ContractError { #[error("{0}")] Std(#[from] StdError), - #[error("error when computing the instantiate2 address")] + #[error("error when computing the instantiate2 address: {0}")] Instantiate2AddressError(#[from] Instantiate2AddressError), #[error("unauthorized")] From ceb1ad37a3740e011c68371e0ed8d61d8ebc0e18 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sat, 4 Nov 2023 15:39:15 +0300 Subject: [PATCH 20/46] imp: owner channel creation test passing now --- e2e/interchaintest/contract_test.go | 2 +- e2e/interchaintest/owner_test.go | 132 +++++++++++++++++++++ e2e/interchaintest/types/owner_contract.go | 32 +++++ e2e/interchaintest/types/owner_msg.go | 36 ++++-- e2e/interchaintest/types/owner_query.go | 17 +-- 5 files changed, 203 insertions(+), 16 deletions(-) create mode 100644 e2e/interchaintest/owner_test.go diff --git a/e2e/interchaintest/contract_test.go b/e2e/interchaintest/contract_test.go index 57ce72d8..b554436a 100644 --- a/e2e/interchaintest/contract_test.go +++ b/e2e/interchaintest/contract_test.go @@ -34,7 +34,7 @@ type ContractTestSuite struct { IcaAddress string } -// SetupContractAndChannel starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, +// SetupContractTestSuite starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, // sets up the contract and does the channel handshake for the contract test suite. func (s *ContractTestSuite) SetupContractTestSuite(ctx context.Context, encoding string) { s.SetupSuite(ctx, chainSpecs) diff --git a/e2e/interchaintest/owner_test.go b/e2e/interchaintest/owner_test.go new file mode 100644 index 00000000..e5bf07d9 --- /dev/null +++ b/e2e/interchaintest/owner_test.go @@ -0,0 +1,132 @@ +package main + +import ( + "context" + "strconv" + "testing" + + "github.com/stretchr/testify/suite" + + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + + "github.com/strangelove-ventures/interchaintest/v7/testutil" + + mysuite "github.com/srdtrk/cw-ica-controller/interchaintest/v2/testsuite" + "github.com/srdtrk/cw-ica-controller/interchaintest/v2/types" +) + +type OwnerTestSuite struct { + mysuite.TestSuite + + IcaContractCodeId uint64 + OwnerContract *types.OwnerContract + NumOfIcaContracts uint32 +} + +// SetupOwnerTestSuite starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, +// sets up the contract and does the channel handshake for the contract test suite. +func (s *OwnerTestSuite) SetupOwnerTestSuite(ctx context.Context) { + s.SetupSuite(ctx, chainSpecs) + + codeId, err := s.ChainA.StoreContract(ctx, s.UserA.KeyName(), "../../artifacts/cw_ica_controller.wasm") + s.Require().NoError(err) + + // codeId is string and needs to be converted to uint64 + s.IcaContractCodeId, err = strconv.ParseUint(codeId, 10, 64) + s.Require().NoError(err) + + s.OwnerContract, err = types.StoreAndInstantiateNewOwnerContract( + ctx, s.ChainA, s.UserA.KeyName(), "../../artifacts/cw_ica_owner.wasm", s.IcaContractCodeId, + ) + s.Require().NoError(err) + + s.NumOfIcaContracts = 0 +} + +func TestWithOwnerTestSuite(t *testing.T) { + suite.Run(t, new(OwnerTestSuite)) +} + +func (s *OwnerTestSuite) TestCreateIcaContract() { + ctx := context.Background() + + // This starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, + // sets up the contract and does the channel handshake for the contract test suite. + s.SetupOwnerTestSuite(ctx) + wasmd, simd := s.ChainA, s.ChainB + wasmdUser := s.UserA + + // Create the ICA Contract + + channelOpenInitOptions := types.ChannelOpenInitOptions{ + ConnectionId: s.ChainAConnID, + CounterpartyConnectionId: s.ChainBConnID, + } + createMsg := types.NewOwnerCreateIcaContractMsg(nil, &channelOpenInitOptions) + + err := s.OwnerContract.Execute(ctx, wasmdUser.KeyName(), createMsg, "--gas", "500000") + s.Require().NoError(err) + + s.NumOfIcaContracts++ + + // Wait for the channel to get set up + err = testutil.WaitForBlocks(ctx, 5, s.ChainA, s.ChainB) + s.Require().NoError(err) + + icaState, err := s.OwnerContract.QueryIcaContractState(ctx, 0) + s.Require().NoError(err) + + icaContract := types.NewIcaContract(types.NewContract(icaState.ContractAddr, strconv.FormatUint(s.IcaContractCodeId, 10), wasmd)) + + s.Run("TestChannelHandshakeSuccess", func() { + // Test if the handshake was successful + wasmdChannels, err := s.Relayer.GetChannels(ctx, s.ExecRep, wasmd.Config().ChainID) + s.Require().NoError(err) + s.Require().Equal(1, len(wasmdChannels)) + + wasmdChannel := wasmdChannels[0] + s.T().Logf("wasmd channel: %s", toJSONString(wasmdChannel)) + s.Require().Equal(icaContract.Port(), wasmdChannel.PortID) + s.Require().Equal(icatypes.HostPortID, wasmdChannel.Counterparty.PortID) + s.Require().Equal(channeltypes.OPEN.String(), wasmdChannel.State) + + simdChannels, err := s.Relayer.GetChannels(ctx, s.ExecRep, simd.Config().ChainID) + s.Require().NoError(err) + // I don't know why sometimes an extra channel is created in simd. + // this is not related to the localhost connection, and is a failed + // clone of the successful channel at index 0. I will log it for now. + s.Require().Greater(len(simdChannels), 0) + if len(simdChannels) > 1 { + s.T().Logf("extra simd channels detected: %s", toJSONString(simdChannels)) + } + + simdChannel := simdChannels[0] + s.T().Logf("simd channel state: %s", toJSONString(simdChannel.State)) + s.Require().Equal(icatypes.HostPortID, simdChannel.PortID) + s.Require().Equal(icaContract.Port(), simdChannel.Counterparty.PortID) + s.Require().Equal(channeltypes.OPEN.String(), simdChannel.State) + + // Check contract's channel state + contractChannelState, err := icaContract.QueryChannelState(ctx) + s.Require().NoError(err) + + s.T().Logf("contract's channel store after handshake: %s", toJSONString(contractChannelState)) + + s.Require().Equal(wasmdChannel.State, contractChannelState.ChannelStatus) + s.Require().Equal(wasmdChannel.Version, contractChannelState.Channel.Version) + s.Require().Equal(wasmdChannel.ConnectionHops[0], contractChannelState.Channel.ConnectionID) + s.Require().Equal(wasmdChannel.ChannelID, contractChannelState.Channel.Endpoint.ChannelID) + s.Require().Equal(wasmdChannel.PortID, contractChannelState.Channel.Endpoint.PortID) + s.Require().Equal(wasmdChannel.Counterparty.ChannelID, contractChannelState.Channel.CounterpartyEndpoint.ChannelID) + s.Require().Equal(wasmdChannel.Counterparty.PortID, contractChannelState.Channel.CounterpartyEndpoint.PortID) + s.Require().Equal(wasmdChannel.Ordering, contractChannelState.Channel.Order) + + // Check contract state + contractState, err := icaContract.QueryContractState(ctx) + s.Require().NoError(err) + + s.Require().Equal(s.OwnerContract.Address, contractState.Admin) + s.Require().Equal(wasmdChannel.ChannelID, contractState.IcaInfo.ChannelID) + }) +} diff --git a/e2e/interchaintest/types/owner_contract.go b/e2e/interchaintest/types/owner_contract.go index 1c2908b6..6b36f19a 100644 --- a/e2e/interchaintest/types/owner_contract.go +++ b/e2e/interchaintest/types/owner_contract.go @@ -40,3 +40,35 @@ func StoreAndInstantiateNewOwnerContract( return NewOwnerContract(contract), nil } + +// QueryContractState queries the contract's state +func (c *OwnerContract) QueryContractState(ctx context.Context) (*OwnerContractState, error) { + queryResp := QueryResponse[OwnerContractState]{} + err := c.chain.QueryContract(ctx, c.Address, newOwnerGetContractStateQueryMsg(), &queryResp) + if err != nil { + return nil, err + } + + contractState, err := queryResp.GetResp() + if err != nil { + return nil, err + } + + return &contractState, nil +} + +// QueryIcaContractState queries the contract's ica state for a given icaID +func (c *OwnerContract) QueryIcaContractState(ctx context.Context, icaID uint64) (*OwnerIcaContractState, error) { + queryResp := QueryResponse[OwnerIcaContractState]{} + err := c.chain.QueryContract(ctx, c.Address, newOwnerGetIcaContractStateQueryMsg(icaID), &queryResp) + if err != nil { + return nil, err + } + + icaContractState, err := queryResp.GetResp() + if err != nil { + return nil, err + } + + return &icaContractState, nil +} diff --git a/e2e/interchaintest/types/owner_msg.go b/e2e/interchaintest/types/owner_msg.go index 4100b23d..7a12d1f5 100644 --- a/e2e/interchaintest/types/owner_msg.go +++ b/e2e/interchaintest/types/owner_msg.go @@ -1,6 +1,9 @@ package types -import "fmt" +import ( + "encoding/json" + "fmt" +) // newOwnerInstantiateMsg creates a new InstantiateMsg. func newOwnerInstantiateMsg(admin *string, icaControllerCodeId uint64) string { @@ -11,11 +14,30 @@ func newOwnerInstantiateMsg(admin *string, icaControllerCodeId uint64) string { } } -// newOwnerCreateIcaContractMsg creates a new CreateIcaContractMsg. -func newOwnerCreateIcaContractMsg(salt *string) string { - if salt == nil { - return `{"create_ica_contract":{}}` - } else { - return fmt.Sprintf(`{"create_ica_contract":{"salt":"%s"}}`, *salt) +// NewOwnerCreateIcaContractMsg creates a new CreateIcaContractMsg. +func NewOwnerCreateIcaContractMsg(salt *string, coip *ChannelOpenInitOptions) string { + type CreateIcaContractMsg struct { + Salt *string `json:"salt,omitempty"` + // The options to initialize the IBC channel upon contract instantiation. + // If not specified, the IBC channel is not initialized, and the relayer must. + ChannelOpenInitOptions *ChannelOpenInitOptions `json:"channel_open_init_options,omitempty"` + } + + type CreateIcaContractMsgWrapper struct { + CreateIcaContractMsg CreateIcaContractMsg `json:"create_ica_contract"` + } + + createIcaContractMsg := CreateIcaContractMsgWrapper{ + CreateIcaContractMsg: CreateIcaContractMsg{ + Salt: salt, + ChannelOpenInitOptions: coip, + }, + } + + jsonBytes, err := json.Marshal(createIcaContractMsg) + if err != nil { + panic(err) } + + return string(jsonBytes) } diff --git a/e2e/interchaintest/types/owner_query.go b/e2e/interchaintest/types/owner_query.go index d574802e..816fe6e5 100644 --- a/e2e/interchaintest/types/owner_query.go +++ b/e2e/interchaintest/types/owner_query.go @@ -20,11 +20,12 @@ func newOwnerGetIcaContractStateQueryMsg(icaID uint64) map[string]interface{} { } } -// newOwnerGetIcaCountQueryMsg creates a new GetIcaCountQueryMsg. -// This function returns a map[string]interface{} instead of []byte -// because interchaintest uses json.Marshal to convert the map to a string -func newOwnerGetIcaCountQueryMsg() map[string]interface{} { - return map[string]interface{}{ - "get_ica_count": struct{}{}, - } -} +// +// // newOwnerGetIcaCountQueryMsg creates a new GetIcaCountQueryMsg. +// // This function returns a map[string]interface{} instead of []byte +// // because interchaintest uses json.Marshal to convert the map to a string +// func newOwnerGetIcaCountQueryMsg() map[string]interface{} { +// return map[string]interface{}{ +// "get_ica_count": struct{}{}, +// } +// } From c0d0431c182300b101174554ae98dd9827425740 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sat, 4 Nov 2023 16:46:26 +0300 Subject: [PATCH 21/46] deps: added cosmos_sdk_proto to deps --- testing/contracts/cw-ica-owner/Cargo.lock | 1 + testing/contracts/cw-ica-owner/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/testing/contracts/cw-ica-owner/Cargo.lock b/testing/contracts/cw-ica-owner/Cargo.lock index bb4d4c22..517df61a 100644 --- a/testing/contracts/cw-ica-owner/Cargo.lock +++ b/testing/contracts/cw-ica-owner/Cargo.lock @@ -253,6 +253,7 @@ dependencies = [ name = "cw-ica-owner" version = "0.1.0" dependencies = [ + "cosmos-sdk-proto", "cosmwasm-schema", "cosmwasm-std", "cw-ica-controller", diff --git a/testing/contracts/cw-ica-owner/Cargo.toml b/testing/contracts/cw-ica-owner/Cargo.toml index ae6041cc..943f9c69 100644 --- a/testing/contracts/cw-ica-owner/Cargo.toml +++ b/testing/contracts/cw-ica-owner/Cargo.toml @@ -52,6 +52,7 @@ cw-ica-controller = { path = "../../..", features = ["library"] } schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } thiserror = { version = "1.0.31" } +cosmos-sdk-proto = { version = "0.20.0", default-features = false } [dev-dependencies] cw-multi-test = "0.16.5" From ba35d4cc5d748d93e55bbf5e730a2cd8851c88f9 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sat, 4 Nov 2023 17:09:54 +0300 Subject: [PATCH 22/46] imp: converted ContractError -> StdError for packet.rs --- src/contract.rs | 6 +++--- src/ibc/types/packet.rs | 15 +++++---------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index 1e42c377..6dd6e3f7 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -187,7 +187,7 @@ mod execute { amount: coins(100, "stake"), } .to_string(); - IcaPacketData::from_json_strings(vec![predefined_json_message], None)? + IcaPacketData::from_json_strings(vec![predefined_json_message], None) } }; let send_packet_msg = ica_packet.to_ibc_msg(&env, &ica_info.channel_id, None)?; @@ -320,7 +320,7 @@ mod tests { let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); let expected_packet = - IcaPacketData::from_json_strings(vec![custom_msg_str.to_string()], None).unwrap(); + IcaPacketData::from_json_strings(vec![custom_msg_str.to_string()], None); let expected_msg = expected_packet.to_ibc_msg(&env, "channel-0", None).unwrap(); assert_eq!(1, res.messages.len()); @@ -378,7 +378,7 @@ mod tests { } .to_string(); - let expected_packet = IcaPacketData::from_json_strings(vec![expected_msg], None).unwrap(); + let expected_packet = IcaPacketData::from_json_strings(vec![expected_msg], None); let expected_msg = expected_packet.to_ibc_msg(&env, "channel-0", None).unwrap(); assert_eq!(1, res.messages.len()); diff --git a/src/ibc/types/packet.rs b/src/ibc/types/packet.rs index eeac9bfb..95542953 100644 --- a/src/ibc/types/packet.rs +++ b/src/ibc/types/packet.rs @@ -3,13 +3,11 @@ //! This module contains the packet data to be send to the ica host and acknowledgement data types. use cosmwasm_schema::cw_serde; -use cosmwasm_std::{to_binary, Env, IbcMsg, IbcTimeout}; +use cosmwasm_std::{to_binary, Env, IbcMsg, IbcTimeout, StdResult}; pub use cosmos_sdk_proto::ibc::applications::interchain_accounts::v1::CosmosTx; use cosmos_sdk_proto::traits::Message; -use crate::types::ContractError; - /// DEFAULT_TIMEOUT_SECONDS is the default timeout for [`IcaPacketData`] pub const DEFAULT_TIMEOUT_SECONDS: u64 = 600; @@ -75,14 +73,11 @@ impl IcaPacketData { /// ``` /// /// In this example, the proposer must be the ICA controller's address. - pub fn from_json_strings( - messages: Vec, - memo: Option, - ) -> Result { + pub fn from_json_strings(messages: Vec, memo: Option) -> Self { let combined_messages = messages.join(", "); let json_txs = format!(r#"{{"messages": [{}]}}"#, combined_messages); let data = json_txs.into_bytes(); - Ok(Self::new(data, memo)) + Self::new(data, memo) } /// Creates a new IcaPacketData from a list of [`cosmos_sdk_proto::Any`] messages @@ -98,7 +93,7 @@ impl IcaPacketData { env: &Env, channel_id: impl Into, timeout_seconds: Option, - ) -> Result { + ) -> StdResult { let timeout_timestamp = env .block .time @@ -145,7 +140,7 @@ mod tests { } let packet_from_string = IcaPacketData::from_json_strings( - vec![r#"{"@type": "/cosmos.bank.v1beta1.MsgSend", "from_address": "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk", "to_address": "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk", "amount": [{"denom": "stake", "amount": "5000"}]}"#.to_string()], None).unwrap(); + vec![r#"{"@type": "/cosmos.bank.v1beta1.MsgSend", "from_address": "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk", "to_address": "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk", "amount": [{"denom": "stake", "amount": "5000"}]}"#.to_string()], None); let packet_data = packet_from_string.data; let cosmos_tx: TestCosmosTx = from_binary(&Binary(packet_data)).unwrap(); From 77a854b6ace4021fde29950a16d5c0e4406bee91 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sat, 4 Nov 2023 17:36:21 +0300 Subject: [PATCH 23/46] feat(testing/owner): implemented SendPredefined action --- testing/contracts/cw-ica-owner/Cargo.lock | 79 ++++++++---- testing/contracts/cw-ica-owner/Cargo.toml | 1 + .../contracts/cw-ica-owner/src/contract.rs | 82 +++++++++++- .../contracts/cw-ica-owner/src/cosmos_msg.rs | 117 ++++++++++++++++++ testing/contracts/cw-ica-owner/src/error.rs | 8 +- testing/contracts/cw-ica-owner/src/lib.rs | 1 + testing/contracts/cw-ica-owner/src/msg.rs | 10 ++ testing/contracts/cw-ica-owner/src/state.rs | 15 ++- 8 files changed, 276 insertions(+), 37 deletions(-) create mode 100644 testing/contracts/cw-ica-owner/src/cosmos_msg.rs diff --git a/testing/contracts/cw-ica-owner/Cargo.lock b/testing/contracts/cw-ica-owner/Cargo.lock index 517df61a..8a10bb69 100644 --- a/testing/contracts/cw-ica-owner/Cargo.lock +++ b/testing/contracts/cw-ica-owner/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[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", @@ -39,9 +39,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" @@ -49,6 +49,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "block-buffer" version = "0.9.0" @@ -113,11 +119,12 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6fb22494cf7d23d0c348740e06e5c742070b2991fd41db77bba0bcfbae1a723" +checksum = "d8bb3c77c3b7ce472056968c745eb501c440fbc07be5004eba02782c35bfbbe3" dependencies = [ "digest 0.10.7", + "ecdsa 0.16.8", "ed25519-zebra", "k256 0.13.1", "rand_core 0.6.4", @@ -126,18 +133,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e199424486ea97d6b211db6387fd72e26b4a439d40cc23140b2d8305728055b" +checksum = "fea73e9162e6efde00018d55ed0061e93a108b5d6ec4548b4f8ce3c706249687" dependencies = [ "syn 1.0.109", ] [[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", @@ -148,9 +155,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", @@ -159,11 +166,12 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d89d680fb60439b7c5947b15f9c84b961b88d1f8a3b20c4bd178a3f87db8bae" +checksum = "04d6864742e3a7662d024b51a94ea81c9af21db6faea2f9a6d2232bb97c6e53e" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -174,14 +182,15 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", ] [[package]] name = "cpufeatures" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -262,6 +271,7 @@ dependencies = [ "cw2", "schemars", "serde", + "serde-json-wasm", "thiserror", ] @@ -276,7 +286,7 @@ dependencies = [ "cw-storage-plus", "cw-utils", "derivative", - "itertools", + "itertools 0.10.5", "k256 0.11.6", "prost 0.9.0", "schemars", @@ -387,9 +397,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" [[package]] name = "ecdsa" @@ -589,6 +599,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -727,7 +746,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", @@ -740,7 +759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" dependencies = [ "anyhow", - "itertools", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.38", @@ -866,9 +885,9 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] @@ -893,9 +912,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", @@ -915,9 +934,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", @@ -988,6 +1007,12 @@ dependencies = [ "der 0.7.8", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" diff --git a/testing/contracts/cw-ica-owner/Cargo.toml b/testing/contracts/cw-ica-owner/Cargo.toml index 943f9c69..c129b8f2 100644 --- a/testing/contracts/cw-ica-owner/Cargo.toml +++ b/testing/contracts/cw-ica-owner/Cargo.toml @@ -51,6 +51,7 @@ cw2 = "1.1.0" cw-ica-controller = { path = "../../..", features = ["library"] } schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } +serde-json-wasm = "0.5.1" thiserror = { version = "1.0.31" } cosmos-sdk-proto = { version = "0.20.0", default-features = false } diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index a51198dd..a66fd781 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -1,6 +1,6 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; // use cw2::set_contract_version; use crate::error::ContractError; @@ -45,24 +45,37 @@ pub fn execute( salt, channel_open_init_options, } => execute::create_ica_contract(deps, env, info, salt, channel_open_init_options), + ExecuteMsg::SendPredefinedAction { ica_id, to_address } => { + execute::send_predefined_action(deps, info, ica_id, to_address) + } } } #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::GetContractState {} => to_binary(&query::state(deps)?), - QueryMsg::GetIcaContractState { ica_id } => to_binary(&query::ica_state(deps, ica_id)?), - QueryMsg::GetIcaCount {} => to_binary(&query::ica_count(deps)?), + QueryMsg::GetContractState {} => to_json_binary(&query::state(deps)?), + QueryMsg::GetIcaContractState { ica_id } => { + to_json_binary(&query::ica_state(deps, ica_id)?) + } + QueryMsg::GetIcaCount {} => to_json_binary(&query::ica_count(deps)?), } } mod execute { - use cosmwasm_std::instantiate2_address; + use cosmwasm_std::{instantiate2_address, Addr}; + use cw_ica_controller::helpers::CwIcaControllerContract; + use cw_ica_controller::ibc::types::packet::IcaPacketData; + use cw_ica_controller::types::msg::ExecuteMsg as IcaControllerExecuteMsg; use cw_ica_controller::{ - helpers::CwIcaControllerCode, types::msg::options::ChannelOpenInitOptions, + helpers::CwIcaControllerCode, ibc::types::metadata::TxEncoding, + types::msg::options::ChannelOpenInitOptions, }; + use cosmos_sdk_proto::cosmos::{bank::v1beta1::MsgSend, base::v1beta1::Coin}; + use cosmos_sdk_proto::Any; + + use crate::cosmos_msg::ExampleCosmosMessages; use crate::state::{self, ICA_COUNT, ICA_STATES}; use super::*; @@ -121,6 +134,63 @@ mod execute { Ok(Response::new().add_message(cosmos_msg)) } + + /// Sends a predefined action to the ICA host. + pub fn send_predefined_action( + deps: DepsMut, + info: MessageInfo, + ica_id: u64, + to_address: String, + ) -> Result { + let contract_state = STATE.load(deps.storage)?; + contract_state.verify_admin(info.sender)?; + + let ica_state = ICA_STATES.load(deps.storage, ica_id)?; + + let ica_info = if let Some(ica_info) = ica_state.ica_state { + ica_info + } else { + return Err(ContractError::IcaInfoNotSet {}); + }; + + let cw_ica_contract = CwIcaControllerContract::new(Addr::unchecked(&ica_info.ica_addr)); + + let ica_packet = match ica_info.tx_encoding { + TxEncoding::Protobuf => { + let predefined_proto_message = MsgSend { + from_address: ica_info.ica_addr, + to_address, + amount: vec![Coin { + denom: "stake".to_string(), + amount: "100".to_string(), + }], + }; + IcaPacketData::from_proto_anys( + vec![Any::from_msg(&predefined_proto_message)?], + None, + ) + } + TxEncoding::Proto3Json => { + let predefined_json_message = ExampleCosmosMessages::MsgSend { + from_address: ica_info.ica_addr, + to_address, + amount: cosmwasm_std::coins(100, "stake"), + } + .to_string(); + IcaPacketData::from_json_strings(vec![predefined_json_message], None) + } + }; + + let ica_controller_msg = IcaControllerExecuteMsg::SendCustomIcaMessages { + messages: Binary(ica_packet.data), + packet_memo: ica_packet.memo, + timeout_seconds: None, + }; + + let msg = cw_ica_contract.call(ica_controller_msg)?; + + Ok(Response::default().add_message(msg)) + } } mod query { diff --git a/testing/contracts/cw-ica-owner/src/cosmos_msg.rs b/testing/contracts/cw-ica-owner/src/cosmos_msg.rs new file mode 100644 index 00000000..228f3bf3 --- /dev/null +++ b/testing/contracts/cw-ica-owner/src/cosmos_msg.rs @@ -0,0 +1,117 @@ +//! This module defines the [`ExampleCosmosMessages`] enum which is used to encode Cosmos messages as Any's in +//! [`proto3json`](crate::ibc::types::metadata::TxEncoding::Proto3Json) encoding format. + +use cosmwasm_std::Coin; + +/// ExampleCosmosMessages is a list of Cosmos messages that can be sent to the ICA host if the channel handshake is +/// completed with the [`proto3json`](crate::ibc::types::metadata::TxEncoding::Proto3Json) encoding format. +/// +/// This enum corresponds to the [Any](https://github.com/cosmos/cosmos-sdk/blob/v0.47.3/codec/types/any.go#L11-L52) +/// type defined in the Cosmos SDK. The Any type is used to encode and decode Cosmos messages. It also has a built-in +/// json codec. This enum is used to encode Cosmos messages using json so that they can be deserialized as an Any by +/// the host chain using the Cosmos SDK's json codec. +/// +/// In general, this ICA controller should be used with custom messages and **not with the messages defined here**. +/// The messages defined here are to demonstrate how an ICA controller can be used with registered +/// ExampleCosmosMessages (in case the contract is a DAO with **predefined actions**) +/// +/// This enum does not derive [Deserialize](serde::Deserialize), see issue +/// [#1443](https://github.com/CosmWasm/cosmwasm/issues/1443) +#[derive(serde::Serialize, Clone, Debug, PartialEq)] +#[cfg_attr(test, derive(serde::Deserialize))] +#[serde(tag = "@type")] +pub enum ExampleCosmosMessages { + /// This is a Cosmos message to send tokens from one account to another. + #[serde(rename = "/cosmos.bank.v1beta1.MsgSend")] + MsgSend { + /// Sender's address. + from_address: String, + /// Recipient's address. + to_address: String, + /// Amount to send + amount: Vec, + }, + /// This is a Cosmos message to delegate tokens to a validator. + #[serde(rename = "/cosmos.staking.v1beta1.MsgDelegate")] + MsgDelegate { + /// Delegator's address. + delegator_address: String, + /// Validator's address. + validator_address: String, + /// Amount to delegate. + amount: Coin, + }, + /// This is a Cosmos message to vote on a proposal. + #[serde(rename = "/cosmos.gov.v1beta1.MsgVote")] + MsgVote { + /// Voter's address. + voter: String, + /// Proposal's ID. + proposal_id: u64, + /// Vote option. + option: u32, + }, + /// This is a legacy submit governance proposal message. + #[serde(rename = "/cosmos.gov.v1beta1.MsgSubmitProposal")] + MsgSubmitProposalLegacy { + /// Content is another Cosmos message. + content: Box, + /// Initial deposit to the proposal. + initial_deposit: Vec, + /// Proposer's address. (In this case, ICA address) + proposer: String, + }, + /// This is a text governance proposal message. + #[serde(rename = "/cosmos.gov.v1beta1.TextProposal")] + TextProposal { + /// Proposal's title + title: String, + /// Proposal's description + description: String, + }, + /// This is a Cosmos message to deposit tokens to a proposal. + #[serde(rename = "/cosmos.gov.v1beta1.MsgDeposit")] + MsgDeposit { + /// Proposal's ID. + proposal_id: u64, + /// Depositor's address. (In this case, ICA address) + depositor: String, + /// Amount to deposit. + amount: Vec, + }, + /// This is an IBC transfer message. + #[serde(rename = "/ibc.applications.transfer.v1.MsgTransfer")] + MsgTransfer { + /// Source port. + source_port: String, + /// Source channel id. + source_channel: String, + /// Amount to transfer. + token: Coin, + /// Sender's address. (In this case, ICA address) + sender: String, + /// Recipient's address. + receiver: String, + /// Timeout height. Disabled when set to 0. + timeout_height: msg_transfer::Height, + /// Timeout timestamp. Disabled when set to 0. + timeout_timestamp: u64, + /// Optional memo. + #[serde(skip_serializing_if = "Option::is_none")] + memo: Option, + }, +} + +impl ToString for ExampleCosmosMessages { + fn to_string(&self) -> String { + serde_json_wasm::to_string(self).unwrap() + } +} + +mod msg_transfer { + #[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq)] + pub struct Height { + pub revision_number: u64, + pub revision_height: u64, + } +} diff --git a/testing/contracts/cw-ica-owner/src/error.rs b/testing/contracts/cw-ica-owner/src/error.rs index 2412e16c..1a869d72 100644 --- a/testing/contracts/cw-ica-owner/src/error.rs +++ b/testing/contracts/cw-ica-owner/src/error.rs @@ -9,8 +9,12 @@ pub enum ContractError { #[error("error when computing the instantiate2 address: {0}")] Instantiate2AddressError(#[from] Instantiate2AddressError), + #[error("prost encoding error: {0}")] + ProstEncodeError(#[from] cosmos_sdk_proto::prost::EncodeError), + #[error("unauthorized")] Unauthorized {}, - // Add any other custom errors you like here. - // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. + + #[error("ica information is not set")] + IcaInfoNotSet {}, } diff --git a/testing/contracts/cw-ica-owner/src/lib.rs b/testing/contracts/cw-ica-owner/src/lib.rs index 233dbf55..8ae6fe2f 100644 --- a/testing/contracts/cw-ica-owner/src/lib.rs +++ b/testing/contracts/cw-ica-owner/src/lib.rs @@ -1,4 +1,5 @@ pub mod contract; +pub mod cosmos_msg; mod error; pub mod helpers; pub mod msg; diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index 3a3c073e..6396fe22 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -13,6 +13,16 @@ pub enum ExecuteMsg { salt: Option, channel_open_init_options: Option, }, + /// SendPredefinedAction sends a predefined action from the ICA controller to the ICA host. + /// This demonstration is useful for contracts that have predefined actions such as DAOs. + /// + /// In this example, the predefined action is a `MsgSend` message which sends 100 "stake" tokens. + SendPredefinedAction { + /// The ICA ID. + ica_id: u64, + /// The recipient's address, on the counterparty chain, to send the tokens to from ICA host. + to_address: String, + }, } #[cw_serde] diff --git a/testing/contracts/cw-ica-owner/src/state.rs b/testing/contracts/cw-ica-owner/src/state.rs index 76d7ed9a..a52806cd 100644 --- a/testing/contracts/cw-ica-owner/src/state.rs +++ b/testing/contracts/cw-ica-owner/src/state.rs @@ -13,6 +13,8 @@ pub const ICA_STATES: Map = Map::new("ica_states"); pub const ICA_COUNT: Item = Item::new("ica_count"); mod contract { + use crate::ContractError; + use super::*; /// ContractState is the state of the IBC application. @@ -32,6 +34,15 @@ mod contract { ica_controller_code_id, } } + + /// Checks if the address is the admin + pub fn verify_admin(&self, sender: impl Into) -> Result<(), ContractError> { + if self.admin == sender.into() { + Ok(()) + } else { + Err(ContractError::Unauthorized {}) + } + } } } @@ -51,7 +62,7 @@ mod ica { #[cw_serde] pub struct IcaState { pub ica_id: u32, - pub ica_addr: Addr, + pub ica_addr: String, pub tx_encoding: TxEncoding, pub channel_state: ChannelState, } @@ -70,7 +81,7 @@ mod ica { /// Creates a new [`IcaState`]. pub fn new( ica_id: u32, - ica_addr: Addr, + ica_addr: String, tx_encoding: TxEncoding, channel_state: ChannelState, ) -> Self { From 48f858bdb0152599a146f336763d5480867fe0f2 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 09:08:17 +0300 Subject: [PATCH 24/46] feat: added callback types --- src/ibc/relay.rs | 2 +- src/types/callbacks.rs | 41 +++++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 1 + 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/types/callbacks.rs diff --git a/src/ibc/relay.rs b/src/ibc/relay.rs index db904cde..9ce58b1f 100644 --- a/src/ibc/relay.rs +++ b/src/ibc/relay.rs @@ -30,7 +30,7 @@ pub fn ibc_packet_ack( } } -/// Handles the `PacketTimeout` for the IBC module. +/// Implements the IBC module's `OnTimeoutPacket` handler. #[entry_point] pub fn ibc_packet_timeout( deps: DepsMut, diff --git a/src/types/callbacks.rs b/src/types/callbacks.rs new file mode 100644 index 00000000..bc4e6b8b --- /dev/null +++ b/src/types/callbacks.rs @@ -0,0 +1,41 @@ +//! # Callbacks +//! +//! This module contains the callbacks that this contract can make to other contracts upon +//! channel and packet lifecycle events. + +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, IbcChannel, IbcPacket}; + +use crate::ibc::types::packet::acknowledgement::AcknowledgementData; + +/// IcaControllerCallbackMsg is the type of message that this contract can send to other contracts. +#[cw_serde] +pub enum IcaControllerCallbackMsg { + /// OnAcknowledgementPacketCallback is the callback that this contract makes to other contracts + /// when it receives an acknowledgement packet. + OnAcknowledgementPacketCallback { + /// The deserialized ICA acknowledgement data + ica_acknowledgement: AcknowledgementData, + /// The original packet that was sent + original_packet: IbcPacket, + /// The relayer that submitted acknowledgement packet + relayer: Addr, + }, + /// OnTimeoutPacketCallback is the callback that this contract makes to other contracts + /// when it receives a timeout packet. + OnTimeoutPacketCallback { + /// The original packet that was sent + original_packet: IbcPacket, + /// The relayer that submitted acknowledgement packet + relayer: Addr, + }, + /// OnChannelOpenAckCallback is the callback that this contract makes to other contracts + /// when it receives a channel open acknowledgement. + OnChannelOpenAckCallback { + /// The channel that was opened. It's version string is not used and should be ignored. + /// Instead the channel_version field of this message should be used. + channel: IbcChannel, + /// The version of the channel. + channel_version: String, + }, +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 6a16c49d..b35d68a0 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,5 +1,6 @@ //! This module contains the types used by the contract's execution and state logic. +pub mod callbacks; pub mod cosmos_msg; mod error; pub mod keys; From 97430caa860c07d13ecf516b368853373de20e2f Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 09:31:04 +0300 Subject: [PATCH 25/46] imp: added callback_address to state --- src/contract.rs | 11 ++++++++- src/types/msg.rs | 4 ++++ src/types/state.rs | 60 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index 6dd6e3f7..f82199d6 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -27,8 +27,14 @@ pub fn instantiate( info.sender }; + let callback_address = if let Some(callback_address) = msg.send_callbacks_to { + Some(deps.api.addr_validate(&callback_address)?) + } else { + None + }; + // Save the admin. Ica address is determined during handshake. - STATE.save(deps.storage, &ContractState::new(admin))?; + STATE.save(deps.storage, &ContractState::new(admin, callback_address))?; // Initialize the callback counter. CALLBACK_COUNTER.save(deps.storage, &CallbackCounter::default())?; @@ -257,6 +263,7 @@ mod tests { let msg = InstantiateMsg { admin: None, channel_open_init_options: None, + send_callbacks_to: None, }; // Ensure the contract is instantiated successfully @@ -294,6 +301,7 @@ mod tests { InstantiateMsg { admin: None, channel_open_init_options: None, + send_callbacks_to: None, }, ) .unwrap(); @@ -353,6 +361,7 @@ mod tests { InstantiateMsg { admin: None, channel_open_init_options: None, + send_callbacks_to: None, }, ) .unwrap(); diff --git a/src/types/msg.rs b/src/types/msg.rs index add934a7..6628c172 100644 --- a/src/types/msg.rs +++ b/src/types/msg.rs @@ -16,6 +16,10 @@ pub struct InstantiateMsg { /// If not specified, the IBC channel is not initialized, and the relayer must. #[serde(skip_serializing_if = "Option::is_none")] pub channel_open_init_options: Option, + /// The contract address that the channel and packet lifecycle callbacks are sent to. + /// If not specified, then no callbacks are sent. + #[serde(skip_serializing_if = "Option::is_none")] + pub send_callbacks_to: Option, } /// The messages to execute the ICA controller contract. diff --git a/src/types/state.rs b/src/types/state.rs index 400772bc..f07362b8 100644 --- a/src/types/state.rs +++ b/src/types/state.rs @@ -30,21 +30,25 @@ mod contract { pub admin: Addr, /// The Interchain Account (ICA) info needed to send packets. /// This is set during the handshake. - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] pub ica_info: Option, /// If true, the IBC application will accept `MsgChannelOpenInit` messages. #[serde(default)] pub allow_channel_open_init: bool, + /// The address of the callback contract. + #[serde(default)] + pub callback_address: Option, } impl ContractState { /// Creates a new ContractState - pub fn new(admin: Addr) -> Self { + pub fn new(admin: Addr, callback_address: Option) -> Self { Self { admin, ica_info: None, // We always allow the first `MsgChannelOpenInit` message. allow_channel_open_init: true, + callback_address, } } @@ -208,11 +212,12 @@ mod channel { mod tests { use super::*; - #[test] - fn test_migration_from_v0_1_2() { + mod v0_1_2 { + use super::*; + /// This is the contract state at version 0.1.2. #[cw_serde] - pub struct ContractStateV0_1_2 { + pub struct ContractState { /// The address of the admin of the IBC application. pub admin: Addr, /// The Interchain Account (ICA) info needed to send packets. @@ -220,10 +225,52 @@ mod tests { #[serde(skip_serializing_if = "Option::is_none")] pub ica_info: Option, } + } + + mod v0_1_3 { + use super::*; + + /// This is the contract state at version 0.1.3. + #[cw_serde] + pub struct ContractState { + /// The address of the admin of the IBC application. + pub admin: Addr, + /// The Interchain Account (ICA) info needed to send packets. + /// This is set during the handshake. + #[serde(skip_serializing_if = "Option::is_none")] + pub ica_info: Option, + /// If true, the IBC application will accept `MsgChannelOpenInit` messages. + #[serde(default)] + pub allow_channel_open_init: bool, + } + } + + #[test] + fn test_migration_from_v0_1_2_to_v0_1_3() { + let mock_state = v0_1_2::ContractState { + admin: Addr::unchecked("admin"), + ica_info: None, + }; - let mock_state = ContractStateV0_1_2 { + let serialized = cosmwasm_std::to_binary(&mock_state).unwrap(); + + let deserialized: v0_1_3::ContractState = cosmwasm_std::from_binary(&serialized).unwrap(); + + let exp_state = v0_1_3::ContractState { admin: Addr::unchecked("admin"), ica_info: None, + allow_channel_open_init: false, + }; + + assert_eq!(deserialized, exp_state); + } + + #[test] + fn test_migration_from_v0_1_3_to_v0_2_0() { + let mock_state = v0_1_3::ContractState { + admin: Addr::unchecked("admin"), + ica_info: None, + allow_channel_open_init: false, }; let serialized = cosmwasm_std::to_binary(&mock_state).unwrap(); @@ -234,6 +281,7 @@ mod tests { admin: Addr::unchecked("admin"), ica_info: None, allow_channel_open_init: false, + callback_address: None, }; assert_eq!(deserialized, exp_state); From b7356ec978d3ef5d1bce588455524771b9f954b2 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 09:32:03 +0300 Subject: [PATCH 26/46] deps: bumped version to 0.2.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33d18ceb..5aba2c53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -223,7 +223,7 @@ dependencies = [ [[package]] name = "cw-ica-controller" -version = "0.1.3" +version = "0.2.0" dependencies = [ "base64 0.13.1", "cosmos-sdk-proto", diff --git a/Cargo.toml b/Cargo.toml index 68a5ebd0..c691cbcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-ica-controller" -version = "0.1.3" +version = "0.2.0" authors = ["srdtrk "] edition = "2021" description = "This is a cosmwasm implementation of an interchain accounts controller." From 4d60ddf5ce0a77db789a1996a96bd0ba6c230014 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 09:45:06 +0300 Subject: [PATCH 27/46] test: added test migrate --- src/contract.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/contract.rs b/src/contract.rs index f82199d6..f5c1773c 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -402,4 +402,50 @@ mod tests { let res = execute(deps.as_mut(), env, info, msg); assert_eq!(res.unwrap_err().to_string(), "unauthorized".to_string()); } + + // In this test, we aim to verify that the semver validation is performed correctly. + // And that the contract version in cw2 is updated correctly. + #[test] + fn test_migrate() { + let mut deps = mock_dependencies(); + + let info = mock_info("creator", &[]); + + // Instantiate the contract + let _res = instantiate( + deps.as_mut(), + mock_env(), + info.clone(), + InstantiateMsg { + admin: None, + channel_open_init_options: None, + send_callbacks_to: None, + }, + ) + .unwrap(); + + // We need to set the contract version manually to a lower version than the current version + cw2::set_contract_version(&mut deps.storage, CONTRACT_NAME, "0.0.1").unwrap(); + + // Ensure that the contract version is updated correctly + let contract_version = cw2::get_contract_version(&deps.storage).unwrap(); + assert_eq!(contract_version.contract, CONTRACT_NAME); + assert_eq!(contract_version.version, "0.0.1"); + + // Perform the migration + let _res = migrate(deps.as_mut(), mock_env(), MigrateMsg {}).unwrap(); + + let contract_version = cw2::get_contract_version(&deps.storage).unwrap(); + assert_eq!(contract_version.contract, CONTRACT_NAME); + assert_eq!(contract_version.version, CONTRACT_VERSION); + + // Ensure that the contract version cannot be downgraded + cw2::set_contract_version(&mut deps.storage, CONTRACT_NAME, "100.0.0").unwrap(); + + let res = migrate(deps.as_mut(), mock_env(), MigrateMsg {}); + assert_eq!( + res.unwrap_err().to_string(), + "invalid migration version: expected > 100.0.0, got 0.2.0".to_string() + ); + } } From 790f358108cd176d99b8633fbc6b1154ff8f8d42 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 11:28:07 +0300 Subject: [PATCH 28/46] feat: implemented callback logic --- src/ibc/handshake.rs | 41 ++++++++++------ src/ibc/relay.rs | 103 +++++++++++++++++++++++++++++++++++------ src/types/callbacks.rs | 16 ++++++- 3 files changed, 130 insertions(+), 30 deletions(-) diff --git a/src/ibc/handshake.rs b/src/ibc/handshake.rs index ebc57358..7341ee27 100644 --- a/src/ibc/handshake.rs +++ b/src/ibc/handshake.rs @@ -57,6 +57,8 @@ pub fn ibc_channel_close( } mod ibc_channel_open { + use crate::types::callbacks::IcaControllerCallbackMsg; + use super::*; /// Handles the `OpenInit` part of the IBC handshake. @@ -121,6 +123,8 @@ mod ibc_channel_open { channel: IbcChannel, counterparty_version: String, ) -> Result { + let mut state = STATE.load(deps.storage)?; + // portID cannot be host chain portID // this is not possible since it is wasm.CONTRACT_ADDRESS // but we check it anyway since this is a recreation of the go code @@ -141,24 +145,33 @@ mod ibc_channel_open { if metadata.address.is_empty() { return Err(ContractError::InvalidAddress {}); } - // save the address to the contract state - STATE.update( + + // update state with the ica info + state.set_ica_info( + metadata.address, + &channel.endpoint.channel_id, + metadata.encoding, + ); + STATE.save(deps.storage, &state)?; + + // Save the channel state + CHANNEL_STATE.save( deps.storage, - |mut contract_state| -> Result<_, ContractError> { - contract_state.set_ica_info( - metadata.address, - &channel.endpoint.channel_id, - metadata.encoding, - ); - Ok(contract_state) - }, + &ChannelState::new_open_channel(channel.clone()), )?; - // Save the channel state - CHANNEL_STATE.save(deps.storage, &ChannelState::new_open_channel(channel))?; + // make callback if needed + if let Some(callback_address) = state.callback_address { + let callback_msg = IcaControllerCallbackMsg::OnChannelOpenAckCallback { + channel, + channel_version: counterparty_version, + } + .into_cosmos_msg(callback_address)?; - // Return the response, emit events if needed. Core IBC modules will emit the events regardless. - Ok(IbcBasicResponse::default()) + Ok(IbcBasicResponse::default().add_message(callback_msg)) + } else { + Ok(IbcBasicResponse::default()) + } } } diff --git a/src/ibc/relay.rs b/src/ibc/relay.rs index 9ce58b1f..31d0b47b 100644 --- a/src/ibc/relay.rs +++ b/src/ibc/relay.rs @@ -25,8 +25,12 @@ pub fn ibc_packet_ack( ) -> Result { // This lets the ICA controller know whether or not the sent transactions succeeded. match from_binary(&ack.acknowledgement.data)? { - AcknowledgementData::Result(res) => ibc_packet_ack::success(deps, ack.original_packet, res), - AcknowledgementData::Error(err) => ibc_packet_ack::error(deps, ack.original_packet, err), + AcknowledgementData::Result(res) => { + ibc_packet_ack::success(deps, ack.original_packet, ack.relayer, res) + } + AcknowledgementData::Error(err) => { + ibc_packet_ack::error(deps, ack.original_packet, ack.relayer, err) + } } } @@ -35,13 +39,8 @@ pub fn ibc_packet_ack( pub fn ibc_packet_timeout( deps: DepsMut, _env: Env, - _msg: IbcPacketTimeoutMsg, + msg: IbcPacketTimeoutMsg, ) -> Result { - // Increment the callback counter. - CALLBACK_COUNTER.update(deps.storage, |mut cc| -> Result<_, ContractError> { - cc.timeout(); - Ok(cc) - })?; // Due to the semantics of ordered channels, the underlying channel end is closed. CHANNEL_STATE.update( deps.storage, @@ -51,7 +50,7 @@ pub fn ibc_packet_timeout( }, )?; - Ok(IbcBasicResponse::default()) + ibc_packet_timeout::callback(deps, msg.packet, msg.relayer) } /// Handles the `PacketReceive` for the IBC module. @@ -67,9 +66,12 @@ pub fn ibc_packet_receive( } mod ibc_packet_ack { - use cosmwasm_std::{Binary, IbcPacket}; + use cosmwasm_std::{Addr, Binary, IbcPacket}; - use crate::types::state::CALLBACK_COUNTER; + use crate::types::{ + callbacks::IcaControllerCallbackMsg, + state::{CALLBACK_COUNTER, STATE}, + }; use super::*; @@ -78,14 +80,32 @@ mod ibc_packet_ack { pub fn success( deps: DepsMut, packet: IbcPacket, + relayer: Addr, res: Binary, ) -> Result { - // Handle the success case. + let state = STATE.load(deps.storage)?; + CALLBACK_COUNTER.update(deps.storage, |mut counter| -> Result<_, ContractError> { counter.success(); Ok(counter) })?; - Ok(IbcBasicResponse::default().add_event(events::packet_ack::success(&packet, &res))) + + let success_event = events::packet_ack::success(&packet, &res); + + if let Some(contract_addr) = state.callback_address { + let callback_msg = IcaControllerCallbackMsg::OnAcknowledgementPacketCallback { + ica_acknowledgement: AcknowledgementData::Result(res), + original_packet: packet, + relayer, + } + .into_cosmos_msg(contract_addr)?; + + Ok(IbcBasicResponse::default() + .add_message(callback_msg) + .add_event(success_event)) + } else { + Ok(IbcBasicResponse::default().add_event(success_event)) + } } /// Handles the unsuccessful acknowledgement of an ica packet. This means that the @@ -93,13 +113,66 @@ mod ibc_packet_ack { pub fn error( deps: DepsMut, packet: IbcPacket, + relayer: Addr, err: String, ) -> Result { - // Handle the error. + let state = STATE.load(deps.storage)?; + CALLBACK_COUNTER.update(deps.storage, |mut counter| -> Result<_, ContractError> { counter.error(); Ok(counter) })?; - Ok(IbcBasicResponse::default().add_event(events::packet_ack::error(&packet, &err))) + + let error_event = events::packet_ack::error(&packet, &err); + + if let Some(contract_addr) = state.callback_address { + let callback_msg = IcaControllerCallbackMsg::OnAcknowledgementPacketCallback { + ica_acknowledgement: AcknowledgementData::Error(err), + original_packet: packet, + relayer, + } + .into_cosmos_msg(contract_addr)?; + + Ok(IbcBasicResponse::default() + .add_message(callback_msg) + .add_event(error_event)) + } else { + Ok(IbcBasicResponse::default().add_event(error_event)) + } + } +} + +mod ibc_packet_timeout { + use cosmwasm_std::{Addr, IbcPacket}; + + use crate::types::{callbacks::IcaControllerCallbackMsg, state::STATE}; + + use super::*; + + /// Handles the timeout callbacks. + pub fn callback( + deps: DepsMut, + packet: IbcPacket, + relayer: Addr, + ) -> Result { + let state = STATE.load(deps.storage)?; + + // Increment the callback counter. + CALLBACK_COUNTER.update(deps.storage, |mut cc| -> Result<_, ContractError> { + cc.timeout(); + Ok(cc) + })?; + + if let Some(contract_addr) = state.callback_address { + let callback_msg = IcaControllerCallbackMsg::OnTimeoutPacketCallback { + original_packet: packet, + relayer, + } + .into_cosmos_msg(contract_addr)?; + + Ok(IbcBasicResponse::default().add_message(callback_msg)) + } else { + Ok(IbcBasicResponse::default()) + } } } diff --git a/src/types/callbacks.rs b/src/types/callbacks.rs index bc4e6b8b..c99f062e 100644 --- a/src/types/callbacks.rs +++ b/src/types/callbacks.rs @@ -4,7 +4,7 @@ //! channel and packet lifecycle events. use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, IbcChannel, IbcPacket}; +use cosmwasm_std::{to_binary, Addr, CosmosMsg, IbcChannel, IbcPacket, StdResult, WasmMsg}; use crate::ibc::types::packet::acknowledgement::AcknowledgementData; @@ -39,3 +39,17 @@ pub enum IcaControllerCallbackMsg { channel_version: String, }, } + +impl IcaControllerCallbackMsg { + /// into_cosmos_msg converts this message into a WasmMsg::Execute message to be sent to the + /// named contract. + pub fn into_cosmos_msg(&self, contract_addr: impl Into) -> StdResult { + let execute = WasmMsg::Execute { + contract_addr: contract_addr.into(), + msg: to_binary(&self)?, + funds: vec![], + }; + + Ok(execute.into()) + } +} From 38e59341d565c8e28a00fcb27ad74b52351c05fc Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 13:28:37 +0300 Subject: [PATCH 29/46] imp: added UpdateAdmin and UpdateCallbackAddress --- src/contract.rs | 127 ++++++++++++++++++++++++++++++++++++++++++++++- src/types/msg.rs | 11 ++++ 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/src/contract.rs b/src/contract.rs index f5c1773c..ba40b2c2 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -79,6 +79,10 @@ pub fn execute( ExecuteMsg::SendPredefinedAction { to_address } => { execute::send_predefined_action(deps, env, info, to_address) } + ExecuteMsg::UpdateAdmin { admin } => execute::update_admin(deps, info, admin), + ExecuteMsg::UpdateCallbackAddress { callback_address } => { + execute::update_callback_address(deps, info, callback_address) + } } } @@ -200,6 +204,40 @@ mod execute { Ok(Response::default().add_message(send_packet_msg)) } + + /// Updates the admin address. + pub fn update_admin( + deps: DepsMut, + info: MessageInfo, + admin: String, + ) -> Result { + let mut contract_state = STATE.load(deps.storage)?; + contract_state.verify_admin(info.sender)?; + + contract_state.admin = deps.api.addr_validate(&admin)?; + STATE.save(deps.storage, &contract_state)?; + + Ok(Response::default()) + } + + /// Updates the callback address. + pub fn update_callback_address( + deps: DepsMut, + info: MessageInfo, + callback_address: Option, + ) -> Result { + let mut contract_state = STATE.load(deps.storage)?; + contract_state.verify_admin(info.sender)?; + + contract_state.callback_address = if let Some(callback_address) = callback_address { + Some(deps.api.addr_validate(&callback_address)?) + } else { + None + }; + STATE.save(deps.storage, &contract_state)?; + + Ok(Response::default()) + } } mod query { @@ -252,7 +290,7 @@ mod tests { use super::*; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{coins, SubMsg}; + use cosmwasm_std::{coins, Api, SubMsg}; #[test] fn test_instantiate() { @@ -403,6 +441,93 @@ mod tests { assert_eq!(res.unwrap_err().to_string(), "unauthorized".to_string()); } + #[test] + fn test_update_admin() { + let mut deps = mock_dependencies(); + + let env = mock_env(); + let info = mock_info("creator", &[]); + + // Instantiate the contract + let _res = instantiate( + deps.as_mut(), + env.clone(), + info.clone(), + InstantiateMsg { + admin: None, + channel_open_init_options: None, + send_callbacks_to: None, + }, + ) + .unwrap(); + + // Ensure the contract admin can update the admin + let new_admin = "new_admin".to_string(); + let msg = ExecuteMsg::UpdateAdmin { + admin: new_admin.clone(), + }; + let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + assert_eq!(0, res.messages.len()); + + let state = STATE.load(&deps.storage).unwrap(); + assert_eq!(state.admin, deps.api.addr_validate(&new_admin).unwrap()); + + // Ensure a non-admin cannot update the admin + let info = mock_info("non-admin", &[]); + let msg = ExecuteMsg::UpdateAdmin { + admin: "new_admin".to_string(), + }; + + let res = execute(deps.as_mut(), env, info, msg); + assert_eq!(res.unwrap_err().to_string(), "unauthorized".to_string()); + } + + #[test] + fn test_update_callback_address() { + let mut deps = mock_dependencies(); + + let env = mock_env(); + let info = mock_info("creator", &[]); + + // Instantiate the contract + let _res = instantiate( + deps.as_mut(), + env.clone(), + info.clone(), + InstantiateMsg { + admin: None, + channel_open_init_options: None, + send_callbacks_to: None, + }, + ) + .unwrap(); + + // Ensure the contract admin can update the callback address + let new_callback_address = "new_callback_address".to_string(); + let msg = ExecuteMsg::UpdateCallbackAddress { + callback_address: Some(new_callback_address.clone()), + }; + let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + + assert_eq!(0, res.messages.len()); + + let state = STATE.load(&deps.storage).unwrap(); + assert_eq!( + state.callback_address, + Some(deps.api.addr_validate(&new_callback_address).unwrap()) + ); + + // Ensure a non-admin cannot update the callback address + let info = mock_info("non-admin", &[]); + let msg = ExecuteMsg::UpdateCallbackAddress { + callback_address: Some("new_callback_address".to_string()), + }; + + let res = execute(deps.as_mut(), env, info, msg); + assert_eq!(res.unwrap_err().to_string(), "unauthorized".to_string()); + } + // In this test, we aim to verify that the semver validation is performed correctly. // And that the contract version in cw2 is updated correctly. #[test] diff --git a/src/types/msg.rs b/src/types/msg.rs index 6628c172..b464cfab 100644 --- a/src/types/msg.rs +++ b/src/types/msg.rs @@ -72,6 +72,17 @@ pub enum ExecuteMsg { /// The recipient's address, on the counterparty chain, to send the tokens to from ICA host. to_address: String, }, + /// UpdateAdmin updates the admin address. + UpdateAdmin { + /// The new admin address. + admin: String, + }, + /// UpdateCallbackAddress updates the contract callback address. + UpdateCallbackAddress { + /// The new callback address. + /// If not specified, then no callbacks are sent. + callback_address: Option, + }, } /// The messages to query the ICA controller contract. From 5a7f8b154ba4bceaebe86e939ee9f5997d5def51 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 13:31:58 +0300 Subject: [PATCH 30/46] deps(testing/owner): updated cw-ica-owner --- testing/contracts/cw-ica-owner/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/contracts/cw-ica-owner/Cargo.lock b/testing/contracts/cw-ica-owner/Cargo.lock index 8a10bb69..062eef47 100644 --- a/testing/contracts/cw-ica-owner/Cargo.lock +++ b/testing/contracts/cw-ica-owner/Cargo.lock @@ -244,7 +244,7 @@ dependencies = [ [[package]] name = "cw-ica-controller" -version = "0.1.3" +version = "0.2.0" dependencies = [ "cosmos-sdk-proto", "cosmwasm-schema", From ade371ea5e8f28621b1cee237a89956544a8a590 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 14:10:11 +0300 Subject: [PATCH 31/46] imp: improved callbacks api --- src/ibc/handshake.rs | 6 ++++-- src/types/callbacks.rs | 6 +++++- src/types/state.rs | 7 ++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ibc/handshake.rs b/src/ibc/handshake.rs index 7341ee27..4738740d 100644 --- a/src/ibc/handshake.rs +++ b/src/ibc/handshake.rs @@ -148,9 +148,9 @@ mod ibc_channel_open { // update state with the ica info state.set_ica_info( - metadata.address, + &metadata.address, &channel.endpoint.channel_id, - metadata.encoding, + metadata.encoding.clone(), ); STATE.save(deps.storage, &state)?; @@ -165,6 +165,8 @@ mod ibc_channel_open { let callback_msg = IcaControllerCallbackMsg::OnChannelOpenAckCallback { channel, channel_version: counterparty_version, + ica_address: metadata.address, + tx_encoding: metadata.encoding, } .into_cosmos_msg(callback_address)?; diff --git a/src/types/callbacks.rs b/src/types/callbacks.rs index c99f062e..9202b645 100644 --- a/src/types/callbacks.rs +++ b/src/types/callbacks.rs @@ -6,7 +6,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_binary, Addr, CosmosMsg, IbcChannel, IbcPacket, StdResult, WasmMsg}; -use crate::ibc::types::packet::acknowledgement::AcknowledgementData; +use crate::ibc::types::{packet::acknowledgement::AcknowledgementData, metadata::TxEncoding}; /// IcaControllerCallbackMsg is the type of message that this contract can send to other contracts. #[cw_serde] @@ -37,6 +37,10 @@ pub enum IcaControllerCallbackMsg { channel: IbcChannel, /// The version of the channel. channel_version: String, + /// The address of the interchain account that was created. + ica_address: String, + /// The tx encoding this ICA channel uses. + tx_encoding: TxEncoding, }, } diff --git a/src/types/state.rs b/src/types/state.rs index f07362b8..cd75c9ea 100644 --- a/src/types/state.rs +++ b/src/types/state.rs @@ -6,7 +6,7 @@ use cw_storage_plus::Item; use super::ContractError; -pub use channel::ChannelState; +pub use channel::{ChannelState, ChannelStatus}; pub use contract::{CallbackCounter, ContractState}; /// The item used to store the state of the IBC application. @@ -165,14 +165,19 @@ mod channel { /// ChannelState is the state of the IBC channel. #[cw_serde] pub enum ChannelStatus { + /// Uninitialized is the default state of the channel. #[serde(rename = "STATE_UNINITIALIZED_UNSPECIFIED")] Uninitialized, + /// Init is the state of the channel when it is created. #[serde(rename = "STATE_INIT")] Init, + /// TryOpen is the state of the channel when it is trying to open. #[serde(rename = "STATE_TRYOPEN")] TryOpen, + /// Open is the state of the channel when it is open. #[serde(rename = "STATE_OPEN")] Open, + /// Closed is the state of the channel when it is closed. #[serde(rename = "STATE_CLOSED")] Closed, } From 442e7c5e5396fe5cb120b92f76f8e50d6f1127bc Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 14:49:36 +0300 Subject: [PATCH 32/46] imp(testing/owner): added callback handling to owner --- .../contracts/cw-ica-owner/src/contract.rs | 51 ++++++++++++++++--- testing/contracts/cw-ica-owner/src/helpers.rs | 27 ---------- testing/contracts/cw-ica-owner/src/lib.rs | 1 - testing/contracts/cw-ica-owner/src/msg.rs | 2 + testing/contracts/cw-ica-owner/src/state.rs | 6 ++- 5 files changed, 50 insertions(+), 37 deletions(-) delete mode 100644 testing/contracts/cw-ica-owner/src/helpers.rs diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index a66fd781..f3032c6a 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -48,6 +48,9 @@ pub fn execute( ExecuteMsg::SendPredefinedAction { ica_id, to_address } => { execute::send_predefined_action(deps, info, ica_id, to_address) } + ExecuteMsg::IcaControllerCallbackMsg(callback_msg) => { + execute::ica_callback_handler(deps, info, callback_msg) + } } } @@ -66,7 +69,9 @@ mod execute { use cosmwasm_std::{instantiate2_address, Addr}; use cw_ica_controller::helpers::CwIcaControllerContract; use cw_ica_controller::ibc::types::packet::IcaPacketData; + use cw_ica_controller::types::callbacks::IcaControllerCallbackMsg; use cw_ica_controller::types::msg::ExecuteMsg as IcaControllerExecuteMsg; + use cw_ica_controller::types::state::{ChannelState, ChannelStatus}; use cw_ica_controller::{ helpers::CwIcaControllerCode, ibc::types::metadata::TxEncoding, types::msg::options::ChannelOpenInitOptions, @@ -76,7 +81,7 @@ mod execute { use cosmos_sdk_proto::Any; use crate::cosmos_msg::ExampleCosmosMessages; - use crate::state::{self, ICA_COUNT, ICA_STATES}; + use crate::state::{self, CONTRACT_ADDR_TO_ICA_ID, ICA_COUNT, ICA_STATES}; use super::*; @@ -97,15 +102,12 @@ mod execute { let instantiate_msg = cw_ica_controller::types::msg::InstantiateMsg { admin: Some(env.contract.address.to_string()), channel_open_init_options, + send_callbacks_to: Some(env.contract.address.to_string()), }; let ica_count = ICA_COUNT.load(deps.storage).unwrap_or(0); - let salt = salt.unwrap_or(format!( - // "test", - "{}", - env.block.time.seconds() - )); + let salt = salt.unwrap_or(env.block.time.seconds().to_string()); let label = format!("icacontroller-{}-{}", env.contract.address, ica_count); let cosmos_msg = ica_code.instantiate2( @@ -126,10 +128,12 @@ mod execute { salt.as_bytes(), )?)?; - let initial_state = state::IcaContractState::new(contract_addr); + let initial_state = state::IcaContractState::new(contract_addr.clone()); ICA_STATES.save(deps.storage, ica_count, &initial_state)?; + CONTRACT_ADDR_TO_ICA_ID.save(deps.storage, contract_addr, &ica_count)?; + ICA_COUNT.save(deps.storage, &(ica_count + 1))?; Ok(Response::new().add_message(cosmos_msg)) @@ -191,6 +195,39 @@ mod execute { Ok(Response::default().add_message(msg)) } + + /// Handles ICA controller callback messages. + pub fn ica_callback_handler( + deps: DepsMut, + info: MessageInfo, + callback_msg: IcaControllerCallbackMsg, + ) -> Result { + let ica_id = CONTRACT_ADDR_TO_ICA_ID.load(deps.storage, info.sender)?; + let mut ica_state = ICA_STATES.load(deps.storage, ica_id)?; + + match callback_msg { + IcaControllerCallbackMsg::OnChannelOpenAckCallback { + channel, + ica_address, + tx_encoding, + } => { + ica_state.ica_state = Some(state::IcaState { + ica_id, + channel_state: ChannelState { + channel, + channel_status: ChannelStatus::Open, + }, + ica_addr: ica_address, + tx_encoding, + }); + + ICA_STATES.save(deps.storage, ica_id, &ica_state)?; + } + _ => (), + } + + Ok(Response::default()) + } } mod query { diff --git a/testing/contracts/cw-ica-owner/src/helpers.rs b/testing/contracts/cw-ica-owner/src/helpers.rs deleted file mode 100644 index a39b1558..00000000 --- a/testing/contracts/cw-ica-owner/src/helpers.rs +++ /dev/null @@ -1,27 +0,0 @@ -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; - -use crate::msg::ExecuteMsg; - -/// CwTemplateContract is a wrapper around Addr that provides a lot of helpers -/// for working with this. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub struct CwTemplateContract(pub Addr); - -impl CwTemplateContract { - pub fn addr(&self) -> Addr { - self.0.clone() - } - - pub fn call>(&self, msg: T) -> StdResult { - let msg = to_binary(&msg.into())?; - Ok(WasmMsg::Execute { - contract_addr: self.addr().into(), - msg, - funds: vec![], - } - .into()) - } -} diff --git a/testing/contracts/cw-ica-owner/src/lib.rs b/testing/contracts/cw-ica-owner/src/lib.rs index 8ae6fe2f..276ff6bc 100644 --- a/testing/contracts/cw-ica-owner/src/lib.rs +++ b/testing/contracts/cw-ica-owner/src/lib.rs @@ -1,7 +1,6 @@ pub mod contract; pub mod cosmos_msg; mod error; -pub mod helpers; pub mod msg; pub mod state; diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index 6396fe22..e65de07c 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -1,4 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cw_ica_controller::types::callbacks::IcaControllerCallbackMsg; use cw_ica_controller::types::msg::options::ChannelOpenInitOptions; #[cw_serde] @@ -23,6 +24,7 @@ pub enum ExecuteMsg { /// The recipient's address, on the counterparty chain, to send the tokens to from ICA host. to_address: String, }, + IcaControllerCallbackMsg(IcaControllerCallbackMsg), } #[cw_serde] diff --git a/testing/contracts/cw-ica-owner/src/state.rs b/testing/contracts/cw-ica-owner/src/state.rs index a52806cd..6dc2c77a 100644 --- a/testing/contracts/cw-ica-owner/src/state.rs +++ b/testing/contracts/cw-ica-owner/src/state.rs @@ -11,6 +11,8 @@ pub const STATE: Item = Item::new("state"); pub const ICA_STATES: Map = Map::new("ica_states"); /// The item used to store the count of the cw-ica-controller contracts. pub const ICA_COUNT: Item = Item::new("ica_count"); +/// The item used to map contract addresses to ICA IDs. +pub const CONTRACT_ADDR_TO_ICA_ID: Map = Map::new("contract_addr_to_ica_id"); mod contract { use crate::ContractError; @@ -61,7 +63,7 @@ mod ica { /// IcaState is the state of the ICA. #[cw_serde] pub struct IcaState { - pub ica_id: u32, + pub ica_id: u64, pub ica_addr: String, pub tx_encoding: TxEncoding, pub channel_state: ChannelState, @@ -80,7 +82,7 @@ mod ica { impl IcaState { /// Creates a new [`IcaState`]. pub fn new( - ica_id: u32, + ica_id: u64, ica_addr: String, tx_encoding: TxEncoding, channel_state: ChannelState, From 236dd6ee3ad37f3b3b6ccfed52ff79a7530effde Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 23:49:10 +0300 Subject: [PATCH 33/46] imp: improved callback api --- src/ibc/handshake.rs | 5 +++-- src/types/callbacks.rs | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ibc/handshake.rs b/src/ibc/handshake.rs index 4738740d..ad73d6ba 100644 --- a/src/ibc/handshake.rs +++ b/src/ibc/handshake.rs @@ -120,7 +120,7 @@ mod ibc_channel_open { /// Handles the `OpenAck` part of the IBC handshake. pub fn on_acknowledgement( deps: DepsMut, - channel: IbcChannel, + mut channel: IbcChannel, counterparty_version: String, ) -> Result { let mut state = STATE.load(deps.storage)?; @@ -154,6 +154,8 @@ mod ibc_channel_open { ); STATE.save(deps.storage, &state)?; + channel.version = counterparty_version; + // Save the channel state CHANNEL_STATE.save( deps.storage, @@ -164,7 +166,6 @@ mod ibc_channel_open { if let Some(callback_address) = state.callback_address { let callback_msg = IcaControllerCallbackMsg::OnChannelOpenAckCallback { channel, - channel_version: counterparty_version, ica_address: metadata.address, tx_encoding: metadata.encoding, } diff --git a/src/types/callbacks.rs b/src/types/callbacks.rs index 9202b645..1fafb1ff 100644 --- a/src/types/callbacks.rs +++ b/src/types/callbacks.rs @@ -6,7 +6,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_binary, Addr, CosmosMsg, IbcChannel, IbcPacket, StdResult, WasmMsg}; -use crate::ibc::types::{packet::acknowledgement::AcknowledgementData, metadata::TxEncoding}; +use crate::ibc::types::{metadata::TxEncoding, packet::acknowledgement::AcknowledgementData}; /// IcaControllerCallbackMsg is the type of message that this contract can send to other contracts. #[cw_serde] @@ -32,11 +32,8 @@ pub enum IcaControllerCallbackMsg { /// OnChannelOpenAckCallback is the callback that this contract makes to other contracts /// when it receives a channel open acknowledgement. OnChannelOpenAckCallback { - /// The channel that was opened. It's version string is not used and should be ignored. - /// Instead the channel_version field of this message should be used. + /// The channel that was opened. channel: IbcChannel, - /// The version of the channel. - channel_version: String, /// The address of the interchain account that was created. ica_address: String, /// The tx encoding this ICA channel uses. From b8fa2ac21f71ab2b6f5f4d194d5e1bf49cd2f734 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Sun, 5 Nov 2023 23:49:58 +0300 Subject: [PATCH 34/46] e2e: added a new e2e owner tests (not passing yet) --- e2e/interchaintest/owner_test.go | 76 +++++++++++++++++----- e2e/interchaintest/types/ica_contract.go | 2 +- e2e/interchaintest/types/owner_contract.go | 8 +++ e2e/interchaintest/types/owner_msg.go | 5 ++ 4 files changed, 74 insertions(+), 17 deletions(-) diff --git a/e2e/interchaintest/owner_test.go b/e2e/interchaintest/owner_test.go index e5bf07d9..41f8f82a 100644 --- a/e2e/interchaintest/owner_test.go +++ b/e2e/interchaintest/owner_test.go @@ -7,6 +7,8 @@ import ( "github.com/stretchr/testify/suite" + sdkmath "cosmossdk.io/math" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" @@ -42,30 +44,15 @@ func (s *OwnerTestSuite) SetupOwnerTestSuite(ctx context.Context) { s.Require().NoError(err) s.NumOfIcaContracts = 0 -} - -func TestWithOwnerTestSuite(t *testing.T) { - suite.Run(t, new(OwnerTestSuite)) -} - -func (s *OwnerTestSuite) TestCreateIcaContract() { - ctx := context.Background() - - // This starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, - // sets up the contract and does the channel handshake for the contract test suite. - s.SetupOwnerTestSuite(ctx) - wasmd, simd := s.ChainA, s.ChainB - wasmdUser := s.UserA // Create the ICA Contract - channelOpenInitOptions := types.ChannelOpenInitOptions{ ConnectionId: s.ChainAConnID, CounterpartyConnectionId: s.ChainBConnID, } createMsg := types.NewOwnerCreateIcaContractMsg(nil, &channelOpenInitOptions) - err := s.OwnerContract.Execute(ctx, wasmdUser.KeyName(), createMsg, "--gas", "500000") + err = s.OwnerContract.Execute(ctx, s.UserA.KeyName(), createMsg, "--gas", "500000") s.Require().NoError(err) s.NumOfIcaContracts++ @@ -73,6 +60,19 @@ func (s *OwnerTestSuite) TestCreateIcaContract() { // Wait for the channel to get set up err = testutil.WaitForBlocks(ctx, 5, s.ChainA, s.ChainB) s.Require().NoError(err) +} + +func TestWithOwnerTestSuite(t *testing.T) { + suite.Run(t, new(OwnerTestSuite)) +} + +func (s *OwnerTestSuite) TestCreateIcaContract() { + ctx := context.Background() + + // This starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, + // sets up the contract and does the channel handshake for the contract test suite. + s.SetupOwnerTestSuite(ctx) + wasmd, simd := s.ChainA, s.ChainB icaState, err := s.OwnerContract.QueryIcaContractState(ctx, 0) s.Require().NoError(err) @@ -130,3 +130,47 @@ func (s *OwnerTestSuite) TestCreateIcaContract() { s.Require().Equal(wasmdChannel.ChannelID, contractState.IcaInfo.ChannelID) }) } + +func (s *OwnerTestSuite) TestOwnerPredefinedAction() { + ctx := context.Background() + + // This starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, + // sets up the contract and does the channel handshake for the contract test suite. + s.SetupOwnerTestSuite(ctx) + wasmd, simd := s.ChainA, s.ChainB + wasmdUser, simdUser := s.UserA, s.UserB + + icaState, err := s.OwnerContract.QueryIcaContractState(ctx, 0) + s.Require().NoError(err) + + icaContract := types.NewIcaContract(types.NewContract(icaState.ContractAddr, strconv.FormatUint(s.IcaContractCodeId, 10), wasmd)) + + // Check contract state + contractState, err := icaContract.QueryContractState(ctx) + s.Require().NoError(err) + + icaAddress := contractState.IcaInfo.IcaAddress + + // Fund the ICA address: + s.FundAddressChainB(ctx, icaAddress) + + s.Run("TestSendPredefinedActionSuccess", func() { + err := s.OwnerContract.ExecSendPredefinedAction(ctx, wasmdUser.KeyName(), 0, simdUser.FormattedAddress()) + s.Require().NoError(err) + + err = testutil.WaitForBlocks(ctx, 6, wasmd, simd) + s.Require().NoError(err) + + icaBalance, err := simd.GetBalance(ctx, icaAddress, simd.Config().Denom) + s.Require().NoError(err) + s.Require().Equal(sdkmath.NewInt(1000000000-100), icaBalance) + + // Check if contract callbacks were executed: + callbackCounter, err := icaContract.QueryCallbackCounter(ctx) + s.Require().NoError(err) + + s.Require().Equal(uint64(1), callbackCounter.Success) + s.Require().Equal(uint64(0), callbackCounter.Error) + s.Require().Equal(uint64(0), callbackCounter.Timeout) + }) +} diff --git a/e2e/interchaintest/types/ica_contract.go b/e2e/interchaintest/types/ica_contract.go index c7cbc9e0..972aaa5c 100644 --- a/e2e/interchaintest/types/ica_contract.go +++ b/e2e/interchaintest/types/ica_contract.go @@ -49,7 +49,7 @@ func StoreAndInstantiateNewIcaContract( return NewIcaContract(contract), nil } -func (c *Contract) ExecCreateChannel( +func (c *IcaContract) ExecCreateChannel( ctx context.Context, callerKeyName string, connectionId string, counterpartyConnectionId string, counterpartyPortId *string, txEncoding *string, extraExecTxArgs ...string, diff --git a/e2e/interchaintest/types/owner_contract.go b/e2e/interchaintest/types/owner_contract.go index 6b36f19a..0689f33e 100644 --- a/e2e/interchaintest/types/owner_contract.go +++ b/e2e/interchaintest/types/owner_contract.go @@ -41,6 +41,14 @@ func StoreAndInstantiateNewOwnerContract( return NewOwnerContract(contract), nil } +func (c *OwnerContract) ExecSendPredefinedAction( + ctx context.Context, callerKeyName string, icaId uint64, toAddress string, +) error { + msg := newOwnerSendPredefinedActionMsg(icaId, toAddress) + err := c.Execute(ctx, callerKeyName, msg) + return err +} + // QueryContractState queries the contract's state func (c *OwnerContract) QueryContractState(ctx context.Context) (*OwnerContractState, error) { queryResp := QueryResponse[OwnerContractState]{} diff --git a/e2e/interchaintest/types/owner_msg.go b/e2e/interchaintest/types/owner_msg.go index 7a12d1f5..1c7ae2ce 100644 --- a/e2e/interchaintest/types/owner_msg.go +++ b/e2e/interchaintest/types/owner_msg.go @@ -41,3 +41,8 @@ func NewOwnerCreateIcaContractMsg(salt *string, coip *ChannelOpenInitOptions) st return string(jsonBytes) } + +// newOwnerSendPredefinedActionMsg creates a new SendPredefinedActionMsg. +func newOwnerSendPredefinedActionMsg(icaId uint64, toAddress string) string { + return fmt.Sprintf(`{"send_predefined_action":{"ica_id":%d,"to_address":"%s"}}`, icaId, toAddress) +} From f8f91dfce4056925a4da5ba5a4c2ec98d8d5083b Mon Sep 17 00:00:00 2001 From: srdtrk Date: Mon, 6 Nov 2023 22:18:44 +0300 Subject: [PATCH 35/46] imp: improved callback serialization --- src/types/callbacks.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/types/callbacks.rs b/src/types/callbacks.rs index 1fafb1ff..6ab48ea3 100644 --- a/src/types/callbacks.rs +++ b/src/types/callbacks.rs @@ -4,7 +4,7 @@ //! channel and packet lifecycle events. use cosmwasm_schema::cw_serde; -use cosmwasm_std::{to_binary, Addr, CosmosMsg, IbcChannel, IbcPacket, StdResult, WasmMsg}; +use cosmwasm_std::{to_binary, Addr, Binary, CosmosMsg, IbcChannel, IbcPacket, StdResult, WasmMsg}; use crate::ibc::types::{metadata::TxEncoding, packet::acknowledgement::AcknowledgementData}; @@ -42,15 +42,31 @@ pub enum IcaControllerCallbackMsg { } impl IcaControllerCallbackMsg { + /// serializes the message + pub fn into_binary(self) -> StdResult { + let msg = ReceiverExecuteMsg::ReceiveIcaCallback(self); + to_binary(&msg) + } + /// into_cosmos_msg converts this message into a WasmMsg::Execute message to be sent to the /// named contract. - pub fn into_cosmos_msg(&self, contract_addr: impl Into) -> StdResult { + pub fn into_cosmos_msg(self, contract_addr: impl Into) -> StdResult> + where + C: Clone + std::fmt::Debug + PartialEq, + { let execute = WasmMsg::Execute { contract_addr: contract_addr.into(), - msg: to_binary(&self)?, + msg: self.into_binary()?, funds: vec![], }; Ok(execute.into()) } } + +/// This is just a helper to properly serialize the above message. +/// The actual receiver should include this variant in the larger ExecuteMsg enum +#[cw_serde] +enum ReceiverExecuteMsg { + ReceiveIcaCallback(IcaControllerCallbackMsg), +} From f5dcf3ffbdab5738760d0fe6606a2d127147abb6 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Mon, 6 Nov 2023 22:27:32 +0300 Subject: [PATCH 36/46] imp(testing/owner): updated callback receiver api --- testing/contracts/cw-ica-owner/src/contract.rs | 2 +- testing/contracts/cw-ica-owner/src/msg.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index f3032c6a..f0a2a391 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -48,7 +48,7 @@ pub fn execute( ExecuteMsg::SendPredefinedAction { ica_id, to_address } => { execute::send_predefined_action(deps, info, ica_id, to_address) } - ExecuteMsg::IcaControllerCallbackMsg(callback_msg) => { + ExecuteMsg::ReceiveIcaCallback(callback_msg) => { execute::ica_callback_handler(deps, info, callback_msg) } } diff --git a/testing/contracts/cw-ica-owner/src/msg.rs b/testing/contracts/cw-ica-owner/src/msg.rs index e65de07c..616a5741 100644 --- a/testing/contracts/cw-ica-owner/src/msg.rs +++ b/testing/contracts/cw-ica-owner/src/msg.rs @@ -24,7 +24,7 @@ pub enum ExecuteMsg { /// The recipient's address, on the counterparty chain, to send the tokens to from ICA host. to_address: String, }, - IcaControllerCallbackMsg(IcaControllerCallbackMsg), + ReceiveIcaCallback(IcaControllerCallbackMsg), } #[cw_serde] From d7164079847d9883c1fecb5afc8e5520a948a568 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 7 Nov 2023 01:15:10 +0300 Subject: [PATCH 37/46] fix(testing/owner): used wrong address in predefined action --- testing/contracts/cw-ica-owner/src/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/contracts/cw-ica-owner/src/contract.rs b/testing/contracts/cw-ica-owner/src/contract.rs index f0a2a391..6f8cc79a 100644 --- a/testing/contracts/cw-ica-owner/src/contract.rs +++ b/testing/contracts/cw-ica-owner/src/contract.rs @@ -157,7 +157,7 @@ mod execute { return Err(ContractError::IcaInfoNotSet {}); }; - let cw_ica_contract = CwIcaControllerContract::new(Addr::unchecked(&ica_info.ica_addr)); + let cw_ica_contract = CwIcaControllerContract::new(Addr::unchecked(&ica_state.contract_addr)); let ica_packet = match ica_info.tx_encoding { TxEncoding::Protobuf => { From 26c9c37be25f25b7fe9b954d07c4ff1ea1e4cc72 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 7 Nov 2023 01:26:51 +0300 Subject: [PATCH 38/46] imp: removed SendPredefinedAction --- src/contract.rs | 114 +---------------------------------------------- src/types/msg.rs | 8 ---- 2 files changed, 2 insertions(+), 120 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index ba40b2c2..4da260d7 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -76,9 +76,6 @@ pub fn execute( packet_memo, timeout_seconds, ), - ExecuteMsg::SendPredefinedAction { to_address } => { - execute::send_predefined_action(deps, env, info, to_address) - } ExecuteMsg::UpdateAdmin { admin } => execute::update_admin(deps, info, admin), ExecuteMsg::UpdateCallbackAddress { callback_address } => { execute::update_callback_address(deps, info, callback_address) @@ -109,15 +106,7 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result Result { - let contract_state = STATE.load(deps.storage)?; - contract_state.verify_admin(info.sender)?; - let ica_info = contract_state.get_ica_info()?; - - let ica_packet = match ica_info.encoding { - TxEncoding::Protobuf => { - let predefined_proto_message = MsgSend { - from_address: ica_info.ica_address, - to_address, - amount: vec![Coin { - denom: "stake".to_string(), - amount: "100".to_string(), - }], - }; - IcaPacketData::from_proto_anys( - vec![Any::from_msg(&predefined_proto_message)?], - None, - ) - } - TxEncoding::Proto3Json => { - let predefined_json_message = ExampleCosmosMessages::MsgSend { - from_address: ica_info.ica_address, - to_address, - amount: coins(100, "stake"), - } - .to_string(); - IcaPacketData::from_json_strings(vec![predefined_json_message], None) - } - }; - let send_packet_msg = ica_packet.to_ibc_msg(&env, &ica_info.channel_id, None)?; - - Ok(Response::default().add_message(send_packet_msg)) - } - /// Updates the admin address. pub fn update_admin( deps: DepsMut, @@ -286,11 +234,10 @@ mod migrate { #[cfg(test)] mod tests { use crate::ibc::types::{metadata::TxEncoding, packet::IcaPacketData}; - use crate::types::cosmos_msg::ExampleCosmosMessages; use super::*; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{coins, Api, SubMsg}; + use cosmwasm_std::{Api, SubMsg}; #[test] fn test_instantiate() { @@ -384,63 +331,6 @@ mod tests { assert_eq!(res.unwrap_err().to_string(), "unauthorized".to_string()); } - #[test] - fn test_execute_send_predefined_action() { - let mut deps = mock_dependencies(); - - let env = mock_env(); - let info = mock_info("creator", &[]); - - // Instantiate the contract - let _res = instantiate( - deps.as_mut(), - env.clone(), - info.clone(), - InstantiateMsg { - admin: None, - channel_open_init_options: None, - send_callbacks_to: None, - }, - ) - .unwrap(); - - // for this unit test, we have to set ica info manually or else the contract will error - STATE - .update(&mut deps.storage, |mut state| -> StdResult { - state.set_ica_info("ica_address", "channel-0", TxEncoding::Proto3Json); - Ok(state) - }) - .unwrap(); - - // Ensure the contract admin can send predefined messages - let msg = ExecuteMsg::SendPredefinedAction { - to_address: "to_address".to_string(), - }; - let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - - let expected_msg = ExampleCosmosMessages::MsgSend { - from_address: "ica_address".to_string(), - to_address: "to_address".to_string(), - amount: coins(100, "stake"), - } - .to_string(); - - let expected_packet = IcaPacketData::from_json_strings(vec![expected_msg], None); - let expected_msg = expected_packet.to_ibc_msg(&env, "channel-0", None).unwrap(); - - assert_eq!(1, res.messages.len()); - assert_eq!(res.messages[0], SubMsg::new(expected_msg)); - - // Ensure a non-admin cannot send predefined messages - let info = mock_info("non-admin", &[]); - let msg = ExecuteMsg::SendPredefinedAction { - to_address: "to_address".to_string(), - }; - - let res = execute(deps.as_mut(), env, info, msg); - assert_eq!(res.unwrap_err().to_string(), "unauthorized".to_string()); - } - #[test] fn test_update_admin() { let mut deps = mock_dependencies(); diff --git a/src/types/msg.rs b/src/types/msg.rs index b464cfab..5fd3ba53 100644 --- a/src/types/msg.rs +++ b/src/types/msg.rs @@ -64,14 +64,6 @@ pub enum ExecuteMsg { #[serde(skip_serializing_if = "Option::is_none")] timeout_seconds: Option, }, - /// SendPredefinedAction sends a predefined action from the ICA controller to the ICA host. - /// This demonstration is useful for contracts that have predefined actions such as DAOs. - /// - /// In this example, the predefined action is a `MsgSend` message which sends 100 "stake" tokens. - SendPredefinedAction { - /// The recipient's address, on the counterparty chain, to send the tokens to from ICA host. - to_address: String, - }, /// UpdateAdmin updates the admin address. UpdateAdmin { /// The new admin address. From 49e799b4b61b78ef1e8d4babb14bea8f215025ec Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 7 Nov 2023 01:27:19 +0300 Subject: [PATCH 39/46] feat(e2e): owner predefined action test passing --- e2e/interchaintest/owner_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e/interchaintest/owner_test.go b/e2e/interchaintest/owner_test.go index 41f8f82a..93e96d43 100644 --- a/e2e/interchaintest/owner_test.go +++ b/e2e/interchaintest/owner_test.go @@ -76,6 +76,7 @@ func (s *OwnerTestSuite) TestCreateIcaContract() { icaState, err := s.OwnerContract.QueryIcaContractState(ctx, 0) s.Require().NoError(err) + s.Require().NotNil(icaState.IcaState) icaContract := types.NewIcaContract(types.NewContract(icaState.ContractAddr, strconv.FormatUint(s.IcaContractCodeId, 10), wasmd)) From 20cf3058d4fea94d1c065b34c3c7eb277158d41b Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 7 Nov 2023 01:50:53 +0300 Subject: [PATCH 40/46] feat(e2e): removed contract predefinedaction test --- e2e/interchaintest/contract_test.go | 41 +++++++++--------------- e2e/interchaintest/types/export_test.go | 5 --- e2e/interchaintest/types/ica_contract.go | 6 ---- e2e/interchaintest/types/ica_msg.go | 5 --- e2e/interchaintest/types/types_test.go | 3 -- 5 files changed, 15 insertions(+), 45 deletions(-) diff --git a/e2e/interchaintest/contract_test.go b/e2e/interchaintest/contract_test.go index b554436a..959c6014 100644 --- a/e2e/interchaintest/contract_test.go +++ b/e2e/interchaintest/contract_test.go @@ -15,6 +15,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" @@ -313,31 +314,11 @@ func (s *ContractTestSuite) IcaContractExecutionTestWithEncoding(encoding string // sets up the contract and does the channel handshake for the contract test suite. s.SetupContractTestSuite(ctx, encoding) wasmd, simd := s.ChainA, s.ChainB - wasmdUser, simdUser := s.UserA, s.UserB + wasmdUser := s.UserA // Fund the ICA address: s.FundAddressChainB(ctx, s.IcaAddress) - s.Run(fmt.Sprintf("TestSendPredefinedActionSuccess-%s", encoding), func() { - err := s.Contract.ExecPredefinedAction(ctx, wasmdUser.KeyName(), simdUser.FormattedAddress()) - s.Require().NoError(err) - - err = testutil.WaitForBlocks(ctx, 6, wasmd, simd) - s.Require().NoError(err) - - icaBalance, err := simd.GetBalance(ctx, s.IcaAddress, simd.Config().Denom) - s.Require().NoError(err) - s.Require().Equal(sdkmath.NewInt(1000000000-100), icaBalance) - - // Check if contract callbacks were executed: - callbackCounter, err := s.Contract.QueryCallbackCounter(ctx) - s.Require().NoError(err) - - s.Require().Equal(uint64(1), callbackCounter.Success) - s.Require().Equal(uint64(0), callbackCounter.Error) - s.Require().Equal(uint64(0), callbackCounter.Timeout) - }) - s.Run(fmt.Sprintf("TestSendCustomIcaMessagesSuccess-%s", encoding), func() { // Send custom ICA messages through the contract: // Let's create a governance proposal on simd and deposit some funds to it. @@ -371,7 +352,7 @@ func (s *ContractTestSuite) IcaContractExecutionTestWithEncoding(encoding string callbackCounter, err := s.Contract.QueryCallbackCounter(ctx) s.Require().NoError(err) - s.Require().Equal(uint64(2), callbackCounter.Success) + s.Require().Equal(uint64(1), callbackCounter.Success) s.Require().Equal(uint64(0), callbackCounter.Error) // Check if the proposal was created: @@ -398,7 +379,7 @@ func (s *ContractTestSuite) IcaContractExecutionTestWithEncoding(encoding string // Check if contract callbacks were executed: callbackCounter, err := s.Contract.QueryCallbackCounter(ctx) s.Require().NoError(err) - s.Require().Equal(uint64(2), callbackCounter.Success) + s.Require().Equal(uint64(1), callbackCounter.Success) s.Require().Equal(uint64(1), callbackCounter.Error) s.Require().Equal(uint64(0), callbackCounter.Timeout) }) @@ -411,7 +392,7 @@ func (s *ContractTestSuite) TestIcaContractTimeoutPacket() { // sets up the contract and does the channel handshake for the contract test suite. s.SetupContractTestSuite(ctx, icatypes.EncodingProto3JSON) wasmd, simd := s.ChainA, s.ChainB - wasmdUser, simdUser := s.UserA, s.UserB + wasmdUser, _ := s.UserA, s.UserB // Fund the ICA address: s.FundAddressChainB(ctx, s.IcaAddress) @@ -531,8 +512,16 @@ func (s *ContractTestSuite) TestIcaContractTimeoutPacket() { s.Require().Equal(uint64(1), callbackCounter.Timeout) }) - s.Run("TestPredefinedActionAfterReopen", func() { - err := s.Contract.ExecPredefinedAction(ctx, wasmdUser.KeyName(), simdUser.FormattedAddress()) + s.Run("TestSendCustomIcaMessagesAfterReopen", func() { + // Send custom ICA message through the contract: + sendMsg := &banktypes.MsgSend{ + FromAddress: s.IcaAddress, + ToAddress: s.UserB.FormattedAddress(), + Amount: sdk.NewCoins(sdk.NewCoin(simd.Config().Denom, sdkmath.NewInt(100))), + } + + // Execute the contract: + err = s.Contract.ExecCustomIcaMessages(ctx, wasmdUser.KeyName(), []proto.Message{sendMsg}, icatypes.EncodingProto3JSON, nil, nil) s.Require().NoError(err) err = testutil.WaitForBlocks(ctx, 10, wasmd, simd) diff --git a/e2e/interchaintest/types/export_test.go b/e2e/interchaintest/types/export_test.go index 9457ad69..7700a556 100644 --- a/e2e/interchaintest/types/export_test.go +++ b/e2e/interchaintest/types/export_test.go @@ -16,11 +16,6 @@ func NewSendCustomIcaMessagesMsg(cdc codec.BinaryCodec, msgs []proto.Message, en return newSendCustomIcaMessagesMsg(cdc, msgs, encoding, memo, timeout) } -// NewSendPredefinedActionMsg is a wrapper for newSendPredefinedActionMsg for internal testing -func NewSendPredefinedActionMsg(to_address string) string { - return newSendPredefinedActionMsg(to_address) -} - // NewGetChannelQueryMsg is a wrapper for newGetChannelQueryMsg for internal testing func NewGetChannelQueryMsg() map[string]interface{} { return newGetCallbackCounterQueryMsg() diff --git a/e2e/interchaintest/types/ica_contract.go b/e2e/interchaintest/types/ica_contract.go index 972aaa5c..74e65aef 100644 --- a/e2e/interchaintest/types/ica_contract.go +++ b/e2e/interchaintest/types/ica_contract.go @@ -70,12 +70,6 @@ func (c *IcaContract) ExecCustomIcaMessages( return err } -// ExecPredefinedAction executes the contract's predefined action message as the caller -func (c *IcaContract) ExecPredefinedAction(ctx context.Context, callerKeyName string, toAddress string) error { - err := c.Execute(ctx, callerKeyName, newSendPredefinedActionMsg(toAddress)) - return err -} - // QueryContractState queries the contract's state func (c *IcaContract) QueryContractState(ctx context.Context) (*IcaContractState, error) { queryResp := QueryResponse[IcaContractState]{} diff --git a/e2e/interchaintest/types/ica_msg.go b/e2e/interchaintest/types/ica_msg.go index 5e378a7b..8f816163 100644 --- a/e2e/interchaintest/types/ica_msg.go +++ b/e2e/interchaintest/types/ica_msg.go @@ -93,11 +93,6 @@ func NewCreateChannelMsg( return string(jsonBytes) } -// newSendPredefinedActionMsg creates a new SendPredefinedActionMsg. -func newSendPredefinedActionMsg(to_address string) string { - return fmt.Sprintf(`{"send_predefined_action":{"to_address":"%s"}}`, to_address) -} - // newSendCustomIcaMessagesMsg creates a new SendCustomIcaMessagesMsg. func newSendCustomIcaMessagesMsg(cdc codec.BinaryCodec, msgs []proto.Message, encoding string, memo *string, timeout *uint64) string { type SendCustomIcaMessagesMsg struct { diff --git a/e2e/interchaintest/types/types_test.go b/e2e/interchaintest/types/types_test.go index e3798ee3..526ac548 100644 --- a/e2e/interchaintest/types/types_test.go +++ b/e2e/interchaintest/types/types_test.go @@ -36,9 +36,6 @@ func TestExecuteMsgs(t *testing.T) { t.Parallel() // Basic tests: - sendPredefinedActionMsg := types.NewSendPredefinedActionMsg(testAddress) - require.Equal(t, `{"send_predefined_action":{"to_address":"srdtrk"}}`, sendPredefinedActionMsg) - sendCustomIcaMessagesMsg := types.NewSendCustomIcaMessagesMsg(nil, nil, "", nil, nil) require.Equal(t, `{"send_custom_ica_messages":{"messages":[]}}`, sendCustomIcaMessagesMsg) memo := "test" From 29afbd380820198cbf4817a07c497a0856cd3ecf Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 7 Nov 2023 22:32:27 +0300 Subject: [PATCH 41/46] docs: updated README --- README.md | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 522ede57..85f860af 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,9 @@ To create an interchain account, the relayer must start the channel handshake on ### Execute an interchain account transaction -In this contract, the `execute` message is used to commit a packet to be sent to the host chain. This contract has two ways of executing an interchain transaction: +In this contract, the `ExecuteMsg::SendCustomIcaMessages` message is used to commit a packet to be sent to the host chain. -1. `SendCustomIcaMessages`: This message requires the sender to give base64 encoded messages that will be sent to the host chain. The host chain will decode the messages and execute them. The result of the execution is sent back to this contract, and a callback is executed based on the result. +`SendCustomIcaMessages`: This message requires the sender to give base64 encoded messages that will be sent to the host chain. The host chain will decode the messages and execute them. The result of the execution is sent back to this contract, and a callback is executed based on the result. If the channel is using `proto3json` encoding, then the format that json messages have to take are defined by the cosmos-sdk's json codec. The following is an example of a json message that is submitting a text legacy governance: (In this example, the `proposer` is the address of the interchain account on the host chain) @@ -145,11 +145,25 @@ func NewSendCustomIcaMessagesMsg(cdc codec.BinaryCodec, msgs []proto.Message, en } ``` -2. `SendPredefinedAction`: This message sends a 100 stake from the ica account to a user defined address on the host chain. This action is used to demonstrate how you can have a contract that executes a predefined action on the host chain. This is more useful for DAOs or other contracts that need to execute specific actions on the host chain. This message type checks which encoding the channel is using, and sends the appropriate message to the host chain. The host chain then executes the message, and sends the result back to this contract. A callback is then executed based on the result. - ### Execute a callback -This contract supports callbacks. See [`src/ibc/relay.rs`](./src/ibc/relay.rs) to learn how to decode whether a transaction was successful or not. Currently, a counter is incremented to record how many transactions were successful and how many failed. This is just a placeholder for more complex logic that can be executed in the callback. +This contract supports external contract callbacks. See [`src/types/callbacks.rs`](./src/types/callbacks.rs) to learn what callbacks are supported. +This contract currently only supports sending callbacks to a single contract. You register the callback contract address during instantiation, or update it later using `ExecuteMsg::UpdateCallbackAddress`. + +The callback contract must include the following variant in its `ExecuteMsg` enum: + +```rust, ignore +use cosmwasm_schema::cw_serde; +use cw_ica_controller::types::callbacks::IcaControllerCallbackMsg; + +#[cw_serde] +pub enum ExecuteMsg { + // ... other variants + + /// The callback message from the ICA controller contract. + ReceiveIcaCallback(IcaControllerCallbackMsg), +} +``` ### Channel Closing and Reopening @@ -165,7 +179,7 @@ In general, the unit tests are for testing the verification functions for the ha ### End to end tests -The end to end tests are for testing the contract's functionality in an environment mimicking production. To see whether or not it can perform the channel handshake, send packets, and execute callbacks. We achieve this by running two local chains, one for the contract, and one for the host chain. The relayer is then used to perform the channel handshake, and send packets. The contract then executes callbacks based on the result of the packet. To learn more about how to run the end to end tests, see the [Readme](./e2e/Readme.md) in the `e2e` directory. +The end to end tests are for testing the contract's functionality in an environment mimicking production. To see whether or not it can perform the channel handshake, send packets, and execute callbacks. We achieve this by running two local chains, one for the contract, and one for the host chain. The relayer is then used to perform the channel handshake, and send packets. The contract then executes callbacks based on the result of the packet. To learn more about the end to end tests, see the [Readme](./e2e/Readme.md) in the `e2e` directory. ## Limitations @@ -173,9 +187,7 @@ This contract is not meant to be used in production. It is meant to be used as a - The contract cannot create multiple interchain accounts. It can only create one. - ICA channels must be ordered (enforced by golang ica/host module). Due to the semantics of ordered channels in IBC, any timeout will cause the channel to be closed. -- The relayer must start the channel handshake on the contract's chain. This is not possible to do in the contract itself. See e2e tests for an example of how to do this. -- The contract cannot initialize with an empty string as the version. This is due to a limitation of the IBCModule interface provided by ibc-go, see issue [#3942](https://github.com/cosmos/ibc-go/issues/3942). (The contract can be initialized with an empty version string if the chain supports stargate queries, but this is not the case for the end to end tests.) ## Acknowledgements -Much thanks to [Art3mix](https://github.com/Art3miX) for all the helpful discussions and nailing down of the encoding/decoding issues. Also thanks to [0xekez](https://github.com/0xekez) for their work on [cw-ibc-example](https://github.com/0xekez/cw-ibc-example) which was a great reference for CosmWasm IBC endpoints and interchaintest. +Much thanks to [Art3mix](https://github.com/Art3miX) and [CyberHoward](https://github.com/CyberHoward) for all the helpful discussions. Also thanks to [0xekez](https://github.com/0xekez) for their work on [cw-ibc-example](https://github.com/0xekez/cw-ibc-example) which was a great reference for CosmWasm IBC endpoints and interchaintest. From ead79c2e0ac3dbf8bfa808053351f22db67af8a0 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 7 Nov 2023 22:38:08 +0300 Subject: [PATCH 42/46] style(e2e): renamed a test --- e2e/interchaintest/owner_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/interchaintest/owner_test.go b/e2e/interchaintest/owner_test.go index 93e96d43..cec5f47e 100644 --- a/e2e/interchaintest/owner_test.go +++ b/e2e/interchaintest/owner_test.go @@ -66,7 +66,7 @@ func TestWithOwnerTestSuite(t *testing.T) { suite.Run(t, new(OwnerTestSuite)) } -func (s *OwnerTestSuite) TestCreateIcaContract() { +func (s *OwnerTestSuite) TestOwnerCreateIcaContract() { ctx := context.Background() // This starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, From 9704da9b8c9d3fe409d965bb9bdf8de0b90df6db Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 7 Nov 2023 22:38:50 +0300 Subject: [PATCH 43/46] ci: added owner e2e tests --- .github/workflows/e2e.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index fcfb2e54..d9bdb9f8 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -30,6 +30,8 @@ jobs: - TestIcaContractExecutionProto3JsonEncoding - TestIcaContractExecutionProtobufEncoding - TestIcaContractTimeoutPacket + - TestOwnerCreateIcaContract + - TestOwnerPredefinedAction name: ${{ matrix.test }} runs-on: ubuntu-latest steps: @@ -52,4 +54,8 @@ jobs: - name: TestContract run: | cd e2e/interchaintest - go test -v . -run TestWithContractTestSuite -testify.m ${{ matrix.test }} + if [[ ${{ matrix.test }} == TestOwner* ]]; then + go test -v . -run TestWithOwnerTestSuite -testify.m ${{ matrix.test }} + else + go test -v . -run TestWithContractTestSuite -testify.m ${{ matrix.test }} + fi From 76a10fa1ffa71d250ce30207e85e4552b308140c Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 7 Nov 2023 22:44:04 +0300 Subject: [PATCH 44/46] docs(e2e): readme updated --- e2e/Readme.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/e2e/Readme.md b/e2e/Readme.md index bdfa9544..e00ad337 100644 --- a/e2e/Readme.md +++ b/e2e/Readme.md @@ -2,8 +2,16 @@ The e2e tests are built using the [interchaintest](https://github.com/strangelove-ventures/interchaintest) library by Strangelove. It runs multiple docker container validators, and lets you test IBC enabled smart contracts. +These end to end tests are designed to run in the ci, but you can also run them locally. + ## Running the tests locally +The end to end tests are currently split into two parts: + +### ICA Contract Tests + +These tests are designed to test the ICA contract itself and its interaction with the relayer. + All contract tests are located in `interchaintest/contract_test.go` file. Currently, there are four tests in this file: - `TestIcaContractChannelHandshake` @@ -30,6 +38,22 @@ Before running the tests, you must have built the optimized contract in the `/ar cargo run-script optimize ``` +### Owner Contract Tests + +These tests are designed to test the ICA contract's interaction with external contracts such as callbacks. For this, a mock owner contract is used. + +All owner contract tests are located in `interchaintest/owner_test.go` file. Currently, there are two tests in this file: + +- `TestOwnerCreateIcaContract` +- `TestOwnerPredefinedAction` + +```text +cd interchaintest/ +go test -v . -run TestWithOwnerTestSuite -testify.m $TEST_NAME +``` + +where `$TEST_NAME` is one of the two tests listed above. + ## In the CI The tests are run in the github CI after every push to the `main` branch. See the [github actions workflow](https://github.com/srdtrk/cw-ica-controller/blob/main/.github/workflows/e2e.yml) for more details. @@ -38,4 +62,4 @@ For some unknown reason, the timeout test sometimes fails in the CI (I'd say abo ## About the tests -The tests are currently run on wasmd `v0.40.2` and ibc-go `v7.3.0`'s simd which implements json encoding feature for the interchain accounts module. +The tests are currently run on wasmd `v0.41.0` and ibc-go `v7.3.0`'s simd which implements json encoding feature for the interchain accounts module. From 345c47883e16d02f8b20858752c7acb6c7f66a61 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Tue, 7 Nov 2023 22:54:53 +0300 Subject: [PATCH 45/46] ci: fixed workflow --- .github/workflows/e2e.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d9bdb9f8..5e6484d2 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -37,6 +37,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v3 + - name: Build Owner Contract with Docker + run: | + docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="devcontract_cache_burner",target=/code/contracts/burner/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.14.0 ./testing/contracts/cw-ica-owner - name: Install cargo-run-script uses: actions-rs/cargo@v1 with: From 3be7b6a449deaec9bb2f8b5f9fa8d4bf2c468da2 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 8 Nov 2023 00:47:15 +0300 Subject: [PATCH 46/46] style: using more idiomatic rust --- src/contract.rs | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index 4da260d7..9e2dbf1d 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -21,17 +21,14 @@ pub fn instantiate( ) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - let admin = if let Some(admin) = msg.admin { - deps.api.addr_validate(&admin)? - } else { - info.sender - }; + let admin = msg + .admin + .map_or(Ok(info.sender), |admin| deps.api.addr_validate(&admin))?; - let callback_address = if let Some(callback_address) = msg.send_callbacks_to { - Some(deps.api.addr_validate(&callback_address)?) - } else { - None - }; + let callback_address = msg + .send_callbacks_to + .map(|addr| deps.api.addr_validate(&addr)) + .transpose()?; // Save the admin. Ica address is determined during handshake. STATE.save(deps.storage, &ContractState::new(admin, callback_address))?; @@ -177,11 +174,10 @@ mod execute { let mut contract_state = STATE.load(deps.storage)?; contract_state.verify_admin(info.sender)?; - contract_state.callback_address = if let Some(callback_address) = callback_address { - Some(deps.api.addr_validate(&callback_address)?) - } else { - None - }; + contract_state.callback_address = callback_address + .map(|addr| deps.api.addr_validate(&addr)) + .transpose()?; + STATE.save(deps.storage, &contract_state)?; Ok(Response::default())