diff --git a/.changelog/unreleased/features/954-restructure-mock-module-into-crate.md b/.changelog/unreleased/features/954-restructure-mock-module-into-crate.md new file mode 100644 index 000000000..266600f91 --- /dev/null +++ b/.changelog/unreleased/features/954-restructure-mock-module-into-crate.md @@ -0,0 +1,3 @@ +- Restructure the mock module implementation and separate its codebase into a + new crate named `ibc-testkit` + ([\#954](https://github.com/cosmos/ibc-rs/issues/953)) diff --git a/.changelog/unreleased/improvements/951-remove-default-portid.md b/.changelog/unreleased/improvements/951-remove-default-portid.md new file mode 100644 index 000000000..f9f586add --- /dev/null +++ b/.changelog/unreleased/improvements/951-remove-default-portid.md @@ -0,0 +1,2 @@ +- Remove the default value and implementation for `PortId` + ([\#951](https://github.com/cosmos/ibc-rs/issues/951)) diff --git a/.changelog/unreleased/improvements/952-expose-channel-message-types copy.md b/.changelog/unreleased/improvements/952-expose-channel-message-types copy.md new file mode 100644 index 000000000..e0a0ccbf2 --- /dev/null +++ b/.changelog/unreleased/improvements/952-expose-channel-message-types copy.md @@ -0,0 +1,2 @@ +- Expose domain message types under the `ics04_channel` as public + ([\#952](https://github.com/cosmos/ibc-rs/issues/952)) diff --git a/.gitignore b/.gitignore index fad389dbc..a7c66805b 100644 --- a/.gitignore +++ b/.gitignore @@ -14,9 +14,6 @@ target/ # Ignore VisualStudio .vscode -# Ignore chain's data -data - # Ignore Python artifacts .mypy_cache/ __pycache__/ diff --git a/Cargo.toml b/Cargo.toml index 5d7413065..87820e6e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ resolver = "2" members = [ "crates/ibc", "crates/ibc-derive", + "crates/ibc-testkit", "crates/ibc-query", ] diff --git a/README.md b/README.md index 2654f16d5..ac2cfd9fc 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,11 @@ the `ibc` rust crate which defines the main data structures and on-chain logic f ## Libraries - [ibc](crates/ibc/README.md) - Data structures and on-chain logic for the IBC protocol. -- [ibc-query](crates/ibc-query/README.md) - Utility traits and implementations for querying the -state of an `ibc-rs` enabled chain. - [ibc-derive](crates/ibc-derive/README.md) - Derive macros for `ClientState` and `ConsensusState` traits, reducing boilerplate. +- [ibc-testkit](crates/ibc-testkit/README.md) - Testing toolkit to aid `ibc-rs` and host chains in writing integration tests. +- [ibc-query](crates/ibc-query/README.md) - Utility traits and implementations for querying the +state of an `ibc-rs` enabled chain. ## Contributing diff --git a/ci/no-std-check/Cargo.lock b/ci/no-std-check/Cargo.lock index 86bab468e..440218f08 100644 --- a/ci/no-std-check/Cargo.lock +++ b/ci/no-std-check/Cargo.lock @@ -98,6 +98,135 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "ark-bls12-377" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "array-bytes" version = "6.1.0" @@ -391,6 +520,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +[[package]] +name = "constcat" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -509,6 +644,7 @@ dependencies = [ "platforms", "rustc_version", "subtle", + "zeroize", ] [[package]] @@ -586,6 +722,17 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +[[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 = "derive_more" version = "0.99.17" @@ -695,7 +842,9 @@ checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" dependencies = [ "curve25519-dalek 4.1.1", "ed25519", + "serde", "sha2 0.10.8", + "zeroize", ] [[package]] @@ -1072,7 +1221,6 @@ dependencies = [ "ibc-derive", "ibc-proto", "ics23", - "num-traits", "primitive-types", "prost", "serde", @@ -1084,7 +1232,6 @@ dependencies = [ "tendermint-light-client-verifier", "tendermint-proto", "time", - "tracing", "uint", ] @@ -1230,6 +1377,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.11.0" @@ -1445,6 +1601,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -1466,6 +1633,16 @@ dependencies = [ "itoa", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -1696,7 +1873,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", @@ -1956,9 +2133,9 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "scale-info" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ "bitvec", "cfg-if", @@ -1970,9 +2147,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -2186,9 +2363,9 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "sp-application-crypto" -version = "26.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74454c936a45ac55c8de95b9fd8b5e38f8b43d97df8f4274dd6777b20d95569" +checksum = "a93da025616ab59639f8e378df579c5aaa2c8b9999f328a0239156a57c991b53" dependencies = [ "parity-scale-codec", "scale-info", @@ -2200,9 +2377,9 @@ dependencies = [ [[package]] name = "sp-arithmetic" -version = "19.0.0" +version = "20.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e41f710a77e9debd1c9b80f862709dce648e50f0904cde4117488e7d11d4796d" +checksum = "f80b5c16afb61dde1037a469d570adcc686440036429e50abe2301ba9d61aad5" dependencies = [ "integer-sqrt", "num-traits", @@ -2215,9 +2392,9 @@ dependencies = [ [[package]] name = "sp-core" -version = "24.0.0" +version = "25.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7921d278ed2aebbb21a644c96e09663dc49a6139d1e2e063c059dc9f866e149b" +checksum = "f9ebb090ead698a6df04347c86a31ba91a387edb8a58534ec70c4f977d1e1e87" dependencies = [ "array-bytes", "bitflags 1.3.2", @@ -2256,14 +2433,15 @@ dependencies = [ "thiserror", "tiny-bip39", "tracing", + "w3f-bls", "zeroize", ] [[package]] name = "sp-core-hashing" -version = "12.0.0" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cb5c31aa385d6997a5b73fdc9837c1c0145559205198555c3000739a474767" +checksum = "cb8524f01591ee58b46cd83c9dbc0fcffd2fd730dabec4f59326cd58a00f17e2" dependencies = [ "blake2b_simd", "byteorder", @@ -2275,9 +2453,9 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "11.0.0" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f7d375610590566e11882bf5b5a4b8d0666a96ba86808b2650bbbd9be50bf8" +checksum = "50535e1a5708d3ba5c1195b59ebefac61cc8679c2c24716b87a86e8b7ed2e4a1" dependencies = [ "proc-macro2", "quote", @@ -2286,9 +2464,9 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede074871514ca7c5d2eca9563515d858c6220b47ae815714ed4393a4e99db4a" +checksum = "884d05160bc89d0943d1c9fb8006c3d44b80f37f8af607aeff8d4d9cc82e279a" dependencies = [ "environmental", "parity-scale-codec", @@ -2298,9 +2476,9 @@ dependencies = [ [[package]] name = "sp-io" -version = "26.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88fb6e281de5054565f07a9f79504d21133e115db549993c99f1b21236c677a5" +checksum = "0ced350da15e8ba3a106206840acc42a6d3eb0d7e8bf7aa43ab00eac0bdf956f" dependencies = [ "bytes", "ed25519-dalek", @@ -2323,9 +2501,9 @@ dependencies = [ [[package]] name = "sp-keystore" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9f19e773319d96223ce8dba960267e6cb977907537a8f738746ceb86592413" +checksum = "8b8ec5ebbba70bee83d79c3fe5e49f12df0a4bb6029858ddf9a15eea7539a592" dependencies = [ "parity-scale-codec", "parking_lot", @@ -2336,9 +2514,9 @@ dependencies = [ [[package]] name = "sp-panic-handler" -version = "11.0.0" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd099ba2d6c1bfe5d0c79aa56e440fa3c9257eadfc0c782c09cdc2122b1e60ed" +checksum = "b00e40857ed3e0187f145b037c733545c5633859f1bd1d1b09deb52805fa696a" dependencies = [ "backtrace", "lazy_static", @@ -2347,9 +2525,9 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c0641e1a9d340960b562bcceea1457680fd0e109fc1040f8f5364fd7bc2506" +checksum = "6d9c40ff7303e62219b55635e5245d963358cb77d6916250991ebcb82c0be2c6" dependencies = [ "either", "hash256-std-hasher", @@ -2370,9 +2548,9 @@ dependencies = [ [[package]] name = "sp-runtime-interface" -version = "20.0.0" +version = "21.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17a4030ad93f05c93f2cc294c74bc5fea227f90fb3d1426d4a6f165e017fb7ea" +checksum = "4f365332922a8cfa98ab00c6d08b1b0f24e159e730dd554e720d950ff3371b1f" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -2389,9 +2567,9 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b232943ee7ca83a6d56face33b8af12e9fb470a15a53835f4e12a6e452a41c1c" +checksum = "9b2afcbd1bd18d323371111b66b7ac2870bdc1c86c3d7b0dae67b112ca52b4d8" dependencies = [ "Inflector", "proc-macro-crate 1.3.1", @@ -2402,9 +2580,9 @@ dependencies = [ [[package]] name = "sp-state-machine" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf4c76bea1a9e4a2e79afe70f42f1d368a8a45308e58f19bfd755c5ddb2b4a3" +checksum = "96e087fa4430befd2047b61d912c9d6fa4eaed408c4b58b46c6e9acd7965f2d3" dependencies = [ "hash-db", "log", @@ -2424,15 +2602,15 @@ dependencies = [ [[package]] name = "sp-std" -version = "11.0.0" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c91d32e165d08a14098ce5ec923eaec59d1d0583758a18a770beec1b780b0d0" +checksum = "54c78c5a66682568cc7b153603c5d01a2cc8f5c221c7b1e921517a0eef18ae05" [[package]] name = "sp-storage" -version = "16.0.0" +version = "17.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9660ecd48314443e73ad0f44d58b76426666a1343d72f6f65664e174da9244" +checksum = "016f20812cc51bd479cc88d048c35d44cd3adde4accdb159d49d6050f2953595" dependencies = [ "impl-serde", "parity-scale-codec", @@ -2444,9 +2622,9 @@ dependencies = [ [[package]] name = "sp-tracing" -version = "13.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69a61948986d2a9f8d67d60884ff0277d910df09ebe08d0e1f309da777516453" +checksum = "0d727cb5265641ffbb7d4e42c18b63e29f6cfdbd240aae3bcf093c3d6eb29a19" dependencies = [ "parity-scale-codec", "sp-std", @@ -2457,9 +2635,9 @@ dependencies = [ [[package]] name = "sp-trie" -version = "25.0.0" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb2d292eb90452dcb0909fb44e74bf04395e3ffa37a66c0f1635a00600382a4" +checksum = "1e359b358263cc322c3f678c272a3a519621d9853dcfa1374dfcbdb5f54c6f85" dependencies = [ "ahash 0.8.3", "hash-db", @@ -2469,6 +2647,7 @@ dependencies = [ "nohash-hasher", "parity-scale-codec", "parking_lot", + "rand 0.8.5", "scale-info", "schnellru", "sp-core", @@ -2481,9 +2660,9 @@ dependencies = [ [[package]] name = "sp-wasm-interface" -version = "17.0.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf43bb0c8eb76dc41057ce0fb6b744b94c9aec28b31dff53a1efc4f04ef25384" +checksum = "d5d85813d46a22484cdf5e5afddbbe85442dd1b4d84d67a8c7792f92f9f93607" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -2495,9 +2674,9 @@ dependencies = [ [[package]] name = "sp-weights" -version = "23.0.0" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1cef0aad13ed8a8522a6e86ace16fb97ab220c16d2357e628352b528582693" +checksum = "751676c1263e7f3600af16bad26a7978a816bc532676fe05eafa23b862c05b9e" dependencies = [ "parity-scale-codec", "scale-info", @@ -2798,11 +2977,10 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2810,9 +2988,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", @@ -2821,9 +2999,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -2974,6 +3152,30 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "w3f-bls" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7335e4c132c28cc43caef6adb339789e599e39adbe78da0c4d547fad48cbc331" +dependencies = [ + "ark-bls12-377", + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-serialize-derive", + "arrayref", + "constcat", + "digest 0.10.7", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "sha2 0.10.8", + "sha3", + "thiserror", + "zeroize", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/crates/ibc-derive/src/utils.rs b/crates/ibc-derive/src/utils.rs index fca155424..333c18da2 100644 --- a/crates/ibc-derive/src/utils.rs +++ b/crates/ibc-derive/src/utils.rs @@ -58,7 +58,7 @@ impl Imports { } pub fn Any() -> TokenStream { - quote! {ibc_proto::google::protobuf::Any} + quote! {ibc::proto::Any} } pub fn Timestamp() -> TokenStream { diff --git a/crates/ibc-testkit/Cargo.toml b/crates/ibc-testkit/Cargo.toml new file mode 100644 index 000000000..cdb496220 --- /dev/null +++ b/crates/ibc-testkit/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "ibc-testkit" +version = "0.47.0" +edition = "2021" +readme = "README.md" +keywords = ["blockchain", "consensus", "cosmos", "ibc", "tendermint"] +repository = "https://github.com/cosmos/ibc-rs" +authors = ["Informal Systems "] +rust-version = "1.60" +description = """ + `ibc-testkit` is a versatile library that provides essential abstractions and implementations, + fulfilling a dual role of enabling rigorous integration testing for the `ibc-rs` implementation + while also aiding host chains in addressing a broad spectrum of testing scenarios during + their integration with `ibc-rs`. +""" + +[features] +default = ["std"] +std = [ + "ibc/std", + "tracing/std", + "prost/std", + "serde/std", + "serde_json/std", +] + +# This feature is required for token transfer (ICS-20) +serde = ["dep:serde", "ibc/serde", "serde_json"] + +[dependencies] +bytes = { version = "1.2.1", default-features = false } +derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display", "try_into"] } +displaydoc = { version = "0.2", default-features = false } +ibc = { version = "0.47.0" , path = "../ibc" } # NOTE: since `ibc-testkit` does not well support `no_std` yet, we keep `ibc` default features enabled +parking_lot = { version = "0.12.1", default-features = false } +prost = { version = "0.12", default-features = false } +serde = { version = "1.0", default-features = false, optional = true } +serde_json = { version = "1", default-features = false, optional = true } +subtle-encoding = { version = "0.5", default-features = false } +tendermint = { version = "0.34", default-features = false } +tendermint-testgen = { version = "0.34", default-features = false } +tracing = { version = "0.1.36", default-features = false } +typed-builder = "0.18.0" +primitive-types = { version = "0.12.0", default-features = false, features = ["serde_no_std"] } + + +[dev-dependencies] +env_logger = "0.10.0" +rstest = "0.18.1" +tracing-subscriber = { version = "0.3.14", features = ["fmt", "env-filter", "json"]} +test-log = { version = "0.2.10", features = ["trace"] } diff --git a/crates/ibc-testkit/README.md b/crates/ibc-testkit/README.md new file mode 100644 index 000000000..28b23dcd7 --- /dev/null +++ b/crates/ibc-testkit/README.md @@ -0,0 +1,6 @@ +# `ibc-testkit` + +`ibc-testkit` is a versatile library that provides essential abstractions and +implementations, fulfilling a dual role of enabling rigorous integration testing +for the `ibc-rs` implementation while also aiding host chains in addressing a +broad spectrum of testing scenarios during their integration with `ibc-rs`. diff --git a/crates/ibc/src/mock/host.rs b/crates/ibc-testkit/src/hosts/block.rs similarity index 87% rename from crates/ibc/src/mock/host.rs rename to crates/ibc-testkit/src/hosts/block.rs index e54e76e93..986ca56ee 100644 --- a/crates/ibc/src/mock/host.rs +++ b/crates/ibc-testkit/src/hosts/block.rs @@ -2,9 +2,15 @@ use core::str::FromStr; -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::lightclients::tendermint::v1::Header as RawHeader; -use ibc_proto::Protobuf as ErasedProtobuf; +use ibc::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; +use ibc::clients::ics07_tendermint::header::{Header, TENDERMINT_HEADER_TYPE_URL}; +use ibc::core::ics02_client::error::ClientError; +use ibc::core::ics24_host::identifier::ChainId; +use ibc::core::timestamp::Timestamp; +use ibc::prelude::*; +use ibc::proto::tendermint::v1::Header as RawHeader; +use ibc::proto::{Any, Protobuf}; +use ibc::Height; use tendermint::block::Header as TmHeader; use tendermint::validator::Set as ValidatorSet; use tendermint_testgen::light_block::TmLightBlock; @@ -13,16 +19,9 @@ use tendermint_testgen::{ Validator as TestgenValidator, }; -use super::context::AnyConsensusState; -use crate::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; -use crate::clients::ics07_tendermint::header::TENDERMINT_HEADER_TYPE_URL; -use crate::core::ics02_client::error::ClientError; -use crate::core::ics24_host::identifier::ChainId; -use crate::core::timestamp::Timestamp; -use crate::mock::consensus_state::MockConsensusState; -use crate::mock::header::MockHeader; -use crate::prelude::*; -use crate::Height; +use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; +use crate::testapp::ibc::clients::mock::header::MockHeader; +use crate::testapp::ibc::clients::AnyConsensusState; /// Defines the different types of host chains that a mock context can emulate. /// The variants are as follows: @@ -49,6 +48,22 @@ impl SyntheticTmBlock { } } +impl From for Header { + fn from(light_block: SyntheticTmBlock) -> Self { + let SyntheticTmBlock { + trusted_height, + trusted_next_validators, + light_block, + } = light_block; + Self { + signed_header: light_block.signed_header, + validator_set: light_block.validators, + trusted_height, + trusted_next_validator_set: trusted_next_validators, + } + } +} + /// Depending on `HostType` (the type of host chain underlying a context mock), this enum defines /// the type of blocks composing the history of the host chain. #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -205,7 +220,7 @@ impl From for AnyConsensusState { } } -impl ErasedProtobuf for HostBlock {} +impl Protobuf for HostBlock {} impl TryFrom for HostBlock { type Error = ClientError; diff --git a/crates/ibc-testkit/src/hosts/mod.rs b/crates/ibc-testkit/src/hosts/mod.rs new file mode 100644 index 000000000..a863eaad2 --- /dev/null +++ b/crates/ibc-testkit/src/hosts/mod.rs @@ -0,0 +1 @@ +pub mod block; diff --git a/crates/ibc-testkit/src/lib.rs b/crates/ibc-testkit/src/lib.rs new file mode 100644 index 000000000..3b42e390e --- /dev/null +++ b/crates/ibc-testkit/src/lib.rs @@ -0,0 +1,20 @@ +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![no_std] +#![deny( + warnings, + trivial_casts, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] +#![forbid(unsafe_code)] + +extern crate alloc; + +extern crate std; + +pub mod hosts; +pub mod relayer; +pub mod testapp; +pub mod utils; diff --git a/crates/ibc/src/mock/ics18_relayer/context.rs b/crates/ibc-testkit/src/relayer/context.rs similarity index 83% rename from crates/ibc/src/mock/ics18_relayer/context.rs rename to crates/ibc-testkit/src/relayer/context.rs index 735650cca..72090614f 100644 --- a/crates/ibc/src/mock/ics18_relayer/context.rs +++ b/crates/ibc-testkit/src/relayer/context.rs @@ -1,9 +1,10 @@ -use crate::core::ics24_host::identifier::ClientId; -use crate::core::ContextError; -use crate::mock::context::AnyClientState; -use crate::prelude::*; -use crate::signer::Signer; -use crate::Height; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::core::{ContextError, ValidationContext}; +use ibc::prelude::*; +use ibc::{Height, Signer}; + +use crate::testapp::ibc::clients::AnyClientState; +use crate::testapp::ibc::core::types::MockContext; /// Trait capturing all dependencies (i.e., the context) which algorithms in ICS18 require to /// relay packets between chains. This trait comprises the dependencies towards a single chain. @@ -22,25 +23,42 @@ pub trait RelayerContext { fn signer(&self) -> Signer; } +impl RelayerContext for MockContext { + fn query_latest_height(&self) -> Result { + ValidationContext::host_height(self) + } + + fn query_client_full_state(&self, client_id: &ClientId) -> Option { + // Forward call to Ics2. + self.client_state(client_id).ok() + } + + fn signer(&self) -> Signer { + "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C" + .to_string() + .into() + } +} + #[cfg(test)] mod tests { + use ibc::clients::ics07_tendermint::client_type as tm_client_type; + use ibc::core::ics02_client::client_state::ClientStateCommon; + use ibc::core::ics02_client::msgs::update_client::MsgUpdateClient; + use ibc::core::ics02_client::msgs::ClientMsg; + use ibc::core::ics24_host::identifier::{ChainId, ClientId}; + use ibc::core::MsgEnvelope; + use ibc::prelude::*; + use ibc::Height; use test_log::test; use tracing::debug; - use crate::clients::ics07_tendermint::client_type as tm_client_type; - use crate::core::ics02_client::client_state::ClientStateCommon; - use crate::core::ics02_client::msgs::update_client::MsgUpdateClient; - use crate::core::ics02_client::msgs::ClientMsg; - use crate::core::ics24_host::identifier::{ChainId, ClientId}; - use crate::core::MsgEnvelope; - use crate::mock::client_state::client_type as mock_client_type; - use crate::mock::context::MockContext; - use crate::mock::host::{HostBlock, HostType}; - use crate::mock::ics18_relayer::context::RelayerContext; - use crate::mock::ics18_relayer::error::RelayerError; - use crate::mock::router::MockRouter; - use crate::prelude::*; - use crate::Height; + use super::RelayerContext; + use crate::hosts::block::{HostBlock, HostType}; + use crate::relayer::error::RelayerError; + use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; + use crate::testapp::ibc::core::router::MockRouter; + use crate::testapp::ibc::core::types::MockContext; /// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` /// context, assuming that the latest header on the source context is `src_header`. @@ -114,7 +132,7 @@ mod tests { Some(client_on_a_for_b_height), ); // dummy; not actually used in client updates - let mut router_a = MockRouter::default(); + let mut router_a = MockRouter::new_with_transfer(); let mut ctx_b = MockContext::new( chain_id_b, @@ -130,7 +148,7 @@ mod tests { Some(client_on_b_for_a_height), ); // dummy; not actually used in client updates - let mut router_b = MockRouter::default(); + let mut router_b = MockRouter::new_with_transfer(); for _i in 0..num_iterations { // Update client on chain B to latest height of A. diff --git a/crates/ibc/src/mock/ics18_relayer/error.rs b/crates/ibc-testkit/src/relayer/error.rs similarity index 91% rename from crates/ibc/src/mock/ics18_relayer/error.rs rename to crates/ibc-testkit/src/relayer/error.rs index 43dedd7b5..3fdf5296a 100644 --- a/crates/ibc/src/mock/ics18_relayer/error.rs +++ b/crates/ibc-testkit/src/relayer/error.rs @@ -1,8 +1,7 @@ use displaydoc::Display; - -use crate::core::ics24_host::identifier::ClientId; -use crate::core::{ics03_connection, RouterError}; -use crate::Height; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::core::{ics03_connection, RouterError}; +use ibc::Height; #[derive(Debug, Display)] pub enum RelayerError { diff --git a/crates/ibc-testkit/src/relayer/mod.rs b/crates/ibc-testkit/src/relayer/mod.rs new file mode 100644 index 000000000..c2c641829 --- /dev/null +++ b/crates/ibc-testkit/src/relayer/mod.rs @@ -0,0 +1,2 @@ +pub mod context; +pub mod error; diff --git a/crates/ibc-testkit/src/testapp/ibc/applications/mod.rs b/crates/ibc-testkit/src/testapp/ibc/applications/mod.rs new file mode 100644 index 000000000..014e52f27 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/applications/mod.rs @@ -0,0 +1 @@ +pub mod transfer; diff --git a/crates/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs new file mode 100644 index 000000000..f8ec4ae88 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/context.rs @@ -0,0 +1,88 @@ +use ibc::applications::transfer::context::{ + cosmos_adr028_escrow_address, TokenTransferExecutionContext, TokenTransferValidationContext, +}; +use ibc::applications::transfer::error::TokenTransferError; +use ibc::applications::transfer::PrefixedCoin; +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::prelude::*; +use ibc::Signer; +use subtle_encoding::bech32; + +use super::types::DummyTransferModule; + +impl TokenTransferValidationContext for DummyTransferModule { + type AccountId = Signer; + + fn get_port(&self) -> Result { + Ok(PortId::transfer()) + } + + fn get_escrow_account( + &self, + port_id: &PortId, + channel_id: &ChannelId, + ) -> Result { + let addr = cosmos_adr028_escrow_address(port_id, channel_id); + Ok(bech32::encode("cosmos", addr).into()) + } + + fn can_send_coins(&self) -> Result<(), TokenTransferError> { + Ok(()) + } + + fn can_receive_coins(&self) -> Result<(), TokenTransferError> { + Ok(()) + } + + fn send_coins_validate( + &self, + _from_account: &Self::AccountId, + _to_account: &Self::AccountId, + _coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError> { + Ok(()) + } + + fn mint_coins_validate( + &self, + _account: &Self::AccountId, + _coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError> { + Ok(()) + } + + fn burn_coins_validate( + &self, + _account: &Self::AccountId, + _coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError> { + Ok(()) + } +} + +impl TokenTransferExecutionContext for DummyTransferModule { + fn send_coins_execute( + &mut self, + _from_account: &Self::AccountId, + _to_account: &Self::AccountId, + _coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError> { + Ok(()) + } + + fn mint_coins_execute( + &mut self, + _account: &Self::AccountId, + _coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError> { + Ok(()) + } + + fn burn_coins_execute( + &mut self, + _account: &Self::AccountId, + _coin: &PrefixedCoin, + ) -> Result<(), TokenTransferError> { + Ok(()) + } +} diff --git a/crates/ibc-testkit/src/testapp/ibc/applications/transfer/mod.rs b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/mod.rs new file mode 100644 index 000000000..7a971ee1d --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/mod.rs @@ -0,0 +1,3 @@ +pub mod context; +pub mod module; +pub mod types; diff --git a/crates/ibc-testkit/src/testapp/ibc/applications/transfer/module.rs b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/module.rs new file mode 100644 index 000000000..d2c0d1dde --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/module.rs @@ -0,0 +1,106 @@ +use ibc::core::ics04_channel::acknowledgement::Acknowledgement; +use ibc::core::ics04_channel::channel::{Counterparty, Order}; +use ibc::core::ics04_channel::error::{ChannelError, PacketError}; +use ibc::core::ics04_channel::packet::Packet; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; +use ibc::core::router::{Module, ModuleExtras}; +use ibc::prelude::*; +use ibc::Signer; + +use super::types::DummyTransferModule; + +impl Module for DummyTransferModule { + fn on_chan_open_init_validate( + &self, + _order: Order, + _connection_hops: &[ConnectionId], + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty: &Counterparty, + version: &Version, + ) -> Result { + Ok(version.clone()) + } + + fn on_chan_open_init_execute( + &mut self, + _order: Order, + _connection_hops: &[ConnectionId], + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty: &Counterparty, + version: &Version, + ) -> Result<(ModuleExtras, Version), ChannelError> { + Ok((ModuleExtras::empty(), version.clone())) + } + + fn on_chan_open_try_validate( + &self, + _order: Order, + _connection_hops: &[ConnectionId], + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty: &Counterparty, + counterparty_version: &Version, + ) -> Result { + Ok(counterparty_version.clone()) + } + + fn on_chan_open_try_execute( + &mut self, + _order: Order, + _connection_hops: &[ConnectionId], + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty: &Counterparty, + counterparty_version: &Version, + ) -> Result<(ModuleExtras, Version), ChannelError> { + Ok((ModuleExtras::empty(), counterparty_version.clone())) + } + + fn on_recv_packet_execute( + &mut self, + _packet: &Packet, + _relayer: &Signer, + ) -> (ModuleExtras, Acknowledgement) { + ( + ModuleExtras::empty(), + Acknowledgement::try_from(vec![1u8]).expect("Never fails"), + ) + } + + fn on_timeout_packet_validate( + &self, + _packet: &Packet, + _relayer: &Signer, + ) -> Result<(), PacketError> { + Ok(()) + } + + fn on_timeout_packet_execute( + &mut self, + _packet: &Packet, + _relayer: &Signer, + ) -> (ModuleExtras, Result<(), PacketError>) { + (ModuleExtras::empty(), Ok(())) + } + + fn on_acknowledgement_packet_validate( + &self, + _packet: &Packet, + _acknowledgement: &Acknowledgement, + _relayer: &Signer, + ) -> Result<(), PacketError> { + Ok(()) + } + + fn on_acknowledgement_packet_execute( + &mut self, + _packet: &Packet, + _acknowledgement: &Acknowledgement, + _relayer: &Signer, + ) -> (ModuleExtras, Result<(), PacketError>) { + (ModuleExtras::empty(), Ok(())) + } +} diff --git a/crates/ibc-testkit/src/testapp/ibc/applications/transfer/types.rs b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/types.rs new file mode 100644 index 000000000..f5d91a531 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/applications/transfer/types.rs @@ -0,0 +1,14 @@ +#[derive(Debug)] +pub struct DummyTransferModule; + +impl DummyTransferModule { + pub fn new() -> Self { + Self + } +} + +impl Default for DummyTransferModule { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/ibc/src/mock/client_state.rs b/crates/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs similarity index 92% rename from crates/ibc/src/mock/client_state.rs rename to crates/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index 4fa159cc1..cc631b40c 100644 --- a/crates/ibc/src/mock/client_state.rs +++ b/crates/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -1,29 +1,28 @@ use core::str::FromStr; use core::time::Duration; -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::mock::ClientState as RawMockClientState; -use ibc_proto::Protobuf; - -use crate::core::ics02_client::client_state::{ +use ibc::core::ics02_client::client_state::{ ClientStateCommon, ClientStateExecution, ClientStateValidation, Status, UpdateKind, }; -use crate::core::ics02_client::client_type::ClientType; -use crate::core::ics02_client::error::{ClientError, UpgradeClientError}; -use crate::core::ics02_client::{ClientExecutionContext, ClientValidationContext}; -use crate::core::ics23_commitment::commitment::{ +use ibc::core::ics02_client::client_type::ClientType; +use ibc::core::ics02_client::error::{ClientError, UpgradeClientError}; +use ibc::core::ics02_client::{ClientExecutionContext, ClientValidationContext}; +use ibc::core::ics23_commitment::commitment::{ CommitmentPrefix, CommitmentProofBytes, CommitmentRoot, }; -use crate::core::ics24_host::identifier::ClientId; -use crate::core::ics24_host::path::{ClientConsensusStatePath, ClientStatePath, Path}; -use crate::core::timestamp::Timestamp; -use crate::core::ContextError; -use crate::mock::client_state::client_type as mock_client_type; -use crate::mock::consensus_state::MockConsensusState; -use crate::mock::header::MockHeader; -use crate::mock::misbehaviour::Misbehaviour; -use crate::prelude::*; -use crate::Height; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::core::ics24_host::path::{ClientConsensusStatePath, ClientStatePath, Path}; +use ibc::core::timestamp::Timestamp; +use ibc::core::ContextError; +use ibc::prelude::*; +use ibc::proto::mock::ClientState as RawMockClientState; +use ibc::proto::{Any, Protobuf}; +use ibc::Height; + +use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; +use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; +use crate::testapp::ibc::clients::mock::header::MockHeader; +use crate::testapp::ibc::clients::mock::misbehaviour::Misbehaviour; pub const MOCK_CLIENT_STATE_TYPE_URL: &str = "/ibc.mock.ClientState"; pub const MOCK_CLIENT_TYPE: &str = "9999-mock"; @@ -87,7 +86,7 @@ impl TryFrom for MockClientState { impl From for RawMockClientState { fn from(value: MockClientState) -> Self { RawMockClientState { - header: Some(ibc_proto::ibc::mock::Header { + header: Some(ibc::proto::mock::Header { height: Some(value.header.height().into()), timestamp: value.header.timestamp.nanoseconds(), }), diff --git a/crates/ibc/src/mock/consensus_state.rs b/crates/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs similarity index 85% rename from crates/ibc/src/mock/consensus_state.rs rename to crates/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs index 4bf3e1129..39154deb5 100644 --- a/crates/ibc/src/mock/consensus_state.rs +++ b/crates/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs @@ -1,13 +1,12 @@ -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::mock::ConsensusState as RawMockConsensusState; -use ibc_proto::Protobuf; +use ibc::core::ics02_client::consensus_state::ConsensusState; +use ibc::core::ics02_client::error::ClientError; +use ibc::core::ics23_commitment::commitment::CommitmentRoot; +use ibc::core::timestamp::Timestamp; +use ibc::prelude::*; +use ibc::proto::mock::ConsensusState as RawMockConsensusState; +use ibc::proto::{Any, Protobuf}; -use crate::core::ics02_client::consensus_state::ConsensusState; -use crate::core::ics02_client::error::ClientError; -use crate::core::ics23_commitment::commitment::CommitmentRoot; -use crate::core::timestamp::Timestamp; -use crate::mock::header::MockHeader; -use crate::prelude::*; +use crate::testapp::ibc::clients::mock::header::MockHeader; pub const MOCK_CONSENSUS_STATE_TYPE_URL: &str = "/ibc.mock.ConsensusState"; @@ -49,7 +48,7 @@ impl TryFrom for MockConsensusState { impl From for RawMockConsensusState { fn from(value: MockConsensusState) -> Self { RawMockConsensusState { - header: Some(ibc_proto::ibc::mock::Header { + header: Some(ibc::proto::mock::Header { height: Some(value.header.height().into()), timestamp: value.header.timestamp.nanoseconds(), }), diff --git a/crates/ibc-testkit/src/testapp/ibc/clients/mock/header copy.rs b/crates/ibc-testkit/src/testapp/ibc/clients/mock/header copy.rs new file mode 100644 index 000000000..0ea825d26 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/clients/mock/header copy.rs @@ -0,0 +1,136 @@ +use alloc::string::ToString; +use core::fmt::{Display, Error as FmtError, Formatter}; + +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::mock::Header as RawMockHeader; +use ibc_proto::Protobuf; + +use crate::core::ics02_client::error::ClientError; +use crate::core::timestamp::Timestamp; +use crate::Height; + +pub const MOCK_HEADER_TYPE_URL: &str = "/ibc.mock.Header"; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct MockHeader { + pub height: Height, + pub timestamp: Timestamp, +} + +impl Default for MockHeader { + fn default() -> Self { + Self { + height: Height::min(0), + timestamp: Default::default(), + } + } +} + +impl Display for MockHeader { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!( + f, + "MockHeader {{ height: {}, timestamp: {} }}", + self.height, self.timestamp + ) + } +} + +impl Protobuf for MockHeader {} + +impl TryFrom for MockHeader { + type Error = ClientError; + + fn try_from(raw: RawMockHeader) -> Result { + Ok(MockHeader { + height: raw + .height + .and_then(|raw_height| raw_height.try_into().ok()) + .ok_or(ClientError::MissingClientMessage)?, + + timestamp: Timestamp::from_nanoseconds(raw.timestamp) + .map_err(ClientError::InvalidPacketTimestamp)?, + }) + } +} + +impl From for RawMockHeader { + fn from(value: MockHeader) -> Self { + RawMockHeader { + height: Some(value.height.into()), + timestamp: value.timestamp.nanoseconds(), + } + } +} + +impl MockHeader { + pub fn height(&self) -> Height { + self.height + } + + pub fn new(height: Height) -> Self { + Self { + height, + timestamp: Timestamp::none(), + } + } + + #[cfg(any(test, feature = "std"))] + pub fn with_current_timestamp(self) -> Self { + self.with_timestamp(Timestamp::now()) + } + + pub fn with_timestamp(self, timestamp: Timestamp) -> Self { + Self { timestamp, ..self } + } +} + +impl Protobuf for MockHeader {} + +impl TryFrom for MockHeader { + type Error = ClientError; + + fn try_from(raw: Any) -> Result { + match raw.type_url.as_str() { + MOCK_HEADER_TYPE_URL => Ok(Protobuf::::decode_vec(&raw.value).map_err( + |e| ClientError::InvalidRawHeader { + reason: e.to_string(), + }, + )?), + _ => Err(ClientError::UnknownHeaderType { + header_type: raw.type_url, + }), + } + } +} + +impl From for Any { + fn from(header: MockHeader) -> Self { + Any { + type_url: MOCK_HEADER_TYPE_URL.to_string(), + value: Protobuf::::encode_vec(header), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn encode_any() { + let header = MockHeader::new(Height::new(1, 10).expect("Never fails")) + .with_timestamp(Timestamp::none()); + let bytes = >::encode_vec(header); + + assert_eq!( + &bytes, + &[ + 10, 16, 47, 105, 98, 99, 46, 109, 111, 99, 107, 46, 72, 101, 97, 100, 101, 114, 18, + 6, 10, 4, 8, 1, 16, 10 + ] + ); + } +} diff --git a/crates/ibc/src/mock/header.rs b/crates/ibc-testkit/src/testapp/ibc/clients/mock/header.rs similarity index 86% rename from crates/ibc/src/mock/header.rs rename to crates/ibc-testkit/src/testapp/ibc/clients/mock/header.rs index bf5ce05eb..bad80b6bd 100644 --- a/crates/ibc/src/mock/header.rs +++ b/crates/ibc-testkit/src/testapp/ibc/clients/mock/header.rs @@ -1,13 +1,11 @@ use alloc::string::ToString; use core::fmt::{Display, Error as FmtError, Formatter}; -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::mock::Header as RawMockHeader; -use ibc_proto::Protobuf; - -use crate::core::ics02_client::error::ClientError; -use crate::core::timestamp::Timestamp; -use crate::Height; +use ibc::core::ics02_client::error::ClientError; +use ibc::core::timestamp::Timestamp; +use ibc::proto::mock::Header as RawMockHeader; +use ibc::proto::{Any, Protobuf}; +use ibc::Height; pub const MOCK_HEADER_TYPE_URL: &str = "/ibc.mock.Header"; @@ -70,16 +68,16 @@ impl MockHeader { } pub fn new(height: Height) -> Self { - if cfg!(any(test, feature = "std")) { - Self { - height, - timestamp: Timestamp::now(), - } - } else { - Self { - height, - timestamp: Timestamp::none(), - } + Self { + height, + timestamp: Timestamp::none(), + } + } + + pub fn with_current_timestamp(self) -> Self { + Self { + timestamp: Timestamp::now(), + ..self } } diff --git a/crates/ibc/src/mock/misbehaviour.rs b/crates/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs similarity index 88% rename from crates/ibc/src/mock/misbehaviour.rs rename to crates/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs index 192d78955..71a852869 100644 --- a/crates/ibc/src/mock/misbehaviour.rs +++ b/crates/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs @@ -1,12 +1,11 @@ use bytes::Buf; -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::mock::Misbehaviour as RawMisbehaviour; -use ibc_proto::Protobuf; +use ibc::core::ics02_client::error::ClientError; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::prelude::*; +use ibc::proto::mock::Misbehaviour as RawMisbehaviour; +use ibc::proto::{Any, Protobuf}; -use crate::core::ics02_client::error::ClientError; -use crate::core::ics24_host::identifier::ClientId; -use crate::mock::header::MockHeader; -use crate::prelude::*; +use crate::testapp::ibc::clients::mock::header::MockHeader; pub const MOCK_MISBEHAVIOUR_TYPE_URL: &str = "/ibc.mock.Misbehavior"; diff --git a/crates/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs b/crates/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs new file mode 100644 index 000000000..746825f7f --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs @@ -0,0 +1,5 @@ +//! Definitions of ibc mock types used in testing. +pub mod client_state; +pub mod consensus_state; +pub mod header; +pub mod misbehaviour; diff --git a/crates/ibc-testkit/src/testapp/ibc/clients/mod.rs b/crates/ibc-testkit/src/testapp/ibc/clients/mod.rs new file mode 100644 index 000000000..d7b09e638 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/clients/mod.rs @@ -0,0 +1,91 @@ +pub mod mock; + +use derive_more::{From, TryInto}; +use ibc::clients::ics07_tendermint::client_state::{ + ClientState as TmClientState, TENDERMINT_CLIENT_STATE_TYPE_URL, +}; +use ibc::clients::ics07_tendermint::consensus_state::{ + ConsensusState as TmConsensusState, TENDERMINT_CONSENSUS_STATE_TYPE_URL, +}; +use ibc::core::ics02_client::client_state::ClientState; +use ibc::core::ics02_client::consensus_state::ConsensusState; +use ibc::core::ics02_client::error::ClientError; +use ibc::prelude::*; +use ibc::proto::{Any, Protobuf}; + +use crate::testapp::ibc::clients::mock::client_state::{ + MockClientState, MOCK_CLIENT_STATE_TYPE_URL, +}; +use crate::testapp::ibc::clients::mock::consensus_state::{ + MockConsensusState, MOCK_CONSENSUS_STATE_TYPE_URL, +}; +use crate::testapp::ibc::core::types::MockContext; + +#[derive(Debug, Clone, From, PartialEq, ClientState)] +#[generics(ClientValidationContext = MockContext, + ClientExecutionContext = MockContext) +] +pub enum AnyClientState { + Tendermint(TmClientState), + Mock(MockClientState), +} + +impl Protobuf for AnyClientState {} + +impl TryFrom for AnyClientState { + type Error = ClientError; + + fn try_from(raw: Any) -> Result { + if raw.type_url == TENDERMINT_CLIENT_STATE_TYPE_URL { + TmClientState::try_from(raw).map(Into::into) + } else if raw.type_url == MOCK_CLIENT_STATE_TYPE_URL { + MockClientState::try_from(raw).map(Into::into) + } else { + Err(ClientError::Other { + description: "failed to deserialize message".to_string(), + }) + } + } +} + +impl From for Any { + fn from(host_client_state: AnyClientState) -> Self { + match host_client_state { + AnyClientState::Tendermint(cs) => cs.into(), + AnyClientState::Mock(cs) => cs.into(), + } + } +} + +#[derive(Debug, Clone, From, TryInto, PartialEq, ConsensusState)] +pub enum AnyConsensusState { + Tendermint(TmConsensusState), + Mock(MockConsensusState), +} + +impl Protobuf for AnyConsensusState {} + +impl TryFrom for AnyConsensusState { + type Error = ClientError; + + fn try_from(raw: Any) -> Result { + if raw.type_url == TENDERMINT_CONSENSUS_STATE_TYPE_URL { + TmConsensusState::try_from(raw).map(Into::into) + } else if raw.type_url == MOCK_CONSENSUS_STATE_TYPE_URL { + MockConsensusState::try_from(raw).map(Into::into) + } else { + Err(ClientError::Other { + description: "failed to deserialize message".to_string(), + }) + } + } +} + +impl From for Any { + fn from(host_consensus_state: AnyConsensusState) -> Self { + match host_consensus_state { + AnyConsensusState::Tendermint(cs) => cs.into(), + AnyConsensusState::Mock(cs) => cs.into(), + } + } +} diff --git a/crates/ibc/src/mock/context/clients.rs b/crates/ibc-testkit/src/testapp/ibc/core/client_ctx.rs similarity index 87% rename from crates/ibc/src/mock/context/clients.rs rename to crates/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index cac89cd6d..17bb7ff61 100644 --- a/crates/ibc/src/mock/context/clients.rs +++ b/crates/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -1,18 +1,33 @@ -//! Client context implementations for `MockContext` +use alloc::collections::BTreeMap; +use alloc::vec::Vec; -use super::{AnyClientState, AnyConsensusState, MockClientRecord, MockContext}; -use crate::clients::ics07_tendermint::{ +use ibc::clients::ics07_tendermint::{ CommonContext as TmCommonContext, ValidationContext as TmValidationContext, }; -use crate::core::ics02_client::error::ClientError; -use crate::core::ics02_client::{ClientExecutionContext, ClientValidationContext}; -use crate::core::ics24_host::identifier::ClientId; -use crate::core::ics24_host::path::{ClientConsensusStatePath, ClientStatePath}; -use crate::core::timestamp::Timestamp; -use crate::core::{ContextError, ValidationContext}; -use crate::mock::client_state::MockClientContext; -use crate::prelude::*; -use crate::Height; +use ibc::core::ics02_client::error::ClientError; +use ibc::core::ics02_client::{ClientExecutionContext, ClientValidationContext}; +use ibc::core::ics24_host::identifier::{ChannelId, ClientId, PortId}; +use ibc::core::ics24_host::path::{ClientConsensusStatePath, ClientStatePath}; +use ibc::core::timestamp::Timestamp; +use ibc::core::{ContextError, ValidationContext}; +use ibc::Height; + +use crate::testapp::ibc::clients::mock::client_state::MockClientContext; +use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; +use crate::testapp::ibc::core::types::MockContext; + +pub type PortChannelIdMap = BTreeMap>; + +/// A mock of an IBC client record as it is stored in a mock context. +/// For testing ICS02 handlers mostly, cf. `MockClientContext`. +#[derive(Clone, Debug)] +pub struct MockClientRecord { + /// The client state (representing only the latest height at the moment). + pub client_state: Option, + + /// Mapping of heights to consensus states for this client. + pub consensus_states: BTreeMap, +} impl MockClientContext for MockContext { type ConversionError = &'static str; diff --git a/crates/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/crates/ibc-testkit/src/testapp/ibc/core/core_ctx.rs new file mode 100644 index 000000000..fa917b493 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -0,0 +1,548 @@ +//! Implementation of a global context mock. Used in testing handlers of all IBC modules. + +use core::ops::Add; +use core::time::Duration; + +use ibc::clients::ics07_tendermint::client_state::ClientState as TmClientState; +use ibc::core::events::IbcEvent; +use ibc::core::ics02_client::error::ClientError; +use ibc::core::ics03_connection::connection::ConnectionEnd; +use ibc::core::ics03_connection::error::ConnectionError; +use ibc::core::ics04_channel::channel::ChannelEnd; +use ibc::core::ics04_channel::commitment::{AcknowledgementCommitment, PacketCommitment}; +use ibc::core::ics04_channel::error::{ChannelError, PacketError}; +use ibc::core::ics04_channel::packet::{Receipt, Sequence}; +use ibc::core::ics23_commitment::commitment::CommitmentPrefix; +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::core::ics24_host::path::{ + AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, CommitmentPath, + ConnectionPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, +}; +use ibc::core::timestamp::Timestamp; +use ibc::core::{ContextError, ExecutionContext, ValidationContext}; +use ibc::prelude::*; +use ibc::proto::Any; +use ibc::{Height, Signer}; + +use super::types::MockContext; +use crate::testapp::ibc::clients::mock::client_state::MockClientState; +use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; + +impl ValidationContext for MockContext { + type V = Self; + type E = Self; + type AnyConsensusState = AnyConsensusState; + type AnyClientState = AnyClientState; + + fn client_state(&self, client_id: &ClientId) -> Result { + match self.ibc_store.lock().clients.get(client_id) { + Some(client_record) => { + client_record + .client_state + .clone() + .ok_or_else(|| ClientError::ClientStateNotFound { + client_id: client_id.clone(), + }) + } + None => Err(ClientError::ClientStateNotFound { + client_id: client_id.clone(), + }), + } + .map_err(ContextError::ClientError) + } + + fn decode_client_state(&self, client_state: Any) -> Result { + if let Ok(client_state) = TmClientState::try_from(client_state.clone()) { + client_state.validate().map_err(ClientError::from)?; + Ok(client_state.into()) + } else if let Ok(client_state) = MockClientState::try_from(client_state.clone()) { + Ok(client_state.into()) + } else { + Err(ClientError::UnknownClientStateType { + client_state_type: client_state.type_url, + }) + } + .map_err(ContextError::ClientError) + } + + fn consensus_state( + &self, + client_cons_state_path: &ClientConsensusStatePath, + ) -> Result { + let client_id = &client_cons_state_path.client_id; + let height = Height::new(client_cons_state_path.epoch, client_cons_state_path.height)?; + match self.ibc_store.lock().clients.get(client_id) { + Some(client_record) => match client_record.consensus_states.get(&height) { + Some(consensus_state) => Ok(consensus_state.clone()), + None => Err(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), + height, + }), + }, + None => Err(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), + height, + }), + } + .map_err(ContextError::ClientError) + } + + fn host_height(&self) -> Result { + Ok(self.latest_height()) + } + + fn host_timestamp(&self) -> Result { + Ok(self + .history + .last() + .expect("history cannot be empty") + .timestamp() + .add(self.block_time) + .expect("Never fails")) + } + + fn host_consensus_state(&self, height: &Height) -> Result { + match self.host_block(height) { + Some(block_ref) => Ok(block_ref.clone().into()), + None => Err(ClientError::MissingLocalConsensusState { height: *height }), + } + .map_err(ConnectionError::Client) + .map_err(ContextError::ConnectionError) + } + + fn client_counter(&self) -> Result { + Ok(self.ibc_store.lock().client_ids_counter) + } + + fn connection_end(&self, cid: &ConnectionId) -> Result { + match self.ibc_store.lock().connections.get(cid) { + Some(connection_end) => Ok(connection_end.clone()), + None => Err(ConnectionError::ConnectionNotFound { + connection_id: cid.clone(), + }), + } + .map_err(ContextError::ConnectionError) + } + + fn validate_self_client( + &self, + client_state_of_host_on_counterparty: Any, + ) -> Result<(), ContextError> { + let mock_client_state = MockClientState::try_from(client_state_of_host_on_counterparty) + .map_err(|_| ConnectionError::InvalidClientState { + reason: "client must be a mock client".to_string(), + }) + .map_err(ContextError::ConnectionError)?; + + if mock_client_state.is_frozen() { + return Err(ClientError::ClientFrozen { + description: String::new(), + } + .into()); + } + + let self_chain_id = &self.host_chain_id; + let self_revision_number = self_chain_id.revision_number(); + if self_revision_number != mock_client_state.latest_height().revision_number() { + return Err(ConnectionError::InvalidClientState { + reason: format!( + "client is not in the same revision as the chain. expected: {}, got: {}", + self_revision_number, + mock_client_state.latest_height().revision_number() + ), + }) + .map_err(ContextError::ConnectionError); + } + + let host_current_height = self.latest_height().increment(); + if mock_client_state.latest_height() >= host_current_height { + return Err(ConnectionError::InvalidClientState { + reason: format!( + "client has latest height {} greater than or equal to chain height {}", + mock_client_state.latest_height(), + host_current_height + ), + }) + .map_err(ContextError::ConnectionError); + } + + Ok(()) + } + + fn commitment_prefix(&self) -> CommitmentPrefix { + CommitmentPrefix::try_from(b"mock".to_vec()).expect("Never fails") + } + + fn connection_counter(&self) -> Result { + Ok(self.ibc_store.lock().connection_ids_counter) + } + + fn channel_end(&self, chan_end_path: &ChannelEndPath) -> Result { + let port_id = &chan_end_path.0; + let channel_id = &chan_end_path.1; + + match self + .ibc_store + .lock() + .channels + .get(port_id) + .and_then(|map| map.get(channel_id)) + { + Some(channel_end) => Ok(channel_end.clone()), + None => Err(ChannelError::ChannelNotFound { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), + } + .map_err(ContextError::ChannelError) + } + + fn get_next_sequence_send( + &self, + seq_send_path: &SeqSendPath, + ) -> Result { + let port_id = &seq_send_path.0; + let channel_id = &seq_send_path.1; + + match self + .ibc_store + .lock() + .next_sequence_send + .get(port_id) + .and_then(|map| map.get(channel_id)) + { + Some(sequence) => Ok(*sequence), + None => Err(PacketError::MissingNextSendSeq { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), + } + .map_err(ContextError::PacketError) + } + + fn get_next_sequence_recv( + &self, + seq_recv_path: &SeqRecvPath, + ) -> Result { + let port_id = &seq_recv_path.0; + let channel_id = &seq_recv_path.1; + + match self + .ibc_store + .lock() + .next_sequence_recv + .get(port_id) + .and_then(|map| map.get(channel_id)) + { + Some(sequence) => Ok(*sequence), + None => Err(PacketError::MissingNextRecvSeq { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), + } + .map_err(ContextError::PacketError) + } + + fn get_next_sequence_ack(&self, seq_ack_path: &SeqAckPath) -> Result { + let port_id = &seq_ack_path.0; + let channel_id = &seq_ack_path.1; + + match self + .ibc_store + .lock() + .next_sequence_ack + .get(port_id) + .and_then(|map| map.get(channel_id)) + { + Some(sequence) => Ok(*sequence), + None => Err(PacketError::MissingNextAckSeq { + port_id: port_id.clone(), + channel_id: channel_id.clone(), + }), + } + .map_err(ContextError::PacketError) + } + + fn get_packet_commitment( + &self, + commitment_path: &CommitmentPath, + ) -> Result { + let port_id = &commitment_path.port_id; + let channel_id = &commitment_path.channel_id; + let seq = &commitment_path.sequence; + + match self + .ibc_store + .lock() + .packet_commitment + .get(port_id) + .and_then(|map| map.get(channel_id)) + .and_then(|map| map.get(seq)) + { + Some(commitment) => Ok(commitment.clone()), + None => Err(PacketError::PacketCommitmentNotFound { sequence: *seq }), + } + .map_err(ContextError::PacketError) + } + + fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result { + let port_id = &receipt_path.port_id; + let channel_id = &receipt_path.channel_id; + let seq = &receipt_path.sequence; + + match self + .ibc_store + .lock() + .packet_receipt + .get(port_id) + .and_then(|map| map.get(channel_id)) + .and_then(|map| map.get(seq)) + { + Some(receipt) => Ok(receipt.clone()), + None => Err(PacketError::PacketReceiptNotFound { sequence: *seq }), + } + .map_err(ContextError::PacketError) + } + + fn get_packet_acknowledgement( + &self, + ack_path: &AckPath, + ) -> Result { + let port_id = &ack_path.port_id; + let channel_id = &ack_path.channel_id; + let seq = &ack_path.sequence; + + match self + .ibc_store + .lock() + .packet_acknowledgement + .get(port_id) + .and_then(|map| map.get(channel_id)) + .and_then(|map| map.get(seq)) + { + Some(ack) => Ok(ack.clone()), + None => Err(PacketError::PacketAcknowledgementNotFound { sequence: *seq }), + } + .map_err(ContextError::PacketError) + } + + fn channel_counter(&self) -> Result { + Ok(self.ibc_store.lock().channel_ids_counter) + } + + fn max_expected_time_per_block(&self) -> Duration { + self.block_time + } + + fn validate_message_signer(&self, _signer: &Signer) -> Result<(), ContextError> { + Ok(()) + } + + fn get_client_validation_context(&self) -> &Self::V { + self + } +} + +impl ExecutionContext for MockContext { + fn get_client_execution_context(&mut self) -> &mut Self::E { + self + } + + fn increase_client_counter(&mut self) -> Result<(), ContextError> { + self.ibc_store.lock().client_ids_counter += 1; + Ok(()) + } + + fn store_connection( + &mut self, + connection_path: &ConnectionPath, + connection_end: ConnectionEnd, + ) -> Result<(), ContextError> { + let connection_id = connection_path.0.clone(); + self.ibc_store + .lock() + .connections + .insert(connection_id, connection_end); + Ok(()) + } + + fn store_connection_to_client( + &mut self, + client_connection_path: &ClientConnectionPath, + conn_id: ConnectionId, + ) -> Result<(), ContextError> { + let client_id = client_connection_path.0.clone(); + self.ibc_store + .lock() + .client_connections + .insert(client_id, conn_id); + Ok(()) + } + + fn increase_connection_counter(&mut self) -> Result<(), ContextError> { + self.ibc_store.lock().connection_ids_counter += 1; + Ok(()) + } + + fn store_packet_commitment( + &mut self, + commitment_path: &CommitmentPath, + commitment: PacketCommitment, + ) -> Result<(), ContextError> { + self.ibc_store + .lock() + .packet_commitment + .entry(commitment_path.port_id.clone()) + .or_default() + .entry(commitment_path.channel_id.clone()) + .or_default() + .insert(commitment_path.sequence, commitment); + Ok(()) + } + + fn delete_packet_commitment( + &mut self, + commitment_path: &CommitmentPath, + ) -> Result<(), ContextError> { + self.ibc_store + .lock() + .packet_commitment + .get_mut(&commitment_path.port_id) + .and_then(|map| map.get_mut(&commitment_path.channel_id)) + .and_then(|map| map.remove(&commitment_path.sequence)); + Ok(()) + } + + fn store_packet_receipt( + &mut self, + path: &ReceiptPath, + receipt: Receipt, + ) -> Result<(), ContextError> { + self.ibc_store + .lock() + .packet_receipt + .entry(path.port_id.clone()) + .or_default() + .entry(path.channel_id.clone()) + .or_default() + .insert(path.sequence, receipt); + Ok(()) + } + + fn store_packet_acknowledgement( + &mut self, + ack_path: &AckPath, + ack_commitment: AcknowledgementCommitment, + ) -> Result<(), ContextError> { + let port_id = ack_path.port_id.clone(); + let channel_id = ack_path.channel_id.clone(); + let seq = ack_path.sequence; + + self.ibc_store + .lock() + .packet_acknowledgement + .entry(port_id) + .or_default() + .entry(channel_id) + .or_default() + .insert(seq, ack_commitment); + Ok(()) + } + + fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result<(), ContextError> { + let port_id = ack_path.port_id.clone(); + let channel_id = ack_path.channel_id.clone(); + let sequence = ack_path.sequence; + + self.ibc_store + .lock() + .packet_acknowledgement + .get_mut(&port_id) + .and_then(|map| map.get_mut(&channel_id)) + .and_then(|map| map.remove(&sequence)); + Ok(()) + } + + fn store_channel( + &mut self, + channel_end_path: &ChannelEndPath, + channel_end: ChannelEnd, + ) -> Result<(), ContextError> { + let port_id = channel_end_path.0.clone(); + let channel_id = channel_end_path.1.clone(); + + self.ibc_store + .lock() + .channels + .entry(port_id) + .or_default() + .insert(channel_id, channel_end); + Ok(()) + } + + fn store_next_sequence_send( + &mut self, + seq_send_path: &SeqSendPath, + seq: Sequence, + ) -> Result<(), ContextError> { + let port_id = seq_send_path.0.clone(); + let channel_id = seq_send_path.1.clone(); + + self.ibc_store + .lock() + .next_sequence_send + .entry(port_id) + .or_default() + .insert(channel_id, seq); + Ok(()) + } + + fn store_next_sequence_recv( + &mut self, + seq_recv_path: &SeqRecvPath, + seq: Sequence, + ) -> Result<(), ContextError> { + let port_id = seq_recv_path.0.clone(); + let channel_id = seq_recv_path.1.clone(); + + self.ibc_store + .lock() + .next_sequence_recv + .entry(port_id) + .or_default() + .insert(channel_id, seq); + Ok(()) + } + + fn store_next_sequence_ack( + &mut self, + seq_ack_path: &SeqAckPath, + seq: Sequence, + ) -> Result<(), ContextError> { + let port_id = seq_ack_path.0.clone(); + let channel_id = seq_ack_path.1.clone(); + + self.ibc_store + .lock() + .next_sequence_ack + .entry(port_id) + .or_default() + .insert(channel_id, seq); + Ok(()) + } + + fn increase_channel_counter(&mut self) -> Result<(), ContextError> { + self.ibc_store.lock().channel_ids_counter += 1; + Ok(()) + } + + fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), ContextError> { + self.events.push(event); + Ok(()) + } + + fn log_message(&mut self, message: String) -> Result<(), ContextError> { + self.logs.push(message); + Ok(()) + } +} diff --git a/crates/ibc-testkit/src/testapp/ibc/core/mod.rs b/crates/ibc-testkit/src/testapp/ibc/core/mod.rs new file mode 100644 index 000000000..052ffa92e --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/core/mod.rs @@ -0,0 +1,4 @@ +pub mod client_ctx; +pub mod core_ctx; +pub mod router; +pub mod types; diff --git a/crates/ibc-testkit/src/testapp/ibc/core/router/context.rs b/crates/ibc-testkit/src/testapp/ibc/core/router/context.rs new file mode 100644 index 000000000..ad6855ac8 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/core/router/context.rs @@ -0,0 +1,31 @@ +use alloc::sync::Arc; + +use ibc::core::ics24_host::identifier::PortId; +use ibc::core::router::{Module, ModuleId, Router}; + +use super::types::MockRouter; + +impl Router for MockRouter { + fn get_route(&self, module_id: &ModuleId) -> Option<&dyn Module> { + self.router.get(module_id).map(Arc::as_ref) + } + fn get_route_mut(&mut self, module_id: &ModuleId) -> Option<&mut dyn Module> { + // NOTE: The following: + + // self.router.get_mut(module_id).and_then(Arc::get_mut) + + // doesn't work due to a compiler bug. So we expand it out manually. + + match self.router.get_mut(module_id) { + Some(arc_mod) => match Arc::get_mut(arc_mod) { + Some(m) => Some(m), + None => None, + }, + None => None, + } + } + + fn lookup_module(&self, port_id: &PortId) -> Option { + self.port_to_module.get(port_id).cloned() + } +} diff --git a/crates/ibc-testkit/src/testapp/ibc/core/router/mod.rs b/crates/ibc-testkit/src/testapp/ibc/core/router/mod.rs new file mode 100644 index 000000000..59680b5b0 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/core/router/mod.rs @@ -0,0 +1,5 @@ +mod context; +mod types; + +pub use context::*; +pub use types::*; diff --git a/crates/ibc-testkit/src/testapp/ibc/core/router/types.rs b/crates/ibc-testkit/src/testapp/ibc/core/router/types.rs new file mode 100644 index 000000000..b0aaaae1b --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/core/router/types.rs @@ -0,0 +1,50 @@ +use alloc::collections::BTreeMap; +use alloc::sync::Arc; + +use ibc::applications::transfer::MODULE_ID_STR; +use ibc::core::ics24_host::identifier::PortId; +use ibc::core::router::{Module, ModuleId}; +use ibc::prelude::*; + +use crate::testapp::ibc::applications::transfer::types::DummyTransferModule; + +#[derive(Default)] +pub struct MockRouter { + pub router: BTreeMap>, + + /// Maps ports to the the module that owns it + pub port_to_module: BTreeMap, +} + +impl MockRouter { + pub fn new_with_transfer() -> Self { + let mut router = Self::default(); + + let module_id = ModuleId::new(MODULE_ID_STR.to_string()); + + router.scope_port_to_module(PortId::transfer(), module_id.clone()); + + let transfer_mod = DummyTransferModule::new(); + + router + .add_route(module_id, transfer_mod) + .expect("Never fails"); + + router + } + + pub fn add_route( + &mut self, + module_id: ModuleId, + module: impl Module + 'static, + ) -> Result<(), String> { + match self.router.insert(module_id, Arc::new(module)) { + None => Ok(()), + Some(_) => Err("Duplicate module_id".to_owned()), + } + } + + pub fn scope_port_to_module(&mut self, port_id: PortId, module_id: ModuleId) { + self.port_to_module.insert(port_id, module_id); + } +} diff --git a/crates/ibc/src/mock/context.rs b/crates/ibc-testkit/src/testapp/ibc/core/types.rs similarity index 59% rename from crates/ibc/src/mock/context.rs rename to crates/ibc-testkit/src/testapp/ibc/core/types.rs index c40477f66..145afcdb1 100644 --- a/crates/ibc/src/mock/context.rs +++ b/crates/ibc-testkit/src/testapp/ibc/core/types.rs @@ -1,7 +1,5 @@ //! Implementation of a global context mock. Used in testing handlers of all IBC modules. -mod clients; - use alloc::collections::btree_map::BTreeMap; use alloc::sync::Arc; use core::cmp::min; @@ -9,137 +7,39 @@ use core::fmt::Debug; use core::ops::{Add, Sub}; use core::time::Duration; -use derive_more::{From, TryInto}; -use ibc_proto::google::protobuf::Any; -use ibc_proto::Protobuf; +use ibc::clients::ics07_tendermint::client_state::ClientState as TmClientState; +use ibc::clients::ics07_tendermint::TENDERMINT_CLIENT_TYPE; +use ibc::core::events::IbcEvent; +use ibc::core::ics02_client::client_type::ClientType; +use ibc::core::ics03_connection::connection::ConnectionEnd; +use ibc::core::ics04_channel::channel::ChannelEnd; +use ibc::core::ics04_channel::commitment::{AcknowledgementCommitment, PacketCommitment}; +use ibc::core::ics04_channel::packet::{Receipt, Sequence}; +use ibc::core::ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, PortId}; +use ibc::core::router::Router; +use ibc::core::timestamp::Timestamp; +use ibc::core::{dispatch, MsgEnvelope, ValidationContext}; +use ibc::prelude::*; +use ibc::Height; use parking_lot::Mutex; use tendermint_testgen::Validator as TestgenValidator; use tracing::debug; use typed_builder::TypedBuilder; -use super::client_state::{MOCK_CLIENT_STATE_TYPE_URL, MOCK_CLIENT_TYPE}; -use super::consensus_state::MOCK_CONSENSUS_STATE_TYPE_URL; -use crate::clients::ics07_tendermint::client_state::test_util::ClientStateConfig as TmClientStateConfig; -use crate::clients::ics07_tendermint::client_state::{ - ClientState as TmClientState, TENDERMINT_CLIENT_STATE_TYPE_URL, -}; -use crate::clients::ics07_tendermint::consensus_state::{ - ConsensusState as TmConsensusState, TENDERMINT_CONSENSUS_STATE_TYPE_URL, +use super::client_ctx::{MockClientRecord, PortChannelIdMap}; +use crate::hosts::block::{HostBlock, HostType}; +use crate::relayer::error::RelayerError; +use crate::testapp::ibc::clients::mock::client_state::{ + client_type as mock_client_type, MockClientState, MOCK_CLIENT_TYPE, }; -use crate::clients::ics07_tendermint::TENDERMINT_CLIENT_TYPE; -use crate::core::events::IbcEvent; -use crate::core::ics02_client::client_state::ClientState; -use crate::core::ics02_client::client_type::ClientType; -use crate::core::ics02_client::consensus_state::ConsensusState; -use crate::core::ics02_client::error::ClientError; -use crate::core::ics03_connection::connection::ConnectionEnd; -use crate::core::ics03_connection::error::ConnectionError; -use crate::core::ics04_channel::channel::ChannelEnd; -use crate::core::ics04_channel::commitment::{AcknowledgementCommitment, PacketCommitment}; -use crate::core::ics04_channel::error::{ChannelError, PacketError}; -use crate::core::ics04_channel::packet::{Receipt, Sequence}; -use crate::core::ics23_commitment::commitment::CommitmentPrefix; -use crate::core::ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, PortId}; -use crate::core::ics24_host::path::{ - AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, CommitmentPath, - ConnectionPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, +use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; +use crate::testapp::ibc::clients::mock::header::MockHeader; +use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; +use crate::utils::clients::tendermint::{ + dummy_tm_client_state_from_header, ClientStateConfig as TmClientStateConfig, }; -use crate::core::router::Router; -use crate::core::timestamp::Timestamp; -use crate::core::{dispatch, ContextError, ExecutionContext, MsgEnvelope, ValidationContext}; -use crate::mock::client_state::{client_type as mock_client_type, MockClientState}; -use crate::mock::consensus_state::MockConsensusState; -use crate::mock::header::MockHeader; -use crate::mock::host::{HostBlock, HostType}; -use crate::mock::ics18_relayer::context::RelayerContext; -use crate::mock::ics18_relayer::error::RelayerError; -use crate::prelude::*; -use crate::signer::Signer; -use crate::Height; - pub const DEFAULT_BLOCK_TIME_SECS: u64 = 3; -#[derive(Debug, Clone, From, PartialEq, ClientState)] -#[generics(ClientValidationContext = MockContext, - ClientExecutionContext = MockContext) -] -#[mock] -pub enum AnyClientState { - Tendermint(TmClientState), - Mock(MockClientState), -} - -impl Protobuf for AnyClientState {} - -impl TryFrom for AnyClientState { - type Error = ClientError; - - fn try_from(raw: Any) -> Result { - if raw.type_url == TENDERMINT_CLIENT_STATE_TYPE_URL { - TmClientState::try_from(raw).map(Into::into) - } else if raw.type_url == MOCK_CLIENT_STATE_TYPE_URL { - MockClientState::try_from(raw).map(Into::into) - } else { - Err(ClientError::Other { - description: "failed to deserialize message".to_string(), - }) - } - } -} - -impl From for Any { - fn from(host_client_state: AnyClientState) -> Self { - match host_client_state { - AnyClientState::Tendermint(cs) => cs.into(), - AnyClientState::Mock(cs) => cs.into(), - } - } -} - -#[derive(Debug, Clone, From, TryInto, PartialEq, ConsensusState)] -pub enum AnyConsensusState { - Tendermint(TmConsensusState), - Mock(MockConsensusState), -} - -impl Protobuf for AnyConsensusState {} - -impl TryFrom for AnyConsensusState { - type Error = ClientError; - - fn try_from(raw: Any) -> Result { - if raw.type_url == TENDERMINT_CONSENSUS_STATE_TYPE_URL { - TmConsensusState::try_from(raw).map(Into::into) - } else if raw.type_url == MOCK_CONSENSUS_STATE_TYPE_URL { - MockConsensusState::try_from(raw).map(Into::into) - } else { - Err(ClientError::Other { - description: "failed to deserialize message".to_string(), - }) - } - } -} - -impl From for Any { - fn from(host_consensus_state: AnyConsensusState) -> Self { - match host_consensus_state { - AnyConsensusState::Tendermint(cs) => cs.into(), - AnyConsensusState::Mock(cs) => cs.into(), - } - } -} - -/// A mock of an IBC client record as it is stored in a mock context. -/// For testing ICS02 handlers mostly, cf. `MockClientContext`. -#[derive(Clone, Debug)] -pub struct MockClientRecord { - /// The client state (representing only the latest height at the moment). - pub client_state: Option, - - /// Mapping of heights to consensus states for this client. - pub consensus_states: BTreeMap, -} - /// An object that stores all IBC related data. #[derive(Clone, Debug, Default)] pub struct MockIbcStore { @@ -196,20 +96,20 @@ pub struct MockIbcStore { #[derive(Debug)] pub struct MockContext { /// The type of host chain underlying this mock context. - host_chain_type: HostType, + pub host_chain_type: HostType, /// Host chain identifier. - host_chain_id: ChainId, + pub host_chain_id: ChainId, /// Maximum size for the history of the host chain. Any block older than this is pruned. - max_history_size: u64, + pub max_history_size: u64, /// The chain of blocks underlying this context. A vector of size up to `max_history_size` /// blocks, ascending order by their height (latest block is on the last position). - history: Vec, + pub history: Vec, /// Average time duration between blocks - block_time: Duration, + pub block_time: Duration, /// An object that stores all IBC related data. pub ibc_store: Arc>, @@ -219,117 +119,6 @@ pub struct MockContext { pub logs: Vec, } -#[derive(Debug, TypedBuilder)] -#[builder(build_method(into = MockContext))] -pub struct MockContextConfig { - #[builder(default = HostType::Mock)] - host_type: HostType, - - host_id: ChainId, - - #[builder(default = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS))] - block_time: Duration, - - // may panic if validator_set_history size is less than max_history_size + 1 - #[builder(default = 5)] - max_history_size: u64, - - #[builder(default, setter(strip_option))] - validator_set_history: Option>>, - - latest_height: Height, - - #[builder(default = Timestamp::now())] - latest_timestamp: Timestamp, -} - -impl From for MockContext { - fn from(params: MockContextConfig) -> Self { - assert_ne!( - params.max_history_size, 0, - "The chain must have a non-zero max_history_size" - ); - - assert_ne!( - params.latest_height.revision_height(), - 0, - "The chain must have a non-zero revision_height" - ); - - // Compute the number of blocks to store. - let n = min( - params.max_history_size, - params.latest_height.revision_height(), - ); - - assert_eq!( - params.host_id.revision_number(), - params.latest_height.revision_number(), - "The version in the chain identifier must match the version in the latest height" - ); - - let next_block_timestamp = params - .latest_timestamp - .add(params.block_time) - .expect("Never fails"); - - let history = if let Some(validator_set_history) = params.validator_set_history { - (0..n) - .rev() - .map(|i| { - // generate blocks with timestamps -> N, N - BT, N - 2BT, ... - // where N = now(), BT = block_time - HostBlock::generate_block_with_validators( - params.host_id.clone(), - params.host_type, - params - .latest_height - .sub(i) - .expect("Never fails") - .revision_height(), - next_block_timestamp - .sub(params.block_time * ((i + 1) as u32)) - .expect("Never fails"), - &validator_set_history[(n - i) as usize - 1], - &validator_set_history[(n - i) as usize], - ) - }) - .collect() - } else { - (0..n) - .rev() - .map(|i| { - // generate blocks with timestamps -> N, N - BT, N - 2BT, ... - // where N = now(), BT = block_time - HostBlock::generate_block( - params.host_id.clone(), - params.host_type, - params - .latest_height - .sub(i) - .expect("Never fails") - .revision_height(), - next_block_timestamp - .sub(params.block_time * ((i + 1) as u32)) - .expect("Never fails"), - ) - }) - .collect() - }; - - MockContext { - host_chain_type: params.host_type, - host_chain_id: params.host_id.clone(), - max_history_size: params.max_history_size, - history, - block_time: params.block_time, - ibc_store: Arc::new(Mutex::new(MockIbcStore::default())), - events: Vec::new(), - logs: Vec::new(), - } - } -} - #[derive(Debug, TypedBuilder)] pub struct MockClientConfig { client_chain_id: ChainId, @@ -560,8 +349,13 @@ impl MockContext { let client_type = client_type.unwrap_or_else(mock_client_type); let (client_state, consensus_state) = if client_type.as_str() == MOCK_CLIENT_TYPE { ( - Some(MockClientState::new(MockHeader::new(client_state_height)).into()), - MockConsensusState::new(MockHeader::new(cs_height)).into(), + Some( + MockClientState::new( + MockHeader::new(client_state_height).with_current_timestamp(), + ) + .into(), + ), + MockConsensusState::new(MockHeader::new(cs_height).with_current_timestamp()).into(), ) } else if client_type.as_str() == TENDERMINT_CLIENT_TYPE { let light_block = HostBlock::generate_tm_block( @@ -571,7 +365,7 @@ impl MockContext { ); let client_state = - TmClientState::new_dummy_from_header(light_block.header().clone()).into(); + dummy_tm_client_state_from_header(light_block.header().clone()).into(); // Return the tuple. (Some(client_state), light_block.into()) @@ -611,7 +405,7 @@ impl MockContext { ) } - pub(crate) fn with_client_parametrized_history_with_chain_id( + pub fn with_client_parametrized_history_with_chain_id( self, client_chain_id: ChainId, client_id: &ClientId, @@ -638,7 +432,7 @@ impl MockContext { HostBlock::generate_tm_block(client_chain_id, cs_height.revision_height(), now); let client_state = - TmClientState::new_dummy_from_header(light_block.header().clone()).into(); + dummy_tm_client_state_from_header(light_block.header().clone()).into(); // Return the tuple. (Some(client_state), light_block.into()) @@ -986,580 +780,22 @@ impl MockContext { } } -type PortChannelIdMap = BTreeMap>; - -impl RelayerContext for MockContext { - fn query_latest_height(&self) -> Result { - ValidationContext::host_height(self) - } - - fn query_client_full_state(&self, client_id: &ClientId) -> Option { - // Forward call to Ics2. - self.client_state(client_id).ok() - } - - fn signer(&self) -> Signer { - "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C" - .to_string() - .into() - } -} - -impl ValidationContext for MockContext { - type V = Self; - type E = Self; - type AnyConsensusState = AnyConsensusState; - type AnyClientState = AnyClientState; - - fn client_state(&self, client_id: &ClientId) -> Result { - match self.ibc_store.lock().clients.get(client_id) { - Some(client_record) => { - client_record - .client_state - .clone() - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_id.clone(), - }) - } - None => Err(ClientError::ClientStateNotFound { - client_id: client_id.clone(), - }), - } - .map_err(ContextError::ClientError) - } - - fn decode_client_state(&self, client_state: Any) -> Result { - if let Ok(client_state) = TmClientState::try_from(client_state.clone()) { - client_state.validate().map_err(ClientError::from)?; - Ok(client_state.into()) - } else if let Ok(client_state) = MockClientState::try_from(client_state.clone()) { - Ok(client_state.into()) - } else { - Err(ClientError::UnknownClientStateType { - client_state_type: client_state.type_url, - }) - } - .map_err(ContextError::ClientError) - } - - fn consensus_state( - &self, - client_cons_state_path: &ClientConsensusStatePath, - ) -> Result { - let client_id = &client_cons_state_path.client_id; - let height = Height::new(client_cons_state_path.epoch, client_cons_state_path.height)?; - match self.ibc_store.lock().clients.get(client_id) { - Some(client_record) => match client_record.consensus_states.get(&height) { - Some(consensus_state) => Ok(consensus_state.clone()), - None => Err(ClientError::ConsensusStateNotFound { - client_id: client_id.clone(), - height, - }), - }, - None => Err(ClientError::ConsensusStateNotFound { - client_id: client_id.clone(), - height, - }), - } - .map_err(ContextError::ClientError) - } - - fn host_height(&self) -> Result { - Ok(self.latest_height()) - } - - fn host_timestamp(&self) -> Result { - Ok(self - .history - .last() - .expect("history cannot be empty") - .timestamp() - .add(self.block_time) - .expect("Never fails")) - } - - fn host_consensus_state(&self, height: &Height) -> Result { - match self.host_block(height) { - Some(block_ref) => Ok(block_ref.clone().into()), - None => Err(ClientError::MissingLocalConsensusState { height: *height }), - } - .map_err(ConnectionError::Client) - .map_err(ContextError::ConnectionError) - } - - fn client_counter(&self) -> Result { - Ok(self.ibc_store.lock().client_ids_counter) - } - - fn connection_end(&self, cid: &ConnectionId) -> Result { - match self.ibc_store.lock().connections.get(cid) { - Some(connection_end) => Ok(connection_end.clone()), - None => Err(ConnectionError::ConnectionNotFound { - connection_id: cid.clone(), - }), - } - .map_err(ContextError::ConnectionError) - } - - fn validate_self_client( - &self, - client_state_of_host_on_counterparty: Any, - ) -> Result<(), ContextError> { - let mock_client_state = MockClientState::try_from(client_state_of_host_on_counterparty) - .map_err(|_| ConnectionError::InvalidClientState { - reason: "client must be a mock client".to_string(), - }) - .map_err(ContextError::ConnectionError)?; - - if mock_client_state.is_frozen() { - return Err(ClientError::ClientFrozen { - description: String::new(), - } - .into()); - } - - let self_chain_id = &self.host_chain_id; - let self_revision_number = self_chain_id.revision_number(); - if self_revision_number != mock_client_state.latest_height().revision_number() { - return Err(ConnectionError::InvalidClientState { - reason: format!( - "client is not in the same revision as the chain. expected: {}, got: {}", - self_revision_number, - mock_client_state.latest_height().revision_number() - ), - }) - .map_err(ContextError::ConnectionError); - } - - let host_current_height = self.latest_height().increment(); - if mock_client_state.latest_height() >= host_current_height { - return Err(ConnectionError::InvalidClientState { - reason: format!( - "client has latest height {} greater than or equal to chain height {}", - mock_client_state.latest_height(), - host_current_height - ), - }) - .map_err(ContextError::ConnectionError); - } - - Ok(()) - } - - fn commitment_prefix(&self) -> CommitmentPrefix { - CommitmentPrefix::try_from(b"mock".to_vec()).expect("Never fails") - } - - fn connection_counter(&self) -> Result { - Ok(self.ibc_store.lock().connection_ids_counter) - } - - fn channel_end(&self, chan_end_path: &ChannelEndPath) -> Result { - let port_id = &chan_end_path.0; - let channel_id = &chan_end_path.1; - - match self - .ibc_store - .lock() - .channels - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(channel_end) => Ok(channel_end.clone()), - None => Err(ChannelError::ChannelNotFound { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::ChannelError) - } - - fn get_next_sequence_send( - &self, - seq_send_path: &SeqSendPath, - ) -> Result { - let port_id = &seq_send_path.0; - let channel_id = &seq_send_path.1; - - match self - .ibc_store - .lock() - .next_sequence_send - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(sequence) => Ok(*sequence), - None => Err(PacketError::MissingNextSendSeq { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::PacketError) - } - - fn get_next_sequence_recv( - &self, - seq_recv_path: &SeqRecvPath, - ) -> Result { - let port_id = &seq_recv_path.0; - let channel_id = &seq_recv_path.1; - - match self - .ibc_store - .lock() - .next_sequence_recv - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(sequence) => Ok(*sequence), - None => Err(PacketError::MissingNextRecvSeq { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::PacketError) - } - - fn get_next_sequence_ack(&self, seq_ack_path: &SeqAckPath) -> Result { - let port_id = &seq_ack_path.0; - let channel_id = &seq_ack_path.1; - - match self - .ibc_store - .lock() - .next_sequence_ack - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(sequence) => Ok(*sequence), - None => Err(PacketError::MissingNextAckSeq { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::PacketError) - } - - fn get_packet_commitment( - &self, - commitment_path: &CommitmentPath, - ) -> Result { - let port_id = &commitment_path.port_id; - let channel_id = &commitment_path.channel_id; - let seq = &commitment_path.sequence; - - match self - .ibc_store - .lock() - .packet_commitment - .get(port_id) - .and_then(|map| map.get(channel_id)) - .and_then(|map| map.get(seq)) - { - Some(commitment) => Ok(commitment.clone()), - None => Err(PacketError::PacketCommitmentNotFound { sequence: *seq }), - } - .map_err(ContextError::PacketError) - } - - fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result { - let port_id = &receipt_path.port_id; - let channel_id = &receipt_path.channel_id; - let seq = &receipt_path.sequence; - - match self - .ibc_store - .lock() - .packet_receipt - .get(port_id) - .and_then(|map| map.get(channel_id)) - .and_then(|map| map.get(seq)) - { - Some(receipt) => Ok(receipt.clone()), - None => Err(PacketError::PacketReceiptNotFound { sequence: *seq }), - } - .map_err(ContextError::PacketError) - } - - fn get_packet_acknowledgement( - &self, - ack_path: &AckPath, - ) -> Result { - let port_id = &ack_path.port_id; - let channel_id = &ack_path.channel_id; - let seq = &ack_path.sequence; - - match self - .ibc_store - .lock() - .packet_acknowledgement - .get(port_id) - .and_then(|map| map.get(channel_id)) - .and_then(|map| map.get(seq)) - { - Some(ack) => Ok(ack.clone()), - None => Err(PacketError::PacketAcknowledgementNotFound { sequence: *seq }), - } - .map_err(ContextError::PacketError) - } - - fn channel_counter(&self) -> Result { - Ok(self.ibc_store.lock().channel_ids_counter) - } - - fn max_expected_time_per_block(&self) -> Duration { - self.block_time - } - - fn validate_message_signer(&self, _signer: &Signer) -> Result<(), ContextError> { - Ok(()) - } - - fn get_client_validation_context(&self) -> &Self::V { - self - } -} - -impl ExecutionContext for MockContext { - fn get_client_execution_context(&mut self) -> &mut Self::E { - self - } - - fn increase_client_counter(&mut self) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - - ibc_store.client_ids_counter = ibc_store - .client_ids_counter - .checked_add(1) - .ok_or(ClientError::CounterOverflow)?; - - Ok(()) - } - - fn store_connection( - &mut self, - connection_path: &ConnectionPath, - connection_end: ConnectionEnd, - ) -> Result<(), ContextError> { - let connection_id = connection_path.0.clone(); - self.ibc_store - .lock() - .connections - .insert(connection_id, connection_end); - Ok(()) - } - - fn store_connection_to_client( - &mut self, - client_connection_path: &ClientConnectionPath, - conn_id: ConnectionId, - ) -> Result<(), ContextError> { - let client_id = client_connection_path.0.clone(); - self.ibc_store - .lock() - .client_connections - .insert(client_id, conn_id); - Ok(()) - } - - fn increase_connection_counter(&mut self) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - - ibc_store.connection_ids_counter = ibc_store - .connection_ids_counter - .checked_add(1) - .ok_or(ClientError::CounterOverflow)?; - - Ok(()) - } - - fn store_packet_commitment( - &mut self, - commitment_path: &CommitmentPath, - commitment: PacketCommitment, - ) -> Result<(), ContextError> { - self.ibc_store - .lock() - .packet_commitment - .entry(commitment_path.port_id.clone()) - .or_default() - .entry(commitment_path.channel_id.clone()) - .or_default() - .insert(commitment_path.sequence, commitment); - Ok(()) - } - - fn delete_packet_commitment( - &mut self, - commitment_path: &CommitmentPath, - ) -> Result<(), ContextError> { - self.ibc_store - .lock() - .packet_commitment - .get_mut(&commitment_path.port_id) - .and_then(|map| map.get_mut(&commitment_path.channel_id)) - .and_then(|map| map.remove(&commitment_path.sequence)); - Ok(()) - } - - fn store_packet_receipt( - &mut self, - path: &ReceiptPath, - receipt: Receipt, - ) -> Result<(), ContextError> { - self.ibc_store - .lock() - .packet_receipt - .entry(path.port_id.clone()) - .or_default() - .entry(path.channel_id.clone()) - .or_default() - .insert(path.sequence, receipt); - Ok(()) - } - - fn store_packet_acknowledgement( - &mut self, - ack_path: &AckPath, - ack_commitment: AcknowledgementCommitment, - ) -> Result<(), ContextError> { - let port_id = ack_path.port_id.clone(); - let channel_id = ack_path.channel_id.clone(); - let seq = ack_path.sequence; - - self.ibc_store - .lock() - .packet_acknowledgement - .entry(port_id) - .or_default() - .entry(channel_id) - .or_default() - .insert(seq, ack_commitment); - Ok(()) - } - - fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result<(), ContextError> { - let port_id = ack_path.port_id.clone(); - let channel_id = ack_path.channel_id.clone(); - let sequence = ack_path.sequence; - - self.ibc_store - .lock() - .packet_acknowledgement - .get_mut(&port_id) - .and_then(|map| map.get_mut(&channel_id)) - .and_then(|map| map.remove(&sequence)); - Ok(()) - } - - fn store_channel( - &mut self, - channel_end_path: &ChannelEndPath, - channel_end: ChannelEnd, - ) -> Result<(), ContextError> { - let port_id = channel_end_path.0.clone(); - let channel_id = channel_end_path.1.clone(); - - self.ibc_store - .lock() - .channels - .entry(port_id) - .or_default() - .insert(channel_id, channel_end); - Ok(()) - } - - fn store_next_sequence_send( - &mut self, - seq_send_path: &SeqSendPath, - seq: Sequence, - ) -> Result<(), ContextError> { - let port_id = seq_send_path.0.clone(); - let channel_id = seq_send_path.1.clone(); - - self.ibc_store - .lock() - .next_sequence_send - .entry(port_id) - .or_default() - .insert(channel_id, seq); - Ok(()) - } - - fn store_next_sequence_recv( - &mut self, - seq_recv_path: &SeqRecvPath, - seq: Sequence, - ) -> Result<(), ContextError> { - let port_id = seq_recv_path.0.clone(); - let channel_id = seq_recv_path.1.clone(); - - self.ibc_store - .lock() - .next_sequence_recv - .entry(port_id) - .or_default() - .insert(channel_id, seq); - Ok(()) - } - - fn store_next_sequence_ack( - &mut self, - seq_ack_path: &SeqAckPath, - seq: Sequence, - ) -> Result<(), ContextError> { - let port_id = seq_ack_path.0.clone(); - let channel_id = seq_ack_path.1.clone(); - - self.ibc_store - .lock() - .next_sequence_ack - .entry(port_id) - .or_default() - .insert(channel_id, seq); - Ok(()) - } - - fn increase_channel_counter(&mut self) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - - ibc_store.channel_ids_counter = ibc_store - .channel_ids_counter - .checked_add(1) - .ok_or(ClientError::CounterOverflow)?; - - Ok(()) - } - - fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), ContextError> { - self.events.push(event); - Ok(()) - } - - fn log_message(&mut self, message: String) -> Result<(), ContextError> { - self.logs.push(message); - Ok(()) - } -} - #[cfg(test)] mod tests { + use ibc::core::ics04_channel::acknowledgement::Acknowledgement; + use ibc::core::ics04_channel::channel::{Counterparty, Order}; + use ibc::core::ics04_channel::error::{ChannelError, PacketError}; + use ibc::core::ics04_channel::packet::Packet; + use ibc::core::ics04_channel::Version; + use ibc::core::ics24_host::identifier::{ChainId, ChannelId, ConnectionId, PortId}; + use ibc::core::router::{Module, ModuleExtras, ModuleId}; + use ibc::{Height, Signer}; use test_log::test; use super::*; - use crate::core::ics04_channel::acknowledgement::Acknowledgement; - use crate::core::ics04_channel::channel::{Counterparty, Order}; - use crate::core::ics04_channel::error::ChannelError; - use crate::core::ics04_channel::packet::Packet; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ChainId, ChannelId, ConnectionId, PortId}; - use crate::core::router::{Module, ModuleExtras, ModuleId}; - use crate::mock::context::MockContext; - use crate::mock::host::HostType; - use crate::mock::router::MockRouter; - use crate::signer::Signer; - use crate::test_utils::get_dummy_bech32_account; - use crate::Height; + use crate::testapp::ibc::core::router::MockRouter; + use crate::utils::core::channel::PacketConfig; + use crate::utils::core::signer::dummy_bech32_account; #[test] fn test_history_manipulation() { @@ -1918,8 +1154,10 @@ mod tests { let mut on_recv_packet_result = |module_id: &'static str| { let module_id = ModuleId::new(module_id.to_string()); let m = router.get_route_mut(&module_id).expect("Never fails"); - let result = - m.on_recv_packet_execute(&Packet::default(), &get_dummy_bech32_account().into()); + + let packet = PacketConfig::builder().build(); + + let result = m.on_recv_packet_execute(&packet, &dummy_bech32_account().into()); (module_id, result) }; diff --git a/crates/ibc-testkit/src/testapp/ibc/mod.rs b/crates/ibc-testkit/src/testapp/ibc/mod.rs new file mode 100644 index 000000000..f13a3c645 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/ibc/mod.rs @@ -0,0 +1,3 @@ +pub mod applications; +pub mod clients; +pub mod core; diff --git a/crates/ibc-testkit/src/testapp/mod.rs b/crates/ibc-testkit/src/testapp/mod.rs new file mode 100644 index 000000000..0887f62f4 --- /dev/null +++ b/crates/ibc-testkit/src/testapp/mod.rs @@ -0,0 +1 @@ +pub mod ibc; diff --git a/crates/ibc-testkit/src/utils/dummies/applications/mod.rs b/crates/ibc-testkit/src/utils/dummies/applications/mod.rs new file mode 100644 index 000000000..544cab2c5 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/applications/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "serde")] +pub mod transfer; diff --git a/crates/ibc-testkit/src/utils/dummies/applications/transfer.rs b/crates/ibc-testkit/src/utils/dummies/applications/transfer.rs new file mode 100644 index 000000000..0bf50c99b --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/applications/transfer.rs @@ -0,0 +1,80 @@ +use alloc::string::ToString; + +use ibc::applications::transfer::msgs::transfer::MsgTransfer; +use ibc::applications::transfer::packet::PacketData; +use ibc::applications::transfer::{Memo, PrefixedCoin}; +use ibc::core::ics04_channel::packet::{Packet, Sequence}; +use ibc::core::ics04_channel::timeout::TimeoutHeight; +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::core::timestamp::Timestamp; +use ibc::Signer; +use typed_builder::TypedBuilder; + +use crate::utils::dummies::core::signer::dummy_account_id; + +/// Configuration of the `MsgTransfer` message for building dummy messages. +#[derive(TypedBuilder, Debug)] +#[builder(build_method(into = MsgTransfer))] +pub struct MsgTransferConfig { + #[builder(default = PortId::transfer())] + pub port_id_on_a: PortId, + #[builder(default)] + pub chan_id_on_a: ChannelId, + pub packet_data: PacketData, + #[builder(default)] + pub timeout_height_on_b: TimeoutHeight, + #[builder(default)] + pub timeout_timestamp_on_b: Timestamp, +} + +impl From for MsgTransfer { + fn from(config: MsgTransferConfig) -> Self { + MsgTransfer { + port_id_on_a: config.port_id_on_a, + chan_id_on_a: config.chan_id_on_a, + packet_data: config.packet_data, + timeout_height_on_b: config.timeout_height_on_b, + timeout_timestamp_on_b: config.timeout_timestamp_on_b, + } + } +} + +pub fn extract_transfer_packet(msg: &MsgTransfer, sequence: Sequence) -> Packet { + let data = serde_json::to_vec(&msg.packet_data) + .expect("PacketData's infallible Serialize impl failed"); + + Packet { + seq_on_a: sequence, + port_id_on_a: msg.port_id_on_a.clone(), + chan_id_on_a: msg.chan_id_on_a.clone(), + port_id_on_b: PortId::transfer(), + chan_id_on_b: ChannelId::default(), + data, + timeout_height_on_b: msg.timeout_height_on_b, + timeout_timestamp_on_b: msg.timeout_timestamp_on_b, + } +} + +/// Configuration of the `PacketData` type for building dummy packets. +#[derive(TypedBuilder, Debug)] +#[builder(build_method(into = PacketData))] +pub struct PacketDataConfig { + pub token: PrefixedCoin, + #[builder(default = dummy_account_id())] + pub sender: Signer, + #[builder(default = dummy_account_id())] + pub receiver: Signer, + #[builder(default = Memo::from("".to_string()))] + pub memo: Memo, +} + +impl From for PacketData { + fn from(config: PacketDataConfig) -> Self { + PacketData { + token: config.token, + sender: config.sender, + receiver: config.receiver, + memo: config.memo, + } + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/clients/mock.rs b/crates/ibc-testkit/src/utils/dummies/clients/mock.rs new file mode 100644 index 000000000..0f9350ff3 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/clients/mock.rs @@ -0,0 +1,8 @@ +use ibc::Height; + +use crate::testapp::ibc::clients::mock::header::MockHeader; + +/// Returns a dummy `MockHeader` with the given revision height. +pub fn dummy_new_mock_header(revision_height: u64) -> MockHeader { + MockHeader::new(Height::new(0, revision_height).expect("Never fails")) +} diff --git a/crates/ibc-testkit/src/utils/dummies/clients/mod.rs b/crates/ibc-testkit/src/utils/dummies/clients/mod.rs new file mode 100644 index 000000000..301d7f544 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/clients/mod.rs @@ -0,0 +1,2 @@ +pub mod mock; +pub mod tendermint; diff --git a/crates/ibc-testkit/src/utils/dummies/clients/tendermint.rs b/crates/ibc-testkit/src/utils/dummies/clients/tendermint.rs new file mode 100644 index 000000000..c249c487e --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/clients/tendermint.rs @@ -0,0 +1,160 @@ +use core::str::FromStr; +use core::time::Duration; + +use ibc::clients::ics07_tendermint::client_state::{AllowUpdate, ClientState}; +use ibc::clients::ics07_tendermint::error::{Error as ClientError, Error}; +#[cfg(feature = "serde")] +use ibc::clients::ics07_tendermint::header::Header; +use ibc::clients::ics07_tendermint::trust_threshold::TrustThreshold; +use ibc::core::ics02_client::height::Height; +use ibc::core::ics23_commitment::specs::ProofSpecs; +use ibc::core::ics24_host::identifier::ChainId; +use ibc::prelude::*; +use ibc::proto::core::client::v1::Height as RawHeight; +use ibc::proto::tendermint::v1::{ClientState as RawTmClientState, Fraction}; +use tendermint::block::Header as TmHeader; + +/// Returns a dummy tendermint `ClientState` by given `frozen_height`, for testing purposes only! +pub fn dummy_tm_client_state_from_raw(frozen_height: RawHeight) -> Result { + ClientState::try_from(dummy_raw_tm_client_state(frozen_height)) +} + +/// Returns a dummy tendermint `ClientState` from a `TmHeader`, for testing purposes only! +pub fn dummy_tm_client_state_from_header(tm_header: TmHeader) -> ClientState { + let chain_id = ChainId::from_str(tm_header.chain_id.as_str()).expect("Never fails"); + ClientState::new( + chain_id.clone(), + Default::default(), + Duration::from_secs(64000), + Duration::from_secs(128000), + Duration::from_millis(3000), + Height::new(chain_id.revision_number(), u64::from(tm_header.height)).expect("Never fails"), + Default::default(), + Default::default(), + AllowUpdate { + after_expiry: false, + after_misbehaviour: false, + }, + ) + .expect("Never fails") +} + +/// Returns a dummy tendermint `RawTmClientState` by given `frozen_height`, for testing purposes only! +pub fn dummy_raw_tm_client_state(frozen_height: RawHeight) -> RawTmClientState { + #[allow(deprecated)] + RawTmClientState { + chain_id: ChainId::new("ibc-0").expect("Never fails").to_string(), + trust_level: Some(Fraction { + numerator: 1, + denominator: 3, + }), + trusting_period: Some(Duration::from_secs(64000).into()), + unbonding_period: Some(Duration::from_secs(128000).into()), + max_clock_drift: Some(Duration::from_millis(3000).into()), + latest_height: Some(Height::new(0, 10).expect("Never fails").into()), + proof_specs: ProofSpecs::default().into(), + upgrade_path: Default::default(), + frozen_height: Some(frozen_height), + allow_update_after_expiry: false, + allow_update_after_misbehaviour: false, + } +} + +#[derive(typed_builder::TypedBuilder, Debug)] +pub struct ClientStateConfig { + pub chain_id: ChainId, + #[builder(default)] + pub trust_level: TrustThreshold, + #[builder(default = Duration::from_secs(64000))] + pub trusting_period: Duration, + #[builder(default = Duration::from_secs(128000))] + pub unbonding_period: Duration, + #[builder(default = Duration::from_millis(3000))] + max_clock_drift: Duration, + pub latest_height: Height, + #[builder(default)] + pub proof_specs: ProofSpecs, + #[builder(default)] + pub upgrade_path: Vec, + #[builder(default = AllowUpdate { after_expiry: false, after_misbehaviour: false })] + allow_update: AllowUpdate, +} + +impl TryFrom for ClientState { + type Error = ClientError; + + fn try_from(config: ClientStateConfig) -> Result { + ClientState::new( + config.chain_id, + config.trust_level, + config.trusting_period, + config.unbonding_period, + config.max_clock_drift, + config.latest_height, + config.proof_specs, + config.upgrade_path, + config.allow_update, + ) + } +} + +#[cfg(feature = "serde")] +pub fn dummy_tendermint_header() -> tendermint::block::Header { + use tendermint::block::signed_header::SignedHeader; + + serde_json::from_str::(include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/data/json/signed_header.json" + ))) + .expect("Never fails") + .header +} + +// TODO: This should be replaced with a ::default() or ::produce(). +// The implementation of this function comprises duplicate code (code borrowed from +// `tendermint-rs` for assembling a Header). +// See https://github.com/informalsystems/tendermint-rs/issues/381. +// +// The normal flow is: +// - get the (trusted) signed header and the `trusted_validator_set` at a `trusted_height` +// - get the `signed_header` and the `validator_set` at latest height +// - build the ics07 Header +// For testing purposes this function does: +// - get the `signed_header` from a .json file +// - create the `validator_set` with a single validator that is also the proposer +// - assume a `trusted_height` of 1 and no change in the validator set since height 1, +// i.e. `trusted_validator_set` = `validator_set` +#[cfg(feature = "serde")] +pub fn dummy_ics07_header() -> Header { + use subtle_encoding::hex; + use tendermint::block::signed_header::SignedHeader; + use tendermint::validator::{Info as ValidatorInfo, Set as ValidatorSet}; + use tendermint::PublicKey; + + // Build a SignedHeader from a JSON file. + let shdr = serde_json::from_str::(include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/data/json/signed_header.json" + ))) + .expect("Never fails"); + + // Build a set of validators. + // Below are test values inspired form `test_validator_set()` in tendermint-rs. + let v1: ValidatorInfo = ValidatorInfo::new( + PublicKey::from_raw_ed25519( + &hex::decode_upper("F349539C7E5EF7C49549B09C4BFC2335318AB0FE51FBFAA2433B4F13E816F4A7") + .expect("Never fails"), + ) + .expect("Never fails"), + 281_815_u64.try_into().expect("Never fails"), + ); + + let vs = ValidatorSet::new(vec![v1.clone()], Some(v1)); + + Header { + signed_header: shdr, + validator_set: vs.clone(), + trusted_height: Height::min(0), + trusted_next_validator_set: vs, + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/acknowledgement.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/acknowledgement.rs new file mode 100644 index 000000000..f1e1e0129 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/acknowledgement.rs @@ -0,0 +1,26 @@ +use ibc::proto::core::channel::v1::{ + MsgAcknowledgement as RawMsgAcknowledgement, Packet as RawPacket, +}; +use ibc::proto::core::client::v1::Height as RawHeight; + +use super::{dummy_proof, dummy_raw_packet}; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgAcknowledgement`, for testing purposes only! +/// The `height` parametrizes both the proof height as well as the timeout height. +pub fn dummy_raw_msg_acknowledgement(height: u64) -> RawMsgAcknowledgement { + dummy_raw_msg_ack_with_packet(dummy_raw_packet(height, 1), height) +} + +pub fn dummy_raw_msg_ack_with_packet(packet: RawPacket, height: u64) -> RawMsgAcknowledgement { + RawMsgAcknowledgement { + packet: Some(packet), + acknowledgement: dummy_proof(), + proof_acked: dummy_proof(), + proof_height: Some(RawHeight { + revision_number: 0, + revision_height: height, + }), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/chan_close_confirm.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_close_confirm.rs new file mode 100644 index 000000000..42645de42 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_close_confirm.rs @@ -0,0 +1,21 @@ +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::prelude::*; +use ibc::proto::core::channel::v1::MsgChannelCloseConfirm as RawMsgChannelCloseConfirm; +use ibc::proto::core::client::v1::Height; + +use super::dummy_proof; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgChannelCloseConfirm`, for testing purposes only! +pub fn dummy_raw_msg_chan_close_confirm(proof_height: u64) -> RawMsgChannelCloseConfirm { + RawMsgChannelCloseConfirm { + port_id: PortId::transfer().to_string(), + channel_id: ChannelId::default().to_string(), + proof_init: dummy_proof(), + proof_height: Some(Height { + revision_number: 0, + revision_height: proof_height, + }), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/chan_close_init.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_close_init.rs new file mode 100644 index 000000000..492090cda --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_close_init.rs @@ -0,0 +1,14 @@ +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::prelude::*; +use ibc::proto::core::channel::v1::MsgChannelCloseInit as RawMsgChannelCloseInit; + +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgChannelCloseInit`, for testing purposes only! +pub fn dummy_raw_msg_chan_close_init() -> RawMsgChannelCloseInit { + RawMsgChannelCloseInit { + port_id: PortId::transfer().to_string(), + channel_id: ChannelId::default().to_string(), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_ack.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_ack.rs new file mode 100644 index 000000000..087c3d31a --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_ack.rs @@ -0,0 +1,23 @@ +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::prelude::*; +use ibc::proto::core::channel::v1::MsgChannelOpenAck as RawMsgChannelOpenAck; +use ibc::proto::core::client::v1::Height; + +use super::dummy_proof; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgChannelOpenAck`, for testing purposes only! +pub fn dummy_raw_msg_chan_open_ack(proof_height: u64) -> RawMsgChannelOpenAck { + RawMsgChannelOpenAck { + port_id: PortId::transfer().to_string(), + channel_id: ChannelId::default().to_string(), + counterparty_channel_id: ChannelId::default().to_string(), + counterparty_version: "".to_string(), + proof_try: dummy_proof(), + proof_height: Some(Height { + revision_number: 0, + revision_height: proof_height, + }), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_confirm.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_confirm.rs new file mode 100644 index 000000000..68140bd2e --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_confirm.rs @@ -0,0 +1,21 @@ +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::prelude::*; +use ibc::proto::core::channel::v1::MsgChannelOpenConfirm as RawMsgChannelOpenConfirm; +use ibc::proto::core::client::v1::Height; + +use super::dummy_proof; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgChannelOpenConfirm`, for testing purposes only! +pub fn dummy_raw_msg_chan_open_confirm(proof_height: u64) -> RawMsgChannelOpenConfirm { + RawMsgChannelOpenConfirm { + port_id: PortId::transfer().to_string(), + channel_id: ChannelId::default().to_string(), + proof_ack: dummy_proof(), + proof_height: Some(Height { + revision_number: 0, + revision_height: proof_height, + }), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_init.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_init.rs new file mode 100644 index 000000000..d161134a2 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_init.rs @@ -0,0 +1,15 @@ +use ibc::core::ics24_host::identifier::PortId; +use ibc::prelude::*; +use ibc::proto::core::channel::v1::MsgChannelOpenInit as RawMsgChannelOpenInit; + +use super::dummy_raw_channel_end; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgChannelOpenInit`, for testing purposes only! +pub fn dummy_raw_msg_chan_open_init(counterparty_channel_id: Option) -> RawMsgChannelOpenInit { + RawMsgChannelOpenInit { + port_id: PortId::transfer().to_string(), + channel: Some(dummy_raw_channel_end(1, counterparty_channel_id)), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_try.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_try.rs new file mode 100644 index 000000000..1bedea696 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/chan_open_try.rs @@ -0,0 +1,24 @@ +use ibc::core::ics24_host::identifier::PortId; +use ibc::prelude::*; +use ibc::proto::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; +use ibc::proto::core::client::v1::Height; + +use super::{dummy_proof, dummy_raw_channel_end}; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgChannelOpenTry`, for testing purposes only! +pub fn dummy_raw_msg_chan_open_try(proof_height: u64) -> RawMsgChannelOpenTry { + #[allow(deprecated)] + RawMsgChannelOpenTry { + port_id: PortId::transfer().to_string(), + previous_channel_id: "".to_string(), + channel: Some(dummy_raw_channel_end(2, Some(0))), + counterparty_version: "".to_string(), + proof_init: dummy_proof(), + proof_height: Some(Height { + revision_number: 0, + revision_height: proof_height, + }), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/mod.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/mod.rs new file mode 100644 index 000000000..3e5a083da --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/mod.rs @@ -0,0 +1,50 @@ +mod acknowledgement; +mod chan_close_confirm; +mod chan_close_init; +mod chan_open_ack; +mod chan_open_confirm; +mod chan_open_init; +mod chan_open_try; +mod packet; +mod recv_packet; +mod timeout; +mod timeout_on_close; + +pub use acknowledgement::*; +pub use chan_close_confirm::*; +pub use chan_close_init::*; +pub use chan_open_ack::*; +pub use chan_open_confirm::*; +pub use chan_open_init::*; +pub use chan_open_try::*; +use ibc::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; +use ibc::prelude::*; +use ibc::proto::core::channel::v1::{Channel as RawChannel, Counterparty as RawCounterparty}; +pub use packet::*; +pub use recv_packet::*; +pub use timeout::*; +pub use timeout_on_close::*; + +/// Returns a dummy `RawCounterparty`, for testing purposes only! +/// Can be optionally parametrized with a specific channel identifier. +pub fn dummy_raw_counterparty_chan(channel_id: String) -> RawCounterparty { + RawCounterparty { + port_id: PortId::transfer().to_string(), + channel_id, + } +} + +/// Returns a dummy `RawChannel`, for testing purposes only! +pub fn dummy_raw_channel_end(state: i32, channel_id: Option) -> RawChannel { + let channel_id = match channel_id { + Some(id) => ChannelId::new(id).to_string(), + None => "".to_string(), + }; + RawChannel { + state, + ordering: 2, + counterparty: Some(dummy_raw_counterparty_chan(channel_id)), + connection_hops: vec![ConnectionId::default().to_string()], + version: "".to_string(), // The version is not validated. + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/packet.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/packet.rs new file mode 100644 index 000000000..9045dd320 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/packet.rs @@ -0,0 +1,68 @@ +use ibc::core::ics04_channel::packet::{Packet, Sequence}; +use ibc::core::ics04_channel::timeout::TimeoutHeight; +use ibc::core::ics24_host::identifier::{ChannelId, PortId}; +use ibc::core::timestamp::Timestamp; +use ibc::prelude::*; +use ibc::proto::core::channel::v1::Packet as RawPacket; +use ibc::proto::core::client::v1::Height as RawHeight; +use typed_builder::TypedBuilder; + +/// Configuration of the `PacketData` type for building dummy packets. +#[derive(TypedBuilder, Debug)] +#[builder(build_method(into = Packet))] +pub struct PacketConfig { + #[builder(default)] + pub seq_on_a: Sequence, + #[builder(default = PortId::transfer())] + pub port_id_on_a: PortId, + #[builder(default)] + pub chan_id_on_a: ChannelId, + #[builder(default = PortId::transfer())] + pub port_id_on_b: PortId, + #[builder(default)] + pub chan_id_on_b: ChannelId, + #[builder(default)] + pub data: Vec, + #[builder(default)] + pub timeout_height_on_b: TimeoutHeight, + #[builder(default)] + pub timeout_timestamp_on_b: Timestamp, +} + +impl From for Packet { + fn from(config: PacketConfig) -> Self { + Packet { + seq_on_a: config.seq_on_a, + port_id_on_a: config.port_id_on_a, + chan_id_on_a: config.chan_id_on_a, + port_id_on_b: config.port_id_on_b, + chan_id_on_b: config.chan_id_on_b, + data: config.data, + timeout_height_on_b: config.timeout_height_on_b, + timeout_timestamp_on_b: config.timeout_timestamp_on_b, + } + } +} + +/// Returns a dummy `RawPacket`, for testing purposes only! +pub fn dummy_raw_packet(timeout_height: u64, timeout_timestamp: u64) -> RawPacket { + RawPacket { + sequence: 1, + source_port: PortId::transfer().to_string(), + source_channel: ChannelId::default().to_string(), + destination_port: PortId::transfer().to_string(), + destination_channel: ChannelId::default().to_string(), + data: vec![0], + timeout_height: Some(RawHeight { + revision_number: 0, + revision_height: timeout_height, + }), + timeout_timestamp, + } +} + +pub fn dummy_proof() -> Vec { + "Y29uc2Vuc3VzU3RhdGUvaWJjb25lY2xpZW50LzIy" + .as_bytes() + .to_vec() +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/recv_packet.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/recv_packet.rs new file mode 100644 index 000000000..a9e521859 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/recv_packet.rs @@ -0,0 +1,45 @@ +use core::ops::Add; +use core::time::Duration; + +use ibc::core::ics04_channel::msgs::MsgRecvPacket; +use ibc::core::ics04_channel::packet::Packet; +use ibc::core::ics23_commitment::commitment::CommitmentProofBytes; +use ibc::core::timestamp::Timestamp; +use ibc::proto::core::channel::v1::MsgRecvPacket as RawMsgRecvPacket; +use ibc::proto::core::client::v1::Height as RawHeight; +use ibc::{Height, Signer}; + +use super::{dummy_proof, dummy_raw_packet}; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +pub fn dummy_msg_recv_packet( + packet: Packet, + proof_commitment_on_a: CommitmentProofBytes, + proof_height_on_a: Height, + signer: Signer, +) -> MsgRecvPacket { + MsgRecvPacket { + packet, + proof_commitment_on_a, + proof_height_on_a, + signer, + } +} + +/// Returns a dummy `RawMsgRecvPacket`, for testing purposes only! The `height` +/// parametrizes both the proof height as well as the timeout height. +pub fn dummy_raw_msg_recv_packet(height: u64) -> RawMsgRecvPacket { + let timestamp = Timestamp::now().add(Duration::from_secs(9)); + RawMsgRecvPacket { + packet: Some(dummy_raw_packet( + height, + timestamp.expect("timestamp").nanoseconds(), + )), + proof_commitment: dummy_proof(), + proof_height: Some(RawHeight { + revision_number: 0, + revision_height: height, + }), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/timeout.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/timeout.rs new file mode 100644 index 000000000..db08b045c --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/timeout.rs @@ -0,0 +1,24 @@ +use ibc::proto::core::channel::v1::MsgTimeout as RawMsgTimeout; +use ibc::proto::core::client::v1::Height as RawHeight; + +use super::{dummy_proof, dummy_raw_packet}; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgTimeout`, for testing purposes only! +/// The `height` parametrizes both the proof height as well as the timeout height. +pub fn dummy_raw_msg_timeout( + proof_height: u64, + timeout_height: u64, + timeout_timestamp: u64, +) -> RawMsgTimeout { + RawMsgTimeout { + packet: Some(dummy_raw_packet(timeout_height, timeout_timestamp)), + proof_unreceived: dummy_proof(), + proof_height: Some(RawHeight { + revision_number: 0, + revision_height: proof_height, + }), + next_sequence_recv: 1, + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/channel/timeout_on_close.rs b/crates/ibc-testkit/src/utils/dummies/core/channel/timeout_on_close.rs new file mode 100644 index 000000000..c11bcd187 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/channel/timeout_on_close.rs @@ -0,0 +1,21 @@ +use ibc::proto::core::channel::v1::MsgTimeoutOnClose as RawMsgTimeoutOnClose; +use ibc::proto::core::client::v1::Height as RawHeight; + +use super::{dummy_proof, dummy_raw_packet}; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgTimeoutOnClose`, for testing purposes only! +/// The `height` parametrizes both the proof height as well as the timeout height. +pub fn dummy_raw_msg_timeout_on_close(height: u64, timeout_timestamp: u64) -> RawMsgTimeoutOnClose { + RawMsgTimeoutOnClose { + packet: Some(dummy_raw_packet(height, timeout_timestamp)), + proof_unreceived: dummy_proof(), + proof_close: dummy_proof(), + proof_height: Some(RawHeight { + revision_number: 0, + revision_height: height, + }), + next_sequence_recv: 1, + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/client/mod.rs b/crates/ibc-testkit/src/utils/dummies/core/client/mod.rs new file mode 100644 index 000000000..aaacce78c --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/client/mod.rs @@ -0,0 +1,11 @@ +#[cfg(feature = "serde")] +mod msg_create_client; +#[cfg(feature = "serde")] +mod msg_update_client; +mod msg_upgrade_client; + +#[cfg(feature = "serde")] +pub use msg_create_client::*; +#[cfg(feature = "serde")] +pub use msg_update_client::*; +pub use msg_upgrade_client::*; diff --git a/crates/ibc-testkit/src/utils/dummies/core/client/msg_create_client.rs b/crates/ibc-testkit/src/utils/dummies/core/client/msg_create_client.rs new file mode 100644 index 000000000..8c93071d0 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/client/msg_create_client.rs @@ -0,0 +1,21 @@ +use ibc::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; +use ibc::proto::core::client::v1::MsgCreateClient; +use ibc::proto::Any; + +use crate::utils::dummies::clients::tendermint::{ + dummy_tendermint_header, dummy_tm_client_state_from_header, +}; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgCreateClient`, for testing purposes only! +pub fn dummy_raw_msg_create_client() -> MsgCreateClient { + let tm_header = dummy_tendermint_header(); + + let tm_client_state = dummy_tm_client_state_from_header(tm_header.clone()); + + MsgCreateClient { + client_state: Some(Any::from(tm_client_state)), + consensus_state: Some(Any::from(TmConsensusState::try_from(tm_header).unwrap())), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/client/msg_update_client.rs b/crates/ibc-testkit/src/utils/dummies/core/client/msg_update_client.rs new file mode 100644 index 000000000..5ff980504 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/client/msg_update_client.rs @@ -0,0 +1,18 @@ +use ibc::proto::core::client::v1::MsgUpdateClient; +use ibc::proto::Any; + +use crate::utils::dummies::clients::tendermint::dummy_ics07_header; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `RawMsgUpdateClient`, for testing purposes only! +pub fn dummy_raw_msg_update_client() -> MsgUpdateClient { + let client_id = "07-tendermint-0".parse().unwrap(); + + let tm_header = dummy_ics07_header(); + + MsgUpdateClient { + client_id, + client_message: Some(Any::from(tm_header)), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/client/msg_upgrade_client.rs b/crates/ibc-testkit/src/utils/dummies/core/client/msg_upgrade_client.rs new file mode 100644 index 000000000..3c42c10f7 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/client/msg_upgrade_client.rs @@ -0,0 +1,38 @@ +use ibc::core::ics02_client::height::Height; +use ibc::core::ics02_client::msgs::upgrade_client::MsgUpgradeClient; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::proto::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; + +use crate::testapp::ibc::clients::mock::client_state::MockClientState; +use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; +use crate::testapp::ibc::clients::mock::header::MockHeader; +use crate::utils::dummies::core::commitment::dummy_commitment_proof_bytes; +use crate::utils::dummies::core::signer::{dummy_account_id, dummy_bech32_account}; + +/// Returns a dummy `MsgUpgradeClient`, for testing purposes only! +pub fn dummy_msg_upgrade_client(client_id: ClientId, upgrade_height: Height) -> MsgUpgradeClient { + MsgUpgradeClient { + client_id, + upgraded_client_state: MockClientState::new(MockHeader::new(upgrade_height)).into(), + upgraded_consensus_state: MockConsensusState::new(MockHeader::new(upgrade_height)).into(), + proof_upgrade_client: dummy_commitment_proof_bytes(), + proof_upgrade_consensus_state: dummy_commitment_proof_bytes(), + signer: dummy_account_id(), + } +} + +/// Returns a dummy `RawMsgUpgradeClient`, for testing purposes only! +pub fn dummy_raw_msg_upgrade_client() -> RawMsgUpgradeClient { + let client_id = "07-tendermint-0".parse().unwrap(); + + let upgrade_height = Height::new(0, 10).expect("Never fails"); + + RawMsgUpgradeClient { + client_id, + client_state: Some(MockClientState::new(MockHeader::new(upgrade_height)).into()), + consensus_state: Some(MockConsensusState::new(MockHeader::new(upgrade_height)).into()), + proof_upgrade_client: dummy_commitment_proof_bytes().into(), + proof_upgrade_consensus_state: dummy_commitment_proof_bytes().into(), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/commitment.rs b/crates/ibc-testkit/src/utils/dummies/core/commitment.rs new file mode 100644 index 000000000..c4939556e --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/commitment.rs @@ -0,0 +1,14 @@ +use ibc::core::ics23_commitment::commitment::CommitmentProofBytes; +use ibc::prelude::*; +use ibc::proto::core::commitment::v1::MerkleProof as RawMerkleProof; +use ibc::proto::ics23::CommitmentProof; + +/// Returns a dummy `CommitmentProofBytes`, for testing purposes only! +pub fn dummy_commitment_proof_bytes() -> CommitmentProofBytes { + let parsed = CommitmentProof { proof: None }; + let mproofs: Vec = vec![parsed]; + let raw_mp = RawMerkleProof { proofs: mproofs }; + raw_mp + .try_into() + .expect("could not convert to CommitmentProofBytes") +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_ack.rs b/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_ack.rs new file mode 100644 index 000000000..b1a18e862 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_ack.rs @@ -0,0 +1,45 @@ +use ibc::core::ics02_client::height::Height; +use ibc::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck; +use ibc::core::ics03_connection::version::Version; +use ibc::core::ics24_host::identifier::ConnectionId; +use ibc::prelude::*; +use ibc::proto::core::client::v1::Height as RawHeight; +use ibc::proto::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck; + +use crate::testapp::ibc::clients::mock::client_state::MockClientState; +use crate::testapp::ibc::clients::mock::header::MockHeader; +use crate::utils::dummies::core::channel::dummy_proof; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `MsgConnectionOpenAck` with dummy values. +pub fn dummy_msg_conn_open_ack(proof_height: u64, consensus_height: u64) -> MsgConnectionOpenAck { + MsgConnectionOpenAck::try_from(dummy_raw_msg_conn_open_ack(proof_height, consensus_height)) + .expect("Never fails") +} + +/// Returns a dummy `RawMsgConnectionOpenAck`, for testing purposes only! +pub fn dummy_raw_msg_conn_open_ack( + proof_height: u64, + consensus_height: u64, +) -> RawMsgConnectionOpenAck { + let client_state_height = Height::new(0, consensus_height).expect("invalid height"); + RawMsgConnectionOpenAck { + connection_id: ConnectionId::new(0).to_string(), + counterparty_connection_id: ConnectionId::new(1).to_string(), + proof_try: dummy_proof(), + proof_height: Some(RawHeight { + revision_number: 0, + revision_height: proof_height, + }), + proof_consensus: dummy_proof(), + consensus_height: Some(RawHeight { + revision_number: 0, + revision_height: consensus_height, + }), + client_state: Some(MockClientState::new(MockHeader::new(client_state_height)).into()), + proof_client: dummy_proof(), + version: Some(Version::default().into()), + signer: dummy_bech32_account(), + host_consensus_state_proof: vec![], + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_confirm.rs b/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_confirm.rs new file mode 100644 index 000000000..f5f4f4764 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_confirm.rs @@ -0,0 +1,26 @@ +use alloc::string::ToString; + +use ibc::core::ics03_connection::msgs::conn_open_confirm::MsgConnectionOpenConfirm; +use ibc::proto::core::client::v1::Height; +use ibc::proto::core::connection::v1::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; + +use crate::utils::dummies::core::channel::dummy_proof; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `MsgConnectionOpenConfirm` for testing purposes only! +pub fn dummy_conn_open_confirm() -> MsgConnectionOpenConfirm { + MsgConnectionOpenConfirm::try_from(dummy_raw_msg_conn_open_confirm()).expect("Never fails") +} + +/// Returns a dummy `RawMsgConnectionOpenConfirm` for testing purposes only! +pub fn dummy_raw_msg_conn_open_confirm() -> RawMsgConnectionOpenConfirm { + RawMsgConnectionOpenConfirm { + connection_id: "srcconnection".to_string(), + proof_ack: dummy_proof(), + proof_height: Some(Height { + revision_number: 0, + revision_height: 10, + }), + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_init.rs b/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_init.rs new file mode 100644 index 000000000..588737a0f --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_init.rs @@ -0,0 +1,80 @@ +use ibc::core::ics03_connection::connection::Counterparty; +use ibc::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; +use ibc::core::ics03_connection::version::Version; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::prelude::*; +use ibc::proto::core::connection::v1::{ + MsgConnectionOpenInit as RawMsgConnectionOpenInit, Version as RawVersion, +}; + +use super::dummy_raw_counterparty_conn; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +pub fn raw_version_from_identifier(identifier: &str) -> Option { + if identifier.is_empty() { + return None; + } + + Some(RawVersion { + identifier: identifier.to_string(), + features: vec![], + }) +} + +/// Returns a dummy `MsgConnectionOpenInit` for testing purposes only! +pub fn dummy_msg_conn_open_init() -> MsgConnectionOpenInit { + MsgConnectionOpenInit::try_from(dummy_raw_msg_conn_open_init()).expect("Never fails") +} + +/// Setter for `client_id`. Amenable to chaining, since it consumes the input message. +pub fn dummy_msg_conn_open_init_with_client_id( + msg: MsgConnectionOpenInit, + client_id: ClientId, +) -> MsgConnectionOpenInit { + MsgConnectionOpenInit { + client_id_on_a: client_id, + ..msg + } +} + +/// Setter for `counterparty`. Amenable to chaining, since it consumes the input message. +pub fn msg_conn_open_init_with_counterparty_conn_id( + msg: MsgConnectionOpenInit, + counterparty_conn_id: u64, +) -> MsgConnectionOpenInit { + let counterparty = + Counterparty::try_from(dummy_raw_counterparty_conn(Some(counterparty_conn_id))) + .expect("Never fails"); + MsgConnectionOpenInit { + counterparty, + ..msg + } +} + +/// Setter for the connection `version` +pub fn msg_conn_open_with_version( + msg: MsgConnectionOpenInit, + identifier: Option<&str>, +) -> MsgConnectionOpenInit { + let version = match identifier { + Some(v) => Version::try_from(RawVersion { + identifier: v.to_string(), + features: vec![], + }) + .expect("could not create version from identifier") + .into(), + None => None, + }; + MsgConnectionOpenInit { version, ..msg } +} + +/// Returns a dummy `RawMsgConnectionOpenInit`, for testing purposes only! +pub fn dummy_raw_msg_conn_open_init() -> RawMsgConnectionOpenInit { + RawMsgConnectionOpenInit { + client_id: ClientId::default().to_string(), + counterparty: Some(dummy_raw_counterparty_conn(None)), + version: Some(Version::default().into()), + delay_period: 0, + signer: dummy_bech32_account(), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_try.rs b/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_try.rs new file mode 100644 index 000000000..3d6bb0956 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/connection/conn_open_try.rs @@ -0,0 +1,66 @@ +use ibc::core::ics02_client::height::Height; +use ibc::core::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::prelude::*; +use ibc::proto::core::client::v1::Height as RawHeight; +use ibc::proto::core::connection::v1::MsgConnectionOpenTry as RawMsgConnectionOpenTry; + +use super::dummy_raw_counterparty_conn; +use crate::testapp::ibc::clients::mock::client_state::MockClientState; +use crate::testapp::ibc::clients::mock::header::MockHeader; +use crate::utils::dummies::core::channel::dummy_proof; +use crate::utils::dummies::core::signer::dummy_bech32_account; + +/// Returns a dummy `MsgConnectionOpenTry` for testing purposes only! +pub fn dummy_msg_conn_open_try(proof_height: u64, consensus_height: u64) -> MsgConnectionOpenTry { + MsgConnectionOpenTry::try_from(dummy_raw_msg_conn_open_try(proof_height, consensus_height)) + .expect("Never fails") +} +/// Setter for the `client_id` +pub fn msg_conn_open_try_with_client_id( + msg: MsgConnectionOpenTry, + client_id: ClientId, +) -> MsgConnectionOpenTry { + MsgConnectionOpenTry { + client_id_on_b: client_id, + ..msg + } +} + +/// Returns a dummy `RawMsgConnectionOpenTry` with parametrized heights. The parameter +/// `proof_height` represents the height, on the source chain, at which this chain produced the +/// proof. Parameter `consensus_height` represents the height of destination chain which a +/// client on the source chain stores. +pub fn dummy_raw_msg_conn_open_try( + proof_height: u64, + consensus_height: u64, +) -> RawMsgConnectionOpenTry { + let client_state_height = Height::new(0, consensus_height).expect("could not create height"); + + #[allow(deprecated)] + RawMsgConnectionOpenTry { + client_id: ClientId::default().to_string(), + previous_connection_id: ConnectionId::default().to_string(), + client_state: Some(MockClientState::new(MockHeader::new(client_state_height)).into()), + counterparty: Some(dummy_raw_counterparty_conn(Some(0))), + delay_period: 0, + counterparty_versions: get_compatible_versions() + .iter() + .map(|v| v.clone().into()) + .collect(), + proof_init: dummy_proof(), + proof_height: Some(RawHeight { + revision_number: 0, + revision_height: proof_height, + }), + proof_consensus: dummy_proof(), + consensus_height: Some(RawHeight { + revision_number: 0, + revision_height: consensus_height, + }), + proof_client: dummy_proof(), + signer: dummy_bech32_account(), + host_consensus_state_proof: vec![], + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/connection/mod.rs b/crates/ibc-testkit/src/utils/dummies/core/connection/mod.rs new file mode 100644 index 000000000..15bc1990b --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/connection/mod.rs @@ -0,0 +1,51 @@ +mod conn_open_ack; +mod conn_open_confirm; +mod conn_open_init; +mod conn_open_try; + +pub use conn_open_ack::*; +pub use conn_open_confirm::*; +pub use conn_open_init::*; +pub use conn_open_try::*; +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::prelude::*; +use ibc::proto::core::commitment::v1::MerklePrefix; +use ibc::proto::core::connection::v1::Counterparty as RawCounterparty; +use typed_builder::TypedBuilder; + +#[derive(TypedBuilder, Debug)] +#[builder(build_method(into = RawCounterparty))] +pub struct CounterpartyConfig { + #[builder(default = "07-tendermint-0")] + client_id: &'static str, + #[builder(default = "connection-0")] + connection_id: &'static str, + #[builder(default = Some(MerklePrefix { + key_prefix: b"ibc".to_vec() + }))] + prefix: Option, +} + +impl From for RawCounterparty { + fn from(config: CounterpartyConfig) -> Self { + Self { + client_id: config.client_id.to_string(), + connection_id: config.connection_id.to_string(), + prefix: config.prefix, + } + } +} + +pub fn dummy_raw_counterparty_conn(conn_id: Option) -> RawCounterparty { + let connection_id = match conn_id { + Some(id) => ConnectionId::new(id).to_string(), + None => "".to_string(), + }; + RawCounterparty { + client_id: ClientId::default().to_string(), + connection_id, + prefix: Some(MerklePrefix { + key_prefix: b"ibc".to_vec(), + }), + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/context.rs b/crates/ibc-testkit/src/utils/dummies/core/context.rs new file mode 100644 index 000000000..6c7b95af1 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/context.rs @@ -0,0 +1,127 @@ +use alloc::sync::Arc; +use core::cmp::min; +use core::ops::{Add, Sub}; +use core::time::Duration; + +use ibc::core::ics24_host::identifier::ChainId; +use ibc::core::timestamp::Timestamp; +use ibc::prelude::*; +use ibc::Height; +use parking_lot::Mutex; +use tendermint_testgen::Validator as TestgenValidator; +use typed_builder::TypedBuilder; + +use crate::hosts::block::{HostBlock, HostType}; +use crate::testapp::ibc::core::types::{MockContext, MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; + +/// Configuration of the `MockContext` type for generating dummy contexts. +#[derive(Debug, TypedBuilder)] +#[builder(build_method(into = MockContext))] +pub struct MockContextConfig { + #[builder(default = HostType::Mock)] + host_type: HostType, + + host_id: ChainId, + + #[builder(default = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS))] + block_time: Duration, + + // may panic if validator_set_history size is less than max_history_size + 1 + #[builder(default = 5)] + max_history_size: u64, + + #[builder(default, setter(strip_option))] + validator_set_history: Option>>, + + latest_height: Height, + + #[builder(default = Timestamp::now())] + latest_timestamp: Timestamp, +} + +impl From for MockContext { + fn from(params: MockContextConfig) -> Self { + assert_ne!( + params.max_history_size, 0, + "The chain must have a non-zero max_history_size" + ); + + assert_ne!( + params.latest_height.revision_height(), + 0, + "The chain must have a non-zero revision_height" + ); + + // Compute the number of blocks to store. + let n = min( + params.max_history_size, + params.latest_height.revision_height(), + ); + + assert_eq!( + params.host_id.revision_number(), + params.latest_height.revision_number(), + "The version in the chain identifier must match the version in the latest height" + ); + + let next_block_timestamp = params + .latest_timestamp + .add(params.block_time) + .expect("Never fails"); + + let history = if let Some(validator_set_history) = params.validator_set_history { + (0..n) + .rev() + .map(|i| { + // generate blocks with timestamps -> N, N - BT, N - 2BT, ... + // where N = now(), BT = block_time + HostBlock::generate_block_with_validators( + params.host_id.clone(), + params.host_type, + params + .latest_height + .sub(i) + .expect("Never fails") + .revision_height(), + next_block_timestamp + .sub(params.block_time * ((i + 1) as u32)) + .expect("Never fails"), + &validator_set_history[(n - i) as usize - 1], + &validator_set_history[(n - i) as usize], + ) + }) + .collect() + } else { + (0..n) + .rev() + .map(|i| { + // generate blocks with timestamps -> N, N - BT, N - 2BT, ... + // where N = now(), BT = block_time + HostBlock::generate_block( + params.host_id.clone(), + params.host_type, + params + .latest_height + .sub(i) + .expect("Never fails") + .revision_height(), + next_block_timestamp + .sub(params.block_time * ((i + 1) as u32)) + .expect("Never fails"), + ) + }) + .collect() + }; + + MockContext { + host_chain_type: params.host_type, + host_chain_id: params.host_id.clone(), + max_history_size: params.max_history_size, + history, + block_time: params.block_time, + ibc_store: Arc::new(Mutex::new(MockIbcStore::default())), + events: Vec::new(), + logs: Vec::new(), + } + } +} diff --git a/crates/ibc-testkit/src/utils/dummies/core/mod.rs b/crates/ibc-testkit/src/utils/dummies/core/mod.rs new file mode 100644 index 000000000..1fe85ed5d --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/mod.rs @@ -0,0 +1,6 @@ +pub mod channel; +pub mod client; +pub mod commitment; +pub mod connection; +pub mod context; +pub mod signer; diff --git a/crates/ibc-testkit/src/utils/dummies/core/signer.rs b/crates/ibc-testkit/src/utils/dummies/core/signer.rs new file mode 100644 index 000000000..2d74049a8 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/core/signer.rs @@ -0,0 +1,12 @@ +use ibc::prelude::*; +use ibc::Signer; + +pub fn dummy_account_id() -> Signer { + "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C" + .to_string() + .into() +} + +pub fn dummy_bech32_account() -> String { + "cosmos1wxeyh7zgn4tctjzs0vtqpc6p5cxq5t2muzl7ng".to_string() +} diff --git a/crates/ibc-testkit/src/utils/dummies/mod.rs b/crates/ibc-testkit/src/utils/dummies/mod.rs new file mode 100644 index 000000000..f13a3c645 --- /dev/null +++ b/crates/ibc-testkit/src/utils/dummies/mod.rs @@ -0,0 +1,3 @@ +pub mod applications; +pub mod clients; +pub mod core; diff --git a/crates/ibc-testkit/src/utils/fixture.rs b/crates/ibc-testkit/src/utils/fixture.rs new file mode 100644 index 000000000..134c41c1f --- /dev/null +++ b/crates/ibc-testkit/src/utils/fixture.rs @@ -0,0 +1,35 @@ +use core::fmt::Debug; + +use ibc::core::RouterError; +use ibc::prelude::{String, *}; + +use crate::testapp::ibc::core::types::MockContext; + +pub enum Expect { + Success, + Failure(Option), +} + +#[derive(Clone, Debug)] +pub struct Fixture { + pub ctx: MockContext, + pub msg: M, +} + +impl Fixture { + pub fn generate_error_msg( + &self, + expect: &Expect, + process: &str, + res: &Result<(), RouterError>, + ) -> String { + let base_error = match expect { + Expect::Success => "step failed!", + Expect::Failure(_) => "step passed but was supposed to fail!", + }; + format!( + "{process} {base_error} /n {res:?} /n {:?} /n {:?}", + &self.msg, &self.ctx + ) + } +} diff --git a/crates/ibc-testkit/src/utils/mod.rs b/crates/ibc-testkit/src/utils/mod.rs new file mode 100644 index 000000000..35f40c216 --- /dev/null +++ b/crates/ibc-testkit/src/utils/mod.rs @@ -0,0 +1,5 @@ +mod dummies; +mod fixture; + +pub use dummies::*; +pub use fixture::*; diff --git a/crates/ibc-testkit/tests/applications/mod.rs b/crates/ibc-testkit/tests/applications/mod.rs new file mode 100644 index 000000000..014e52f27 --- /dev/null +++ b/crates/ibc-testkit/tests/applications/mod.rs @@ -0,0 +1 @@ +pub mod transfer; diff --git a/crates/ibc-testkit/tests/applications/transfer.rs b/crates/ibc-testkit/tests/applications/transfer.rs new file mode 100644 index 000000000..64d56b8c2 --- /dev/null +++ b/crates/ibc-testkit/tests/applications/transfer.rs @@ -0,0 +1,167 @@ +use ibc::applications::transfer::context::{ + cosmos_adr028_escrow_address, on_chan_open_init_execute, on_chan_open_init_validate, + on_chan_open_try_execute, on_chan_open_try_validate, +}; +use ibc::applications::transfer::VERSION; +use ibc::core::ics04_channel::channel::{Counterparty, Order}; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; +use ibc::prelude::*; +use ibc_testkit::testapp::ibc::applications::transfer::types::DummyTransferModule; +use subtle_encoding::bech32; + +fn get_defaults() -> ( + DummyTransferModule, + Order, + Vec, + PortId, + ChannelId, + Counterparty, +) { + let order = Order::Unordered; + let connection_hops = vec![ConnectionId::new(1)]; + let port_id = PortId::transfer(); + let channel_id = ChannelId::new(1); + let counterparty = Counterparty::new(port_id.clone(), Some(channel_id.clone())); + + ( + DummyTransferModule, + order, + connection_hops, + port_id, + channel_id, + counterparty, + ) +} + +#[test] +fn test_cosmos_escrow_address() { + fn assert_eq_escrow_address(port_id: &str, channel_id: &str, address: &str) { + let port_id = port_id.parse().unwrap(); + let channel_id = channel_id.parse().unwrap(); + let gen_address = { + let addr = cosmos_adr028_escrow_address(&port_id, &channel_id); + bech32::encode("cosmos", addr) + }; + assert_eq!(gen_address, address.to_owned()) + } + + // addresses obtained using `gaiad query ibc-transfer escrow-address [port-id] [channel-id]` + assert_eq_escrow_address( + "transfer", + "channel-141", + "cosmos1x54ltnyg88k0ejmk8ytwrhd3ltm84xehrnlslf", + ); + assert_eq_escrow_address( + "transfer", + "channel-207", + "cosmos1ju6tlfclulxumtt2kglvnxduj5d93a64r5czge", + ); + assert_eq_escrow_address( + "transfer", + "channel-187", + "cosmos177x69sver58mcfs74x6dg0tv6ls4s3xmmcaw53", + ); +} + +/// If the relayer passed "", indicating that it wants us to return the versions we support. +/// We currently only support ics20 +#[test] +fn test_on_chan_open_init_empty_version() { + let (mut ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); + + let in_version = Version::new("".to_string()); + + let (_, out_version) = on_chan_open_init_execute( + &mut ctx, + order, + &connection_hops, + &port_id, + &channel_id, + &counterparty, + &in_version, + ) + .unwrap(); + + assert_eq!(out_version, Version::new(VERSION.to_string())); +} + +/// If the relayer passed in the only supported version (ics20), then return ics20 +#[test] +fn test_on_chan_open_init_ics20_version() { + let (mut ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); + + let in_version = Version::new(VERSION.to_string()); + let (_, out_version) = on_chan_open_init_execute( + &mut ctx, + order, + &connection_hops, + &port_id, + &channel_id, + &counterparty, + &in_version, + ) + .unwrap(); + + assert_eq!(out_version, Version::new(VERSION.to_string())); +} + +/// If the relayer passed in an unsupported version, then fail +#[test] +fn test_on_chan_open_init_incorrect_version() { + let (ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); + + let in_version = Version::new("some-unsupported-version".to_string()); + let res = on_chan_open_init_validate( + &ctx, + order, + &connection_hops, + &port_id, + &channel_id, + &counterparty, + &in_version, + ); + + assert!(res.is_err()); +} + +/// If the counterparty supports ics20, then return ics20 +#[test] +fn test_on_chan_open_try_counterparty_correct_version() { + let (mut ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); + + let counterparty_version = Version::new(VERSION.to_string()); + + let (_, out_version) = on_chan_open_try_execute( + &mut ctx, + order, + &connection_hops, + &port_id, + &channel_id, + &counterparty, + &counterparty_version, + ) + .unwrap(); + + assert_eq!(out_version, Version::new(VERSION.to_string())); +} + +/// If the counterparty doesn't support ics20, then fail +#[test] +fn test_on_chan_open_try_counterparty_incorrect_version() { + let (ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); + + let counterparty_version = Version::new("some-unsupported-version".to_string()); + + let res = on_chan_open_try_validate( + &ctx, + order, + &connection_hops, + &port_id, + &channel_id, + &counterparty, + &counterparty_version, + ); + + assert!(res.is_err()); +} diff --git a/crates/ibc-testkit/tests/core/ics02_client/create_client.rs b/crates/ibc-testkit/tests/core/ics02_client/create_client.rs new file mode 100644 index 000000000..0e239b3a1 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -0,0 +1,95 @@ +use ibc::clients::ics07_tendermint::client_type as tm_client_type; +use ibc::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; +use ibc::core::ics02_client::client_state::ClientStateCommon; +use ibc::core::ics02_client::msgs::create_client::MsgCreateClient; +use ibc::core::ics02_client::msgs::ClientMsg; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::core::{execute, validate, MsgEnvelope, ValidationContext}; +use ibc::Height; +use ibc_testkit::testapp::ibc::clients::mock::client_state::{ + client_type as mock_client_type, MockClientState, +}; +use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; +use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::clients::tendermint::{ + dummy_tendermint_header, dummy_tm_client_state_from_header, +}; +use ibc_testkit::utils::core::signer::dummy_account_id; +use test_log::test; + +#[test] +fn test_create_client_ok() { + let mut ctx = MockContext::default(); + let mut router = MockRouter::new_with_transfer(); + let signer = dummy_account_id(); + let height = Height::new(0, 42).unwrap(); + + let msg = MsgCreateClient::new( + MockClientState::new(MockHeader::new(height)).into(), + MockConsensusState::new(MockHeader::new(height)).into(), + signer, + ); + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + + let client_type = mock_client_type(); + + let client_id = { + let id_counter = ctx.client_counter().unwrap(); + ClientId::new(client_type.clone(), id_counter).unwrap() + }; + + let res = validate(&ctx, &router, msg_envelope.clone()); + + assert!(res.is_ok(), "validation happy path"); + + let res = execute(&mut ctx, &mut router, msg_envelope); + + assert!(res.is_ok(), "execution happy path"); + + let expected_client_state = ctx.decode_client_state(msg.client_state).unwrap(); + assert_eq!(expected_client_state.client_type(), client_type); + assert_eq!(ctx.client_state(&client_id).unwrap(), expected_client_state); +} + +#[test] +fn test_tm_create_client_ok() { + let signer = dummy_account_id(); + + let mut ctx = MockContext::default(); + + let mut router = MockRouter::new_with_transfer(); + + let tm_header = dummy_tendermint_header(); + + let tm_client_state = dummy_tm_client_state_from_header(tm_header.clone()).into(); + + let client_type = tm_client_type(); + + let client_id = { + let id_counter = ctx.client_counter().unwrap(); + ClientId::new(client_type.clone(), id_counter).unwrap() + }; + + let msg = MsgCreateClient::new( + tm_client_state, + TmConsensusState::try_from(tm_header).unwrap().into(), + signer, + ); + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + + let res = validate(&ctx, &router, msg_envelope.clone()); + + assert!(res.is_ok(), "tendermint client validation happy path"); + + let res = execute(&mut ctx, &mut router, msg_envelope); + + assert!(res.is_ok(), "tendermint client execution happy path"); + + let expected_client_state = ctx.decode_client_state(msg.client_state).unwrap(); + assert_eq!(expected_client_state.client_type(), client_type); + assert_eq!(ctx.client_state(&client_id).unwrap(), expected_client_state); +} diff --git a/crates/ibc-testkit/tests/core/ics02_client/mod.rs b/crates/ibc-testkit/tests/core/ics02_client/mod.rs new file mode 100644 index 000000000..acab4918d --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics02_client/mod.rs @@ -0,0 +1,3 @@ +pub mod create_client; +pub mod update_client; +pub mod upgrade_client; diff --git a/crates/ibc-testkit/tests/core/ics02_client/update_client.rs b/crates/ibc-testkit/tests/core/ics02_client/update_client.rs new file mode 100644 index 000000000..11ba9e9a3 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -0,0 +1,997 @@ +use core::str::FromStr; +use core::time::Duration; + +use ibc::clients::ics07_tendermint::client_state::ClientState as TmClientState; +use ibc::clients::ics07_tendermint::client_type as tm_client_type; +use ibc::clients::ics07_tendermint::header::Header as TmHeader; +use ibc::clients::ics07_tendermint::misbehaviour::Misbehaviour as TmMisbehaviour; +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics02_client::client_state::{ClientStateCommon, ClientStateValidation}; +use ibc::core::ics02_client::client_type::ClientType; +use ibc::core::ics02_client::msgs::misbehaviour::MsgSubmitMisbehaviour; +use ibc::core::ics02_client::msgs::update_client::MsgUpdateClient; +use ibc::core::ics02_client::msgs::ClientMsg; +use ibc::core::ics02_client::ClientValidationContext; +use ibc::core::ics23_commitment::specs::ProofSpecs; +use ibc::core::ics24_host::identifier::{ChainId, ClientId}; +use ibc::core::ics24_host::path::ClientConsensusStatePath; +use ibc::core::timestamp::Timestamp; +use ibc::core::{execute, validate, MsgEnvelope, ValidationContext}; +use ibc::prelude::*; +use ibc::proto::tendermint::v1::{ClientState as RawTmClientState, Fraction}; +use ibc::proto::Any; +use ibc::{downcast, Height}; +use ibc_testkit::hosts::block::{HostBlock, HostType}; +use ibc_testkit::testapp::ibc::clients::mock::client_state::{ + client_type as mock_client_type, MockClientState, +}; +use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; +use ibc_testkit::testapp::ibc::clients::mock::misbehaviour::Misbehaviour as MockMisbehaviour; +use ibc_testkit::testapp::ibc::clients::AnyConsensusState; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::utils::core::context::MockContextConfig; +use ibc_testkit::utils::core::signer::dummy_account_id; +use prost::Message; +use tendermint_testgen::Validator as TestgenValidator; +use test_log::test; + +#[test] +fn test_update_client_ok() { + let client_id = ClientId::default(); + + let signer = dummy_account_id(); + + let timestamp = Timestamp::now(); + + let mut ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); + let height = Height::new(0, 46).unwrap(); + let msg = MsgUpdateClient { + client_id, + client_message: MockHeader::new(height).with_timestamp(timestamp).into(), + signer, + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + + let mut router = MockRouter::new_with_transfer(); + + let res = validate(&ctx, &router, msg_envelope.clone()); + + assert!(res.is_ok(), "validation happy path"); + + let res = execute(&mut ctx, &mut router, msg_envelope); + + assert!(res.is_ok(), "execution happy path"); + + assert_eq!( + ctx.client_state(&msg.client_id).unwrap(), + MockClientState::new(MockHeader::new(height).with_timestamp(timestamp)).into() + ); +} + +/// Tests that the Tendermint client consensus state pruning logic +/// functions correctly. +/// +/// This test sets up a MockContext with host height 1 and a trusting +/// period of 3 seconds. It then advances the state of the MockContext +/// by 2 heights, and thus 6 seconds, due to the DEFAULT_BLOCK_TIME_SECS +/// constant being set to 3 seconds. At this point, the chain is at height +/// 3. Any consensus states associated with a block more than 3 seconds +/// in the past should be expired and pruned from the IBC store. The test +/// thus checks that the consensus state at height 1 is not contained in +/// the store. It also checks that the consensus state at height 2 is +/// contained in the store and has not expired. +#[test] +fn test_consensus_state_pruning() { + let chain_id = ChainId::new("mockgaiaA-1").unwrap(); + + let client_height = Height::new(1, 1).unwrap(); + + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + + let mut ctx = MockContextConfig::builder() + .host_id(chain_id.clone()) + .host_type(HostType::SyntheticTendermint) + .latest_height(client_height) + .latest_timestamp(Timestamp::now()) + .max_history_size(u64::MAX) + .build() + .with_client_config( + MockClientConfig::builder() + .client_chain_id(chain_id.clone()) + .client_id(client_id.clone()) + .client_state_height(client_height) + .client_type(tm_client_type()) + .trusting_period(Duration::from_secs(3)) + .build(), + ); + + let mut router = MockRouter::new_with_transfer(); + + let start_host_timestamp = ctx.host_timestamp().unwrap(); + + // Move the chain forward by 2 blocks to pass the trusting period. + for _ in 1..=2 { + let signer = dummy_account_id(); + + let update_height = ctx.latest_height(); + + ctx.advance_host_chain_height(); + + let mut block = ctx.host_block(&update_height).unwrap().clone(); + + block.set_trusted_height(client_height); + + let msg = MsgUpdateClient { + client_id: client_id.clone(), + client_message: block.clone().into(), + signer, + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let _ = validate(&ctx, &router, msg_envelope.clone()); + let _ = execute(&mut ctx, &mut router, msg_envelope); + } + + // Check that latest expired consensus state is pruned. + let expired_height = Height::new(1, 1).unwrap(); + let client_cons_state_path = ClientConsensusStatePath::new(&client_id, &expired_height); + assert!(ctx + .client_update_height(&client_id, &expired_height) + .is_err()); + assert!(ctx.client_update_time(&client_id, &expired_height).is_err()); + assert!(ctx.consensus_state(&client_cons_state_path).is_err()); + + // Check that latest valid consensus state exists. + let earliest_valid_height = Height::new(1, 2).unwrap(); + let client_cons_state_path = ClientConsensusStatePath::new(&client_id, &earliest_valid_height); + + assert!(ctx + .client_update_height(&client_id, &earliest_valid_height) + .is_ok()); + + assert!(ctx + .client_update_time(&client_id, &earliest_valid_height) + .is_ok()); + + assert!(ctx.consensus_state(&client_cons_state_path).is_ok()); + + let end_host_timestamp = ctx.host_timestamp().unwrap(); + + assert_eq!( + end_host_timestamp, + (start_host_timestamp + Duration::from_secs(6)).unwrap() + ); +} + +#[test] +fn test_update_nonexisting_client() { + let client_id = ClientId::from_str("mockclient1").unwrap(); + + let signer = dummy_account_id(); + + let ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); + + let router = MockRouter::new_with_transfer(); + + let msg = MsgUpdateClient { + client_id: ClientId::from_str("nonexistingclient").unwrap(), + client_message: MockHeader::new(Height::new(0, 46).unwrap()).into(), + signer, + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!(res.is_err()); +} + +#[test] +fn test_update_synthetic_tendermint_client_adjacent_ok() { + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + let client_height = Height::new(1, 20).unwrap(); + let update_height = Height::new(1, 21).unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + let mut ctx = MockContext::new( + ChainId::new("mockgaiaA-1").unwrap(), + HostType::Mock, + 5, + Height::new(1, 1).unwrap(), + ) + .with_client_parametrized_with_chain_id( + chain_id_b.clone(), + &client_id, + client_height, + Some(tm_client_type()), // The target host chain (B) is synthetic TM. + Some(client_height), + ); + + let mut router = MockRouter::new_with_transfer(); + + let ctx_b = MockContext::new(chain_id_b, HostType::SyntheticTendermint, 5, update_height); + + let signer = dummy_account_id(); + + let mut block = ctx_b.host_block(&update_height).unwrap().clone(); + block.set_trusted_height(client_height); + + let latest_header_height = block.height(); + let msg = MsgUpdateClient { + client_id, + client_message: block.into(), + signer, + }; + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + + let res = validate(&ctx, &router, msg_envelope.clone()); + assert!(res.is_ok()); + + let res = execute(&mut ctx, &mut router, msg_envelope); + assert!(res.is_ok(), "result: {res:?}"); + + let client_state = ctx.client_state(&msg.client_id).unwrap(); + + assert!(client_state + .status(&ctx, &msg.client_id) + .unwrap() + .is_active()); + + assert_eq!(client_state.latest_height(), latest_header_height); +} + +#[test] +fn test_update_synthetic_tendermint_client_validator_change_ok() { + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + let client_height = Height::new(1, 20).unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + let mut ctx_a = MockContextConfig::builder() + .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .latest_height(Height::new(1, 1).unwrap()) + .build() + .with_client_config( + // client state initialized with client_height, and + // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. + MockClientConfig::builder() + .client_chain_id(chain_id_b.clone()) + .client_id(client_id.clone()) + .client_state_height(client_height) + .client_type(tm_client_type()) + .build(), + ); + + let mut router_a = MockRouter::new_with_transfer(); + + let ctx_b_val_history = vec![ + // First two validator sets are default at client creation + // + // validator set of height-20 + vec![ + TestgenValidator::new("1").voting_power(50), + TestgenValidator::new("2").voting_power(50), + ], + // validator set of height-21 + vec![ + TestgenValidator::new("1").voting_power(50), + TestgenValidator::new("2").voting_power(50), + ], + // validator set of height-22 + vec![ + TestgenValidator::new("1").voting_power(30), + TestgenValidator::new("2").voting_power(70), + ], + // validator set of height-23 + vec![ + TestgenValidator::new("1").voting_power(20), + TestgenValidator::new("2").voting_power(80), + ], + ]; + + let update_height = client_height.add(ctx_b_val_history.len() as u64 - 2); + + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .host_type(HostType::SyntheticTendermint) + .latest_height(update_height) + .max_history_size(ctx_b_val_history.len() as u64 - 1) + .validator_set_history(ctx_b_val_history) + .build(); + + let signer = dummy_account_id(); + + let mut block = ctx_b.host_block(&update_height).unwrap().clone(); + block.set_trusted_height(client_height); + + let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") { + HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(), + _ => panic!("unexpected host block type"), + }; + + block.set_trusted_next_validators_set(trusted_next_validator_set); + + let latest_header_height = block.height(); + let msg = MsgUpdateClient { + client_id, + client_message: block.into(), + signer, + }; + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + + let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + assert!(res.is_ok()); + + let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + assert!(res.is_ok(), "result: {res:?}"); + + let client_state = ctx_a.client_state(&msg.client_id).unwrap(); + assert!(client_state + .status(&ctx_a, &msg.client_id) + .unwrap() + .is_active()); + assert_eq!(client_state.latest_height(), latest_header_height); +} + +#[test] +fn test_update_synthetic_tendermint_client_validator_change_fail() { + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + let client_height = Height::new(1, 20).unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + let ctx_a = MockContextConfig::builder() + .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .latest_height(Height::new(1, 1).unwrap()) + .build() + .with_client_config( + // client state initialized with client_height, and + // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. + MockClientConfig::builder() + .client_chain_id(chain_id_b.clone()) + .client_id(client_id.clone()) + .client_state_height(client_height) + .client_type(tm_client_type()) + .build(), + ); + + let router = MockRouter::new_with_transfer(); + + let ctx_b_val_history = vec![ + // First two validator sets are default at client creation + // + // validator set of height-20 + vec![ + TestgenValidator::new("1").voting_power(50), + TestgenValidator::new("2").voting_power(50), + ], + // incorrect next validator set for height-20 + // validator set of height-21 + vec![ + TestgenValidator::new("1").voting_power(45), + TestgenValidator::new("2").voting_power(55), + ], + // validator set of height-22 + vec![ + TestgenValidator::new("1").voting_power(30), + TestgenValidator::new("2").voting_power(70), + ], + // validator set of height-23 + vec![ + TestgenValidator::new("1").voting_power(20), + TestgenValidator::new("2").voting_power(80), + ], + ]; + + let update_height = client_height.add(ctx_b_val_history.len() as u64 - 2); + + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .host_type(HostType::SyntheticTendermint) + .latest_height(update_height) + .max_history_size(ctx_b_val_history.len() as u64 - 1) + .validator_set_history(ctx_b_val_history) + .build(); + + let signer = dummy_account_id(); + + let mut block = ctx_b.host_block(&update_height).unwrap().clone(); + block.set_trusted_height(client_height); + + let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") { + HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(), + _ => panic!("unexpected host block type"), + }; + + block.set_trusted_next_validators_set(trusted_next_validator_set); + + let msg = MsgUpdateClient { + client_id, + client_message: block.into(), + signer, + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let res = validate(&ctx_a, &router, msg_envelope); + + assert!(res.is_err()); +} + +#[test] +fn test_update_synthetic_tendermint_client_non_adjacent_ok() { + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + let client_height = Height::new(1, 20).unwrap(); + let update_height = Height::new(1, 21).unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + let mut ctx = MockContext::new( + ChainId::new("mockgaiaA-1").unwrap(), + HostType::Mock, + 5, + Height::new(1, 1).unwrap(), + ) + .with_client_parametrized_history_with_chain_id( + chain_id_b.clone(), + &client_id, + client_height, + Some(tm_client_type()), // The target host chain (B) is synthetic TM. + Some(client_height), + ); + + let mut router = MockRouter::new_with_transfer(); + + let ctx_b = MockContext::new(chain_id_b, HostType::SyntheticTendermint, 5, update_height); + + let signer = dummy_account_id(); + + let mut block = ctx_b.host_block(&update_height).unwrap().clone(); + let trusted_height = client_height.clone().sub(1).unwrap(); + block.set_trusted_height(trusted_height); + + let latest_header_height = block.height(); + let msg = MsgUpdateClient { + client_id, + client_message: block.into(), + signer, + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + + let res = validate(&ctx, &router, msg_envelope.clone()); + assert!(res.is_ok()); + + let res = execute(&mut ctx, &mut router, msg_envelope); + assert!(res.is_ok(), "result: {res:?}"); + + let client_state = ctx.client_state(&msg.client_id).unwrap(); + + assert!(client_state + .status(&ctx, &msg.client_id) + .unwrap() + .is_active()); + + assert_eq!(client_state.latest_height(), latest_header_height); +} + +#[test] +fn test_update_synthetic_tendermint_client_duplicate_ok() { + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + let client_height = Height::new(1, 20).unwrap(); + + let ctx_a_chain_id = ChainId::new("mockgaiaA-1").unwrap(); + let ctx_b_chain_id = ChainId::new("mockgaiaB-1").unwrap(); + let start_height = Height::new(1, 11).unwrap(); + + let mut ctx_a = MockContext::new(ctx_a_chain_id, HostType::Mock, 5, start_height) + .with_client_parametrized_with_chain_id( + ctx_b_chain_id.clone(), + &client_id, + client_height, + Some(tm_client_type()), // The target host chain (B) is synthetic TM. + Some(start_height), + ); + + let mut router_a = MockRouter::new_with_transfer(); + + let ctx_b = MockContext::new( + ctx_b_chain_id, + HostType::SyntheticTendermint, + 5, + client_height, + ); + + let signer = dummy_account_id(); + + let block = ctx_b.host_block(&client_height).unwrap().clone(); + + // Update the trusted height of the header to point to the previous height + // (`start_height` in this case). + // + // Note: The current MockContext interface doesn't allow us to + // do this without a major redesign. + let block = match block { + HostBlock::SyntheticTendermint(mut theader) => { + // current problem: the timestamp of the new header doesn't match the timestamp of + // the stored consensus state. If we hack them to match, then commit check fails. + // FIXME: figure out why they don't match. + theader.trusted_height = start_height; + + HostBlock::SyntheticTendermint(theader) + } + _ => block, + }; + + // Update the client height to `client_height` + // + // Note: The current MockContext interface doesn't allow us to + // do this without a major redesign. + { + // FIXME: idea: we need to update the light client with the latest block from + // chain B + let consensus_state: AnyConsensusState = block.clone().into(); + + let tm_block = downcast!(block.clone() => HostBlock::SyntheticTendermint).unwrap(); + + let chain_id = ChainId::from_str(tm_block.header().chain_id.as_str()).unwrap(); + + let client_state = { + #[allow(deprecated)] + let raw_client_state = RawTmClientState { + chain_id: chain_id.to_string(), + trust_level: Some(Fraction { + numerator: 1, + denominator: 3, + }), + trusting_period: Some(Duration::from_secs(64000).into()), + unbonding_period: Some(Duration::from_secs(128000).into()), + max_clock_drift: Some(Duration::from_millis(3000).into()), + latest_height: Some( + Height::new( + chain_id.revision_number(), + u64::from(tm_block.header().height), + ) + .unwrap() + .into(), + ), + proof_specs: ProofSpecs::default().into(), + upgrade_path: Default::default(), + frozen_height: None, + allow_update_after_expiry: false, + allow_update_after_misbehaviour: false, + }; + + let client_state = TmClientState::try_from(raw_client_state).unwrap(); + + client_state.into() + }; + + let mut ibc_store = ctx_a.ibc_store.lock(); + let client_record = ibc_store.clients.get_mut(&client_id).unwrap(); + + client_record + .consensus_states + .insert(client_height, consensus_state); + + client_record.client_state = Some(client_state); + } + + let latest_header_height = block.height(); + let msg = MsgUpdateClient { + client_id, + client_message: block.into(), + signer, + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + + let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + assert!(res.is_ok(), "result: {res:?}"); + + let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + assert!(res.is_ok(), "result: {res:?}"); + + let client_state = ctx_a.client_state(&msg.client_id).unwrap(); + assert!(client_state + .status(&ctx_a, &msg.client_id) + .unwrap() + .is_active()); + assert_eq!(client_state.latest_height(), latest_header_height); + assert_eq!(client_state, ctx_a.latest_client_states(&msg.client_id)); +} + +#[test] +fn test_update_synthetic_tendermint_client_lower_height() { + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + let client_height = Height::new(1, 20).unwrap(); + + let client_update_height = Height::new(1, 19).unwrap(); + + let chain_start_height = Height::new(1, 11).unwrap(); + + let ctx = MockContext::new( + ChainId::new("mockgaiaA-1").unwrap(), + HostType::Mock, + 5, + chain_start_height, + ) + .with_client_parametrized( + &client_id, + client_height, + Some(tm_client_type()), // The target host chain (B) is synthetic TM. + Some(client_height), + ); + + let router = MockRouter::new_with_transfer(); + + let ctx_b = MockContext::new( + ChainId::new("mockgaiaB-1").unwrap(), + HostType::SyntheticTendermint, + 5, + client_height, + ); + + let signer = dummy_account_id(); + + let block_ref = ctx_b.host_block(&client_update_height).unwrap(); + + let msg = MsgUpdateClient { + client_id, + client_message: block_ref.clone().into(), + signer, + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + assert!(res.is_err()); +} + +#[test] +fn test_update_client_events() { + let client_id = ClientId::default(); + let signer = dummy_account_id(); + + let timestamp = Timestamp::now(); + + let mut ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); + let mut router = MockRouter::new_with_transfer(); + let height = Height::new(0, 46).unwrap(); + let header: Any = MockHeader::new(height).with_timestamp(timestamp).into(); + let msg = MsgUpdateClient { + client_id: client_id.clone(), + client_message: header.clone(), + signer, + }; + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let res = execute(&mut ctx, &mut router, msg_envelope); + assert!(res.is_ok()); + + assert!(matches!( + ctx.events[0], + IbcEvent::Message(MessageEvent::Client) + )); + let update_client_event = downcast!(&ctx.events[1] => IbcEvent::UpdateClient).unwrap(); + + assert_eq!(update_client_event.client_id(), &client_id); + assert_eq!(update_client_event.client_type(), &mock_client_type()); + assert_eq!(update_client_event.consensus_height(), &height); + assert_eq!(update_client_event.consensus_heights(), &vec![height]); + assert_eq!(update_client_event.header(), &header.encode_to_vec()); +} + +fn ensure_misbehaviour(ctx: &MockContext, client_id: &ClientId, client_type: &ClientType) { + let client_state = ctx.client_state(client_id).unwrap(); + + let status = client_state.status(ctx, client_id).unwrap(); + assert!(status.is_frozen(), "client_state status: {status}"); + + // check events + assert_eq!(ctx.events.len(), 2); + assert!(matches!( + ctx.events[0], + IbcEvent::Message(MessageEvent::Client), + )); + let misbehaviour_client_event = + downcast!(&ctx.events[1] => IbcEvent::ClientMisbehaviour).unwrap(); + assert_eq!(misbehaviour_client_event.client_id(), client_id); + assert_eq!(misbehaviour_client_event.client_type(), client_type); +} + +/// Tests misbehaviour handling for the mock client. +/// Misbehaviour evidence consists of identical headers - mock misbehaviour handler considers it +/// a valid proof of misbehaviour +#[test] +fn test_misbehaviour_client_ok() { + let client_id = ClientId::default(); + let timestamp = Timestamp::now(); + let height = Height::new(0, 46).unwrap(); + let msg = MsgSubmitMisbehaviour { + client_id: client_id.clone(), + misbehaviour: MockMisbehaviour { + client_id: client_id.clone(), + header1: MockHeader::new(height).with_timestamp(timestamp), + header2: MockHeader::new(height).with_timestamp(timestamp), + } + .into(), + signer: dummy_account_id(), + }; + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let mut ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); + let mut router = MockRouter::new_with_transfer(); + + let res = validate(&ctx, &router, msg_envelope.clone()); + assert!(res.is_ok()); + let res = execute(&mut ctx, &mut router, msg_envelope); + assert!(res.is_ok()); + + ensure_misbehaviour(&ctx, &client_id, &mock_client_type()); +} + +/// Tests misbehaviour handling failure for a non-existent client +#[test] +fn test_misbehaviour_nonexisting_client() { + let client_id = ClientId::from_str("mockclient1").unwrap(); + let height = Height::new(0, 46).unwrap(); + let msg = MsgSubmitMisbehaviour { + client_id: ClientId::from_str("nonexistingclient").unwrap(), + misbehaviour: MockMisbehaviour { + client_id: client_id.clone(), + header1: MockHeader::new(height), + header2: MockHeader::new(height), + } + .into(), + signer: dummy_account_id(), + }; + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); + let router = MockRouter::new_with_transfer(); + let res = validate(&ctx, &router, msg_envelope); + assert!(res.is_err()); +} + +/// Tests misbehaviour handling for the synthetic Tendermint client. +/// Misbehaviour evidence consists of equivocal headers. +#[test] +fn test_misbehaviour_synthetic_tendermint_equivocation() { + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + let client_height = Height::new(1, 20).unwrap(); + let misbehaviour_height = Height::new(1, 21).unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + // Create a mock context for chain-A with a synthetic tendermint light client for chain-B + let mut ctx_a = MockContext::new( + ChainId::new("mockgaiaA-1").unwrap(), + HostType::Mock, + 5, + Height::new(1, 1).unwrap(), + ) + .with_client_parametrized_with_chain_id( + chain_id_b.clone(), + &client_id, + client_height, + Some(tm_client_type()), + Some(client_height), + ); + + let mut router_a = MockRouter::new_with_transfer(); + + // Create a mock context for chain-B + let ctx_b = MockContext::new( + chain_id_b.clone(), + HostType::SyntheticTendermint, + 5, + misbehaviour_height, + ); + + // Get chain-B's header at `misbehaviour_height` + let header1: TmHeader = { + let mut block = ctx_b.host_block(&misbehaviour_height).unwrap().clone(); + block.set_trusted_height(client_height); + block.try_into_tm_block().unwrap().into() + }; + + // Generate an equivocal header for chain-B at `misbehaviour_height` + let header2 = { + let mut tm_block = HostBlock::generate_tm_block( + chain_id_b, + misbehaviour_height.revision_height(), + Timestamp::now(), + ); + tm_block.trusted_height = client_height; + tm_block.into() + }; + + let msg = MsgSubmitMisbehaviour { + client_id: client_id.clone(), + misbehaviour: TmMisbehaviour::new(client_id.clone(), header1, header2).into(), + signer: dummy_account_id(), + }; + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + assert!(res.is_ok()); + let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + assert!(res.is_ok()); + ensure_misbehaviour(&ctx_a, &client_id, &tm_client_type()); +} + +#[test] +fn test_misbehaviour_synthetic_tendermint_bft_time() { + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + let client_height = Height::new(1, 20).unwrap(); + let misbehaviour_height = Height::new(1, 21).unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + // Create a mock context for chain-A with a synthetic tendermint light client for chain-B + let mut ctx_a = MockContext::new( + ChainId::new("mockgaiaA-1").unwrap(), + HostType::Mock, + 5, + Height::new(1, 1).unwrap(), + ) + .with_client_parametrized_with_chain_id( + chain_id_b.clone(), + &client_id, + client_height, + Some(tm_client_type()), + Some(client_height), + ); + + let mut router_a = MockRouter::new_with_transfer(); + + // Generate `header1` for chain-B + let header1 = { + let mut tm_block = HostBlock::generate_tm_block( + chain_id_b.clone(), + misbehaviour_height.revision_height(), + Timestamp::now(), + ); + tm_block.trusted_height = client_height; + tm_block + }; + + // Generate `header2` for chain-B which is identical to `header1` but with a conflicting + // timestamp + let header2 = { + let timestamp = + Timestamp::from_nanoseconds(Timestamp::now().nanoseconds() + 1_000_000_000).unwrap(); + let mut tm_block = HostBlock::generate_tm_block( + chain_id_b, + misbehaviour_height.revision_height(), + timestamp, + ); + tm_block.trusted_height = client_height; + tm_block + }; + + let msg = MsgSubmitMisbehaviour { + client_id: client_id.clone(), + misbehaviour: TmMisbehaviour::new(client_id.clone(), header1.into(), header2.into()).into(), + signer: dummy_account_id(), + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + assert!(res.is_ok()); + let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + assert!(res.is_ok()); + ensure_misbehaviour(&ctx_a, &client_id, &tm_client_type()); +} + +#[test] +fn test_expired_client() { + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + let update_height = Height::new(1, 21).unwrap(); + let client_height = update_height.sub(3).unwrap(); + + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + + let timestamp = Timestamp::now(); + + let trusting_period = Duration::from_secs(64); + + let mut ctx = MockContextConfig::builder() + .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .latest_height(Height::new(1, 1).unwrap()) + .latest_timestamp(timestamp) + .build() + .with_client_config( + MockClientConfig::builder() + .client_chain_id(chain_id_b.clone()) + .client_id(client_id.clone()) + .client_state_height(client_height) + .client_type(tm_client_type()) + .latest_timestamp(timestamp) + .trusting_period(trusting_period) + .build(), + ); + + while ctx.host_timestamp().expect("no error") < (timestamp + trusting_period).expect("no error") + { + ctx.advance_host_chain_height(); + } + + let client_state = ctx.client_state(&client_id).unwrap(); + + assert!(client_state.status(&ctx, &client_id).unwrap().is_expired()); +} + +#[test] +fn test_client_update_max_clock_drift() { + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + let client_height = Height::new(1, 20).unwrap(); + + let client_id = ClientId::new(tm_client_type(), 0).unwrap(); + + let timestamp = Timestamp::now(); + + let max_clock_drift = Duration::from_secs(64); + + let ctx_a = MockContextConfig::builder() + .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .latest_height(Height::new(1, 1).unwrap()) + .latest_timestamp(timestamp) + .build() + .with_client_config( + MockClientConfig::builder() + .client_chain_id(chain_id_b.clone()) + .client_id(client_id.clone()) + .client_state_height(client_height) + .client_type(tm_client_type()) + .latest_timestamp(timestamp) + .max_clock_drift(max_clock_drift) + .build(), + ); + + let router_a = MockRouter::new_with_transfer(); + + let mut ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .host_type(HostType::SyntheticTendermint) + .latest_height(client_height) + .latest_timestamp(timestamp) + .max_history_size(u64::MAX) + .build(); + + while ctx_b.host_timestamp().expect("no error") + < (ctx_a.host_timestamp().expect("no error") + max_clock_drift).expect("no error") + { + ctx_b.advance_host_chain_height(); + } + + // include current block + ctx_b.advance_host_chain_height(); + + let update_height = ctx_b.latest_height(); + + let signer = dummy_account_id(); + + let mut block = ctx_b.host_block(&update_height).unwrap().clone(); + block.set_trusted_height(client_height); + + let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") { + HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(), + _ => panic!("unexpected host block type"), + }; + + block.set_trusted_next_validators_set(trusted_next_validator_set); + + let msg = MsgUpdateClient { + client_id, + client_message: block.clone().into(), + signer, + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let res = validate(&ctx_a, &router_a, msg_envelope); + assert!(res.is_err()); +} diff --git a/crates/ibc-testkit/tests/core/ics02_client/upgrade_client.rs b/crates/ibc-testkit/tests/core/ics02_client/upgrade_client.rs new file mode 100644 index 000000000..79733107b --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics02_client/upgrade_client.rs @@ -0,0 +1,165 @@ +use ibc::clients::ics07_tendermint::client_type; +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics02_client::error::{ClientError, UpgradeClientError}; +use ibc::core::ics02_client::msgs::upgrade_client::MsgUpgradeClient; +use ibc::core::ics02_client::msgs::ClientMsg; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::core::ics24_host::path::ClientConsensusStatePath; +use ibc::core::{execute, validate, ContextError, MsgEnvelope, ValidationContext}; +use ibc::prelude::*; +use ibc::{downcast, Height}; +use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; +use ibc_testkit::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::clients::tendermint::{ + dummy_tendermint_header, dummy_tm_client_state_from_header, +}; +use ibc_testkit::utils::core::client::dummy_msg_upgrade_client; +use ibc_testkit::utils::{Expect, Fixture}; + +enum Ctx { + Default, + WithClient, +} + +enum Msg { + Default, + LowUpgradeHeight, + UnknownUpgradedClientStateType, +} + +fn msg_upgrade_client_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { + let client_id = ClientId::new(mock_client_type(), 0).unwrap(); + + let ctx_default = MockContext::default(); + let ctx_with_client = ctx_default + .clone() + .with_client(&client_id, Height::new(0, 42).unwrap()); + let ctx = match ctx_variant { + Ctx::Default => ctx_default, + Ctx::WithClient => ctx_with_client, + }; + + let upgrade_height = Height::new(1, 26).unwrap(); + let msg_default = dummy_msg_upgrade_client(client_id.clone(), upgrade_height); + + let low_upgrade_height = Height::new(0, 26).unwrap(); + let msg_with_low_upgrade_height = dummy_msg_upgrade_client(client_id, low_upgrade_height); + + let msg_with_unknown_upgraded_cs = MsgUpgradeClient { + upgraded_client_state: dummy_tm_client_state_from_header(dummy_tendermint_header()).into(), + ..msg_default.clone() + }; + + let msg = match msg_variant { + Msg::Default => msg_default, + Msg::LowUpgradeHeight => msg_with_low_upgrade_height, + Msg::UnknownUpgradedClientStateType => msg_with_unknown_upgraded_cs, + }; + + Fixture { ctx, msg } +} + +fn upgrade_client_validate(fxt: &Fixture, expect: Expect) { + let Fixture { ctx, msg } = fxt; + let router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + let res = validate(ctx, &router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "validation", &res); + + match expect { + Expect::Failure(_) => { + assert!(res.is_err(), "{err_msg}"); + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}"); + } + }; +} + +fn upgrade_client_execute(fxt: &mut Fixture, expect: Expect) { + let mut router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ClientMsg::from(fxt.msg.clone())); + let res = execute(&mut fxt.ctx, &mut router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "execution", &res); + match expect { + Expect::Failure(err) => { + assert!(res.is_err(), "{err_msg}"); + assert_eq!( + core::mem::discriminant(res.as_ref().unwrap_err()), + core::mem::discriminant(&err.unwrap()) + ); + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}"); + assert!(matches!( + fxt.ctx.events[0], + IbcEvent::Message(MessageEvent::Client) + )); + let upgrade_client_event = + downcast!(&fxt.ctx.events[1] => IbcEvent::UpgradeClient).unwrap(); + let plan_height = Height::new(1, 26).unwrap(); + + assert_eq!(upgrade_client_event.client_id(), &fxt.msg.client_id); + assert_eq!(upgrade_client_event.client_type(), &mock_client_type()); + assert_eq!(upgrade_client_event.consensus_height(), &plan_height); + + let client_state = fxt.ctx.client_state(&fxt.msg.client_id).unwrap(); + let msg_client_state: AnyClientState = + fxt.msg.upgraded_client_state.clone().try_into().unwrap(); + assert_eq!(client_state, msg_client_state); + + let consensus_state = fxt + .ctx + .consensus_state(&ClientConsensusStatePath::new( + &fxt.msg.client_id, + &plan_height, + )) + .unwrap(); + let msg_consensus_state: AnyConsensusState = + fxt.msg.upgraded_consensus_state.clone().try_into().unwrap(); + assert_eq!(consensus_state, msg_consensus_state); + } + }; +} + +#[test] +fn msg_upgrade_client_healthy() { + let mut fxt = msg_upgrade_client_fixture(Ctx::WithClient, Msg::Default); + upgrade_client_validate(&fxt, Expect::Success); + upgrade_client_execute(&mut fxt, Expect::Success); +} + +#[test] +fn upgrade_client_fail_nonexisting_client() { + let fxt = msg_upgrade_client_fixture(Ctx::Default, Msg::Default); + let expected_err = ContextError::ClientError(ClientError::ClientStateNotFound { + client_id: fxt.msg.client_id.clone(), + }); + upgrade_client_validate(&fxt, Expect::Failure(Some(expected_err.into()))); +} + +#[test] +fn upgrade_client_fail_low_upgrade_height() { + let fxt: Fixture = + msg_upgrade_client_fixture(Ctx::WithClient, Msg::LowUpgradeHeight); + let expected_err: ClientError = UpgradeClientError::LowUpgradeHeight { + upgraded_height: Height::new(0, 26).unwrap(), + client_height: fxt.ctx.latest_height(), + } + .into(); + upgrade_client_validate( + &fxt, + Expect::Failure(Some(ContextError::from(expected_err).into())), + ); +} + +#[test] +fn upgrade_client_fail_unknown_upgraded_client_state() { + let fxt = msg_upgrade_client_fixture(Ctx::WithClient, Msg::UnknownUpgradedClientStateType); + let expected_err = ContextError::ClientError(ClientError::UnknownClientStateType { + client_state_type: client_type().to_string(), + }); + upgrade_client_validate(&fxt, Expect::Failure(Some(expected_err.into()))); +} diff --git a/crates/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs b/crates/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs new file mode 100644 index 000000000..7cd0a2333 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs @@ -0,0 +1,194 @@ +use core::str::FromStr; + +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics02_client::height::Height; +use ibc::core::ics03_connection::connection::{ConnectionEnd, Counterparty, State}; +use ibc::core::ics03_connection::error::ConnectionError; +use ibc::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck; +use ibc::core::ics03_connection::msgs::ConnectionMsg; +use ibc::core::ics23_commitment::commitment::CommitmentPrefix; +use ibc::core::ics24_host::identifier::{ChainId, ClientId}; +use ibc::core::timestamp::ZERO_DURATION; +use ibc::core::{execute, validate, ContextError, MsgEnvelope, RouterError, ValidationContext}; +use ibc::prelude::*; +use ibc_testkit::hosts::block::HostType; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::connection::dummy_msg_conn_open_ack; +use ibc_testkit::utils::{Expect, Fixture}; +use test_log::test; + +enum Ctx { + New, + NewWithConnection, + NewWithConnectionEndOpen, + DefaultWithConnection, +} + +fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { + let msg = dummy_msg_conn_open_ack(10, 10); + + // Client parameters -- identifier and correct height (matching the proof height) + let client_id = ClientId::from_str("mock_clientid").unwrap(); + let proof_height = msg.proofs_height_on_b; + let conn_id = msg.conn_id_on_a.clone(); + + // Parametrize the host chain to have a height at least as recent as the + // the height of the proofs in the Ack msg. + let latest_height = proof_height.increment(); + let max_history_size = 5; + + // A connection end that will exercise the successful path. + let default_conn_end = ConnectionEnd::new( + State::Init, + client_id.clone(), + Counterparty::new( + client_id.clone(), + Some(msg.conn_id_on_b.clone()), + CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), + ), + vec![msg.version.clone()], + ZERO_DURATION, + ) + .unwrap(); + + // A connection end with incorrect state `Open`; will be part of the context. + let mut conn_end_open = default_conn_end.clone(); + conn_end_open.set_state(State::Open); // incorrect field + + let ctx_default = MockContext::default(); + let ctx_new = MockContext::new( + ChainId::new(&format!("mockgaia-{}", latest_height.revision_number())).unwrap(), + HostType::Mock, + max_history_size, + latest_height, + ); + let ctx = match ctx { + Ctx::New => ctx_new, + Ctx::NewWithConnection => ctx_new + .with_client(&client_id, proof_height) + .with_connection(conn_id, default_conn_end), + Ctx::DefaultWithConnection => ctx_default + .with_client(&client_id, proof_height) + .with_connection(conn_id, default_conn_end), + Ctx::NewWithConnectionEndOpen => ctx_new + .with_client(&client_id, proof_height) + .with_connection(conn_id, conn_end_open), + }; + + Fixture { ctx, msg } +} + +fn conn_open_ack_validate(fxt: &Fixture, expect: Expect) { + let router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ConnectionMsg::from(fxt.msg.clone())); + let res = validate(&fxt.ctx, &router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "validation", &res); + match expect { + Expect::Failure(err) => { + assert!(res.is_err(), "{err_msg}"); + assert_eq!( + core::mem::discriminant(res.as_ref().unwrap_err()), + core::mem::discriminant(&err.unwrap()) + ); + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}"); + return; + } + }; + let right_connection_id = fxt.msg.conn_id_on_a.clone(); + let cons_state_height = fxt.msg.consensus_height_of_a_on_b; + + let ctx_err = match res.unwrap_err() { + RouterError::ContextError(e) => e, + _ => panic!("unexpected error type"), + }; + + match ctx_err { + ContextError::ConnectionError(ConnectionError::ConnectionNotFound { connection_id }) => { + assert_eq!(connection_id, right_connection_id) + } + ContextError::ConnectionError(ConnectionError::InvalidConsensusHeight { + target_height, + current_height: _, + }) => { + assert_eq!(cons_state_height, target_height); + } + ContextError::ConnectionError(ConnectionError::InvalidState { + expected: _, + actual: _, + }) => {} + _ => unreachable!(), + } +} + +fn conn_open_ack_execute(fxt: &mut Fixture, expect: Expect) { + let mut router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ConnectionMsg::from(fxt.msg.clone())); + let res = execute(&mut fxt.ctx, &mut router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "execution", &res); + match expect { + Expect::Failure(_) => { + assert!(res.is_err(), "{err_msg}"); + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}"); + assert_eq!(fxt.ctx.events.len(), 2); + + assert!(matches!( + fxt.ctx.events[0], + IbcEvent::Message(MessageEvent::Connection) + )); + let event = &fxt.ctx.events[1]; + assert!(matches!(event, &IbcEvent::OpenAckConnection(_))); + + let conn_open_try_event = match event { + IbcEvent::OpenAckConnection(e) => e, + _ => unreachable!(), + }; + let conn_end = ::connection_end( + &fxt.ctx, + conn_open_try_event.conn_id_on_a(), + ) + .unwrap(); + assert_eq!(conn_end.state().clone(), State::Open); + } + } +} + +#[test] +fn conn_open_ack_healthy() { + let mut fxt = conn_open_ack_fixture(Ctx::NewWithConnection); + conn_open_ack_validate(&fxt, Expect::Success); + conn_open_ack_execute(&mut fxt, Expect::Success); +} + +#[test] +fn conn_open_ack_no_connection() { + let fxt = conn_open_ack_fixture(Ctx::New); + let expected_err = ContextError::ConnectionError(ConnectionError::ConnectionNotFound { + connection_id: fxt.msg.conn_id_on_a.clone(), + }); + conn_open_ack_validate(&fxt, Expect::Failure(Some(expected_err.into()))); +} + +#[test] +fn conn_open_ack_invalid_consensus_height() { + let fxt = conn_open_ack_fixture(Ctx::DefaultWithConnection); + let expected_err = ContextError::ConnectionError(ConnectionError::InvalidConsensusHeight { + target_height: fxt.msg.consensus_height_of_a_on_b, + current_height: Height::new(0, 10).unwrap(), + }); + conn_open_ack_validate(&fxt, Expect::Failure(Some(expected_err.into()))); +} + +#[test] +fn conn_open_ack_connection_mismatch() { + let fxt = conn_open_ack_fixture(Ctx::NewWithConnectionEndOpen); + let expected_err = ContextError::ConnectionError(ConnectionError::InvalidState { + expected: State::Init.to_string(), + actual: State::Open.to_string(), + }); + conn_open_ack_validate(&fxt, Expect::Failure(Some(expected_err.into()))); +} diff --git a/crates/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs b/crates/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs new file mode 100644 index 000000000..6d629162e --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs @@ -0,0 +1,124 @@ +use core::str::FromStr; + +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics03_connection::connection::{ConnectionEnd, Counterparty, State}; +use ibc::core::ics03_connection::msgs::conn_open_confirm::MsgConnectionOpenConfirm; +use ibc::core::ics03_connection::msgs::ConnectionMsg; +use ibc::core::ics23_commitment::commitment::CommitmentPrefix; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::core::timestamp::ZERO_DURATION; +use ibc::core::{execute, validate, MsgEnvelope, ValidationContext}; +use ibc::Height; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::connection::dummy_conn_open_confirm; +use ibc_testkit::utils::{Expect, Fixture}; +use test_log::test; + +enum Ctx { + Default, + CorrectConnection, + IncorrectConnection, +} + +fn conn_open_confirm_fixture(ctx: Ctx) -> Fixture { + let client_id = ClientId::from_str("mock_clientid").unwrap(); + let msg = dummy_conn_open_confirm(); + let counterparty = Counterparty::new( + client_id.clone(), + Some(msg.conn_id_on_b.clone()), + CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), + ); + + let ctx_default = MockContext::default(); + + let incorrect_conn_end_state = ConnectionEnd::new( + State::Init, + client_id.clone(), + counterparty, + ValidationContext::get_compatible_versions(&ctx_default), + ZERO_DURATION, + ) + .unwrap(); + + let mut correct_conn_end = incorrect_conn_end_state.clone(); + correct_conn_end.set_state(State::TryOpen); + + let ctx = match ctx { + Ctx::Default => ctx_default, + Ctx::IncorrectConnection => ctx_default + .with_client(&client_id, Height::new(0, 10).unwrap()) + .with_connection(msg.conn_id_on_b.clone(), incorrect_conn_end_state), + Ctx::CorrectConnection => ctx_default + .with_client(&client_id, Height::new(0, 10).unwrap()) + .with_connection(msg.conn_id_on_b.clone(), correct_conn_end), + }; + + Fixture { ctx, msg } +} + +fn conn_open_confirm_validate(fxt: &Fixture, expect: Expect) { + let router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ConnectionMsg::from(fxt.msg.clone())); + let res = validate(&fxt.ctx, &router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "validation", &res); + match expect { + Expect::Failure(_) => { + assert!(res.is_err(), "{err_msg}"); + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}"); + } + }; +} + +fn conn_open_confirm_execute(fxt: &mut Fixture, expect: Expect) { + let mut router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ConnectionMsg::from(fxt.msg.clone())); + let res = execute(&mut fxt.ctx, &mut router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "execution", &res); + match expect { + Expect::Failure(_) => { + assert!(res.is_err(), "{err_msg}"); + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}"); + assert_eq!(fxt.ctx.events.len(), 2); + + assert!(matches!( + fxt.ctx.events[0], + IbcEvent::Message(MessageEvent::Connection) + )); + let event = &fxt.ctx.events[1]; + assert!(matches!(event, &IbcEvent::OpenConfirmConnection(_))); + + let conn_open_try_event = match event { + IbcEvent::OpenConfirmConnection(e) => e, + _ => unreachable!(), + }; + let conn_end = + ValidationContext::connection_end(&fxt.ctx, conn_open_try_event.conn_id_on_b()) + .unwrap(); + assert_eq!(conn_end.state().clone(), State::Open); + } + } +} + +#[test] +fn conn_open_confirm_healthy() { + let mut fxt = conn_open_confirm_fixture(Ctx::CorrectConnection); + conn_open_confirm_validate(&fxt, Expect::Success); + conn_open_confirm_execute(&mut fxt, Expect::Success); +} + +#[test] +fn conn_open_confirm_no_connection() { + let fxt = conn_open_confirm_fixture(Ctx::Default); + conn_open_confirm_validate(&fxt, Expect::Failure(None)); +} + +#[test] +fn conn_open_confirm_connection_mismatch() { + let fxt = conn_open_confirm_fixture(Ctx::IncorrectConnection); + conn_open_confirm_validate(&fxt, Expect::Failure(None)); +} diff --git a/crates/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs b/crates/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs new file mode 100644 index 000000000..02e9a38a6 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs @@ -0,0 +1,140 @@ +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics03_connection::connection::State; +use ibc::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; +use ibc::core::ics03_connection::msgs::ConnectionMsg; +use ibc::core::ics03_connection::version::Version; +use ibc::core::{execute, validate, MsgEnvelope, ValidationContext}; +use ibc::prelude::*; +use ibc::Height; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::connection::{ + dummy_msg_conn_open_init, msg_conn_open_init_with_counterparty_conn_id, + msg_conn_open_with_version, +}; +use ibc_testkit::utils::{Expect, Fixture}; +use test_log::test; + +enum Ctx { + Default, + WithClient, +} + +enum Msg { + Default, + NoVersion, + BadVersion, + WithCounterpartyConnId, +} + +fn conn_open_init_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { + let msg_default = dummy_msg_conn_open_init(); + let msg = match msg_variant { + Msg::Default => msg_default.clone(), + Msg::NoVersion => msg_conn_open_with_version(msg_default, None), + Msg::BadVersion => { + msg_conn_open_with_version(msg_default, Some("random identifier 424242")) + } + Msg::WithCounterpartyConnId => msg_conn_open_init_with_counterparty_conn_id(msg_default, 2), + }; + + let ctx_default = MockContext::default(); + let ctx = match ctx_variant { + Ctx::WithClient => { + ctx_default.with_client(&msg.client_id_on_a, Height::new(0, 10).unwrap()) + } + _ => ctx_default, + }; + + Fixture { ctx, msg } +} + +fn conn_open_init_validate(fxt: &Fixture, expect: Expect) { + let router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ConnectionMsg::from(fxt.msg.clone())); + let res = validate(&fxt.ctx, &router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "validation", &res); + match expect { + Expect::Failure(_) => { + assert!(res.is_err(), "{err_msg}") + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}") + } + } +} + +fn conn_open_init_execute( + fxt: &mut Fixture, + expect: Expect, + expected_version: Vec, +) { + let mut router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ConnectionMsg::from(fxt.msg.clone())); + let res = execute(&mut fxt.ctx, &mut router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "execution", &res); + match expect { + Expect::Failure(_) => { + assert!(res.is_err(), "{err_msg}") + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}"); + + assert_eq!(fxt.ctx.connection_counter().unwrap(), 1); + + assert_eq!(fxt.ctx.events.len(), 2); + + assert!(matches!( + fxt.ctx.events[0], + IbcEvent::Message(MessageEvent::Connection) + )); + let event = &fxt.ctx.events[1]; + assert!(matches!(event, &IbcEvent::OpenInitConnection(_))); + + let conn_open_init_event = match event { + IbcEvent::OpenInitConnection(e) => e, + _ => unreachable!(), + }; + let conn_end = + ValidationContext::connection_end(&fxt.ctx, conn_open_init_event.conn_id_on_a()) + .unwrap(); + assert_eq!(conn_end.state().clone(), State::Init); + assert_eq!(conn_end.versions(), expected_version); + } + } +} + +#[test] +fn conn_open_init_healthy() { + let mut fxt = conn_open_init_fixture(Ctx::WithClient, Msg::Default); + conn_open_init_validate(&fxt, Expect::Success); + let expected_version = vec![fxt.msg.version.clone().unwrap()]; + conn_open_init_execute(&mut fxt, Expect::Success, expected_version); +} + +#[test] +fn conn_open_init_no_context() { + let fxt = conn_open_init_fixture(Ctx::Default, Msg::Default); + conn_open_init_validate(&fxt, Expect::Failure(None)); +} + +#[test] +fn conn_open_init_no_version() { + let mut fxt = conn_open_init_fixture(Ctx::WithClient, Msg::NoVersion); + conn_open_init_validate(&fxt, Expect::Success); + let expected_version = ValidationContext::get_compatible_versions(&fxt.ctx.clone()); + conn_open_init_execute(&mut fxt, Expect::Success, expected_version); +} +#[test] +fn conn_open_init_incompatible_version() { + let fxt = conn_open_init_fixture(Ctx::WithClient, Msg::BadVersion); + conn_open_init_validate(&fxt, Expect::Failure(None)); +} + +#[test] +fn conn_open_init_with_counterparty_conn_id() { + let mut fxt = conn_open_init_fixture(Ctx::WithClient, Msg::WithCounterpartyConnId); + conn_open_init_validate(&fxt, Expect::Success); + let expected_version = vec![fxt.msg.version.clone().unwrap()]; + conn_open_init_execute(&mut fxt, Expect::Success, expected_version); +} diff --git a/crates/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs b/crates/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs new file mode 100644 index 000000000..47a87a673 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs @@ -0,0 +1,147 @@ +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics03_connection::connection::State; +use ibc::core::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; +use ibc::core::ics03_connection::msgs::ConnectionMsg; +use ibc::core::ics24_host::identifier::ChainId; +use ibc::core::{execute, validate, MsgEnvelope, ValidationContext}; +use ibc::Height; +use ibc_testkit::hosts::block::HostType; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::connection::dummy_msg_conn_open_try; +use ibc_testkit::utils::{Expect, Fixture}; +use test_log::test; + +enum Ctx { + Default, + WithClient, +} + +enum Msg { + Default, + HeightAdvanced, + HeightOld, + ProofHeightMissing, +} + +fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { + let max_history_size = 5; + let client_cons_state_height = 10; + let host_chain_height = Height::new(0, 35).unwrap(); + let pruned_height = host_chain_height + .sub(max_history_size + 1) + .unwrap() + .revision_height(); + + let msg = match msg_variant { + Msg::Default => dummy_msg_conn_open_try( + client_cons_state_height, + host_chain_height.revision_height(), + ), + Msg::HeightAdvanced => dummy_msg_conn_open_try( + client_cons_state_height, + host_chain_height.increment().revision_height(), + ), + Msg::HeightOld => dummy_msg_conn_open_try(client_cons_state_height, pruned_height), + Msg::ProofHeightMissing => dummy_msg_conn_open_try( + client_cons_state_height - 1, + host_chain_height.revision_height(), + ), + }; + + let ctx_new = MockContext::new( + ChainId::new("mockgaia-0").unwrap(), + HostType::Mock, + max_history_size, + host_chain_height, + ); + let ctx = match ctx_variant { + Ctx::Default => MockContext::default(), + Ctx::WithClient => ctx_new.with_client( + &msg.client_id_on_b, + Height::new(0, client_cons_state_height).unwrap(), + ), + }; + Fixture { ctx, msg } +} + +fn conn_open_try_validate(fxt: &Fixture, expect: Expect) { + let router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ConnectionMsg::from(fxt.msg.clone())); + let res = validate(&fxt.ctx, &router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "validation", &res); + match expect { + Expect::Failure(_) => { + assert!(res.is_err(), "{err_msg}") + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}"); + } + } +} + +fn conn_open_try_execute(fxt: &mut Fixture, expect: Expect) { + let mut router = MockRouter::new_with_transfer(); + let msg_envelope = MsgEnvelope::from(ConnectionMsg::from(fxt.msg.clone())); + let res = execute(&mut fxt.ctx, &mut router, msg_envelope); + let err_msg = fxt.generate_error_msg(&expect, "execution", &res); + match expect { + Expect::Failure(_) => { + assert!(res.is_err(), "{err_msg}") + } + Expect::Success => { + assert!(res.is_ok(), "{err_msg}"); + + assert_eq!(fxt.ctx.connection_counter().unwrap(), 1); + + assert_eq!(fxt.ctx.events.len(), 2); + + assert!(matches!( + fxt.ctx.events[0], + IbcEvent::Message(MessageEvent::Connection) + )); + let event = &fxt.ctx.events[1]; + assert!(matches!(event, &IbcEvent::OpenTryConnection(_))); + + let conn_open_try_event = match event { + IbcEvent::OpenTryConnection(e) => e, + _ => unreachable!(), + }; + let conn_end = + ValidationContext::connection_end(&fxt.ctx, conn_open_try_event.conn_id_on_b()) + .unwrap(); + assert_eq!(conn_end.state().clone(), State::TryOpen); + } + } +} + +#[test] +fn conn_open_try_healthy() { + let mut fxt = conn_open_try_fixture(Ctx::WithClient, Msg::Default); + conn_open_try_validate(&fxt, Expect::Success); + conn_open_try_execute(&mut fxt, Expect::Success); +} + +#[test] +fn conn_open_try_height_advanced() { + let fxt = conn_open_try_fixture(Ctx::WithClient, Msg::HeightAdvanced); + conn_open_try_validate(&fxt, Expect::Failure(None)); +} + +#[test] +fn conn_open_try_height_old() { + let fxt = conn_open_try_fixture(Ctx::WithClient, Msg::HeightOld); + conn_open_try_validate(&fxt, Expect::Failure(None)); +} + +#[test] +fn conn_open_try_proof_height_missing() { + let fxt = conn_open_try_fixture(Ctx::WithClient, Msg::ProofHeightMissing); + conn_open_try_validate(&fxt, Expect::Failure(None)); +} + +#[test] +fn conn_open_try_no_client() { + let fxt = conn_open_try_fixture(Ctx::Default, Msg::Default); + conn_open_try_validate(&fxt, Expect::Failure(None)); +} diff --git a/crates/ibc-testkit/tests/core/ics03_connection/mod.rs b/crates/ibc-testkit/tests/core/ics03_connection/mod.rs new file mode 100644 index 000000000..c504edc00 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics03_connection/mod.rs @@ -0,0 +1,4 @@ +pub mod conn_open_ack; +pub mod conn_open_confirm; +pub mod conn_open_init; +pub mod conn_open_try; diff --git a/crates/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs b/crates/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs new file mode 100644 index 000000000..c79908f8f --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs @@ -0,0 +1,265 @@ +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics02_client::height::Height; +use ibc::core::ics02_client::ClientExecutionContext; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; +use ibc::core::ics04_channel::commitment::{compute_packet_commitment, PacketCommitment}; +use ibc::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; +use ibc::core::ics04_channel::msgs::PacketMsg; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; +use ibc::core::timestamp::{Timestamp, ZERO_DURATION}; +use ibc::core::{execute, validate, ExecutionContext, MsgEnvelope}; +use ibc::prelude::*; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_msg_acknowledgement; +use rstest::*; +use test_log::test; + +struct Fixture { + ctx: MockContext, + router: MockRouter, + client_height: Height, + msg: MsgAcknowledgement, + packet_commitment: PacketCommitment, + conn_end_on_a: ConnectionEnd, + chan_end_on_a_ordered: ChannelEnd, + chan_end_on_a_unordered: ChannelEnd, +} + +#[fixture] +fn fixture() -> Fixture { + let client_height = Height::new(0, 2).unwrap(); + let ctx = MockContext::default().with_client(&ClientId::default(), client_height); + + let router = MockRouter::new_with_transfer(); + + let msg = MsgAcknowledgement::try_from(dummy_raw_msg_acknowledgement( + client_height.revision_height(), + )) + .unwrap(); + + let packet = msg.packet.clone(); + + let packet_commitment = compute_packet_commitment( + &packet.data, + &packet.timeout_height_on_b, + &packet.timeout_timestamp_on_b, + ); + + let chan_end_on_a_unordered = ChannelEnd::new( + State::Open, + Order::Unordered, + Counterparty::new(packet.port_id_on_b, Some(packet.chan_id_on_b)), + vec![ConnectionId::default()], + Version::new("ics20-1".to_string()), + ) + .unwrap(); + + let mut chan_end_on_a_ordered = chan_end_on_a_unordered.clone(); + chan_end_on_a_ordered.ordering = Order::Ordered; + + let conn_end_on_a = ConnectionEnd::new( + ConnectionState::Open, + ClientId::default(), + ConnectionCounterparty::new( + ClientId::default(), + Some(ConnectionId::default()), + Default::default(), + ), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + Fixture { + ctx, + router, + client_height, + msg, + packet_commitment, + conn_end_on_a, + chan_end_on_a_unordered, + chan_end_on_a_ordered, + } +} + +#[rstest] +fn ack_fail_no_channel(fixture: Fixture) { + let Fixture { + ctx, router, msg, .. + } = fixture; + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because no channel exists in the context" + ) +} + +/// NO-OP case +#[rstest] +fn ack_success_no_packet_commitment(fixture: Fixture) { + let Fixture { + ctx, + router, + msg, + conn_end_on_a, + chan_end_on_a_unordered, + client_height, + .. + } = fixture; + let ctx = ctx + .with_client(&ClientId::default(), client_height) + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_unordered, + ) + .with_connection(ConnectionId::default(), conn_end_on_a); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!( + res.is_ok(), + "Validation should succeed when no packet commitment is present" + ) +} + +#[rstest] +fn ack_success_happy_path(fixture: Fixture) { + let Fixture { + ctx, + router, + msg, + packet_commitment, + conn_end_on_a, + chan_end_on_a_unordered, + client_height, + .. + } = fixture; + let mut ctx: MockContext = ctx + .with_client(&ClientId::default(), client_height) + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_unordered, + ) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_packet_commitment( + msg.packet.port_id_on_a.clone(), + msg.packet.chan_id_on_a.clone(), + msg.packet.seq_on_a, + packet_commitment, + ); + ctx.get_client_execution_context() + .store_update_time( + ClientId::default(), + client_height, + Timestamp::from_nanoseconds(1000).unwrap(), + ) + .unwrap(); + ctx.get_client_execution_context() + .store_update_height( + ClientId::default(), + client_height, + Height::new(0, 4).unwrap(), + ) + .unwrap(); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!( + res.is_ok(), + "Happy path: validation should succeed. err: {res:?}" + ) +} + +#[rstest] +fn ack_unordered_chan_execute(fixture: Fixture) { + let Fixture { + ctx, + mut router, + msg, + packet_commitment, + conn_end_on_a, + chan_end_on_a_unordered, + .. + } = fixture; + let mut ctx = ctx + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_unordered, + ) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_packet_commitment( + msg.packet.port_id_on_a.clone(), + msg.packet.chan_id_on_a.clone(), + msg.packet.seq_on_a, + packet_commitment, + ); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = execute(&mut ctx, &mut router, msg_envelope); + + assert!(res.is_ok()); + + assert_eq!(ctx.events.len(), 2); + assert!(matches!( + ctx.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(ctx.events[1], IbcEvent::AcknowledgePacket(_))); +} + +#[rstest] +fn ack_ordered_chan_execute(fixture: Fixture) { + let Fixture { + ctx, + mut router, + msg, + packet_commitment, + conn_end_on_a, + chan_end_on_a_ordered, + .. + } = fixture; + let mut ctx = ctx + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_ordered, + ) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_packet_commitment( + msg.packet.port_id_on_a.clone(), + msg.packet.chan_id_on_a.clone(), + msg.packet.seq_on_a, + packet_commitment, + ); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = execute(&mut ctx, &mut router, msg_envelope); + + assert!(res.is_ok()); + + assert_eq!(ctx.events.len(), 2); + assert!(matches!( + ctx.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(ctx.events[1], IbcEvent::AcknowledgePacket(_))); +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs b/crates/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs new file mode 100644 index 000000000..6ab70d27d --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs @@ -0,0 +1,134 @@ +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State as ChannelState}; +use ibc::core::ics04_channel::msgs::{ChannelMsg, MsgChannelCloseConfirm}; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::core::timestamp::ZERO_DURATION; +use ibc::core::{execute, validate, MsgEnvelope, ValidationContext}; +use ibc::prelude::*; +use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_close_confirm; +use ibc_testkit::utils::core::connection::dummy_raw_counterparty_conn; + +#[test] +fn test_chan_close_confirm_validate() { + let client_id = ClientId::new(mock_client_type(), 24).unwrap(); + let conn_id = ConnectionId::new(2); + let default_context = MockContext::default(); + let client_consensus_state_height = default_context.host_height().unwrap(); + + let conn_end = ConnectionEnd::new( + ConnectionState::Open, + client_id.clone(), + ConnectionCounterparty::try_from(dummy_raw_counterparty_conn(Some(0))).unwrap(), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + let msg_chan_close_confirm = MsgChannelCloseConfirm::try_from( + dummy_raw_msg_chan_close_confirm(client_consensus_state_height.revision_height()), + ) + .unwrap(); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg_chan_close_confirm.clone())); + + let chan_end = ChannelEnd::new( + ChannelState::Open, + Order::default(), + Counterparty::new( + msg_chan_close_confirm.port_id_on_b.clone(), + Some(msg_chan_close_confirm.chan_id_on_b.clone()), + ), + vec![conn_id.clone()], + Version::default(), + ) + .unwrap(); + + let context = default_context + .with_client(&client_id, client_consensus_state_height) + .with_connection(conn_id, conn_end) + .with_channel( + msg_chan_close_confirm.port_id_on_b.clone(), + msg_chan_close_confirm.chan_id_on_b.clone(), + chan_end, + ); + + let router = MockRouter::new_with_transfer(); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_ok(), + "Validation expected to succeed (happy path). Error: {res:?}" + ); +} + +#[test] +fn test_chan_close_confirm_execute() { + let client_id = ClientId::new(mock_client_type(), 24).unwrap(); + let conn_id = ConnectionId::new(2); + let default_context = MockContext::default(); + let client_consensus_state_height = default_context.host_height().unwrap(); + + let conn_end = ConnectionEnd::new( + ConnectionState::Open, + client_id.clone(), + ConnectionCounterparty::try_from(dummy_raw_counterparty_conn(Some(0))).unwrap(), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + let msg_chan_close_confirm = MsgChannelCloseConfirm::try_from( + dummy_raw_msg_chan_close_confirm(client_consensus_state_height.revision_height()), + ) + .unwrap(); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg_chan_close_confirm.clone())); + + let chan_end = ChannelEnd::new( + ChannelState::Open, + Order::default(), + Counterparty::new( + msg_chan_close_confirm.port_id_on_b.clone(), + Some(msg_chan_close_confirm.chan_id_on_b.clone()), + ), + vec![conn_id.clone()], + Version::default(), + ) + .unwrap(); + + let mut context = default_context + .with_client(&client_id, client_consensus_state_height) + .with_connection(conn_id, conn_end) + .with_channel( + msg_chan_close_confirm.port_id_on_b.clone(), + msg_chan_close_confirm.chan_id_on_b.clone(), + chan_end, + ); + + let mut router = MockRouter::new_with_transfer(); + + let res = execute(&mut context, &mut router, msg_envelope); + + assert!(res.is_ok(), "Execution success: happy path"); + + assert_eq!(context.events.len(), 2); + + assert!(matches!( + context.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + + assert!(matches!( + context.events[1], + IbcEvent::CloseConfirmChannel(_) + )); +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs b/crates/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs new file mode 100644 index 000000000..51d880cfe --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs @@ -0,0 +1,133 @@ +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State as ChannelState}; +use ibc::core::ics04_channel::msgs::{ChannelMsg, MsgChannelCloseInit}; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::core::timestamp::ZERO_DURATION; +use ibc::core::{execute, validate, MsgEnvelope, ValidationContext}; +use ibc::prelude::*; +use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_close_init; +use ibc_testkit::utils::core::connection::dummy_raw_counterparty_conn; + +#[test] +fn test_chan_close_init_validate() { + let client_id = ClientId::new(mock_client_type(), 24).unwrap(); + let conn_id = ConnectionId::new(2); + + let conn_end = ConnectionEnd::new( + ConnectionState::Open, + client_id.clone(), + ConnectionCounterparty::try_from(dummy_raw_counterparty_conn(Some(0))).unwrap(), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + let msg_chan_close_init = + MsgChannelCloseInit::try_from(dummy_raw_msg_chan_close_init()).unwrap(); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg_chan_close_init.clone())); + + let chan_end = ChannelEnd::new( + ChannelState::Open, + Order::default(), + Counterparty::new( + msg_chan_close_init.port_id_on_a.clone(), + Some(msg_chan_close_init.chan_id_on_a.clone()), + ), + vec![conn_id.clone()], + Version::default(), + ) + .unwrap(); + + let context = { + let default_context = MockContext::default(); + let client_consensus_state_height = default_context.host_height().unwrap(); + + default_context + .with_client(&client_id, client_consensus_state_height) + .with_connection(conn_id, conn_end) + .with_channel( + msg_chan_close_init.port_id_on_a.clone(), + msg_chan_close_init.chan_id_on_a.clone(), + chan_end, + ) + }; + + let router = MockRouter::new_with_transfer(); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_ok(), + "Validation expected to succeed (happy path). Error: {res:?}" + ); +} + +#[test] +fn test_chan_close_init_execute() { + let client_id = ClientId::new(mock_client_type(), 24).unwrap(); + let conn_id = ConnectionId::new(2); + + let conn_end = ConnectionEnd::new( + ConnectionState::Open, + client_id.clone(), + ConnectionCounterparty::try_from(dummy_raw_counterparty_conn(Some(0))).unwrap(), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + let msg_chan_close_init = + MsgChannelCloseInit::try_from(dummy_raw_msg_chan_close_init()).unwrap(); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg_chan_close_init.clone())); + + let chan_end = ChannelEnd::new( + ChannelState::Open, + Order::default(), + Counterparty::new( + msg_chan_close_init.port_id_on_a.clone(), + Some(msg_chan_close_init.chan_id_on_a.clone()), + ), + vec![conn_id.clone()], + Version::default(), + ) + .unwrap(); + + let mut context = { + let default_context = MockContext::default(); + let client_consensus_state_height = default_context.host_height().unwrap(); + + default_context + .with_client(&client_id, client_consensus_state_height) + .with_connection(conn_id, conn_end) + .with_channel( + msg_chan_close_init.port_id_on_a.clone(), + msg_chan_close_init.chan_id_on_a.clone(), + chan_end, + ) + }; + + let mut router = MockRouter::new_with_transfer(); + + let res = execute(&mut context, &mut router, msg_envelope); + + assert!(res.is_ok(), "Execution happy path"); + + assert_eq!(context.events.len(), 2); + + assert!(matches!( + context.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + + assert!(matches!(context.events[1], IbcEvent::CloseInitChannel(_))); +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs new file mode 100644 index 000000000..25a7d36c5 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs @@ -0,0 +1,240 @@ +use ibc::applications::transfer::MODULE_ID_STR; +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; +use ibc::core::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; +use ibc::core::ics04_channel::msgs::ChannelMsg; +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::core::router::ModuleId; +use ibc::core::timestamp::ZERO_DURATION; +use ibc::core::{execute, validate, MsgEnvelope}; +use ibc::prelude::*; +use ibc::Height; +use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_open_ack; +use ibc_testkit::utils::core::connection::dummy_raw_counterparty_conn; +use rstest::*; +use test_log::test; + +pub struct Fixture { + pub context: MockContext, + pub router: MockRouter, + pub module_id: ModuleId, + pub msg: MsgChannelOpenAck, + pub client_id_on_a: ClientId, + pub conn_id_on_a: ConnectionId, + pub conn_end_on_a: ConnectionEnd, + pub chan_end_on_a: ChannelEnd, + pub proof_height: u64, +} + +#[fixture] +fn fixture() -> Fixture { + let proof_height = 10; + let context = MockContext::default(); + + let module_id = ModuleId::new(MODULE_ID_STR.to_string()); + let router = MockRouter::new_with_transfer(); + + let client_id_on_a = ClientId::new(mock_client_type(), 45).unwrap(); + let conn_id_on_a = ConnectionId::new(2); + let conn_end_on_a = ConnectionEnd::new( + ConnectionState::Open, + client_id_on_a.clone(), + ConnectionCounterparty::try_from(dummy_raw_counterparty_conn(Some(0))).unwrap(), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + let msg = MsgChannelOpenAck::try_from(dummy_raw_msg_chan_open_ack(proof_height)).unwrap(); + + let chan_end_on_a = ChannelEnd::new( + State::Init, + Order::Unordered, + Counterparty::new(msg.port_id_on_a.clone(), Some(msg.chan_id_on_b.clone())), + vec![conn_id_on_a.clone()], + msg.version_on_b.clone(), + ) + .unwrap(); + + Fixture { + context, + router, + module_id, + msg, + client_id_on_a, + conn_id_on_a, + conn_end_on_a, + chan_end_on_a, + proof_height, + } +} + +#[rstest] +fn chan_open_ack_happy_path(fixture: Fixture) { + let Fixture { + context, + router, + msg, + client_id_on_a, + conn_id_on_a, + conn_end_on_a, + chan_end_on_a, + proof_height, + .. + } = fixture; + + let context = context + .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_a, conn_end_on_a) + .with_channel( + msg.port_id_on_a.clone(), + msg.chan_id_on_a.clone(), + chan_end_on_a, + ); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!(res.is_ok(), "Validation happy path") +} + +#[rstest] +fn chan_open_ack_execute_happy_path(fixture: Fixture) { + let Fixture { + context, + mut router, + msg, + client_id_on_a, + conn_id_on_a, + conn_end_on_a, + chan_end_on_a, + proof_height, + .. + } = fixture; + + let mut context = context + .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_a, conn_end_on_a) + .with_channel( + msg.port_id_on_a.clone(), + msg.chan_id_on_a.clone(), + chan_end_on_a, + ); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg.clone())); + + let res = execute(&mut context, &mut router, msg_envelope); + + assert!(res.is_ok(), "Execution happy path"); + + assert_eq!(context.events.len(), 2); + assert!(matches!( + context.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(context.events[1], IbcEvent::OpenAckChannel(_))); +} + +#[rstest] +fn chan_open_ack_fail_no_connection(fixture: Fixture) { + let Fixture { + context, + router, + msg, + client_id_on_a, + chan_end_on_a, + proof_height, + .. + } = fixture; + + let context = context + .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) + .with_channel( + msg.port_id_on_a.clone(), + msg.chan_id_on_a.clone(), + chan_end_on_a, + ); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because no connection exists in the context" + ) +} + +#[rstest] +fn chan_open_ack_fail_no_channel(fixture: Fixture) { + let Fixture { + context, + router, + msg, + client_id_on_a, + conn_id_on_a, + conn_end_on_a, + proof_height, + .. + } = fixture; + let context = context + .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_a, conn_end_on_a); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because no channel exists in the context" + ) +} + +#[rstest] +fn chan_open_ack_fail_channel_wrong_state(fixture: Fixture) { + let Fixture { + context, + router, + msg, + client_id_on_a, + conn_id_on_a, + conn_end_on_a, + proof_height, + .. + } = fixture; + + let wrong_chan_end = ChannelEnd::new( + State::Open, + Order::Unordered, + Counterparty::new(msg.port_id_on_a.clone(), Some(msg.chan_id_on_b.clone())), + vec![conn_id_on_a.clone()], + msg.version_on_b.clone(), + ) + .unwrap(); + let context = context + .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_a, conn_end_on_a) + .with_channel( + msg.port_id_on_a.clone(), + msg.chan_id_on_a.clone(), + wrong_chan_end, + ); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because channel is in the wrong state" + ) +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs new file mode 100644 index 000000000..1b6df3e4b --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs @@ -0,0 +1,209 @@ +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; +use ibc::core::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; +use ibc::core::ics04_channel::msgs::ChannelMsg; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId}; +use ibc::core::timestamp::ZERO_DURATION; +use ibc::core::{execute, validate, MsgEnvelope}; +use ibc::prelude::*; +use ibc::Height; +use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_open_confirm; +use ibc_testkit::utils::core::connection::dummy_raw_counterparty_conn; +use rstest::*; +use test_log::test; + +pub struct Fixture { + pub context: MockContext, + pub router: MockRouter, + pub msg: MsgChannelOpenConfirm, + pub client_id_on_b: ClientId, + pub conn_id_on_b: ConnectionId, + pub conn_end_on_b: ConnectionEnd, + pub chan_end_on_b: ChannelEnd, + pub proof_height: u64, +} + +#[fixture] +fn fixture() -> Fixture { + let proof_height = 10; + let context = MockContext::default(); + + let router = MockRouter::new_with_transfer(); + + let client_id_on_b = ClientId::new(mock_client_type(), 45).unwrap(); + let conn_id_on_b = ConnectionId::new(2); + let conn_end_on_b = ConnectionEnd::new( + ConnectionState::Open, + client_id_on_b.clone(), + ConnectionCounterparty::try_from(dummy_raw_counterparty_conn(Some(0))).unwrap(), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + let msg = + MsgChannelOpenConfirm::try_from(dummy_raw_msg_chan_open_confirm(proof_height)).unwrap(); + + let chan_end_on_b = ChannelEnd::new( + State::TryOpen, + Order::Unordered, + Counterparty::new(msg.port_id_on_b.clone(), Some(ChannelId::default())), + vec![conn_id_on_b.clone()], + Version::default(), + ) + .unwrap(); + + Fixture { + context, + router, + msg, + client_id_on_b, + conn_id_on_b, + conn_end_on_b, + chan_end_on_b, + proof_height, + } +} + +#[rstest] +fn chan_open_confirm_validate_happy_path(fixture: Fixture) { + let Fixture { + context, + router, + msg, + client_id_on_b, + conn_id_on_b, + conn_end_on_b, + chan_end_on_b, + proof_height, + .. + } = fixture; + + let context = context + .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_b, conn_end_on_b) + .with_channel( + msg.port_id_on_b.clone(), + ChannelId::default(), + chan_end_on_b, + ); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!(res.is_ok(), "Validation happy path") +} + +#[rstest] +fn chan_open_confirm_execute_happy_path(fixture: Fixture) { + let Fixture { + context, + mut router, + msg, + client_id_on_b, + conn_id_on_b, + conn_end_on_b, + chan_end_on_b, + proof_height, + .. + } = fixture; + + let mut context = context + .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_b, conn_end_on_b) + .with_channel( + msg.port_id_on_b.clone(), + ChannelId::default(), + chan_end_on_b, + ); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); + + let res = execute(&mut context, &mut router, msg_envelope); + + assert!(res.is_ok(), "Execution happy path"); + + assert_eq!(context.events.len(), 2); + + assert!(matches!( + context.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + + assert!(matches!(context.events[1], IbcEvent::OpenConfirmChannel(_))); +} + +#[rstest] +fn chan_open_confirm_fail_no_channel(fixture: Fixture) { + let Fixture { + context, + router, + msg, + client_id_on_b, + conn_id_on_b, + conn_end_on_b, + proof_height, + .. + } = fixture; + let context = context + .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_b, conn_end_on_b); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because no channel exists in the context" + ) +} + +#[rstest] +fn chan_open_confirm_fail_channel_wrong_state(fixture: Fixture) { + let Fixture { + context, + router, + msg, + client_id_on_b, + conn_id_on_b, + conn_end_on_b, + proof_height, + .. + } = fixture; + + let wrong_chan_end = ChannelEnd::new( + State::Init, + Order::Unordered, + Counterparty::new(msg.port_id_on_b.clone(), Some(ChannelId::default())), + vec![conn_id_on_b.clone()], + Version::default(), + ) + .unwrap(); + let context = context + .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_b, conn_end_on_b) + .with_channel( + msg.port_id_on_b.clone(), + ChannelId::default(), + wrong_chan_end, + ); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because channel is in the wrong state" + ) +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs new file mode 100644 index 000000000..645d1a180 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs @@ -0,0 +1,116 @@ +use ibc::clients::ics07_tendermint::client_type as tm_client_type; +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics02_client::height::Height; +use ibc::core::ics03_connection::connection::{ConnectionEnd, State as ConnectionState}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; +use ibc::core::ics04_channel::msgs::ChannelMsg; +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::core::{execute, validate, MsgEnvelope, ValidationContext}; +use ibc::prelude::*; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_open_init; +use ibc_testkit::utils::core::connection::dummy_msg_conn_open_init; +use rstest::*; +use test_log::test; + +pub struct Fixture { + pub ctx: MockContext, + pub router: MockRouter, + pub msg: MsgEnvelope, +} + +#[fixture] +fn fixture() -> Fixture { + let msg_chan_open_init = + MsgChannelOpenInit::try_from(dummy_raw_msg_chan_open_init(None)).unwrap(); + + let msg = MsgEnvelope::from(ChannelMsg::from(msg_chan_open_init)); + + let default_ctx = MockContext::default(); + let router = MockRouter::new_with_transfer(); + + let msg_conn_init = dummy_msg_conn_open_init(); + + let client_id_on_a = ClientId::new(tm_client_type(), 0).unwrap(); + let client_height = Height::new(0, 10).unwrap(); + + let conn_end_on_a = ConnectionEnd::new( + ConnectionState::Init, + msg_conn_init.client_id_on_a.clone(), + msg_conn_init.counterparty.clone(), + get_compatible_versions(), + msg_conn_init.delay_period, + ) + .unwrap(); + + let ctx = default_ctx + .with_client(&client_id_on_a, client_height) + .with_connection(ConnectionId::default(), conn_end_on_a); + + Fixture { ctx, router, msg } +} + +#[rstest] +fn chan_open_init_validate_happy_path(fixture: Fixture) { + let Fixture { + ctx, router, msg, .. + } = fixture; + + let res = validate(&ctx, &router, msg); + + assert!(res.is_ok(), "Validation succeeds; good parameters") +} + +#[rstest] +fn chan_open_init_validate_counterparty_chan_id_set(fixture: Fixture) { + let Fixture { ctx, router, .. } = fixture; + + let msg = MsgChannelOpenInit::try_from(dummy_raw_msg_chan_open_init(None)).unwrap(); + + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!( + res.is_ok(), + "Validation succeeds even if counterparty channel id is set by relayer" + ) +} + +#[rstest] +fn chan_open_init_execute_happy_path(fixture: Fixture) { + let Fixture { + mut ctx, + mut router, + msg, + .. + } = fixture; + + let res = execute(&mut ctx, &mut router, msg); + + assert!(res.is_ok(), "Execution succeeds; good parameters"); + + assert_eq!(ctx.channel_counter().unwrap(), 1); + + assert_eq!(ctx.events.len(), 2); + + assert!(matches!( + ctx.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(ctx.events[1], IbcEvent::OpenInitChannel(_))); +} + +#[rstest] +fn chan_open_init_fail_no_connection(fixture: Fixture) { + let Fixture { router, msg, .. } = fixture; + + let res = validate(&MockContext::default(), &router, msg); + + assert!( + res.is_err(), + "Validation fails because no connection exists in the context" + ) +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs new file mode 100644 index 000000000..bcee388bf --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs @@ -0,0 +1,158 @@ +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; +use ibc::core::ics04_channel::msgs::ChannelMsg; +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::core::timestamp::ZERO_DURATION; +use ibc::core::{execute, validate, MsgEnvelope, ValidationContext}; +use ibc::prelude::*; +use ibc::Height; +use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_open_try; +use ibc_testkit::utils::core::connection::dummy_raw_counterparty_conn; +use rstest::*; +use test_log::test; + +pub struct Fixture { + pub ctx: MockContext, + pub router: MockRouter, + pub msg: MsgEnvelope, + pub client_id_on_b: ClientId, + pub conn_id_on_b: ConnectionId, + pub conn_end_on_b: ConnectionEnd, + pub proof_height: u64, +} + +#[fixture] +fn fixture() -> Fixture { + let proof_height = 10; + let conn_id_on_b = ConnectionId::new(2); + let client_id_on_b = ClientId::new(mock_client_type(), 45).unwrap(); + + // This is the connection underlying the channel we're trying to open. + let conn_end_on_b = ConnectionEnd::new( + ConnectionState::Open, + client_id_on_b.clone(), + ConnectionCounterparty::try_from(dummy_raw_counterparty_conn(Some(0))).unwrap(), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + // We're going to test message processing against this message. + // Note: we make the counterparty's channel_id `None`. + let mut msg_chan_open_try = + MsgChannelOpenTry::try_from(dummy_raw_msg_chan_open_try(proof_height)).unwrap(); + + let hops = vec![conn_id_on_b.clone()]; + msg_chan_open_try.connection_hops_on_b = hops; + + let msg = MsgEnvelope::from(ChannelMsg::from(msg_chan_open_try)); + + let ctx = MockContext::default(); + + let router = MockRouter::new_with_transfer(); + + Fixture { + ctx, + router, + msg, + client_id_on_b, + conn_id_on_b, + conn_end_on_b, + proof_height, + } +} + +#[rstest] +fn chan_open_try_validate_happy_path(fixture: Fixture) { + let Fixture { + ctx, + router, + msg, + client_id_on_b, + conn_id_on_b, + conn_end_on_b, + proof_height, + .. + } = fixture; + + let ctx = ctx + .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_b, conn_end_on_b); + + let res = validate(&ctx, &router, msg); + + assert!(res.is_ok(), "Validation success: happy path") +} + +#[rstest] +fn chan_open_try_execute_happy_path(fixture: Fixture) { + let Fixture { + ctx, + mut router, + msg, + client_id_on_b, + conn_id_on_b, + conn_end_on_b, + proof_height, + .. + } = fixture; + + let mut ctx = ctx + .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) + .with_connection(conn_id_on_b, conn_end_on_b); + + let res = execute(&mut ctx, &mut router, msg); + + assert!(res.is_ok(), "Execution success: happy path"); + + assert_eq!(ctx.channel_counter().unwrap(), 1); + + assert_eq!(ctx.events.len(), 2); + + assert!(matches!( + ctx.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(ctx.events[1], IbcEvent::OpenTryChannel(_))); +} + +#[rstest] +fn chan_open_try_fail_no_connection(fixture: Fixture) { + let Fixture { + ctx, router, msg, .. + } = fixture; + + let res = validate(&ctx, &router, msg); + + assert!( + res.is_err(), + "Validation fails because no connection exists in the context" + ) +} + +#[rstest] +fn chan_open_try_fail_no_client_state(fixture: Fixture) { + let Fixture { + ctx, + router, + msg, + conn_id_on_b, + conn_end_on_b, + .. + } = fixture; + let ctx = ctx.with_connection(conn_id_on_b, conn_end_on_b); + + let res = validate(&ctx, &router, msg); + + assert!( + res.is_err(), + "Validation fails because the context has no client state" + ) +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/mod.rs b/crates/ibc-testkit/tests/core/ics04_channel/mod.rs new file mode 100644 index 000000000..01d376a35 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/mod.rs @@ -0,0 +1,11 @@ +pub mod acknowledgement; +pub mod chan_close_confirm; +pub mod chan_close_init; +pub mod chan_open_ack; +pub mod chan_open_confirm; +pub mod chan_open_init; +pub mod chan_open_try; +pub mod recv_packet; +pub mod send_packet; +pub mod timeout; +pub mod timeout_on_close; diff --git a/crates/ibc-testkit/tests/core/ics04_channel/recv_packet.rs b/crates/ibc-testkit/tests/core/ics04_channel/recv_packet.rs new file mode 100644 index 000000000..64b4b5ca8 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/recv_packet.rs @@ -0,0 +1,245 @@ +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics02_client::ClientExecutionContext; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; +use ibc::core::ics04_channel::msgs::recv_packet::MsgRecvPacket; +use ibc::core::ics04_channel::msgs::PacketMsg; +use ibc::core::ics04_channel::packet::Packet; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; +use ibc::core::timestamp::{Timestamp, ZERO_DURATION}; +use ibc::core::{execute, validate, ExecutionContext, MsgEnvelope}; +use ibc::prelude::*; +use ibc::Height; +use ibc_testkit::relayer::context::RelayerContext; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::{dummy_msg_recv_packet, dummy_raw_msg_recv_packet}; +use ibc_testkit::utils::core::signer::dummy_account_id; +use rstest::*; +use test_log::test; + +pub struct Fixture { + pub context: MockContext, + pub router: MockRouter, + pub client_height: Height, + pub host_height: Height, + pub msg: MsgRecvPacket, + pub conn_end_on_b: ConnectionEnd, + pub chan_end_on_b: ChannelEnd, +} + +#[fixture] +fn fixture() -> Fixture { + let context = MockContext::default(); + + let router = MockRouter::new_with_transfer(); + + let host_height = context.query_latest_height().unwrap().increment(); + + let client_height = host_height.increment(); + + let msg = MsgRecvPacket::try_from(dummy_raw_msg_recv_packet(client_height.revision_height())) + .unwrap(); + + let packet = msg.packet.clone(); + + let chan_end_on_b = ChannelEnd::new( + State::Open, + Order::default(), + Counterparty::new(packet.port_id_on_a, Some(packet.chan_id_on_a)), + vec![ConnectionId::default()], + Version::new("ics20-1".to_string()), + ) + .unwrap(); + + let conn_end_on_b = ConnectionEnd::new( + ConnectionState::Open, + ClientId::default(), + ConnectionCounterparty::new( + ClientId::default(), + Some(ConnectionId::default()), + Default::default(), + ), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + Fixture { + context, + router, + client_height, + host_height, + msg, + conn_end_on_b, + chan_end_on_b, + } +} + +#[rstest] +fn recv_packet_fail_no_channel(fixture: Fixture) { + let Fixture { + context, + router, + msg, + .. + } = fixture; + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because no channel exists in the context" + ) +} + +#[rstest] +fn recv_packet_validate_happy_path(fixture: Fixture) { + let Fixture { + context, + router, + msg, + conn_end_on_b, + chan_end_on_b, + client_height, + host_height, + .. + } = fixture; + + let packet = &msg.packet; + let mut context = context + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_b) + .with_channel( + packet.port_id_on_b.clone(), + packet.chan_id_on_b.clone(), + chan_end_on_b, + ) + .with_send_sequence( + packet.port_id_on_b.clone(), + packet.chan_id_on_b.clone(), + 1.into(), + ) + .with_height(host_height) + // This `with_recv_sequence` is required for ordered channels + .with_recv_sequence( + packet.port_id_on_b.clone(), + packet.chan_id_on_b.clone(), + packet.seq_on_a, + ); + + context + .get_client_execution_context() + .store_update_time( + ClientId::default(), + client_height, + Timestamp::from_nanoseconds(1000).unwrap(), + ) + .unwrap(); + context + .get_client_execution_context() + .store_update_height( + ClientId::default(), + client_height, + Height::new(0, 5).unwrap(), + ) + .unwrap(); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_ok(), + "Happy path: validation should succeed. err: {res:?}" + ) +} + +#[rstest] +fn recv_packet_timeout_expired(fixture: Fixture) { + let Fixture { + context, + router, + msg, + conn_end_on_b, + chan_end_on_b, + client_height, + host_height, + .. + } = fixture; + + let packet_old = Packet { + seq_on_a: 1.into(), + port_id_on_a: PortId::transfer(), + chan_id_on_a: ChannelId::default(), + port_id_on_b: PortId::transfer(), + chan_id_on_b: ChannelId::default(), + data: Vec::new(), + timeout_height_on_b: client_height.into(), + timeout_timestamp_on_b: Timestamp::from_nanoseconds(1).unwrap(), + }; + + let msg_packet_old = dummy_msg_recv_packet( + packet_old, + msg.proof_commitment_on_a.clone(), + msg.proof_height_on_a, + dummy_account_id(), + ); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg_packet_old)); + + let context = context + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_b) + .with_channel(PortId::transfer(), ChannelId::default(), chan_end_on_b) + .with_send_sequence(PortId::transfer(), ChannelId::default(), 1.into()) + .with_height(host_height); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_err(), + "recv_packet validation should fail when the packet has timed out" + ) +} + +#[rstest] +fn recv_packet_execute_happy_path(fixture: Fixture) { + let Fixture { + context, + mut router, + msg, + conn_end_on_b, + chan_end_on_b, + client_height, + .. + } = fixture; + let mut ctx = context + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_b) + .with_channel(PortId::transfer(), ChannelId::default(), chan_end_on_b); + + let msg_env = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = execute(&mut ctx, &mut router, msg_env); + + assert!(res.is_ok()); + + assert_eq!(ctx.events.len(), 4); + assert!(matches!( + &ctx.events[0], + &IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(&ctx.events[1], &IbcEvent::ReceivePacket(_))); + assert!(matches!( + &ctx.events[2], + &IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(&ctx.events[3], &IbcEvent::WriteAcknowledgement(_))); +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/send_packet.rs b/crates/ibc-testkit/tests/core/ics04_channel/send_packet.rs new file mode 100644 index 000000000..4262627c0 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/send_packet.rs @@ -0,0 +1,186 @@ +use core::ops::Add; +use core::time::Duration; + +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics02_client::height::Height; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; +use ibc::core::ics04_channel::packet::Packet; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; +use ibc::core::send_packet; +use ibc::core::timestamp::{Timestamp, ZERO_DURATION}; +use ibc::prelude::*; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_packet; +use test_log::test; + +#[test] +fn send_packet_processing() { + struct Test { + name: String, + ctx: MockContext, + packet: Packet, + want_pass: bool, + } + + let context = MockContext::default(); + + let chan_end_on_a = ChannelEnd::new( + State::Open, + Order::default(), + Counterparty::new(PortId::transfer(), Some(ChannelId::default())), + vec![ConnectionId::default()], + Version::new("ics20-1".to_string()), + ) + .unwrap(); + + let conn_end_on_a = ConnectionEnd::new( + ConnectionState::Open, + ClientId::default(), + ConnectionCounterparty::new( + ClientId::default(), + Some(ConnectionId::default()), + Default::default(), + ), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + let timestamp_future = Timestamp::now().add(Duration::from_secs(10)).unwrap(); + let timestamp_ns_past = 1; + + let timeout_height_future = 10; + + let mut packet: Packet = + dummy_raw_packet(timeout_height_future, timestamp_future.nanoseconds()) + .try_into() + .unwrap(); + packet.seq_on_a = 1.into(); + packet.data = vec![0]; + + let mut packet_with_timestamp_old: Packet = + dummy_raw_packet(timeout_height_future, timestamp_ns_past) + .try_into() + .unwrap(); + packet_with_timestamp_old.seq_on_a = 1.into(); + packet_with_timestamp_old.data = vec![0]; + + let client_raw_height = 5; + let packet_timeout_equal_client_height: Packet = + dummy_raw_packet(client_raw_height, timestamp_future.nanoseconds()) + .try_into() + .unwrap(); + let packet_timeout_one_before_client_height: Packet = + dummy_raw_packet(client_raw_height - 1, timestamp_future.nanoseconds()) + .try_into() + .unwrap(); + + let client_height = Height::new(0, client_raw_height).unwrap(); + + let tests: Vec = vec![ + Test { + name: "Processing fails because no channel exists in the context".to_string(), + ctx: context.clone(), + packet: packet.clone(), + want_pass: false, + }, + Test { + name: "Good parameters".to_string(), + ctx: context + .clone() + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_a.clone()) + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a.clone(), + ) + .with_send_sequence(PortId::transfer(), ChannelId::default(), 1.into()), + packet, + want_pass: true, + }, + Test { + name: "Packet timeout height same as destination chain height".to_string(), + ctx: context + .clone() + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_a.clone()) + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a.clone(), + ) + .with_send_sequence(PortId::transfer(), ChannelId::default(), 1.into()), + packet: packet_timeout_equal_client_height, + want_pass: true, + }, + Test { + name: "Packet timeout height one more than destination chain height".to_string(), + ctx: context + .clone() + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_a.clone()) + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a.clone(), + ) + .with_send_sequence(PortId::transfer(), ChannelId::default(), 1.into()), + packet: packet_timeout_one_before_client_height, + want_pass: false, + }, + Test { + name: "Packet timeout due to timestamp".to_string(), + ctx: context + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_channel(PortId::transfer(), ChannelId::default(), chan_end_on_a) + .with_send_sequence(PortId::transfer(), ChannelId::default(), 1.into()), + packet: packet_with_timestamp_old, + want_pass: false, + }, + ] + .into_iter() + .collect(); + + for mut test in tests { + let res = send_packet(&mut test.ctx, test.packet.clone()); + // Additionally check the events and the output objects in the result. + match res { + Ok(()) => { + assert!( + test.want_pass, + "send_packet: test passed but was supposed to fail for test: {}, \nparams {:?} {:?}", + test.name, + test.packet.clone(), + test.ctx.clone() + ); + + assert!(!test.ctx.events.is_empty()); // Some events must exist. + + assert_eq!(test.ctx.events.len(), 2); + assert!(matches!( + &test.ctx.events[0], + &IbcEvent::Message(MessageEvent::Channel) + )); + // TODO: The object in the output is a PacketResult what can we check on it? + assert!(matches!(&test.ctx.events[1], &IbcEvent::SendPacket(_))); + } + Err(e) => { + assert!( + !test.want_pass, + "send_packet: did not pass test: {}, \nparams {:?} {:?} error: {:?}", + test.name, + test.packet.clone(), + test.ctx.clone(), + e, + ); + } + } + } +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/timeout.rs b/crates/ibc-testkit/tests/core/ics04_channel/timeout.rs new file mode 100644 index 000000000..d0e2bcd01 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/timeout.rs @@ -0,0 +1,431 @@ +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics02_client::height::Height; +use ibc::core::ics02_client::ClientExecutionContext; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; +use ibc::core::ics04_channel::commitment::{compute_packet_commitment, PacketCommitment}; +use ibc::core::ics04_channel::msgs::timeout::MsgTimeout; +use ibc::core::ics04_channel::msgs::PacketMsg; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; +use ibc::core::timestamp::{Timestamp, ZERO_DURATION}; +use ibc::core::{execute, validate, ExecutionContext, MsgEnvelope}; +use ibc::prelude::*; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_msg_timeout; +use rstest::*; + +struct Fixture { + ctx: MockContext, + pub router: MockRouter, + client_height: Height, + msg: MsgTimeout, + packet_commitment: PacketCommitment, + conn_end_on_a: ConnectionEnd, + chan_end_on_a_ordered: ChannelEnd, + chan_end_on_a_unordered: ChannelEnd, +} + +#[fixture] +fn fixture() -> Fixture { + let client_height = Height::new(0, 2).unwrap(); + let ctx = MockContext::default().with_client(&ClientId::default(), client_height); + + let client_height = Height::new(0, 2).unwrap(); + + let router = MockRouter::new_with_transfer(); + + let msg_proof_height = 2; + let msg_timeout_height = 5; + let timeout_timestamp = Timestamp::now().nanoseconds(); + + let msg = MsgTimeout::try_from(dummy_raw_msg_timeout( + msg_proof_height, + msg_timeout_height, + timeout_timestamp, + )) + .unwrap(); + + let packet = msg.packet.clone(); + + let packet_commitment = compute_packet_commitment( + &msg.packet.data, + &msg.packet.timeout_height_on_b, + &msg.packet.timeout_timestamp_on_b, + ); + + let chan_end_on_a_unordered = ChannelEnd::new( + State::Open, + Order::Unordered, + Counterparty::new(packet.port_id_on_b.clone(), Some(packet.chan_id_on_b)), + vec![ConnectionId::default()], + Version::new("ics20-1".to_string()), + ) + .unwrap(); + + let mut chan_end_on_a_ordered = chan_end_on_a_unordered.clone(); + chan_end_on_a_ordered.ordering = Order::Ordered; + + let conn_end_on_a = ConnectionEnd::new( + ConnectionState::Open, + ClientId::default(), + ConnectionCounterparty::new( + ClientId::default(), + Some(ConnectionId::default()), + Default::default(), + ), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + Fixture { + ctx, + router, + client_height, + msg, + packet_commitment, + conn_end_on_a, + chan_end_on_a_ordered, + chan_end_on_a_unordered, + } +} + +#[rstest] +fn timeout_fail_no_channel(fixture: Fixture) { + let Fixture { + ctx, + router, + msg, + client_height, + .. + } = fixture; + let ctx = ctx.with_client(&ClientId::default(), client_height); + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + let res = validate(&ctx, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because no channel exists in the context" + ) +} + +#[rstest] +fn timeout_fail_no_consensus_state_for_height(fixture: Fixture) { + let Fixture { + ctx, + router, + msg, + chan_end_on_a_unordered, + conn_end_on_a, + packet_commitment, + .. + } = fixture; + + let packet = msg.packet.clone(); + + let ctx = ctx + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_unordered, + ) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_packet_commitment( + packet.port_id_on_a, + packet.chan_id_on_a, + packet.seq_on_a, + packet_commitment, + ); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because the client does not have a consensus state for the required height" + ) +} + +#[rstest] +fn timeout_fail_proof_timeout_not_reached(fixture: Fixture) { + let Fixture { + ctx, + router, + mut msg, + chan_end_on_a_unordered, + conn_end_on_a, + client_height, + .. + } = fixture; + + // timeout timestamp has not reached yet + let timeout_timestamp_on_b = + (msg.packet.timeout_timestamp_on_b + core::time::Duration::new(10, 0)).unwrap(); + msg.packet.timeout_timestamp_on_b = timeout_timestamp_on_b; + let packet_commitment = compute_packet_commitment( + &msg.packet.data, + &msg.packet.timeout_height_on_b, + &msg.packet.timeout_timestamp_on_b, + ); + + let packet = msg.packet.clone(); + + let mut ctx = ctx + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_unordered, + ) + .with_packet_commitment( + packet.port_id_on_a, + packet.chan_id_on_a, + packet.seq_on_a, + packet_commitment, + ); + + ctx.store_update_time( + ClientId::default(), + client_height, + Timestamp::from_nanoseconds(5).unwrap(), + ) + .unwrap(); + ctx.store_update_height( + ClientId::default(), + client_height, + Height::new(0, 4).unwrap(), + ) + .unwrap(); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation should fail because the timeout height was reached, but the timestamp hasn't been reached. Both the height and timestamp need to be reached for the packet to be considered timed out" + ) +} + +/// NO-OP case +#[rstest] +fn timeout_success_no_packet_commitment(fixture: Fixture) { + let Fixture { + ctx, + router, + msg, + conn_end_on_a, + chan_end_on_a_unordered, + .. + } = fixture; + let ctx = ctx + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_unordered, + ) + .with_connection(ConnectionId::default(), conn_end_on_a); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!( + res.is_ok(), + "Validation should succeed when no packet commitment is present" + ) +} + +#[rstest] +fn timeout_unordered_channel_validate(fixture: Fixture) { + let Fixture { + ctx, + router, + msg, + chan_end_on_a_unordered, + conn_end_on_a, + packet_commitment, + client_height, + .. + } = fixture; + + let packet = msg.packet.clone(); + + let mut ctx = ctx + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_unordered, + ) + .with_packet_commitment( + packet.port_id_on_a, + packet.chan_id_on_a, + packet.seq_on_a, + packet_commitment, + ); + + ctx.get_client_execution_context() + .store_update_time( + ClientId::default(), + client_height, + Timestamp::from_nanoseconds(1000).unwrap(), + ) + .unwrap(); + ctx.get_client_execution_context() + .store_update_height( + ClientId::default(), + client_height, + Height::new(0, 5).unwrap(), + ) + .unwrap(); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!(res.is_ok(), "Good parameters for unordered channels") +} + +#[rstest] +fn timeout_ordered_channel_validate(fixture: Fixture) { + let Fixture { + ctx, + router, + msg, + chan_end_on_a_ordered, + conn_end_on_a, + packet_commitment, + client_height, + .. + } = fixture; + + let packet = msg.packet.clone(); + + let mut ctx = ctx + .with_client(&ClientId::default(), client_height) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_ordered, + ) + .with_packet_commitment( + packet.port_id_on_a, + packet.chan_id_on_a, + packet.seq_on_a, + packet_commitment, + ); + + ctx.store_update_time( + ClientId::default(), + client_height, + Timestamp::from_nanoseconds(1000).unwrap(), + ) + .unwrap(); + ctx.store_update_height( + ClientId::default(), + client_height, + Height::new(0, 4).unwrap(), + ) + .unwrap(); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&ctx, &router, msg_envelope); + + assert!(res.is_ok(), "Good parameters for unordered channels") +} + +#[rstest] +fn timeout_unordered_chan_execute(fixture: Fixture) { + let Fixture { + ctx, + mut router, + msg, + packet_commitment, + conn_end_on_a, + chan_end_on_a_unordered, + .. + } = fixture; + let mut ctx = ctx + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_unordered, + ) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_packet_commitment( + msg.packet.port_id_on_a.clone(), + msg.packet.chan_id_on_a.clone(), + msg.packet.seq_on_a, + packet_commitment, + ); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = execute(&mut ctx, &mut router, msg_envelope); + + assert!(res.is_ok()); + + // Unordered channels only emit one event + assert_eq!(ctx.events.len(), 2); + assert!(matches!( + ctx.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(ctx.events[1], IbcEvent::TimeoutPacket(_))); +} + +#[rstest] +fn timeout_ordered_chan_execute(fixture: Fixture) { + let Fixture { + ctx, + mut router, + msg, + packet_commitment, + conn_end_on_a, + chan_end_on_a_ordered, + .. + } = fixture; + let mut ctx = ctx + .with_channel( + PortId::transfer(), + ChannelId::default(), + chan_end_on_a_ordered, + ) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_packet_commitment( + msg.packet.port_id_on_a.clone(), + msg.packet.chan_id_on_a.clone(), + msg.packet.seq_on_a, + packet_commitment, + ); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = execute(&mut ctx, &mut router, msg_envelope); + + assert!(res.is_ok()); + + // Ordered channels emit 2 events + assert_eq!(ctx.events.len(), 4); + assert!(matches!( + ctx.events[0], + IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(ctx.events[1], IbcEvent::TimeoutPacket(_))); + assert!(matches!( + ctx.events[2], + IbcEvent::Message(MessageEvent::Channel) + )); + assert!(matches!(ctx.events[3], IbcEvent::ChannelClosed(_))); +} diff --git a/crates/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs b/crates/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs new file mode 100644 index 000000000..8aa8d9e00 --- /dev/null +++ b/crates/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs @@ -0,0 +1,173 @@ +use ibc::core::ics02_client::ClientExecutionContext; +use ibc::core::ics03_connection::connection::{ + ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, +}; +use ibc::core::ics03_connection::version::get_compatible_versions; +use ibc::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; +use ibc::core::ics04_channel::commitment::{compute_packet_commitment, PacketCommitment}; +use ibc::core::ics04_channel::msgs::timeout_on_close::MsgTimeoutOnClose; +use ibc::core::ics04_channel::msgs::PacketMsg; +use ibc::core::ics04_channel::Version; +use ibc::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; +use ibc::core::timestamp::{Timestamp, ZERO_DURATION}; +use ibc::core::{validate, ExecutionContext, MsgEnvelope}; +use ibc::prelude::*; +use ibc::Height; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::core::channel::dummy_raw_msg_timeout_on_close; +use rstest::*; + +pub struct Fixture { + pub context: MockContext, + pub router: MockRouter, + pub msg: MsgTimeoutOnClose, + pub packet_commitment: PacketCommitment, + pub conn_end_on_a: ConnectionEnd, + pub chan_end_on_a: ChannelEnd, +} + +#[fixture] +fn fixture() -> Fixture { + let client_height = Height::new(0, 2).unwrap(); + let context = MockContext::default().with_client(&ClientId::default(), client_height); + let router = MockRouter::new_with_transfer(); + + let height = 2; + let timeout_timestamp = 5; + + let msg = + MsgTimeoutOnClose::try_from(dummy_raw_msg_timeout_on_close(height, timeout_timestamp)) + .unwrap(); + + let packet = msg.packet.clone(); + + let packet_commitment = compute_packet_commitment( + &msg.packet.data, + &msg.packet.timeout_height_on_b, + &msg.packet.timeout_timestamp_on_b, + ); + + let chan_end_on_a = ChannelEnd::new( + State::Open, + Order::Ordered, + Counterparty::new(packet.port_id_on_b.clone(), Some(packet.chan_id_on_b)), + vec![ConnectionId::default()], + Version::new("ics20-1".to_string()), + ) + .unwrap(); + + let conn_end_on_a = ConnectionEnd::new( + ConnectionState::Open, + ClientId::default(), + ConnectionCounterparty::new( + ClientId::default(), + Some(ConnectionId::default()), + Default::default(), + ), + get_compatible_versions(), + ZERO_DURATION, + ) + .unwrap(); + + Fixture { + context, + router, + msg, + packet_commitment, + conn_end_on_a, + chan_end_on_a, + } +} + +#[rstest] +fn timeout_on_close_fail_no_channel(fixture: Fixture) { + let Fixture { + context, + router, + msg, + .. + } = fixture; + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_err(), + "Validation fails because no channel exists in the context" + ) +} + +/// NO-OP case +#[rstest] +fn timeout_on_close_success_no_packet_commitment(fixture: Fixture) { + let Fixture { + context, + router, + msg, + conn_end_on_a, + chan_end_on_a, + .. + } = fixture; + let context = context + .with_channel(PortId::transfer(), ChannelId::default(), chan_end_on_a) + .with_connection(ConnectionId::default(), conn_end_on_a); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_ok(), + "Validation should succeed when no packet commitment is present" + ) +} + +#[rstest] +fn timeout_on_close_success_happy_path(fixture: Fixture) { + let Fixture { + context, + router, + msg, + packet_commitment, + conn_end_on_a, + chan_end_on_a, + .. + } = fixture; + let mut context = context + .with_channel(PortId::transfer(), ChannelId::default(), chan_end_on_a) + .with_connection(ConnectionId::default(), conn_end_on_a) + .with_packet_commitment( + msg.packet.port_id_on_a.clone(), + msg.packet.chan_id_on_a.clone(), + msg.packet.seq_on_a, + packet_commitment, + ); + + context + .get_client_execution_context() + .store_update_time( + ClientId::default(), + Height::new(0, 2).unwrap(), + Timestamp::from_nanoseconds(5000).unwrap(), + ) + .unwrap(); + context + .get_client_execution_context() + .store_update_height( + ClientId::default(), + Height::new(0, 2).unwrap(), + Height::new(0, 5).unwrap(), + ) + .unwrap(); + + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); + + let res = validate(&context, &router, msg_envelope); + + assert!( + res.is_ok(), + "Happy path: validation should succeed. err: {res:?}" + ) +} diff --git a/crates/ibc-testkit/tests/core/mod.rs b/crates/ibc-testkit/tests/core/mod.rs new file mode 100644 index 000000000..97dce615a --- /dev/null +++ b/crates/ibc-testkit/tests/core/mod.rs @@ -0,0 +1,4 @@ +pub mod ics02_client; +pub mod ics03_connection; +pub mod ics04_channel; +pub mod router; diff --git a/crates/ibc-testkit/tests/core/router.rs b/crates/ibc-testkit/tests/core/router.rs new file mode 100644 index 000000000..94a5a0c2a --- /dev/null +++ b/crates/ibc-testkit/tests/core/router.rs @@ -0,0 +1,447 @@ +use ibc::applications::transfer::error::TokenTransferError; +use ibc::applications::transfer::msgs::transfer::MsgTransfer; +use ibc::applications::transfer::{send_transfer, BaseCoin}; +use ibc::core::events::{IbcEvent, MessageEvent}; +use ibc::core::ics02_client::msgs::create_client::MsgCreateClient; +use ibc::core::ics02_client::msgs::update_client::MsgUpdateClient; +use ibc::core::ics02_client::msgs::ClientMsg; +use ibc::core::ics03_connection::msgs::ConnectionMsg; +use ibc::core::ics04_channel::error::ChannelError; +use ibc::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; +use ibc::core::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; +use ibc::core::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; +use ibc::core::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; +use ibc::core::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; +use ibc::core::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; +use ibc::core::ics04_channel::msgs::recv_packet::MsgRecvPacket; +use ibc::core::ics04_channel::msgs::timeout_on_close::MsgTimeoutOnClose; +use ibc::core::ics04_channel::msgs::{ChannelMsg, PacketMsg}; +use ibc::core::ics04_channel::timeout::TimeoutHeight; +use ibc::core::ics24_host::identifier::ConnectionId; +use ibc::core::ics24_host::path::CommitmentPath; +use ibc::core::timestamp::Timestamp; +use ibc::core::{dispatch, MsgEnvelope, RouterError, ValidationContext}; +use ibc::prelude::*; +use ibc::Height; +use ibc_testkit::testapp::ibc::applications::transfer::types::DummyTransferModule; +use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; +use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; +use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::utils::applications::transfer::{ + extract_transfer_packet, MsgTransferConfig, PacketDataConfig, +}; +use ibc_testkit::utils::core::channel::{ + dummy_raw_msg_ack_with_packet, dummy_raw_msg_chan_close_confirm, dummy_raw_msg_chan_close_init, + dummy_raw_msg_chan_open_ack, dummy_raw_msg_chan_open_init, dummy_raw_msg_chan_open_try, + dummy_raw_msg_recv_packet, dummy_raw_msg_timeout_on_close, +}; +use ibc_testkit::utils::core::client::dummy_msg_upgrade_client; +use ibc_testkit::utils::core::connection::{ + dummy_msg_conn_open_ack, dummy_msg_conn_open_init, dummy_msg_conn_open_init_with_client_id, + dummy_msg_conn_open_try, msg_conn_open_try_with_client_id, +}; +use ibc_testkit::utils::core::signer::dummy_account_id; +use primitive_types::U256; +use test_log::test; + +#[test] +/// These tests exercise two main paths: (1) the ability of the ICS26 routing module to dispatch +/// messages to the correct module handler, and more importantly: (2) the ability of ICS handlers +/// to work with the context and correctly store results. +fn routing_module_and_keepers() { + #[derive(Clone, Debug)] + enum TestMsg { + Ics26(MsgEnvelope), + Ics20(MsgTransfer), + } + + impl From for TestMsg { + fn from(msg: MsgEnvelope) -> Self { + Self::Ics26(msg) + } + } + + impl From for TestMsg { + fn from(msg: MsgTransfer) -> Self { + Self::Ics20(msg) + } + } + + type StateCheckFn = dyn FnOnce(&MockContext) -> bool; + + // Test parameters + struct Test { + name: String, + msg: TestMsg, + want_pass: bool, + state_check: Option>, + } + let default_signer = dummy_account_id(); + let client_height = 5; + let start_client_height = Height::new(0, client_height).unwrap(); + let update_client_height = Height::new(0, 34).unwrap(); + let update_client_height_after_send = Height::new(0, 35).unwrap(); + + let update_client_height_after_second_send = Height::new(0, 36).unwrap(); + + let upgrade_client_height = Height::new(1, 2).unwrap(); + + let upgrade_client_height_second = Height::new(1, 1).unwrap(); + + // We reuse this same context across all tests. Nothing in particular needs parametrizing. + let mut ctx = MockContext::default(); + + let mut router = MockRouter::new_with_transfer(); + + let create_client_msg = MsgCreateClient::new( + MockClientState::new(MockHeader::new(start_client_height).with_current_timestamp()).into(), + MockConsensusState::new(MockHeader::new(start_client_height).with_current_timestamp()) + .into(), + default_signer.clone(), + ); + + // + // Connection handshake messages. + // + let msg_conn_init = dummy_msg_conn_open_init(); + + let correct_msg_conn_try = dummy_msg_conn_open_try(client_height, client_height); + + // The handler will fail to process this msg because the client height is too advanced. + let incorrect_msg_conn_try = dummy_msg_conn_open_try(client_height + 1, client_height + 1); + + let msg_conn_ack = dummy_msg_conn_open_ack(client_height, client_height); + + // + // Channel handshake messages. + // + let msg_chan_init = MsgChannelOpenInit::try_from(dummy_raw_msg_chan_open_init(None)).unwrap(); + + // The handler will fail to process this b/c the associated connection does not exist + let mut incorrect_msg_chan_init = msg_chan_init.clone(); + incorrect_msg_chan_init.connection_hops_on_a = vec![ConnectionId::new(590)]; + + let msg_chan_try = + MsgChannelOpenTry::try_from(dummy_raw_msg_chan_open_try(client_height)).unwrap(); + + let msg_chan_ack = + MsgChannelOpenAck::try_from(dummy_raw_msg_chan_open_ack(client_height)).unwrap(); + + let msg_chan_close_init = + MsgChannelCloseInit::try_from(dummy_raw_msg_chan_close_init()).unwrap(); + + let msg_chan_close_confirm = + MsgChannelCloseConfirm::try_from(dummy_raw_msg_chan_close_confirm(client_height)).unwrap(); + + let packet_data = PacketDataConfig::builder() + .token( + BaseCoin { + denom: "uatom".parse().expect("parse denom"), + amount: U256::from(10).into(), + } + .into(), + ) + .build(); + + let msg_transfer = MsgTransferConfig::builder() + .packet_data(packet_data.clone()) + .timeout_height_on_b(TimeoutHeight::At(Height::new(0, 35).unwrap())) + .build(); + + let msg_transfer_two = MsgTransferConfig::builder() + .packet_data(packet_data.clone()) + .timeout_height_on_b(TimeoutHeight::At(Height::new(0, 36).unwrap())) + .build(); + + let msg_transfer_no_timeout = MsgTransferConfig::builder() + .packet_data(packet_data.clone()) + .build(); + + let msg_transfer_no_timeout_or_timestamp = MsgTransferConfig::builder() + .packet_data(packet_data.clone()) + .timeout_timestamp_on_b(Timestamp::from_nanoseconds(0).unwrap()) + .build(); + + let mut msg_to_on_close = + MsgTimeoutOnClose::try_from(dummy_raw_msg_timeout_on_close(36, 5)).unwrap(); + msg_to_on_close.packet.seq_on_a = 2.into(); + msg_to_on_close.packet.timeout_height_on_b = msg_transfer_two.timeout_height_on_b; + msg_to_on_close.packet.timeout_timestamp_on_b = msg_transfer_two.timeout_timestamp_on_b; + + let packet_data = serde_json::to_vec(&msg_transfer_two.packet_data) + .expect("PacketData's infallible Serialize impl failed"); + + msg_to_on_close.packet.data = packet_data; + + let msg_recv_packet = MsgRecvPacket::try_from(dummy_raw_msg_recv_packet(35)).unwrap(); + let msg_ack_packet = MsgAcknowledgement::try_from(dummy_raw_msg_ack_with_packet( + extract_transfer_packet(&msg_transfer, 1u64.into()).into(), + 35, + )) + .unwrap(); + + // First, create a client.. + let res = dispatch( + &mut ctx, + &mut router, + MsgEnvelope::Client(ClientMsg::CreateClient(create_client_msg.clone())), + ); + + assert!( + res.is_ok(), + "ICS26 routing dispatch test 'client creation' failed for message {create_client_msg:?} with result: {res:?}", + ); + + // Figure out the ID of the client that was just created. + assert!(matches!( + ctx.events[0], + IbcEvent::Message(MessageEvent::Client) + )); + let client_id_event = ctx.events.get(1); + assert!( + client_id_event.is_some(), + "There was no event generated for client creation!" + ); + let client_id = match client_id_event.unwrap() { + IbcEvent::CreateClient(create_client) => create_client.client_id().clone(), + event => panic!("unexpected IBC event: {:?}", event), + }; + + let tests: Vec = vec![ + // Test some ICS2 client functionality. + Test { + name: "Client update successful".to_string(), + msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: client_id.clone(), + client_message: MockHeader::new(update_client_height) + .with_current_timestamp() + .into(), + signer: default_signer.clone(), + })) + .into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Client update fails due to stale header".to_string(), + msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: client_id.clone(), + client_message: MockHeader::new(update_client_height).into(), + signer: default_signer.clone(), + })) + .into(), + want_pass: false, + state_check: None, + }, + Test { + name: "Connection open init succeeds".to_string(), + msg: MsgEnvelope::Connection(ConnectionMsg::OpenInit( + dummy_msg_conn_open_init_with_client_id(msg_conn_init, client_id.clone()), + )) + .into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Connection open try fails due to InvalidConsensusHeight (too high)".to_string(), + msg: MsgEnvelope::Connection(ConnectionMsg::OpenTry(incorrect_msg_conn_try)).into(), + want_pass: false, + state_check: None, + }, + Test { + name: "Connection open try succeeds".to_string(), + msg: MsgEnvelope::Connection(ConnectionMsg::OpenTry(msg_conn_open_try_with_client_id( + correct_msg_conn_try, + client_id.clone(), + ))) + .into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Connection open ack succeeds".to_string(), + msg: MsgEnvelope::Connection(ConnectionMsg::OpenAck(msg_conn_ack)).into(), + want_pass: true, + state_check: None, + }, + // ICS04 + Test { + name: "Channel open init succeeds".to_string(), + msg: MsgEnvelope::Channel(ChannelMsg::OpenInit(msg_chan_init)).into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Channel open init fail due to missing connection".to_string(), + msg: MsgEnvelope::Channel(ChannelMsg::OpenInit(incorrect_msg_chan_init)).into(), + want_pass: false, + state_check: None, + }, + Test { + name: "Channel open try succeeds".to_string(), + msg: MsgEnvelope::Channel(ChannelMsg::OpenTry(msg_chan_try)).into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Channel open ack succeeds".to_string(), + msg: MsgEnvelope::Channel(ChannelMsg::OpenAck(msg_chan_ack)).into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Packet send".to_string(), + msg: msg_transfer.into(), + want_pass: true, + state_check: None, + }, + // The client update is required in this test, because the proof associated with + // msg_recv_packet has the same height as the packet TO height (see get_dummy_raw_msg_recv_packet) + Test { + name: "Client update successful #2".to_string(), + msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: client_id.clone(), + client_message: MockHeader::new(update_client_height_after_send) + .with_current_timestamp() + .into(), + signer: default_signer.clone(), + })) + .into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Receive packet".to_string(), + msg: MsgEnvelope::Packet(PacketMsg::Recv(msg_recv_packet.clone())).into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Re-Receive packet".to_string(), + msg: MsgEnvelope::Packet(PacketMsg::Recv(msg_recv_packet)).into(), + want_pass: true, + state_check: None, + }, + // Ack packet + Test { + name: "Ack packet".to_string(), + msg: MsgEnvelope::Packet(PacketMsg::Ack(msg_ack_packet.clone())).into(), + want_pass: true, + state_check: Some(Box::new(move |ctx| { + ctx.get_packet_commitment(&CommitmentPath::new( + &msg_ack_packet.packet.port_id_on_a, + &msg_ack_packet.packet.chan_id_on_a, + msg_ack_packet.packet.seq_on_a, + )) + .is_err() + })), + }, + Test { + name: "Packet send".to_string(), + msg: msg_transfer_two.into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Client update successful".to_string(), + msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: client_id.clone(), + client_message: MockHeader::new(update_client_height_after_second_send) + .with_current_timestamp() + .into(), + signer: default_signer, + })) + .into(), + want_pass: true, + state_check: None, + }, + // Timeout packets + Test { + name: "Transfer message no timeout".to_string(), + msg: msg_transfer_no_timeout.into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Transfer message no timeout nor timestamp".to_string(), + msg: msg_transfer_no_timeout_or_timestamp.into(), + want_pass: true, + state_check: None, + }, + //ICS04-close channel + Test { + name: "Channel close init succeeds".to_string(), + msg: MsgEnvelope::Channel(ChannelMsg::CloseInit(msg_chan_close_init)).into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Channel close confirm fails cause channel is already closed".to_string(), + msg: MsgEnvelope::Channel(ChannelMsg::CloseConfirm(msg_chan_close_confirm)).into(), + want_pass: false, + state_check: None, + }, + //ICS04-to_on_close + Test { + name: "Timeout on close".to_string(), + msg: MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(msg_to_on_close)).into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Client upgrade successful".to_string(), + msg: MsgEnvelope::Client(ClientMsg::UpgradeClient(dummy_msg_upgrade_client( + client_id.clone(), + upgrade_client_height, + ))) + .into(), + want_pass: true, + state_check: None, + }, + Test { + name: "Client upgrade un-successful".to_string(), + msg: MsgEnvelope::Client(ClientMsg::UpgradeClient(dummy_msg_upgrade_client( + client_id, + upgrade_client_height_second, + ))) + .into(), + want_pass: false, + state_check: None, + }, + ] + .into_iter() + .collect(); + + for test in tests { + let res = match test.msg.clone() { + TestMsg::Ics26(msg) => dispatch(&mut ctx, &mut router, msg).map(|_| ()), + TestMsg::Ics20(msg) => send_transfer(&mut ctx, &mut DummyTransferModule, msg) + .map_err(|e: TokenTransferError| ChannelError::AppModule { + description: e.to_string(), + }) + .map_err(|e| RouterError::ContextError(e.into())), + }; + + assert_eq!( + test.want_pass, + res.is_ok(), + "ICS26 routing dispatch test '{}' failed for message {:?}\nwith result: {:?}", + test.name, + test.msg, + res + ); + + if let Some(state_check) = test.state_check { + assert_eq!( + test.want_pass, + state_check(&ctx), + "ICS26 routing state check '{}' failed for message {:?}\nwith result: {:?}", + test.name, + test.msg, + res + ); + } + } +} diff --git a/crates/ibc/tests/support/query/serialization/client_state.json b/crates/ibc-testkit/tests/data/json/client_state.json similarity index 100% rename from crates/ibc/tests/support/query/serialization/client_state.json rename to crates/ibc-testkit/tests/data/json/client_state.json diff --git a/crates/ibc/tests/support/query/serialization/client_state_proof.json b/crates/ibc-testkit/tests/data/json/client_state_proof.json similarity index 100% rename from crates/ibc/tests/support/query/serialization/client_state_proof.json rename to crates/ibc-testkit/tests/data/json/client_state_proof.json diff --git a/crates/ibc/tests/support/query/serialization/consensus_state.json b/crates/ibc-testkit/tests/data/json/consensus_state.json similarity index 100% rename from crates/ibc/tests/support/query/serialization/consensus_state.json rename to crates/ibc-testkit/tests/data/json/consensus_state.json diff --git a/crates/ibc/tests/support/query/serialization/consensus_state_proof.json b/crates/ibc-testkit/tests/data/json/consensus_state_proof.json similarity index 100% rename from crates/ibc/tests/support/query/serialization/consensus_state_proof.json rename to crates/ibc-testkit/tests/data/json/consensus_state_proof.json diff --git a/crates/ibc/tests/support/signed_header.json b/crates/ibc-testkit/tests/data/json/signed_header.json similarity index 100% rename from crates/ibc/tests/support/signed_header.json rename to crates/ibc-testkit/tests/data/json/signed_header.json diff --git a/crates/ibc-testkit/tests/mod.rs b/crates/ibc-testkit/tests/mod.rs new file mode 100644 index 000000000..1f32c1d98 --- /dev/null +++ b/crates/ibc-testkit/tests/mod.rs @@ -0,0 +1,10 @@ +#![deny( + warnings, + trivial_casts, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] +pub mod applications; +pub mod core; diff --git a/crates/ibc/Cargo.toml b/crates/ibc/Cargo.toml index 1528e4008..52238ed20 100644 --- a/crates/ibc/Cargo.toml +++ b/crates/ibc/Cargo.toml @@ -22,14 +22,12 @@ std = [ "ibc-proto/std", "ics23/std", "serde/std", - "tracing/std", "prost/std", "bytes/std", "subtle-encoding/std", "serde_json/std", "sha2/std", "displaydoc/std", - "num-traits/std", "uint/std", "primitive-types/std", "tendermint/clock", @@ -44,10 +42,6 @@ serde = ["dep:serde", "dep:serde_derive", "serde_json", "ibc-proto/serde", "ics2 # CosmWasm message API generator compatible, should not be inside on chain code schema = ["dep:schemars", "ibc-proto/json-schema", "serde", "std"] -# This feature grants access to development-time mocking libraries, such as `MockContext` or `MockHeader`. -# Depends on the `testgen` suite for generating Tendermint light blocks. -mocks = ["tendermint-testgen", "parking_lot", "typed-builder", "std"] - [dependencies] # Proto definitions for all IBC-related interfaces, e.g., connections or channels. ibc-proto = { version = "0.38.0", default-features = false } @@ -56,13 +50,11 @@ time = { version = ">=0.3.0, <0.3.31", default-features = false } serde_derive = { version = "1.0", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true } serde_json = { package = "serde-json-wasm", version = "1.0.0" , default-features = false, optional = true} -tracing = { version = "0.1.40", default-features = false } prost = { version = "0.12", default-features = false } bytes = { version = "1.5.0", default-features = false } subtle-encoding = { version = "0.5", default-features = false } sha2 = { version = "0.10.8", default-features = false } displaydoc = { version = "0.2", default-features = false } -num-traits = { version = "0.2.17", default-features = false } derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display", "try_into"] } uint = { version = "0.9", default-features = false } primitive-types = { version = "0.12.2", default-features = false, features = ["serde_no_std"] } @@ -72,10 +64,9 @@ parity-scale-codec = { version = "3.6.5", default-features = false, features = [ scale-info = { version = "2.10.0", default-features = false, features = ["derive"], optional = true } ## for borsh encode or decode borsh = {version = "0.10", default-features = false, optional = true } -parking_lot = { version = "0.12.1", default-features = false, optional = true } typed-builder = { version = "0.18.0", optional = true } -ibc-derive = { version ="0.3.0", path = "../ibc-derive" } +ibc-derive = { version = "0.3.0", path = "../ibc-derive" } schemars = { version = "0.8.15", optional = true } @@ -92,15 +83,10 @@ version = "0.34" default-features = false features = ["rust-crypto"] -[dependencies.tendermint-testgen] -version = "0.34" -optional = true -default-features = false - [dev-dependencies] +ibc-testkit = { version = "0.47.0", path = "../ibc-testkit", default-features = false, features = ["serde"] } env_logger = "0.10.0" rstest = "0.18.2" tracing-subscriber = { version = "0.3.17", features = ["fmt", "env-filter", "json"]} test-log = { version = "0.2.13", features = ["trace"] } tendermint-rpc = { version = "0.34", features = ["http-client", "websocket-client"] } -ibc = { path = ".", features = ["mocks"] } diff --git a/crates/ibc/clippy.toml b/crates/ibc/clippy.toml index af3114623..87ee0dae3 100644 --- a/crates/ibc/clippy.toml +++ b/crates/ibc/clippy.toml @@ -2,7 +2,6 @@ disallowed-types = [ # { path = "usize", reason = "variable size" }, # cannot on now, because mocks use it and serde, even if there is no usize in type { path = "f64", reason = "not supported in CosmWasm" }, { path = "f32", reason = "not supported in CosmWasm" }, - { path = "num_traits::float::*", reason = "harware dependant" }, ] disallowed-methods = [ diff --git a/crates/ibc/src/applications/transfer/context.rs b/crates/ibc/src/applications/transfer/context.rs index 81b4030af..33d080f30 100644 --- a/crates/ibc/src/applications/transfer/context.rs +++ b/crates/ibc/src/applications/transfer/context.rs @@ -411,39 +411,11 @@ pub fn on_timeout_packet_execute( } #[cfg(test)] -pub(crate) mod test { +mod tests { use subtle_encoding::bech32; use super::*; use crate::applications::transfer::context::cosmos_adr028_escrow_address; - use crate::core::ics04_channel::channel::{Counterparty, Order}; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; - use crate::test_utils::DummyTransferModule; - - fn get_defaults() -> ( - DummyTransferModule, - Order, - Vec, - PortId, - ChannelId, - Counterparty, - ) { - let order = Order::Unordered; - let connection_hops = vec![ConnectionId::new(1)]; - let port_id = PortId::transfer(); - let channel_id = ChannelId::new(1); - let counterparty = Counterparty::new(port_id.clone(), Some(channel_id.clone())); - - ( - DummyTransferModule, - order, - connection_hops, - port_id, - channel_id, - counterparty, - ) - } #[test] fn test_cosmos_escrow_address() { @@ -474,106 +446,4 @@ pub(crate) mod test { "cosmos177x69sver58mcfs74x6dg0tv6ls4s3xmmcaw53", ); } - - /// If the relayer passed "", indicating that it wants us to return the versions we support. - /// We currently only support ics20 - #[test] - fn test_on_chan_open_init_empty_version() { - let (mut ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); - - let in_version = Version::new("".to_string()); - - let (_, out_version) = on_chan_open_init_execute( - &mut ctx, - order, - &connection_hops, - &port_id, - &channel_id, - &counterparty, - &in_version, - ) - .unwrap(); - - assert_eq!(out_version, Version::new(VERSION.to_string())); - } - - /// If the relayer passed in the only supported version (ics20), then return ics20 - #[test] - fn test_on_chan_open_init_ics20_version() { - let (mut ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); - - let in_version = Version::new(VERSION.to_string()); - let (_, out_version) = on_chan_open_init_execute( - &mut ctx, - order, - &connection_hops, - &port_id, - &channel_id, - &counterparty, - &in_version, - ) - .unwrap(); - - assert_eq!(out_version, Version::new(VERSION.to_string())); - } - - /// If the relayer passed in an unsupported version, then fail - #[test] - fn test_on_chan_open_init_incorrect_version() { - let (ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); - - let in_version = Version::new("some-unsupported-version".to_string()); - let res = on_chan_open_init_validate( - &ctx, - order, - &connection_hops, - &port_id, - &channel_id, - &counterparty, - &in_version, - ); - - assert!(res.is_err()); - } - - /// If the counterparty supports ics20, then return ics20 - #[test] - fn test_on_chan_open_try_counterparty_correct_version() { - let (mut ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); - - let counterparty_version = Version::new(VERSION.to_string()); - - let (_, out_version) = on_chan_open_try_execute( - &mut ctx, - order, - &connection_hops, - &port_id, - &channel_id, - &counterparty, - &counterparty_version, - ) - .unwrap(); - - assert_eq!(out_version, Version::new(VERSION.to_string())); - } - - /// If the counterparty doesn't support ics20, then fail - #[test] - fn test_on_chan_open_try_counterparty_incorrect_version() { - let (ctx, order, connection_hops, port_id, channel_id, counterparty) = get_defaults(); - - let counterparty_version = Version::new("some-unsupported-version".to_string()); - - let res = on_chan_open_try_validate( - &ctx, - order, - &connection_hops, - &port_id, - &channel_id, - &counterparty, - &counterparty_version, - ); - - assert!(res.is_err()); - } } diff --git a/crates/ibc/src/applications/transfer/msgs/transfer.rs b/crates/ibc/src/applications/transfer/msgs/transfer.rs index 026e38b3f..44a8406a6 100644 --- a/crates/ibc/src/applications/transfer/msgs/transfer.rs +++ b/crates/ibc/src/applications/transfer/msgs/transfer.rs @@ -125,49 +125,3 @@ impl TryFrom for MsgTransfer { } } } - -#[cfg(test)] -pub mod test_util { - use core::ops::Add; - use core::time::Duration; - - use super::{MsgTransfer, *}; - use crate::applications::transfer::packet::PacketData; - use crate::core::ics04_channel::packet::{Packet, Sequence}; - use crate::core::ics04_channel::timeout::TimeoutHeight; - use crate::core::ics24_host::identifier::{ChannelId, PortId}; - - impl MsgTransfer { - // Returns a dummy ICS20 `MsgTransfer`. If no `timeout_timestamp` is - // specified, a timestamp of 10 seconds in the future is used. - pub fn new_dummy( - timeout_height: TimeoutHeight, - timeout_timestamp: Option, - ) -> Self { - Self { - port_id_on_a: PortId::default(), - chan_id_on_a: ChannelId::default(), - packet_data: PacketData::new_dummy(), - timeout_timestamp_on_b: timeout_timestamp - .unwrap_or_else(|| Timestamp::now().add(Duration::from_secs(10)).unwrap()), - timeout_height_on_b: timeout_height, - } - } - - pub fn get_transfer_packet(&self, sequence: Sequence) -> Packet { - let data = serde_json::to_vec(&self.packet_data) - .expect("PacketData's infallible Serialize impl failed"); - - Packet { - seq_on_a: sequence, - port_id_on_a: self.port_id_on_a.clone(), - chan_id_on_a: self.chan_id_on_a.clone(), - port_id_on_b: PortId::default(), - chan_id_on_b: ChannelId::default(), - data, - timeout_height_on_b: self.timeout_height_on_b, - timeout_timestamp_on_b: self.timeout_timestamp_on_b, - } - } - } -} diff --git a/crates/ibc/src/applications/transfer/packet.rs b/crates/ibc/src/applications/transfer/packet.rs index 758af8439..63058b2ed 100644 --- a/crates/ibc/src/applications/transfer/packet.rs +++ b/crates/ibc/src/applications/transfer/packet.rs @@ -65,16 +65,15 @@ impl From for RawPacketData { #[cfg(test)] mod tests { - + use ibc_testkit::utils::core::signer::dummy_bech32_account; use primitive_types::U256; use super::*; use crate::applications::transfer::BaseCoin; - use crate::test_utils::get_dummy_bech32_account; impl PacketData { pub fn new_dummy() -> Self { - let address: Signer = get_dummy_bech32_account().into(); + let address: Signer = dummy_bech32_account().into(); Self { token: BaseCoin { diff --git a/crates/ibc/src/clients/ics07_tendermint/client_state.rs b/crates/ibc/src/clients/ics07_tendermint/client_state.rs index a4ff405fb..d7ada244f 100644 --- a/crates/ibc/src/clients/ics07_tendermint/client_state.rs +++ b/crates/ibc/src/clients/ics07_tendermint/client_state.rs @@ -792,23 +792,96 @@ fn check_header_trusted_next_validator_set( } } +#[cfg(all(test, feature = "serde"))] +mod serde_tests { + use tendermint_rpc::endpoint::abci_query::AbciQuery; + + use crate::serializers::tests::test_serialization_roundtrip; + #[test] + fn serialization_roundtrip_no_proof() { + let json_data = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../ibc-testkit/tests/data/json/client_state.json" + )); + test_serialization_roundtrip::(json_data); + } + + #[test] + fn serialization_roundtrip_with_proof() { + let json_data = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../ibc-testkit/tests/data/json/client_state_proof.json" + )); + test_serialization_roundtrip::(json_data); + } +} + #[cfg(test)] mod tests { + use core::str::FromStr; use core::time::Duration; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::client::v1::Height as RawHeight; + use ibc_proto::ibc::lightclients::tendermint::v1::{ClientState as RawTmClientState, Fraction}; use ibc_proto::ics23::ProofSpec as Ics23ProofSpec; + use ibc_testkit::utils::clients::tendermint::dummy_tendermint_header; + use tendermint::block::Header; use test_log::test; use super::*; - use crate::clients::ics07_tendermint::client_state::AllowUpdate; + use crate::clients::ics07_tendermint::client_state::{AllowUpdate, ClientState}; use crate::clients::ics07_tendermint::error::Error; - use crate::clients::ics07_tendermint::header::test_util::get_dummy_tendermint_header; + use crate::core::ics02_client::height::Height; use crate::core::ics23_commitment::specs::ProofSpecs; use crate::core::ics24_host::identifier::ChainId; use crate::core::timestamp::ZERO_DURATION; - use crate::Height; + + impl ClientState { + pub fn new_dummy_from_raw(frozen_height: RawHeight) -> Result { + Self::try_from(get_dummy_raw_tm_client_state(frozen_height)) + } + + pub fn new_dummy_from_header(tm_header: Header) -> Self { + let chain_id = ChainId::from_str(tm_header.chain_id.as_str()).expect("Never fails"); + Self::new( + chain_id.clone(), + Default::default(), + Duration::from_secs(64000), + Duration::from_secs(128000), + Duration::from_millis(3000), + Height::new(chain_id.revision_number(), u64::from(tm_header.height)) + .expect("Never fails"), + Default::default(), + Default::default(), + AllowUpdate { + after_expiry: false, + after_misbehaviour: false, + }, + ) + .expect("Never fails") + } + } + + pub fn get_dummy_raw_tm_client_state(frozen_height: RawHeight) -> RawTmClientState { + #[allow(deprecated)] + RawTmClientState { + chain_id: ChainId::new("ibc-0").expect("Never fails").to_string(), + trust_level: Some(Fraction { + numerator: 1, + denominator: 3, + }), + trusting_period: Some(Duration::from_secs(64000).into()), + unbonding_period: Some(Duration::from_secs(128000).into()), + max_clock_drift: Some(Duration::from_millis(3000).into()), + latest_height: Some(Height::new(0, 10).expect("Never fails").into()), + proof_specs: ProofSpecs::default().into(), + upgrade_path: Default::default(), + frozen_height: Some(frozen_height), + allow_update_after_expiry: false, + allow_update_after_misbehaviour: false, + } + } #[derive(Clone, Debug, PartialEq)] struct ClientStateParams { @@ -1083,7 +1156,7 @@ mod tests { ); // check client state creation path from a tendermint header - let tm_header = get_dummy_tendermint_header(); + let tm_header = dummy_tendermint_header(); let tm_client_state_from_header = ClientState::new_dummy_from_header(tm_header); let any_from_header = Any::from(tm_client_state_from_header.clone()); let tm_client_state_from_any = ClientState::try_from(any_from_header); @@ -1106,125 +1179,3 @@ mod tests { } } } - -#[cfg(all(test, feature = "serde"))] -mod serde_tests { - use tendermint_rpc::endpoint::abci_query::AbciQuery; - - use crate::serializers::tests::test_serialization_roundtrip; - #[test] - fn serialization_roundtrip_no_proof() { - let json_data = - include_str!("../../../tests/support/query/serialization/client_state.json"); - test_serialization_roundtrip::(json_data); - } - - #[test] - fn serialization_roundtrip_with_proof() { - let json_data = - include_str!("../../../tests/support/query/serialization/client_state_proof.json"); - test_serialization_roundtrip::(json_data); - } -} - -#[cfg(any(test, feature = "mocks"))] -pub mod test_util { - use core::str::FromStr; - use core::time::Duration; - - use ibc_proto::ibc::core::client::v1::Height as RawHeight; - use ibc_proto::ibc::lightclients::tendermint::v1::{ClientState as RawTmClientState, Fraction}; - use tendermint::block::Header; - - use crate::clients::ics07_tendermint::client_state::{AllowUpdate, ClientState}; - use crate::clients::ics07_tendermint::error::{Error as ClientError, Error}; - use crate::clients::ics07_tendermint::trust_threshold::TrustThreshold; - use crate::core::ics02_client::height::Height; - use crate::core::ics23_commitment::specs::ProofSpecs; - use crate::core::ics24_host::identifier::ChainId; - use crate::prelude::*; - - impl ClientState { - pub fn new_dummy_from_raw(frozen_height: RawHeight) -> Result { - Self::try_from(get_dummy_raw_tm_client_state(frozen_height)) - } - - pub fn new_dummy_from_header(tm_header: Header) -> Self { - let chain_id = ChainId::from_str(tm_header.chain_id.as_str()).expect("Never fails"); - Self::new( - chain_id.clone(), - Default::default(), - Duration::from_secs(64000), - Duration::from_secs(128000), - Duration::from_millis(3000), - Height::new(chain_id.revision_number(), u64::from(tm_header.height)) - .expect("Never fails"), - Default::default(), - Default::default(), - AllowUpdate { - after_expiry: false, - after_misbehaviour: false, - }, - ) - .expect("Never fails") - } - } - - pub fn get_dummy_raw_tm_client_state(frozen_height: RawHeight) -> RawTmClientState { - #[allow(deprecated)] - RawTmClientState { - chain_id: ChainId::new("ibc-0").expect("Never fails").to_string(), - trust_level: Some(Fraction { - numerator: 1, - denominator: 3, - }), - trusting_period: Some(Duration::from_secs(64000).into()), - unbonding_period: Some(Duration::from_secs(128000).into()), - max_clock_drift: Some(Duration::from_millis(3000).into()), - latest_height: Some(Height::new(0, 10).expect("Never fails").into()), - proof_specs: ProofSpecs::default().into(), - upgrade_path: Default::default(), - frozen_height: Some(frozen_height), - allow_update_after_expiry: false, - allow_update_after_misbehaviour: false, - } - } - - #[derive(typed_builder::TypedBuilder, Debug)] - pub struct ClientStateConfig { - pub chain_id: ChainId, - #[builder(default)] - pub trust_level: TrustThreshold, - #[builder(default = Duration::from_secs(64000))] - pub trusting_period: Duration, - #[builder(default = Duration::from_secs(128000))] - pub unbonding_period: Duration, - #[builder(default = Duration::from_millis(3000))] - max_clock_drift: Duration, - pub latest_height: Height, - #[builder(default)] - pub proof_specs: ProofSpecs, - #[builder(default)] - pub upgrade_path: Vec, - #[builder(default = AllowUpdate { after_expiry: false, after_misbehaviour: false })] - allow_update: AllowUpdate, - } - - impl TryFrom for ClientState { - type Error = ClientError; - - fn try_from(config: ClientStateConfig) -> Result { - ClientState::new( - config.chain_id, - config.trust_level, - config.trusting_period, - config.unbonding_period, - config.max_clock_drift, - config.latest_height, - config.proof_specs, - config.upgrade_path, - config.allow_update, - ) - } - } -} diff --git a/crates/ibc/src/clients/ics07_tendermint/consensus_state.rs b/crates/ibc/src/clients/ics07_tendermint/consensus_state.rs index 14cd0b81b..f00a70b8c 100644 --- a/crates/ibc/src/clients/ics07_tendermint/consensus_state.rs +++ b/crates/ibc/src/clients/ics07_tendermint/consensus_state.rs @@ -161,8 +161,7 @@ impl ConsensusStateTrait for ConsensusState { } } -#[cfg(test)] -#[cfg(feature = "serde")] +#[cfg(all(test, feature = "serde"))] mod tests { use tendermint_rpc::endpoint::abci_query::AbciQuery; use test_log::test; @@ -171,15 +170,19 @@ mod tests { #[test] fn serialization_roundtrip_no_proof() { - let json_data = - include_str!("../../../tests/support/query/serialization/consensus_state.json"); + let json_data = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../ibc-testkit/tests/data/json/consensus_state.json" + )); test_serialization_roundtrip::(json_data); } #[test] fn serialization_roundtrip_with_proof() { - let json_data = - include_str!("../../../tests/support/query/serialization/consensus_state_proof.json"); + let json_data = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../ibc-testkit/tests/data/json/consensus_state_proof.json" + )); test_serialization_roundtrip::(json_data); } } diff --git a/crates/ibc/src/clients/ics07_tendermint/header.rs b/crates/ibc/src/clients/ics07_tendermint/header.rs index f73b79d36..cb1f56c4f 100644 --- a/crates/ibc/src/clients/ics07_tendermint/header.rs +++ b/crates/ibc/src/clients/ics07_tendermint/header.rs @@ -23,7 +23,7 @@ use crate::core::timestamp::Timestamp; use crate::prelude::*; use crate::Height; -pub(crate) const TENDERMINT_HEADER_TYPE_URL: &str = "/ibc.lightclients.tendermint.v1.Header"; +pub const TENDERMINT_HEADER_TYPE_URL: &str = "/ibc.lightclients.tendermint.v1.Header"; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] /// Tendermint consensus header @@ -248,85 +248,3 @@ mod pretty { } } } - -#[cfg(any(test, feature = "mocks"))] -pub mod test_util { - use alloc::vec; - - use subtle_encoding::hex; - use tendermint::block::signed_header::SignedHeader; - use tendermint::validator::{Info as ValidatorInfo, Set as ValidatorSet}; - use tendermint::PublicKey; - - use crate::clients::ics07_tendermint::header::Header; - use crate::mock::host::SyntheticTmBlock; - use crate::Height; - - pub fn get_dummy_tendermint_header() -> tendermint::block::Header { - serde_json::from_str::(include_str!( - "../../../tests/support/signed_header.json" - )) - .expect("Never fails") - .header - } - - // TODO: This should be replaced with a ::default() or ::produce(). - // The implementation of this function comprises duplicate code (code borrowed from - // `tendermint-rs` for assembling a Header). - // See https://github.com/informalsystems/tendermint-rs/issues/381. - // - // The normal flow is: - // - get the (trusted) signed header and the `trusted_validator_set` at a `trusted_height` - // - get the `signed_header` and the `validator_set` at latest height - // - build the ics07 Header - // For testing purposes this function does: - // - get the `signed_header` from a .json file - // - create the `validator_set` with a single validator that is also the proposer - // - assume a `trusted_height` of 1 and no change in the validator set since height 1, - // i.e. `trusted_validator_set` = `validator_set` - pub fn get_dummy_ics07_header() -> Header { - // Build a SignedHeader from a JSON file. - let shdr = serde_json::from_str::(include_str!( - "../../../tests/support/signed_header.json" - )) - .expect("Never fails"); - - // Build a set of validators. - // Below are test values inspired form `test_validator_set()` in tendermint-rs. - let v1: ValidatorInfo = ValidatorInfo::new( - PublicKey::from_raw_ed25519( - &hex::decode_upper( - "F349539C7E5EF7C49549B09C4BFC2335318AB0FE51FBFAA2433B4F13E816F4A7", - ) - .expect("Never fails"), - ) - .expect("Never fails"), - 281_815_u64.try_into().expect("Never fails"), - ); - - let vs = ValidatorSet::new(vec![v1.clone()], Some(v1)); - - Header { - signed_header: shdr, - validator_set: vs.clone(), - trusted_height: Height::min(0), - trusted_next_validator_set: vs, - } - } - - impl From for Header { - fn from(light_block: SyntheticTmBlock) -> Self { - let SyntheticTmBlock { - trusted_height, - trusted_next_validators, - light_block, - } = light_block; - Self { - signed_header: light_block.signed_header, - validator_set: light_block.validators, - trusted_height, - trusted_next_validator_set: trusted_next_validators, - } - } - } -} diff --git a/crates/ibc/src/clients/ics07_tendermint/mod.rs b/crates/ibc/src/clients/ics07_tendermint/mod.rs index 803e7814e..13fa13694 100644 --- a/crates/ibc/src/clients/ics07_tendermint/mod.rs +++ b/crates/ibc/src/clients/ics07_tendermint/mod.rs @@ -15,7 +15,7 @@ pub mod trust_threshold; mod context; pub use context::*; -pub(crate) const TENDERMINT_CLIENT_TYPE: &str = "07-tendermint"; +pub const TENDERMINT_CLIENT_TYPE: &str = "07-tendermint"; /// Returns the tendermint `ClientType` pub fn client_type() -> ClientType { diff --git a/crates/ibc/src/core/events.rs b/crates/ibc/src/core/events.rs index 6b451c960..b4a2b5292 100644 --- a/crates/ibc/src/core/events.rs +++ b/crates/ibc/src/core/events.rs @@ -283,10 +283,11 @@ impl From for IbcEvent { pub mod tests { use alloc::vec; + use ibc_testkit::utils::core::channel::dummy_raw_packet; + use super::*; use crate::core::ics04_channel::channel::Order; use crate::core::ics04_channel::events::SendPacket; - use crate::core::ics04_channel::packet::test_utils::get_dummy_raw_packet; use crate::core::ics04_channel::packet::Packet; use crate::core::ics24_host::identifier::ConnectionId; @@ -294,7 +295,7 @@ pub mod tests { /// Ensures that we don't panic when packet data is not valid UTF-8. /// See issue [#199](https://github.com/cosmos/ibc-rs/issues/199) pub fn test_packet_data_non_utf8() { - let mut packet = Packet::try_from(get_dummy_raw_packet(1, 1)).unwrap(); + let mut packet = Packet::try_from(dummy_raw_packet(1, 1)).unwrap(); packet.data = vec![128]; let ibc_event = IbcEvent::SendPacket(SendPacket::new( diff --git a/crates/ibc/src/core/handler.rs b/crates/ibc/src/core/handler.rs index 7c2babfe3..7c766a0dd 100644 --- a/crates/ibc/src/core/handler.rs +++ b/crates/ibc/src/core/handler.rs @@ -195,667 +195,3 @@ where } } } - -#[cfg(test)] -mod tests { - use core::default::Default; - use core::time::Duration; - - use test_log::test; - - use super::*; - use crate::applications::transfer::error::TokenTransferError; - use crate::applications::transfer::msgs::transfer::MsgTransfer; - use crate::applications::transfer::{send_transfer, MODULE_ID_STR}; - use crate::core::dispatch; - use crate::core::events::{IbcEvent, MessageEvent}; - use crate::core::ics02_client::msgs::create_client::MsgCreateClient; - use crate::core::ics02_client::msgs::update_client::MsgUpdateClient; - use crate::core::ics02_client::msgs::upgrade_client::MsgUpgradeClient; - use crate::core::ics02_client::msgs::ClientMsg; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnCounterparty, State as ConnState, - }; - use crate::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck; - use crate::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; - use crate::core::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; - use crate::core::ics03_connection::msgs::ConnectionMsg; - use crate::core::ics03_connection::version::Version as ConnVersion; - use crate::core::ics04_channel::channel::{ - ChannelEnd, Counterparty as ChannelCounterparty, Order as ChannelOrder, - State as ChannelState, - }; - use crate::core::ics04_channel::error::ChannelError; - use crate::core::ics04_channel::msgs::acknowledgement::test_util::get_dummy_raw_msg_ack_with_packet; - use crate::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; - use crate::core::ics04_channel::msgs::chan_close_confirm::test_util::get_dummy_raw_msg_chan_close_confirm; - use crate::core::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; - use crate::core::ics04_channel::msgs::chan_close_init::test_util::get_dummy_raw_msg_chan_close_init; - use crate::core::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; - use crate::core::ics04_channel::msgs::chan_open_ack::test_util::get_dummy_raw_msg_chan_open_ack; - use crate::core::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; - use crate::core::ics04_channel::msgs::chan_open_confirm::test_util::get_dummy_raw_msg_chan_open_confirm; - use crate::core::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; - use crate::core::ics04_channel::msgs::chan_open_init::test_util::get_dummy_raw_msg_chan_open_init; - use crate::core::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; - use crate::core::ics04_channel::msgs::chan_open_try::test_util::get_dummy_raw_msg_chan_open_try; - use crate::core::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; - use crate::core::ics04_channel::msgs::recv_packet::test_util::get_dummy_raw_msg_recv_packet; - use crate::core::ics04_channel::msgs::recv_packet::MsgRecvPacket; - use crate::core::ics04_channel::msgs::timeout_on_close::test_util::get_dummy_raw_msg_timeout_on_close; - use crate::core::ics04_channel::msgs::timeout_on_close::MsgTimeoutOnClose; - use crate::core::ics04_channel::msgs::{ChannelMsg, PacketMsg}; - use crate::core::ics04_channel::timeout::TimeoutHeight; - use crate::core::ics04_channel::Version as ChannelVersion; - use crate::core::ics23_commitment::commitment::CommitmentPrefix; - use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; - use crate::core::ics24_host::path::CommitmentPath; - use crate::core::msgs::MsgEnvelope; - use crate::core::router::ModuleId; - use crate::core::timestamp::Timestamp; - use crate::mock::client_state::MockClientState; - use crate::mock::consensus_state::MockConsensusState; - use crate::mock::context::MockContext; - use crate::mock::header::MockHeader; - use crate::mock::router::MockRouter; - use crate::prelude::*; - use crate::test_utils::{get_dummy_account_id, DummyTransferModule}; - use crate::Height; - - #[test] - /// These tests exercise two main paths: (1) the ability of the ICS26 routing module to dispatch - /// messages to the correct module handler, and more importantly: (2) the ability of ICS handlers - /// to work with the context and correctly store results. - fn routing_module_and_keepers() { - #[derive(Clone, Debug)] - enum TestMsg { - Ics26(MsgEnvelope), - Ics20(MsgTransfer), - } - - impl From for TestMsg { - fn from(msg: MsgEnvelope) -> Self { - Self::Ics26(msg) - } - } - - impl From for TestMsg { - fn from(msg: MsgTransfer) -> Self { - Self::Ics20(msg) - } - } - - type StateCheckFn = dyn FnOnce(&MockContext) -> bool; - - // Test parameters - struct Test { - name: String, - msg: TestMsg, - want_pass: bool, - state_check: Option>, - } - let default_signer = get_dummy_account_id(); - let client_height = 5; - let start_client_height = Height::new(0, client_height).unwrap(); - let update_client_height = Height::new(0, 34).unwrap(); - let update_client_height_after_send = Height::new(0, 35).unwrap(); - - let update_client_height_after_second_send = Height::new(0, 36).unwrap(); - - let upgrade_client_height = Height::new(1, 2).unwrap(); - - let upgrade_client_height_second = Height::new(1, 1).unwrap(); - - let transfer_module_id: ModuleId = ModuleId::new(MODULE_ID_STR.to_string()); - - // We reuse this same context across all tests. Nothing in particular needs parametrizing. - let mut ctx = MockContext::default(); - - let mut router = MockRouter::default(); - router - .add_route(transfer_module_id.clone(), DummyTransferModule::new()) - .unwrap(); - - let create_client_msg = MsgCreateClient::new( - MockClientState::new(MockHeader::new(start_client_height)).into(), - MockConsensusState::new(MockHeader::new(start_client_height)).into(), - default_signer.clone(), - ); - - // - // Connection handshake messages. - // - let msg_conn_init = MsgConnectionOpenInit::new_dummy(); - - let correct_msg_conn_try = MsgConnectionOpenTry::new_dummy(client_height, client_height); - - // The handler will fail to process this msg because the client height is too advanced. - let incorrect_msg_conn_try = - MsgConnectionOpenTry::new_dummy(client_height + 1, client_height + 1); - - let msg_conn_ack = MsgConnectionOpenAck::new_dummy(client_height, client_height); - - // - // Channel handshake messages. - // - let msg_chan_init = - MsgChannelOpenInit::try_from(get_dummy_raw_msg_chan_open_init(None)).unwrap(); - - // The handler will fail to process this b/c the associated connection does not exist - let mut incorrect_msg_chan_init = msg_chan_init.clone(); - incorrect_msg_chan_init.connection_hops_on_a = vec![ConnectionId::new(590)]; - - let msg_chan_try = - MsgChannelOpenTry::try_from(get_dummy_raw_msg_chan_open_try(client_height)).unwrap(); - - let msg_chan_ack = - MsgChannelOpenAck::try_from(get_dummy_raw_msg_chan_open_ack(client_height)).unwrap(); - - let msg_chan_close_init = - MsgChannelCloseInit::try_from(get_dummy_raw_msg_chan_close_init()).unwrap(); - - let msg_chan_close_confirm = - MsgChannelCloseConfirm::try_from(get_dummy_raw_msg_chan_close_confirm(client_height)) - .unwrap(); - - let msg_transfer = MsgTransfer::new_dummy(Height::new(0, 35).unwrap().into(), None); - let msg_transfer_two = MsgTransfer::new_dummy(Height::new(0, 36).unwrap().into(), None); - let msg_transfer_no_timeout = MsgTransfer::new_dummy(TimeoutHeight::no_timeout(), None); - let msg_transfer_no_timeout_or_timestamp = MsgTransfer::new_dummy( - TimeoutHeight::no_timeout(), - Some(Timestamp::from_nanoseconds(0).unwrap()), - ); - - let mut msg_to_on_close = - MsgTimeoutOnClose::try_from(get_dummy_raw_msg_timeout_on_close(36, 5)).unwrap(); - msg_to_on_close.packet.seq_on_a = 2.into(); - msg_to_on_close.packet.timeout_height_on_b = msg_transfer_two.timeout_height_on_b; - msg_to_on_close.packet.timeout_timestamp_on_b = msg_transfer_two.timeout_timestamp_on_b; - - let packet_data = serde_json::to_vec(&msg_transfer_two.packet_data) - .expect("PacketData's infallible Serialize impl failed"); - - msg_to_on_close.packet.data = packet_data; - - let msg_recv_packet = MsgRecvPacket::try_from(get_dummy_raw_msg_recv_packet(35)).unwrap(); - let msg_ack_packet = MsgAcknowledgement::try_from(get_dummy_raw_msg_ack_with_packet( - msg_transfer.get_transfer_packet(1u64.into()).into(), - 35, - )) - .unwrap(); - - // First, create a client.. - let res = dispatch( - &mut ctx, - &mut router, - MsgEnvelope::Client(ClientMsg::CreateClient(create_client_msg.clone())), - ); - - assert!( - res.is_ok(), - "ICS26 routing dispatch test 'client creation' failed for message {create_client_msg:?} with result: {res:?}", - ); - - router.scope_port_to_module(msg_chan_init.port_id_on_a.clone(), transfer_module_id); - - // Figure out the ID of the client that was just created. - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Client) - )); - let client_id_event = ctx.events.get(1); - assert!( - client_id_event.is_some(), - "There was no event generated for client creation!" - ); - let client_id = match client_id_event.unwrap() { - IbcEvent::CreateClient(create_client) => create_client.client_id().clone(), - event => panic!("unexpected IBC event: {:?}", event), - }; - - let tests: Vec = vec![ - // Test some ICS2 client functionality. - Test { - name: "Client update successful".to_string(), - msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { - client_id: client_id.clone(), - client_message: MockHeader::new(update_client_height) - .with_timestamp(Timestamp::now()) - .into(), - signer: default_signer.clone(), - })) - .into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Client update fails due to stale header".to_string(), - msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { - client_id: client_id.clone(), - client_message: MockHeader::new(update_client_height).into(), - signer: default_signer.clone(), - })) - .into(), - want_pass: false, - state_check: None, - }, - Test { - name: "Connection open init succeeds".to_string(), - msg: MsgEnvelope::Connection(ConnectionMsg::OpenInit( - msg_conn_init.with_client_id(client_id.clone()), - )) - .into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Connection open try fails due to InvalidConsensusHeight (too high)" - .to_string(), - msg: MsgEnvelope::Connection(ConnectionMsg::OpenTry(incorrect_msg_conn_try)).into(), - want_pass: false, - state_check: None, - }, - Test { - name: "Connection open try succeeds".to_string(), - msg: MsgEnvelope::Connection(ConnectionMsg::OpenTry( - correct_msg_conn_try.with_client_id(client_id.clone()), - )) - .into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Connection open ack succeeds".to_string(), - msg: MsgEnvelope::Connection(ConnectionMsg::OpenAck(msg_conn_ack)).into(), - want_pass: true, - state_check: None, - }, - // ICS04 - Test { - name: "Channel open init succeeds".to_string(), - msg: MsgEnvelope::Channel(ChannelMsg::OpenInit(msg_chan_init)).into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Channel open init fail due to missing connection".to_string(), - msg: MsgEnvelope::Channel(ChannelMsg::OpenInit(incorrect_msg_chan_init)).into(), - want_pass: false, - state_check: None, - }, - Test { - name: "Channel open try succeeds".to_string(), - msg: MsgEnvelope::Channel(ChannelMsg::OpenTry(msg_chan_try)).into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Channel open ack succeeds".to_string(), - msg: MsgEnvelope::Channel(ChannelMsg::OpenAck(msg_chan_ack)).into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Packet send".to_string(), - msg: msg_transfer.into(), - want_pass: true, - state_check: None, - }, - // The client update is required in this test, because the proof associated with - // msg_recv_packet has the same height as the packet TO height (see get_dummy_raw_msg_recv_packet) - Test { - name: "Client update successful #2".to_string(), - msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { - client_id: client_id.clone(), - client_message: MockHeader::new(update_client_height_after_send) - .with_timestamp(Timestamp::now()) - .into(), - signer: default_signer.clone(), - })) - .into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Receive packet".to_string(), - msg: MsgEnvelope::Packet(PacketMsg::Recv(msg_recv_packet.clone())).into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Re-Receive packet".to_string(), - msg: MsgEnvelope::Packet(PacketMsg::Recv(msg_recv_packet)).into(), - want_pass: true, - state_check: None, - }, - // Ack packet - Test { - name: "Ack packet".to_string(), - msg: MsgEnvelope::Packet(PacketMsg::Ack(msg_ack_packet.clone())).into(), - want_pass: true, - state_check: Some(Box::new(move |ctx| { - ctx.get_packet_commitment(&CommitmentPath::new( - &msg_ack_packet.packet.port_id_on_a, - &msg_ack_packet.packet.chan_id_on_a, - msg_ack_packet.packet.seq_on_a, - )) - .is_err() - })), - }, - Test { - name: "Packet send".to_string(), - msg: msg_transfer_two.into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Client update successful".to_string(), - msg: MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { - client_id: client_id.clone(), - client_message: MockHeader::new(update_client_height_after_second_send).into(), - signer: default_signer, - })) - .into(), - want_pass: true, - state_check: None, - }, - // Timeout packets - Test { - name: "Transfer message no timeout".to_string(), - msg: msg_transfer_no_timeout.into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Transfer message no timeout nor timestamp".to_string(), - msg: msg_transfer_no_timeout_or_timestamp.into(), - want_pass: true, - state_check: None, - }, - //ICS04-close channel - Test { - name: "Channel close init succeeds".to_string(), - msg: MsgEnvelope::Channel(ChannelMsg::CloseInit(msg_chan_close_init)).into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Channel close confirm fails cause channel is already closed".to_string(), - msg: MsgEnvelope::Channel(ChannelMsg::CloseConfirm(msg_chan_close_confirm)).into(), - want_pass: false, - state_check: None, - }, - //ICS04-to_on_close - Test { - name: "Timeout on close".to_string(), - msg: MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(msg_to_on_close)).into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Client upgrade successful".to_string(), - msg: MsgEnvelope::Client(ClientMsg::UpgradeClient( - MsgUpgradeClient::new_dummy(upgrade_client_height) - .with_client_id(client_id.clone()), - )) - .into(), - want_pass: true, - state_check: None, - }, - Test { - name: "Client upgrade un-successful".to_string(), - msg: MsgEnvelope::Client(ClientMsg::UpgradeClient( - MsgUpgradeClient::new_dummy(upgrade_client_height_second) - .with_client_id(client_id), - )) - .into(), - want_pass: false, - state_check: None, - }, - ] - .into_iter() - .collect(); - - for test in tests { - let res = match test.msg.clone() { - TestMsg::Ics26(msg) => dispatch(&mut ctx, &mut router, msg).map(|_| ()), - TestMsg::Ics20(msg) => send_transfer(&mut ctx, &mut DummyTransferModule, msg) - .map_err(|e: TokenTransferError| ChannelError::AppModule { - description: e.to_string(), - }) - .map_err(|e| RouterError::ContextError(e.into())), - }; - - assert_eq!( - test.want_pass, - res.is_ok(), - "ICS26 routing dispatch test '{}' failed for message {:?}\nwith result: {:?}", - test.name, - test.msg, - res - ); - - if let Some(state_check) = test.state_check { - assert_eq!( - test.want_pass, - state_check(&ctx), - "ICS26 routing state check '{}' failed for message {:?}\nwith result: {:?}", - test.name, - test.msg, - res - ); - } - } - } - - fn get_channel_events_ctx_router() -> (MockContext, MockRouter) { - let module_id: ModuleId = ModuleId::new(MODULE_ID_STR.to_string()); - let ctx = MockContext::default() - .with_client(&ClientId::default(), Height::new(0, 1).unwrap()) - .with_connection( - ConnectionId::new(0), - ConnectionEnd::new( - ConnState::Open, - ClientId::default(), - ConnCounterparty::new( - ClientId::default(), - Some(ConnectionId::new(0)), - CommitmentPrefix::default(), - ), - vec![ConnVersion::default()], - Duration::MAX, - ) - .unwrap(), - ); - let mut router = MockRouter::default(); - - router - .add_route(module_id.clone(), DummyTransferModule::new()) - .unwrap(); - - // Note: messages will be using the default port - router.scope_port_to_module(PortId::default(), module_id); - - (ctx, router) - } - - #[test] - fn test_chan_open_init_event() { - let (mut ctx, mut router) = get_channel_events_ctx_router(); - - let msg_chan_open_init = - MsgChannelOpenInit::try_from(get_dummy_raw_msg_chan_open_init(None)).unwrap(); - - dispatch( - &mut ctx, - &mut router, - MsgEnvelope::Channel(ChannelMsg::OpenInit(msg_chan_open_init)), - ) - .unwrap(); - - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::OpenInitChannel(_))); - } - - #[test] - fn test_chan_open_try_event() { - let (mut ctx, mut router) = get_channel_events_ctx_router(); - - let msg_chan_open_try = - MsgChannelOpenTry::try_from(get_dummy_raw_msg_chan_open_try(1)).unwrap(); - - dispatch( - &mut ctx, - &mut router, - MsgEnvelope::Channel(ChannelMsg::OpenTry(msg_chan_open_try)), - ) - .unwrap(); - - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::OpenTryChannel(_))); - } - - #[test] - fn test_chan_open_ack_event() { - let (ctx, mut router) = get_channel_events_ctx_router(); - let mut ctx = ctx.with_channel( - PortId::default(), - ChannelId::default(), - ChannelEnd::new( - ChannelState::Init, - ChannelOrder::Unordered, - ChannelCounterparty::new(PortId::default(), Some(ChannelId::default())), - vec![ConnectionId::new(0)], - ChannelVersion::default(), - ) - .unwrap(), - ); - - let msg_chan_open_ack = - MsgChannelOpenAck::try_from(get_dummy_raw_msg_chan_open_ack(1)).unwrap(); - - dispatch( - &mut ctx, - &mut router, - MsgEnvelope::Channel(ChannelMsg::OpenAck(msg_chan_open_ack)), - ) - .unwrap(); - - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::OpenAckChannel(_))); - } - - #[test] - fn test_chan_open_confirm_event() { - let (ctx, mut router) = get_channel_events_ctx_router(); - let mut ctx = ctx.with_channel( - PortId::default(), - ChannelId::default(), - ChannelEnd::new( - ChannelState::TryOpen, - ChannelOrder::Unordered, - ChannelCounterparty::new(PortId::default(), Some(ChannelId::default())), - vec![ConnectionId::new(0)], - ChannelVersion::default(), - ) - .unwrap(), - ); - - let msg_chan_open_confirm = - MsgChannelOpenConfirm::try_from(get_dummy_raw_msg_chan_open_confirm(1)).unwrap(); - - dispatch( - &mut ctx, - &mut router, - MsgEnvelope::Channel(ChannelMsg::OpenConfirm(msg_chan_open_confirm)), - ) - .unwrap(); - - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::OpenConfirmChannel(_))); - } - - #[test] - fn test_chan_close_init_event() { - let (ctx, mut router) = get_channel_events_ctx_router(); - let mut ctx = ctx.with_channel( - PortId::default(), - ChannelId::default(), - ChannelEnd::new( - ChannelState::Open, - ChannelOrder::Unordered, - ChannelCounterparty::new(PortId::default(), Some(ChannelId::default())), - vec![ConnectionId::new(0)], - ChannelVersion::default(), - ) - .unwrap(), - ); - - let msg_chan_close_init = - MsgChannelCloseInit::try_from(get_dummy_raw_msg_chan_close_init()).unwrap(); - - dispatch( - &mut ctx, - &mut router, - MsgEnvelope::Channel(ChannelMsg::CloseInit(msg_chan_close_init)), - ) - .unwrap(); - - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::CloseInitChannel(_))); - } - - #[test] - fn test_chan_close_confirm_event() { - let (ctx, mut router) = get_channel_events_ctx_router(); - let mut ctx = ctx.with_channel( - PortId::default(), - ChannelId::default(), - ChannelEnd::new( - ChannelState::Open, - ChannelOrder::Unordered, - ChannelCounterparty::new(PortId::default(), Some(ChannelId::default())), - vec![ConnectionId::new(0)], - ChannelVersion::default(), - ) - .unwrap(), - ); - - let msg_chan_close_confirm = - MsgChannelCloseConfirm::try_from(get_dummy_raw_msg_chan_close_confirm(1)).unwrap(); - - dispatch( - &mut ctx, - &mut router, - MsgEnvelope::Channel(ChannelMsg::CloseConfirm(msg_chan_close_confirm)), - ) - .unwrap(); - - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::CloseConfirmChannel(_))); - } -} diff --git a/crates/ibc/src/core/ics02_client/events.rs b/crates/ibc/src/core/ics02_client/events.rs index 8b9f027cb..e55bb9017 100644 --- a/crates/ibc/src/core/ics02_client/events.rs +++ b/crates/ibc/src/core/ics02_client/events.rs @@ -423,12 +423,11 @@ mod tests { use std::str::FromStr; use ibc_proto::google::protobuf::Any; + use ibc_testkit::utils::clients::mock::dummy_new_mock_header; use prost::Message; use tendermint::abci::Event as AbciEvent; use super::*; - use crate::core::timestamp::Timestamp; - use crate::mock::header::MockHeader; #[test] fn ibc_to_abci_client_events() { @@ -444,9 +443,7 @@ mod tests { let client_id = ClientId::new(client_type.clone(), 0).unwrap(); let consensus_height = Height::new(0, 5).unwrap(); let consensus_heights = vec![Height::new(0, 5).unwrap(), Height::new(0, 7).unwrap()]; - let header: Any = MockHeader::new(consensus_height) - .with_timestamp(Timestamp::none()) - .into(); + let header: Any = dummy_new_mock_header(5).into(); let expected_keys = vec![ "client_id", "client_type", diff --git a/crates/ibc/src/core/ics02_client/handler/create_client.rs b/crates/ibc/src/core/ics02_client/handler/create_client.rs index 0f8a9b143..342fefb16 100644 --- a/crates/ibc/src/core/ics02_client/handler/create_client.rs +++ b/crates/ibc/src/core/ics02_client/handler/create_client.rs @@ -93,99 +93,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use test_log::test; - - use super::*; - use crate::clients::ics07_tendermint::client_state::ClientState as TmClientState; - use crate::clients::ics07_tendermint::client_type as tm_client_type; - use crate::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; - use crate::clients::ics07_tendermint::header::test_util::get_dummy_tendermint_header; - use crate::core::ics02_client::handler::create_client::{execute, validate}; - use crate::core::ics02_client::msgs::create_client::MsgCreateClient; - use crate::core::ics24_host::identifier::ClientId; - use crate::mock::client_state::{client_type as mock_client_type, MockClientState}; - use crate::mock::consensus_state::MockConsensusState; - use crate::mock::context::MockContext; - use crate::mock::header::MockHeader; - use crate::test_utils::get_dummy_account_id; - use crate::Height; - - #[test] - fn test_create_client_ok() { - let mut ctx = MockContext::default(); - let signer = get_dummy_account_id(); - let height = Height::new(0, 42).unwrap(); - - let msg = MsgCreateClient::new( - MockClientState::new(MockHeader::new(height)).into(), - MockConsensusState::new(MockHeader::new(height)).into(), - signer, - ); - - let client_type = mock_client_type(); - - let client_id = { - let id_counter = ctx.client_counter().unwrap(); - ClientId::new(client_type.clone(), id_counter).unwrap() - }; - - let res = validate(&ctx, msg.clone()); - - assert!(res.is_ok(), "validation happy path"); - - let res = execute(&mut ctx, msg.clone()); - - assert!(res.is_ok(), "execution happy path"); - - assert_eq!(ctx.client_counter().unwrap(), 1); - - let expected_client_state = ctx.decode_client_state(msg.client_state).unwrap(); - - assert_eq!(expected_client_state.client_type(), client_type); - - assert_eq!(ctx.client_state(&client_id).unwrap(), expected_client_state); - } - - #[test] - fn test_tm_create_client_ok() { - let signer = get_dummy_account_id(); - - let mut ctx = MockContext::default(); - - let tm_header = get_dummy_tendermint_header(); - - let tm_client_state = TmClientState::new_dummy_from_header(tm_header.clone()).into(); - - let client_type = tm_client_type(); - - let client_id = { - let id_counter = ctx.client_counter().unwrap(); - ClientId::new(client_type.clone(), id_counter).unwrap() - }; - - let msg = MsgCreateClient::new( - tm_client_state, - TmConsensusState::try_from(tm_header).unwrap().into(), - signer, - ); - - let res = validate(&ctx, msg.clone()); - - assert!(res.is_ok(), "tendermint client validation happy path"); - - let res = execute(&mut ctx, msg.clone()); - - assert!(res.is_ok(), "tendermint client execution happy path"); - - assert_eq!(ctx.client_counter().unwrap(), 1); - - let expected_client_state = ctx.decode_client_state(msg.client_state).unwrap(); - - assert_eq!(expected_client_state.client_type(), client_type); - - assert_eq!(ctx.client_state(&client_id).unwrap(), expected_client_state); - } -} diff --git a/crates/ibc/src/core/ics02_client/handler/update_client.rs b/crates/ibc/src/core/ics02_client/handler/update_client.rs index 47e4cdd60..9227495a0 100644 --- a/crates/ibc/src/core/ics02_client/handler/update_client.rs +++ b/crates/ibc/src/core/ics02_client/handler/update_client.rs @@ -118,947 +118,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use core::str::FromStr; - use core::time::Duration; - - use ibc_proto::google::protobuf::Any; - use ibc_proto::ibc::lightclients::tendermint::v1::{ClientState as RawTmClientState, Fraction}; - use tendermint_testgen::Validator as TestgenValidator; - use test_log::test; - - use super::*; - use crate::clients::ics07_tendermint::client_state::ClientState as TmClientState; - use crate::clients::ics07_tendermint::client_type as tm_client_type; - use crate::clients::ics07_tendermint::header::Header as TmHeader; - use crate::clients::ics07_tendermint::misbehaviour::Misbehaviour as TmMisbehaviour; - use crate::core::events::IbcEvent; - use crate::core::ics02_client::client_type::ClientType; - use crate::core::ics02_client::handler::update_client::{execute, validate}; - use crate::core::ics02_client::msgs::misbehaviour::MsgSubmitMisbehaviour; - use crate::core::ics02_client::msgs::update_client::MsgUpdateClient; - use crate::core::ics02_client::ClientValidationContext; - use crate::core::ics23_commitment::specs::ProofSpecs; - use crate::core::ics24_host::identifier::{ChainId, ClientId}; - use crate::core::ics24_host::path::ClientConsensusStatePath; - use crate::core::timestamp::Timestamp; - use crate::mock::client_state::{client_type as mock_client_type, MockClientState}; - use crate::mock::context::{ - AnyConsensusState, MockClientConfig, MockContext, MockContextConfig, - }; - use crate::mock::header::MockHeader; - use crate::mock::host::{HostBlock, HostType}; - use crate::mock::misbehaviour::Misbehaviour as MockMisbehaviour; - use crate::test_utils::get_dummy_account_id; - use crate::{downcast, Height}; - - #[test] - fn test_update_client_ok() { - let client_id = ClientId::default(); - let signer = get_dummy_account_id(); - - let timestamp = Timestamp::now(); - - let mut ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); - let height = Height::new(0, 46).unwrap(); - let msg = MsgUpdateClient { - client_id, - client_message: MockHeader::new(height).with_timestamp(timestamp).into(), - signer, - }; - - let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - - assert!(res.is_ok(), "validation happy path"); - - let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - assert!(res.is_ok(), "execution happy path"); - - assert_eq!( - ctx.client_state(&msg.client_id).unwrap(), - MockClientState::new(MockHeader::new(height).with_timestamp(timestamp)).into() - ); - } - - /// Tests that the Tendermint client consensus state pruning logic - /// functions correctly. - /// - /// This test sets up a MockContext with host height 1 and a trusting - /// period of 3 seconds. It then advances the state of the MockContext - /// by 2 heights, and thus 6 seconds, due to the DEFAULT_BLOCK_TIME_SECS - /// constant being set to 3 seconds. At this point, the chain is at height - /// 3. Any consensus states associated with a block more than 3 seconds - /// in the past should be expired and pruned from the IBC store. The test - /// thus checks that the consensus state at height 1 is not contained in - /// the store. It also checks that the consensus state at height 2 is - /// contained in the store and has not expired. - #[test] - fn test_consensus_state_pruning() { - let chain_id = ChainId::new("mockgaiaA-1").unwrap(); - - let client_height = Height::new(1, 1).unwrap(); - - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - - let mut ctx = MockContextConfig::builder() - .host_id(chain_id.clone()) - .host_type(HostType::SyntheticTendermint) - .latest_height(client_height) - .latest_timestamp(Timestamp::now()) - .max_history_size(u64::MAX) - .build() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id.clone()) - .client_id(client_id.clone()) - .client_state_height(client_height) - .client_type(tm_client_type()) - .trusting_period(Duration::from_secs(3)) - .build(), - ); - - let start_host_timestamp = ctx.host_timestamp().unwrap(); - - // Move the chain forward by 2 blocks to pass the trusting period. - for _ in 1..=2 { - let signer = get_dummy_account_id(); - - let update_height = ctx.latest_height(); - - ctx.advance_host_chain_height(); - - let mut block = ctx.host_block(&update_height).unwrap().clone(); - - block.set_trusted_height(client_height); - - let msg = MsgUpdateClient { - client_id: client_id.clone(), - client_message: block.clone().into(), - signer, - }; - - let _ = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - let _ = execute(&mut ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - } - - // Check that latest expired consensus state is pruned. - let expired_height = Height::new(1, 1).unwrap(); - let client_cons_state_path = ClientConsensusStatePath::new(&client_id, &expired_height); - assert!(ctx - .client_update_height(&client_id, &expired_height) - .is_err()); - assert!(ctx.client_update_time(&client_id, &expired_height).is_err()); - assert!(ctx.consensus_state(&client_cons_state_path).is_err()); - - // Check that latest valid consensus state exists. - let earliest_valid_height = Height::new(1, 2).unwrap(); - let client_cons_state_path = - ClientConsensusStatePath::new(&client_id, &earliest_valid_height); - - assert!(ctx - .client_update_height(&client_id, &earliest_valid_height) - .is_ok()); - assert!(ctx - .client_update_time(&client_id, &earliest_valid_height) - .is_ok()); - assert!(ctx.consensus_state(&client_cons_state_path).is_ok()); - - let end_host_timestamp = ctx.host_timestamp().unwrap(); - assert_eq!( - end_host_timestamp, - (start_host_timestamp + Duration::from_secs(6)).unwrap() - ); - } - - #[test] - fn test_update_nonexisting_client() { - let client_id = ClientId::from_str("mockclient1").unwrap(); - let signer = get_dummy_account_id(); - - let ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); - - let msg = MsgUpdateClient { - client_id: ClientId::from_str("nonexistingclient").unwrap(), - client_message: MockHeader::new(Height::new(0, 46).unwrap()).into(), - signer, - }; - - let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg)); - - assert!(res.is_err()); - } - - #[test] - fn test_update_synthetic_tendermint_client_adjacent_ok() { - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - let client_height = Height::new(1, 20).unwrap(); - let update_height = Height::new(1, 21).unwrap(); - let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - - let mut ctx = MockContext::new( - ChainId::new("mockgaiaA-1").unwrap(), - HostType::Mock, - 5, - Height::new(1, 1).unwrap(), - ) - .with_client_parametrized_with_chain_id( - chain_id_b.clone(), - &client_id, - client_height, - Some(tm_client_type()), // The target host chain (B) is synthetic TM. - Some(client_height), - ); - - let ctx_b = MockContext::new(chain_id_b, HostType::SyntheticTendermint, 5, update_height); - - let signer = get_dummy_account_id(); - - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); - block.set_trusted_height(client_height); - - let latest_header_height = block.height(); - let msg = MsgUpdateClient { - client_id, - client_message: block.into(), - signer, - }; - - let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - assert!(res.is_ok()); - - let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - assert!(res.is_ok(), "result: {res:?}"); - - let client_state = ctx.client_state(&msg.client_id).unwrap(); - assert!(client_state - .status(&ctx, &msg.client_id) - .unwrap() - .is_active()); - assert_eq!(client_state.latest_height(), latest_header_height); - } - - #[test] - fn test_update_synthetic_tendermint_client_validator_change_ok() { - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - let client_height = Height::new(1, 20).unwrap(); - let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - - let mut ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) - .latest_height(Height::new(1, 1).unwrap()) - .build() - .with_client_config( - // client state initialized with client_height, and - // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .client_state_height(client_height) - .client_type(tm_client_type()) - .build(), - ); - - let ctx_b_val_history = vec![ - // First two validator sets are default at client creation - // - // validator set of height-20 - vec![ - TestgenValidator::new("1").voting_power(50), - TestgenValidator::new("2").voting_power(50), - ], - // validator set of height-21 - vec![ - TestgenValidator::new("1").voting_power(50), - TestgenValidator::new("2").voting_power(50), - ], - // validator set of height-22 - vec![ - TestgenValidator::new("1").voting_power(30), - TestgenValidator::new("2").voting_power(70), - ], - // validator set of height-23 - vec![ - TestgenValidator::new("1").voting_power(20), - TestgenValidator::new("2").voting_power(80), - ], - ]; - - let update_height = client_height.add(ctx_b_val_history.len() as u64 - 2); - - let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) - .host_type(HostType::SyntheticTendermint) - .latest_height(update_height) - .max_history_size(ctx_b_val_history.len() as u64 - 1) - .validator_set_history(ctx_b_val_history) - .build(); - - let signer = get_dummy_account_id(); - - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); - block.set_trusted_height(client_height); - - let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") { - HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(), - _ => panic!("unexpected host block type"), - }; - - block.set_trusted_next_validators_set(trusted_next_validator_set); - - let latest_header_height = block.height(); - let msg = MsgUpdateClient { - client_id, - client_message: block.into(), - signer, - }; - - let res = validate(&ctx_a, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - assert!(res.is_ok()); - - let res = execute( - &mut ctx_a, - MsgUpdateOrMisbehaviour::UpdateClient(msg.clone()), - ); - assert!(res.is_ok(), "result: {res:?}"); - - let client_state = ctx_a.client_state(&msg.client_id).unwrap(); - assert!(client_state - .status(&ctx_a, &msg.client_id) - .unwrap() - .is_active()); - assert_eq!(client_state.latest_height(), latest_header_height); - } - - #[test] - fn test_update_synthetic_tendermint_client_validator_change_fail() { - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - let client_height = Height::new(1, 20).unwrap(); - let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - - let ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) - .latest_height(Height::new(1, 1).unwrap()) - .build() - .with_client_config( - // client state initialized with client_height, and - // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .client_state_height(client_height) - .client_type(tm_client_type()) - .build(), - ); - - let ctx_b_val_history = vec![ - // First two validator sets are default at client creation - // - // validator set of height-20 - vec![ - TestgenValidator::new("1").voting_power(50), - TestgenValidator::new("2").voting_power(50), - ], - // incorrect next validator set for height-20 - // validator set of height-21 - vec![ - TestgenValidator::new("1").voting_power(45), - TestgenValidator::new("2").voting_power(55), - ], - // validator set of height-22 - vec![ - TestgenValidator::new("1").voting_power(30), - TestgenValidator::new("2").voting_power(70), - ], - // validator set of height-23 - vec![ - TestgenValidator::new("1").voting_power(20), - TestgenValidator::new("2").voting_power(80), - ], - ]; - - let update_height = client_height.add(ctx_b_val_history.len() as u64 - 2); - - let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) - .host_type(HostType::SyntheticTendermint) - .latest_height(update_height) - .max_history_size(ctx_b_val_history.len() as u64 - 1) - .validator_set_history(ctx_b_val_history) - .build(); - - let signer = get_dummy_account_id(); - - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); - block.set_trusted_height(client_height); - - let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") { - HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(), - _ => panic!("unexpected host block type"), - }; - - block.set_trusted_next_validators_set(trusted_next_validator_set); - - let msg = MsgUpdateClient { - client_id, - client_message: block.into(), - signer, - }; - - let res = validate(&ctx_a, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - assert!(res.is_err()); - } - - #[test] - fn test_update_synthetic_tendermint_client_non_adjacent_ok() { - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - let client_height = Height::new(1, 20).unwrap(); - let update_height = Height::new(1, 21).unwrap(); - let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - - let mut ctx = MockContext::new( - ChainId::new("mockgaiaA-1").unwrap(), - HostType::Mock, - 5, - Height::new(1, 1).unwrap(), - ) - .with_client_parametrized_history_with_chain_id( - chain_id_b.clone(), - &client_id, - client_height, - Some(tm_client_type()), // The target host chain (B) is synthetic TM. - Some(client_height), - ); - - let ctx_b = MockContext::new(chain_id_b, HostType::SyntheticTendermint, 5, update_height); - - let signer = get_dummy_account_id(); - - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); - let trusted_height = client_height.clone().sub(1).unwrap(); - block.set_trusted_height(trusted_height); - - let latest_header_height = block.height(); - let msg = MsgUpdateClient { - client_id, - client_message: block.into(), - signer, - }; - - let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - assert!(res.is_ok()); - - let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - assert!(res.is_ok(), "result: {res:?}"); - - let client_state = ctx.client_state(&msg.client_id).unwrap(); - assert!(client_state - .status(&ctx, &msg.client_id) - .unwrap() - .is_active()); - assert_eq!(client_state.latest_height(), latest_header_height); - } - - #[test] - fn test_update_synthetic_tendermint_client_duplicate_ok() { - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - let client_height = Height::new(1, 20).unwrap(); - - let ctx_a_chain_id = ChainId::new("mockgaiaA-1").unwrap(); - let ctx_b_chain_id = ChainId::new("mockgaiaB-1").unwrap(); - let start_height = Height::new(1, 11).unwrap(); - - let mut ctx_a = MockContext::new(ctx_a_chain_id, HostType::Mock, 5, start_height) - .with_client_parametrized_with_chain_id( - ctx_b_chain_id.clone(), - &client_id, - client_height, - Some(tm_client_type()), // The target host chain (B) is synthetic TM. - Some(start_height), - ); - - let ctx_b = MockContext::new( - ctx_b_chain_id, - HostType::SyntheticTendermint, - 5, - client_height, - ); - - let signer = get_dummy_account_id(); - - let block = ctx_b.host_block(&client_height).unwrap().clone(); - - // Update the trusted height of the header to point to the previous height - // (`start_height` in this case). - // - // Note: The current MockContext interface doesn't allow us to - // do this without a major redesign. - let block = match block { - HostBlock::SyntheticTendermint(mut theader) => { - // current problem: the timestamp of the new header doesn't match the timestamp of - // the stored consensus state. If we hack them to match, then commit check fails. - // FIXME: figure out why they don't match. - theader.trusted_height = start_height; - - HostBlock::SyntheticTendermint(theader) - } - _ => block, - }; - - // Update the client height to `client_height` - // - // Note: The current MockContext interface doesn't allow us to - // do this without a major redesign. - { - // FIXME: idea: we need to update the light client with the latest block from - // chain B - let consensus_state: AnyConsensusState = block.clone().into(); - - let tm_block = downcast!(block.clone() => HostBlock::SyntheticTendermint).unwrap(); - - let chain_id = ChainId::from_str(tm_block.header().chain_id.as_str()).unwrap(); - - let client_state = { - #[allow(deprecated)] - let raw_client_state = RawTmClientState { - chain_id: chain_id.to_string(), - trust_level: Some(Fraction { - numerator: 1, - denominator: 3, - }), - trusting_period: Some(Duration::from_secs(64000).into()), - unbonding_period: Some(Duration::from_secs(128000).into()), - max_clock_drift: Some(Duration::from_millis(3000).into()), - latest_height: Some( - Height::new( - chain_id.revision_number(), - u64::from(tm_block.header().height), - ) - .unwrap() - .into(), - ), - proof_specs: ProofSpecs::default().into(), - upgrade_path: Default::default(), - frozen_height: None, - allow_update_after_expiry: false, - allow_update_after_misbehaviour: false, - }; - - let client_state = TmClientState::try_from(raw_client_state).unwrap(); - - client_state.into() - }; - - let mut ibc_store = ctx_a.ibc_store.lock(); - let client_record = ibc_store.clients.get_mut(&client_id).unwrap(); - - client_record - .consensus_states - .insert(client_height, consensus_state); - - client_record.client_state = Some(client_state); - } - - let latest_header_height = block.height(); - let msg = MsgUpdateClient { - client_id, - client_message: block.into(), - signer, - }; - - let res = validate(&ctx_a, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - assert!(res.is_ok(), "result: {res:?}"); - - let res = execute( - &mut ctx_a, - MsgUpdateOrMisbehaviour::UpdateClient(msg.clone()), - ); - assert!(res.is_ok(), "result: {res:?}"); - - let client_state = ctx_a.client_state(&msg.client_id).unwrap(); - assert!(client_state - .status(&ctx_a, &msg.client_id) - .unwrap() - .is_active()); - assert_eq!(client_state.latest_height(), latest_header_height); - assert_eq!(client_state, ctx_a.latest_client_states(&msg.client_id)); - } - - #[test] - fn test_update_synthetic_tendermint_client_lower_height() { - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - let client_height = Height::new(1, 20).unwrap(); - - let client_update_height = Height::new(1, 19).unwrap(); - - let chain_start_height = Height::new(1, 11).unwrap(); - - let ctx = MockContext::new( - ChainId::new("mockgaiaA-1").unwrap(), - HostType::Mock, - 5, - chain_start_height, - ) - .with_client_parametrized( - &client_id, - client_height, - Some(tm_client_type()), // The target host chain (B) is synthetic TM. - Some(client_height), - ); - - let ctx_b = MockContext::new( - ChainId::new("mockgaiaB-1").unwrap(), - HostType::SyntheticTendermint, - 5, - client_height, - ); - - let signer = get_dummy_account_id(); - - let block_ref = ctx_b.host_block(&client_update_height).unwrap(); - - let msg = MsgUpdateClient { - client_id, - client_message: block_ref.clone().into(), - signer, - }; - - let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg)); - assert!(res.is_err()); - } - - #[test] - fn test_update_client_events() { - let client_id = ClientId::default(); - let signer = get_dummy_account_id(); - - let timestamp = Timestamp::now(); - - let mut ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); - let height = Height::new(0, 46).unwrap(); - let header: Any = MockHeader::new(height).with_timestamp(timestamp).into(); - let msg = MsgUpdateClient { - client_id: client_id.clone(), - client_message: header.clone(), - signer, - }; - - let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg)); - assert!(res.is_ok()); - - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Client) - )); - let update_client_event = downcast!(&ctx.events[1] => IbcEvent::UpdateClient).unwrap(); - - assert_eq!(update_client_event.client_id(), &client_id); - assert_eq!(update_client_event.client_type(), &mock_client_type()); - assert_eq!(update_client_event.consensus_height(), &height); - assert_eq!(update_client_event.consensus_heights(), &vec![height]); - assert_eq!(update_client_event.header(), &header.encode_to_vec()); - } - - fn ensure_misbehaviour(ctx: &MockContext, client_id: &ClientId, client_type: &ClientType) { - let client_state = ctx.client_state(client_id).unwrap(); - - let status = client_state.status(ctx, client_id).unwrap(); - assert!(status.is_frozen(), "client_state status: {status}"); - - // check events - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Client), - )); - let misbehaviour_client_event = - downcast!(&ctx.events[1] => IbcEvent::ClientMisbehaviour).unwrap(); - assert_eq!(misbehaviour_client_event.client_id(), client_id); - assert_eq!(misbehaviour_client_event.client_type(), client_type); - } - - /// Tests misbehaviour handling for the mock client. - /// Misbehaviour evidence consists of identical headers - mock misbehaviour handler considers it - /// a valid proof of misbehaviour - #[test] - fn test_misbehaviour_client_ok() { - let client_id = ClientId::default(); - let timestamp = Timestamp::now(); - let height = Height::new(0, 46).unwrap(); - let msg = MsgSubmitMisbehaviour { - client_id: client_id.clone(), - misbehaviour: MockMisbehaviour { - client_id: client_id.clone(), - header1: MockHeader::new(height).with_timestamp(timestamp), - header2: MockHeader::new(height).with_timestamp(timestamp), - } - .into(), - signer: get_dummy_account_id(), - }; - - let mut ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); - - let res = validate(&ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg.clone())); - assert!(res.is_ok()); - let res = execute(&mut ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg)); - assert!(res.is_ok()); - - ensure_misbehaviour(&ctx, &client_id, &mock_client_type()); - } - - /// Tests misbehaviour handling failure for a non-existent client - #[test] - fn test_misbehaviour_nonexisting_client() { - let client_id = ClientId::from_str("mockclient1").unwrap(); - let height = Height::new(0, 46).unwrap(); - let msg = MsgSubmitMisbehaviour { - client_id: ClientId::from_str("nonexistingclient").unwrap(), - misbehaviour: MockMisbehaviour { - client_id: client_id.clone(), - header1: MockHeader::new(height), - header2: MockHeader::new(height), - } - .into(), - signer: get_dummy_account_id(), - }; - - let ctx = MockContext::default().with_client(&client_id, Height::new(0, 42).unwrap()); - let res = validate(&ctx, MsgUpdateOrMisbehaviour::Misbehaviour(msg)); - assert!(res.is_err()); - } - - /// Tests misbehaviour handling for the synthetic Tendermint client. - /// Misbehaviour evidence consists of equivocal headers. - #[test] - fn test_misbehaviour_synthetic_tendermint_equivocation() { - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - let client_height = Height::new(1, 20).unwrap(); - let misbehaviour_height = Height::new(1, 21).unwrap(); - let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - - // Create a mock context for chain-A with a synthetic tendermint light client for chain-B - let mut ctx_a = MockContext::new( - ChainId::new("mockgaiaA-1").unwrap(), - HostType::Mock, - 5, - Height::new(1, 1).unwrap(), - ) - .with_client_parametrized_with_chain_id( - chain_id_b.clone(), - &client_id, - client_height, - Some(tm_client_type()), - Some(client_height), - ); - - // Create a mock context for chain-B - let ctx_b = MockContext::new( - chain_id_b.clone(), - HostType::SyntheticTendermint, - 5, - misbehaviour_height, - ); - - // Get chain-B's header at `misbehaviour_height` - let header1: TmHeader = { - let mut block = ctx_b.host_block(&misbehaviour_height).unwrap().clone(); - block.set_trusted_height(client_height); - block.try_into_tm_block().unwrap().into() - }; - - // Generate an equivocal header for chain-B at `misbehaviour_height` - let header2 = { - let mut tm_block = HostBlock::generate_tm_block( - chain_id_b, - misbehaviour_height.revision_height(), - Timestamp::now(), - ); - tm_block.trusted_height = client_height; - tm_block.into() - }; - - let msg = MsgSubmitMisbehaviour { - client_id: client_id.clone(), - misbehaviour: TmMisbehaviour::new(client_id.clone(), header1, header2).into(), - signer: get_dummy_account_id(), - }; - - let res = validate(&ctx_a, MsgUpdateOrMisbehaviour::Misbehaviour(msg.clone())); - assert!(res.is_ok()); - let res = execute(&mut ctx_a, MsgUpdateOrMisbehaviour::Misbehaviour(msg)); - assert!(res.is_ok()); - ensure_misbehaviour(&ctx_a, &client_id, &tm_client_type()); - } - - #[test] - fn test_misbehaviour_synthetic_tendermint_bft_time() { - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - let client_height = Height::new(1, 20).unwrap(); - let misbehaviour_height = Height::new(1, 21).unwrap(); - let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - - // Create a mock context for chain-A with a synthetic tendermint light client for chain-B - let mut ctx_a = MockContext::new( - ChainId::new("mockgaiaA-1").unwrap(), - HostType::Mock, - 5, - Height::new(1, 1).unwrap(), - ) - .with_client_parametrized_with_chain_id( - chain_id_b.clone(), - &client_id, - client_height, - Some(tm_client_type()), - Some(client_height), - ); - - // Generate `header1` for chain-B - let header1 = { - let mut tm_block = HostBlock::generate_tm_block( - chain_id_b.clone(), - misbehaviour_height.revision_height(), - Timestamp::now(), - ); - tm_block.trusted_height = client_height; - tm_block - }; - - // Generate `header2` for chain-B which is identical to `header1` but with a conflicting - // timestamp - let header2 = { - let timestamp = - Timestamp::from_nanoseconds(Timestamp::now().nanoseconds() + 1_000_000_000) - .unwrap(); - let mut tm_block = HostBlock::generate_tm_block( - chain_id_b, - misbehaviour_height.revision_height(), - timestamp, - ); - tm_block.trusted_height = client_height; - tm_block - }; - - let msg = MsgSubmitMisbehaviour { - client_id: client_id.clone(), - misbehaviour: TmMisbehaviour::new(client_id.clone(), header1.into(), header2.into()) - .into(), - signer: get_dummy_account_id(), - }; - - let res = validate(&ctx_a, MsgUpdateOrMisbehaviour::Misbehaviour(msg.clone())); - assert!(res.is_ok()); - let res = execute(&mut ctx_a, MsgUpdateOrMisbehaviour::Misbehaviour(msg)); - assert!(res.is_ok()); - ensure_misbehaviour(&ctx_a, &client_id, &tm_client_type()); - } - - #[test] - fn test_expired_client() { - let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - - let update_height = Height::new(1, 21).unwrap(); - let client_height = update_height.sub(3).unwrap(); - - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - - let timestamp = Timestamp::now(); - - let trusting_period = Duration::from_secs(64); - - let mut ctx = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) - .latest_height(Height::new(1, 1).unwrap()) - .latest_timestamp(timestamp) - .build() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .client_state_height(client_height) - .client_type(tm_client_type()) - .latest_timestamp(timestamp) - .trusting_period(trusting_period) - .build(), - ); - - while ctx.host_timestamp().expect("no error") - < (timestamp + trusting_period).expect("no error") - { - ctx.advance_host_chain_height(); - } - - let client_state = ctx.client_state(&client_id).unwrap(); - - assert!(client_state.status(&ctx, &client_id).unwrap().is_expired()); - } - - #[test] - fn test_client_update_max_clock_drift() { - let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - - let client_height = Height::new(1, 20).unwrap(); - - let client_id = ClientId::new(tm_client_type(), 0).unwrap(); - - let timestamp = Timestamp::now(); - - let max_clock_drift = Duration::from_secs(64); - - let ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) - .latest_height(Height::new(1, 1).unwrap()) - .latest_timestamp(timestamp) - .build() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .client_state_height(client_height) - .client_type(tm_client_type()) - .latest_timestamp(timestamp) - .max_clock_drift(max_clock_drift) - .build(), - ); - - let mut ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) - .host_type(HostType::SyntheticTendermint) - .latest_height(client_height) - .latest_timestamp(timestamp) - .max_history_size(u64::MAX) - .build(); - - while ctx_b.host_timestamp().expect("no error") - < (ctx_a.host_timestamp().expect("no error") + max_clock_drift).expect("no error") - { - ctx_b.advance_host_chain_height(); - } - - // include current block - ctx_b.advance_host_chain_height(); - - let update_height = ctx_b.latest_height(); - - let signer = get_dummy_account_id(); - - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); - block.set_trusted_height(client_height); - - let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") { - HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(), - _ => panic!("unexpected host block type"), - }; - - block.set_trusted_next_validators_set(trusted_next_validator_set); - - let msg = MsgUpdateClient { - client_id, - client_message: block.clone().into(), - signer, - }; - - let res = validate(&ctx_a, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone())); - assert!(res.is_err()); - } -} diff --git a/crates/ibc/src/core/ics02_client/handler/upgrade_client.rs b/crates/ibc/src/core/ics02_client/handler/upgrade_client.rs index b964e2b5c..de5ebfbbd 100644 --- a/crates/ibc/src/core/ics02_client/handler/upgrade_client.rs +++ b/crates/ibc/src/core/ics02_client/handler/upgrade_client.rs @@ -81,159 +81,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::clients::ics07_tendermint::client_state::ClientState as TmClientState; - use crate::clients::ics07_tendermint::client_type; - use crate::clients::ics07_tendermint::header::test_util::get_dummy_tendermint_header; - use crate::core::ics02_client::error::UpgradeClientError; - use crate::core::ics03_connection::handler::test_util::{Expect, Fixture}; - use crate::core::ics24_host::identifier::ClientId; - use crate::mock::client_state::client_type as mock_client_type; - use crate::mock::context::{AnyClientState, AnyConsensusState, MockContext}; - use crate::{downcast, Height}; - - enum Ctx { - Default, - WithClient, - } - - enum Msg { - Default, - LowUpgradeHeight, - UnknownUpgradedClientStateType, - } - - fn msg_upgrade_client_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { - let client_id = ClientId::new(mock_client_type(), 0).unwrap(); - - let ctx_default = MockContext::default(); - let ctx_with_client = ctx_default - .clone() - .with_client(&client_id, Height::new(0, 42).unwrap()); - let ctx = match ctx_variant { - Ctx::Default => ctx_default, - Ctx::WithClient => ctx_with_client, - }; - - let upgrade_height = Height::new(1, 26).unwrap(); - let msg_default = MsgUpgradeClient::new_dummy(upgrade_height); - - let low_upgrade_height = Height::new(0, 26).unwrap(); - let msg_with_low_upgrade_height = MsgUpgradeClient::new_dummy(low_upgrade_height); - - let msg_with_unknown_upgraded_cs = MsgUpgradeClient { - upgraded_client_state: TmClientState::new_dummy_from_header( - get_dummy_tendermint_header(), - ) - .into(), - ..msg_default.clone() - }; - - let msg = match msg_variant { - Msg::Default => msg_default, - Msg::LowUpgradeHeight => msg_with_low_upgrade_height, - Msg::UnknownUpgradedClientStateType => msg_with_unknown_upgraded_cs, - }; - - Fixture { ctx, msg } - } - - fn upgrade_client_validate(fxt: &Fixture, expect: Expect) { - let Fixture { ctx, msg } = fxt; - let res = validate(ctx, msg.clone()); - let err_msg = fxt.generate_error_msg(&expect, "validation", &res); - - match expect { - Expect::Failure(_) => { - assert!(res.is_err(), "{err_msg}"); - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}"); - } - }; - } - - fn upgrade_client_execute(fxt: &mut Fixture, expect: Expect) { - let res = execute(&mut fxt.ctx, fxt.msg.clone()); - let err_msg = fxt.generate_error_msg(&expect, "execution", &res); - match expect { - Expect::Failure(err) => { - assert!(res.is_err(), "{err_msg}"); - assert_eq!( - core::mem::discriminant(res.as_ref().unwrap_err()), - core::mem::discriminant(&err.unwrap()) - ); - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}"); - assert!(matches!( - fxt.ctx.events[0], - IbcEvent::Message(MessageEvent::Client) - )); - let upgrade_client_event = - downcast!(&fxt.ctx.events[1] => IbcEvent::UpgradeClient).unwrap(); - let plan_height = Height::new(1, 26).unwrap(); - - assert_eq!(upgrade_client_event.client_id(), &fxt.msg.client_id); - assert_eq!(upgrade_client_event.client_type(), &mock_client_type()); - assert_eq!(upgrade_client_event.consensus_height(), &plan_height); - - let client_state = fxt.ctx.client_state(&fxt.msg.client_id).unwrap(); - let msg_client_state: AnyClientState = - fxt.msg.upgraded_client_state.clone().try_into().unwrap(); - assert_eq!(client_state, msg_client_state); - - let consensus_state = fxt - .ctx - .consensus_state(&ClientConsensusStatePath::new( - &fxt.msg.client_id, - &plan_height, - )) - .unwrap(); - let msg_consensus_state: AnyConsensusState = - fxt.msg.upgraded_consensus_state.clone().try_into().unwrap(); - assert_eq!(consensus_state, msg_consensus_state); - } - }; - } - - #[test] - fn msg_upgrade_client_healthy() { - let mut fxt = msg_upgrade_client_fixture(Ctx::WithClient, Msg::Default); - upgrade_client_validate(&fxt, Expect::Success); - upgrade_client_execute(&mut fxt, Expect::Success); - } - - #[test] - fn upgrade_client_fail_nonexisting_client() { - let fxt = msg_upgrade_client_fixture(Ctx::Default, Msg::Default); - let expected_err = ContextError::ClientError(ClientError::ClientStateNotFound { - client_id: fxt.msg.client_id.clone(), - }); - upgrade_client_validate(&fxt, Expect::Failure(Some(expected_err))); - } - - #[test] - fn upgrade_client_fail_low_upgrade_height() { - let fxt: Fixture = - msg_upgrade_client_fixture(Ctx::WithClient, Msg::LowUpgradeHeight); - let expected_err: ClientError = UpgradeClientError::LowUpgradeHeight { - upgraded_height: Height::new(0, 26).unwrap(), - client_height: fxt.ctx.latest_height(), - } - .into(); - upgrade_client_validate(&fxt, Expect::Failure(Some(expected_err.into()))); - } - - #[test] - fn upgrade_client_fail_unknown_upgraded_client_state() { - let fxt = msg_upgrade_client_fixture(Ctx::WithClient, Msg::UnknownUpgradedClientStateType); - let expected_err = ContextError::ClientError(ClientError::UnknownClientStateType { - client_state_type: client_type().to_string(), - }); - upgrade_client_validate(&fxt, Expect::Failure(Some(expected_err))); - } -} diff --git a/crates/ibc/src/core/ics02_client/msgs/create_client.rs b/crates/ibc/src/core/ics02_client/msgs/create_client.rs index 495d8eea2..9e1e6af13 100644 --- a/crates/ibc/src/core/ics02_client/msgs/create_client.rs +++ b/crates/ibc/src/core/ics02_client/msgs/create_client.rs @@ -76,30 +76,17 @@ impl From for RawMsgCreateClient { mod tests { use ibc_proto::ibc::core::client::v1::MsgCreateClient as RawMsgCreateClient; + use ibc_testkit::utils::core::client::dummy_raw_msg_create_client; use test_log::test; - use crate::clients::ics07_tendermint::client_state::ClientState as TmClientState; - use crate::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; - use crate::clients::ics07_tendermint::header::test_util::get_dummy_tendermint_header; use crate::core::ics02_client::msgs::create_client::MsgCreateClient; - use crate::test_utils::get_dummy_account_id; #[test] fn msg_create_client_serialization() { - let signer = get_dummy_account_id(); - - let tm_header = get_dummy_tendermint_header(); - let tm_client_state = TmClientState::new_dummy_from_header(tm_header.clone()).into(); - - let msg = MsgCreateClient::new( - tm_client_state, - TmConsensusState::try_from(tm_header).unwrap().into(), - signer, - ); - - let raw = RawMsgCreateClient::from(msg.clone()); - let msg_back = MsgCreateClient::try_from(raw.clone()).unwrap(); - let raw_back = RawMsgCreateClient::from(msg_back.clone()); + let raw = dummy_raw_msg_create_client(); + let msg = MsgCreateClient::try_from(raw.clone()).unwrap(); + let raw_back = RawMsgCreateClient::from(msg.clone()); + let msg_back = MsgCreateClient::try_from(raw_back.clone()).unwrap(); assert_eq!(msg, msg_back); assert_eq!(raw, raw_back); } diff --git a/crates/ibc/src/core/ics02_client/msgs/update_client.rs b/crates/ibc/src/core/ics02_client/msgs/update_client.rs index 9efcbe32b..9c19e77d2 100644 --- a/crates/ibc/src/core/ics02_client/msgs/update_client.rs +++ b/crates/ibc/src/core/ics02_client/msgs/update_client.rs @@ -67,38 +67,19 @@ impl From for RawMsgUpdateClient { #[cfg(test)] mod tests { - use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::client::v1::MsgUpdateClient as RawMsgUpdateClient; + use ibc_testkit::utils::core::client::dummy_raw_msg_update_client; use test_log::test; use super::*; - use crate::clients::ics07_tendermint::header::test_util::get_dummy_ics07_header; use crate::core::ics02_client::msgs::MsgUpdateClient; - use crate::core::ics24_host::identifier::ClientId; - use crate::signer::Signer; - use crate::test_utils::get_dummy_account_id; - - impl MsgUpdateClient { - pub fn new(client_id: ClientId, client_message: Any, signer: Signer) -> Self { - MsgUpdateClient { - client_id, - client_message, - signer, - } - } - } #[test] fn msg_update_client_serialization() { - let client_id: ClientId = "tendermint".parse().unwrap(); - let signer = get_dummy_account_id(); - - let header = get_dummy_ics07_header(); - - let msg = MsgUpdateClient::new(client_id, header.into(), signer); - let raw = RawMsgUpdateClient::from(msg.clone()); - let msg_back = MsgUpdateClient::try_from(raw.clone()).unwrap(); - let raw_back = RawMsgUpdateClient::from(msg_back.clone()); + let raw = dummy_raw_msg_update_client(); + let msg = MsgUpdateClient::try_from(raw.clone()).unwrap(); + let raw_back = RawMsgUpdateClient::from(msg.clone()); + let msg_back = MsgUpdateClient::try_from(raw_back.clone()).unwrap(); assert_eq!(msg, msg_back); assert_eq!(raw, raw_back); } diff --git a/crates/ibc/src/core/ics02_client/msgs/upgrade_client.rs b/crates/ibc/src/core/ics02_client/msgs/upgrade_client.rs index d6dffcc67..baf363b38 100644 --- a/crates/ibc/src/core/ics02_client/msgs/upgrade_client.rs +++ b/crates/ibc/src/core/ics02_client/msgs/upgrade_client.rs @@ -97,60 +97,19 @@ impl TryFrom for MsgUpgradeClient { } } -#[cfg(test)] -pub mod test_util { - use super::*; - use crate::core::ics02_client::height::Height; - use crate::core::ics23_commitment::commitment::test_util::get_dummy_commitment_proof_bytes; - use crate::core::ics24_host::identifier::ClientId; - use crate::mock::client_state::{client_type as mock_client_type, MockClientState}; - use crate::mock::consensus_state::MockConsensusState; - use crate::mock::header::MockHeader; - use crate::test_utils::{get_dummy_account_id, get_dummy_bech32_account, get_dummy_proof}; - - /// Extends the implementation with additional helper methods. - impl MsgUpgradeClient { - pub fn new_dummy(upgrade_height: Height) -> Self { - MsgUpgradeClient { - client_id: ClientId::new(mock_client_type(), 0).unwrap(), - upgraded_client_state: MockClientState::new(MockHeader::new(upgrade_height)).into(), - upgraded_consensus_state: MockConsensusState::new(MockHeader::new(upgrade_height)) - .into(), - proof_upgrade_client: get_dummy_commitment_proof_bytes(), - proof_upgrade_consensus_state: get_dummy_commitment_proof_bytes(), - signer: get_dummy_account_id(), - } - } - - pub fn with_client_id(self, client_id: ClientId) -> Self { - MsgUpgradeClient { client_id, ..self } - } - } - - /// Returns a dummy `RawMsgUpgradeClient`, for testing only! - pub fn get_dummy_raw_msg_upgrade_client(upgrade_height: Height) -> RawMsgUpgradeClient { - RawMsgUpgradeClient { - client_id: mock_client_type().to_string(), - client_state: Some(MockClientState::new(MockHeader::new(upgrade_height)).into()), - consensus_state: Some(MockConsensusState::new(MockHeader::new(upgrade_height)).into()), - proof_upgrade_client: get_dummy_proof(), - proof_upgrade_consensus_state: get_dummy_proof(), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod tests { - use super::*; - use crate::core::ics02_client::height::Height; + use ibc_proto::ibc::core::client::v1::MsgUpgradeClient as RawMsgUpgradeClient; + use ibc_testkit::utils::core::client::dummy_raw_msg_upgrade_client; + + use crate::core::ics02_client::msgs::upgrade_client::MsgUpgradeClient; #[test] fn msg_upgrade_client_serialization() { - let msg = MsgUpgradeClient::new_dummy(Height::new(1, 1).unwrap()); - let raw: RawMsgUpgradeClient = RawMsgUpgradeClient::from(msg.clone()); - let msg_back = MsgUpgradeClient::try_from(raw.clone()).unwrap(); - let raw_back: RawMsgUpgradeClient = RawMsgUpgradeClient::from(msg_back.clone()); + let raw = dummy_raw_msg_upgrade_client(); + let msg = MsgUpgradeClient::try_from(raw.clone()).unwrap(); + let raw_back: RawMsgUpgradeClient = RawMsgUpgradeClient::from(msg.clone()); + let msg_back = MsgUpgradeClient::try_from(raw_back.clone()).unwrap(); assert_eq!(msg, msg_back); assert_eq!(raw, raw_back); } diff --git a/crates/ibc/src/core/ics03_connection/connection.rs b/crates/ibc/src/core/ics03_connection/connection.rs index 5b43fd48e..ae989a9a0 100644 --- a/crates/ibc/src/core/ics03_connection/connection.rs +++ b/crates/ibc/src/core/ics03_connection/connection.rs @@ -380,9 +380,9 @@ impl ConnectionEnd { #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] pub struct Counterparty { - client_id: ClientId, + pub client_id: ClientId, pub connection_id: Option, - prefix: CommitmentPrefix, + pub prefix: CommitmentPrefix, } impl Protobuf for Counterparty {} diff --git a/crates/ibc/src/core/ics03_connection/handler.rs b/crates/ibc/src/core/ics03_connection/handler.rs index a79a2d87b..20c34a37a 100644 --- a/crates/ibc/src/core/ics03_connection/handler.rs +++ b/crates/ibc/src/core/ics03_connection/handler.rs @@ -5,42 +5,3 @@ pub mod conn_open_ack; pub mod conn_open_confirm; pub mod conn_open_init; pub mod conn_open_try; - -#[cfg(test)] -pub mod test_util { - use alloc::format; - use core::fmt::Debug; - - use crate::core::ContextError; - use crate::mock::context::MockContext; - use crate::prelude::String; - - pub enum Expect { - Success, - Failure(Option), - } - - #[derive(Clone, Debug)] - pub struct Fixture { - pub ctx: MockContext, - pub msg: M, - } - - impl Fixture { - pub fn generate_error_msg( - &self, - expect: &Expect, - process: &str, - res: &Result<(), ContextError>, - ) -> String { - let base_error = match expect { - Expect::Success => "step failed!", - Expect::Failure(_) => "step passed but was supposed to fail!", - }; - format!( - "{process} {base_error} /n {res:?} /n {:?} /n {:?}", - &self.msg, &self.ctx - ) - } - } -} diff --git a/crates/ibc/src/core/ics03_connection/handler/conn_open_ack.rs b/crates/ibc/src/core/ics03_connection/handler/conn_open_ack.rs index 285d1299b..cdc137d66 100644 --- a/crates/ibc/src/core/ics03_connection/handler/conn_open_ack.rs +++ b/crates/ibc/src/core/ics03_connection/handler/conn_open_ack.rs @@ -202,189 +202,3 @@ impl LocalVars { self.conn_end_on_a.counterparty().client_id() } } - -#[cfg(test)] -mod tests { - use core::str::FromStr; - - use test_log::test; - - use super::*; - use crate::core::events::IbcEvent; - use crate::core::ics02_client::height::Height; - use crate::core::ics03_connection::connection::{ConnectionEnd, Counterparty, State}; - use crate::core::ics03_connection::handler::test_util::{Expect, Fixture}; - use crate::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck; - use crate::core::ics23_commitment::commitment::CommitmentPrefix; - use crate::core::ics24_host::identifier::{ChainId, ClientId}; - use crate::core::timestamp::ZERO_DURATION; - use crate::mock::context::MockContext; - use crate::mock::host::HostType; - - enum Ctx { - New, - NewWithConnection, - NewWithConnectionEndOpen, - DefaultWithConnection, - } - - fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { - let msg = MsgConnectionOpenAck::new_dummy(10, 10); - - // Client parameters -- identifier and correct height (matching the proof height) - let client_id = ClientId::from_str("mock_clientid").unwrap(); - let proof_height = msg.proofs_height_on_b; - let conn_id = msg.conn_id_on_a.clone(); - - // Parametrize the host chain to have a height at least as recent as the - // the height of the proofs in the Ack msg. - let latest_height = proof_height.increment(); - let max_history_size = 5; - - // A connection end that will exercise the successful path. - let default_conn_end = ConnectionEnd::new( - State::Init, - client_id.clone(), - Counterparty::new( - client_id.clone(), - Some(msg.conn_id_on_b.clone()), - CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), - ), - vec![msg.version.clone()], - ZERO_DURATION, - ) - .unwrap(); - - // A connection end with incorrect state `Open`; will be part of the context. - let mut conn_end_open = default_conn_end.clone(); - conn_end_open.set_state(State::Open); // incorrect field - - let ctx_default = MockContext::default(); - let ctx_new = MockContext::new( - ChainId::new(&format!("mockgaia-{}", latest_height.revision_number())).unwrap(), - HostType::Mock, - max_history_size, - latest_height, - ); - let ctx = match ctx { - Ctx::New => ctx_new, - Ctx::NewWithConnection => ctx_new - .with_client(&client_id, proof_height) - .with_connection(conn_id, default_conn_end), - Ctx::DefaultWithConnection => ctx_default - .with_client(&client_id, proof_height) - .with_connection(conn_id, default_conn_end), - Ctx::NewWithConnectionEndOpen => ctx_new - .with_client(&client_id, proof_height) - .with_connection(conn_id, conn_end_open), - }; - - Fixture { ctx, msg } - } - - fn conn_open_ack_validate(fxt: &Fixture, expect: Expect) { - let res = validate(&fxt.ctx, fxt.msg.clone()); - let err_msg = fxt.generate_error_msg(&expect, "validation", &res); - match expect { - Expect::Failure(err) => { - assert!(res.is_err(), "{err_msg}"); - assert_eq!( - core::mem::discriminant(res.as_ref().unwrap_err()), - core::mem::discriminant(&err.unwrap()) - ); - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}"); - return; - } - }; - let right_connection_id = fxt.msg.conn_id_on_a.clone(); - let cons_state_height = fxt.msg.consensus_height_of_a_on_b; - match res.unwrap_err() { - ContextError::ConnectionError(ConnectionError::ConnectionNotFound { - connection_id, - }) => { - assert_eq!(connection_id, right_connection_id) - } - ContextError::ConnectionError(ConnectionError::InvalidConsensusHeight { - target_height, - current_height: _, - }) => { - assert_eq!(cons_state_height, target_height); - } - ContextError::ConnectionError(ConnectionError::InvalidState { - expected: _, - actual: _, - }) => {} - _ => unreachable!(), - } - } - - fn conn_open_ack_execute(fxt: &mut Fixture, expect: Expect) { - let res = execute(&mut fxt.ctx, fxt.msg.clone()); - let err_msg = fxt.generate_error_msg(&expect, "execution", &res); - match expect { - Expect::Failure(_) => { - assert!(res.is_err(), "{err_msg}"); - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}"); - assert_eq!(fxt.ctx.events.len(), 2); - - assert!(matches!( - fxt.ctx.events[0], - IbcEvent::Message(MessageEvent::Connection) - )); - let event = &fxt.ctx.events[1]; - assert!(matches!(event, &IbcEvent::OpenAckConnection(_))); - - let conn_open_try_event = match event { - IbcEvent::OpenAckConnection(e) => e, - _ => unreachable!(), - }; - let conn_end = ::connection_end( - &fxt.ctx, - conn_open_try_event.conn_id_on_a(), - ) - .unwrap(); - assert_eq!(conn_end.state().clone(), State::Open); - } - } - } - - #[test] - fn conn_open_ack_healthy() { - let mut fxt = conn_open_ack_fixture(Ctx::NewWithConnection); - conn_open_ack_validate(&fxt, Expect::Success); - conn_open_ack_execute(&mut fxt, Expect::Success); - } - - #[test] - fn conn_open_ack_no_connection() { - let fxt = conn_open_ack_fixture(Ctx::New); - let expected_err = ContextError::ConnectionError(ConnectionError::ConnectionNotFound { - connection_id: fxt.msg.conn_id_on_a.clone(), - }); - conn_open_ack_validate(&fxt, Expect::Failure(Some(expected_err))); - } - - #[test] - fn conn_open_ack_invalid_consensus_height() { - let fxt = conn_open_ack_fixture(Ctx::DefaultWithConnection); - let expected_err = ContextError::ConnectionError(ConnectionError::InvalidConsensusHeight { - target_height: fxt.msg.consensus_height_of_a_on_b, - current_height: Height::new(0, 10).unwrap(), - }); - conn_open_ack_validate(&fxt, Expect::Failure(Some(expected_err))); - } - - #[test] - fn conn_open_ack_connection_mismatch() { - let fxt = conn_open_ack_fixture(Ctx::NewWithConnectionEndOpen); - let expected_err = ContextError::ConnectionError(ConnectionError::InvalidState { - expected: State::Init.to_string(), - actual: State::Open.to_string(), - }); - conn_open_ack_validate(&fxt, Expect::Failure(Some(expected_err))); - } -} diff --git a/crates/ibc/src/core/ics03_connection/handler/conn_open_confirm.rs b/crates/ibc/src/core/ics03_connection/handler/conn_open_confirm.rs index d7896f2bc..83392ff6d 100644 --- a/crates/ibc/src/core/ics03_connection/handler/conn_open_confirm.rs +++ b/crates/ibc/src/core/ics03_connection/handler/conn_open_confirm.rs @@ -168,125 +168,3 @@ impl LocalVars { .ok_or(ConnectionError::InvalidCounterparty) } } - -#[cfg(test)] -mod tests { - use core::str::FromStr; - - use test_log::test; - - use super::*; - use crate::core::events::IbcEvent; - use crate::core::ics03_connection::connection::{ConnectionEnd, Counterparty, State}; - use crate::core::ics03_connection::handler::test_util::{Expect, Fixture}; - use crate::core::ics03_connection::msgs::conn_open_confirm::MsgConnectionOpenConfirm; - use crate::core::ics23_commitment::commitment::CommitmentPrefix; - use crate::core::ics24_host::identifier::ClientId; - use crate::core::timestamp::ZERO_DURATION; - use crate::mock::context::MockContext; - use crate::Height; - - enum Ctx { - Default, - CorrectConnection, - IncorrectConnection, - } - - fn conn_open_confirm_fixture(ctx: Ctx) -> Fixture { - let client_id = ClientId::from_str("mock_clientid").unwrap(); - let msg = MsgConnectionOpenConfirm::new_dummy(); - let counterparty = Counterparty::new( - client_id.clone(), - Some(msg.conn_id_on_b.clone()), - CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), - ); - - let ctx_default = MockContext::default(); - - let incorrect_conn_end_state = ConnectionEnd::new( - State::Init, - client_id.clone(), - counterparty, - ValidationContext::get_compatible_versions(&ctx_default), - ZERO_DURATION, - ) - .unwrap(); - - let mut correct_conn_end = incorrect_conn_end_state.clone(); - correct_conn_end.set_state(State::TryOpen); - - let ctx = match ctx { - Ctx::Default => ctx_default, - Ctx::IncorrectConnection => ctx_default - .with_client(&client_id, Height::new(0, 10).unwrap()) - .with_connection(msg.conn_id_on_b.clone(), incorrect_conn_end_state), - Ctx::CorrectConnection => ctx_default - .with_client(&client_id, Height::new(0, 10).unwrap()) - .with_connection(msg.conn_id_on_b.clone(), correct_conn_end), - }; - - Fixture { ctx, msg } - } - - fn conn_open_confirm_validate(fxt: &Fixture, expect: Expect) { - let res = validate(&fxt.ctx, &fxt.msg); - let err_msg = fxt.generate_error_msg(&expect, "validation", &res); - match expect { - Expect::Failure(_) => { - assert!(res.is_err(), "{err_msg}"); - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}"); - } - }; - } - - fn conn_open_confirm_execute(fxt: &mut Fixture, expect: Expect) { - let res = execute(&mut fxt.ctx, &fxt.msg); - let err_msg = fxt.generate_error_msg(&expect, "execution", &res); - match expect { - Expect::Failure(_) => { - assert!(res.is_err(), "{err_msg}"); - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}"); - assert_eq!(fxt.ctx.events.len(), 2); - - assert!(matches!( - fxt.ctx.events[0], - IbcEvent::Message(MessageEvent::Connection) - )); - let event = &fxt.ctx.events[1]; - assert!(matches!(event, &IbcEvent::OpenConfirmConnection(_))); - - let conn_open_try_event = match event { - IbcEvent::OpenConfirmConnection(e) => e, - _ => unreachable!(), - }; - let conn_end = - ValidationContext::connection_end(&fxt.ctx, conn_open_try_event.conn_id_on_b()) - .unwrap(); - assert_eq!(conn_end.state().clone(), State::Open); - } - } - } - - #[test] - fn conn_open_confirm_healthy() { - let mut fxt = conn_open_confirm_fixture(Ctx::CorrectConnection); - conn_open_confirm_validate(&fxt, Expect::Success); - conn_open_confirm_execute(&mut fxt, Expect::Success); - } - - #[test] - fn conn_open_confirm_no_connection() { - let fxt = conn_open_confirm_fixture(Ctx::Default); - conn_open_confirm_validate(&fxt, Expect::Failure(None)); - } - - #[test] - fn conn_open_confirm_connection_mismatch() { - let fxt = conn_open_confirm_fixture(Ctx::IncorrectConnection); - conn_open_confirm_validate(&fxt, Expect::Failure(None)); - } -} diff --git a/crates/ibc/src/core/ics03_connection/handler/conn_open_init.rs b/crates/ibc/src/core/ics03_connection/handler/conn_open_init.rs index cc470e81b..f1ba3c95a 100644 --- a/crates/ibc/src/core/ics03_connection/handler/conn_open_init.rs +++ b/crates/ibc/src/core/ics03_connection/handler/conn_open_init.rs @@ -86,140 +86,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use test_log::test; - - use super::*; - use crate::core::events::IbcEvent; - use crate::core::ics03_connection::connection::State; - use crate::core::ics03_connection::handler::test_util::{Expect, Fixture}; - use crate::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; - use crate::core::ics03_connection::version::Version; - use crate::mock::context::MockContext; - use crate::Height; - - enum Ctx { - Default, - WithClient, - } - - enum Msg { - Default, - NoVersion, - BadVersion, - WithCounterpartyConnId, - } - - fn conn_open_init_fixture( - ctx_variant: Ctx, - msg_variant: Msg, - ) -> Fixture { - let msg_default = MsgConnectionOpenInit::new_dummy(); - let msg = match msg_variant { - Msg::Default => msg_default, - Msg::NoVersion => msg_default.with_version(None), - Msg::BadVersion => msg_default.with_version(Some("random identifier 424242")), - Msg::WithCounterpartyConnId => msg_default.with_counterparty_conn_id(2), - }; - - let ctx_default = MockContext::default(); - let ctx = match ctx_variant { - Ctx::WithClient => { - ctx_default.with_client(&msg.client_id_on_a, Height::new(0, 10).unwrap()) - } - _ => ctx_default, - }; - - Fixture { ctx, msg } - } - - fn conn_open_init_validate(fxt: &Fixture, expect: Expect) { - let res = validate(&fxt.ctx, fxt.msg.clone()); - let err_msg = fxt.generate_error_msg(&expect, "validation", &res); - match expect { - Expect::Failure(_) => { - assert!(res.is_err(), "{err_msg}") - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}") - } - } - } - - fn conn_open_init_execute( - fxt: &mut Fixture, - expect: Expect, - expected_version: Vec, - ) { - let res = execute(&mut fxt.ctx, fxt.msg.clone()); - let err_msg = fxt.generate_error_msg(&expect, "execution", &res); - match expect { - Expect::Failure(_) => { - assert!(res.is_err(), "{err_msg}") - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}"); - - assert_eq!(fxt.ctx.connection_counter().unwrap(), 1); - - assert_eq!(fxt.ctx.events.len(), 2); - - assert!(matches!( - fxt.ctx.events[0], - IbcEvent::Message(MessageEvent::Connection) - )); - let event = &fxt.ctx.events[1]; - assert!(matches!(event, &IbcEvent::OpenInitConnection(_))); - - let conn_open_init_event = match event { - IbcEvent::OpenInitConnection(e) => e, - _ => unreachable!(), - }; - let conn_end = ValidationContext::connection_end( - &fxt.ctx, - conn_open_init_event.conn_id_on_a(), - ) - .unwrap(); - assert_eq!(conn_end.state().clone(), State::Init); - assert_eq!(conn_end.versions(), expected_version); - } - } - } - - #[test] - fn conn_open_init_healthy() { - let mut fxt = conn_open_init_fixture(Ctx::WithClient, Msg::Default); - conn_open_init_validate(&fxt, Expect::Success); - let expected_version = vec![fxt.msg.version.clone().unwrap()]; - conn_open_init_execute(&mut fxt, Expect::Success, expected_version); - } - - #[test] - fn conn_open_init_no_context() { - let fxt = conn_open_init_fixture(Ctx::Default, Msg::Default); - conn_open_init_validate(&fxt, Expect::Failure(None)); - } - - #[test] - fn conn_open_init_no_version() { - let mut fxt = conn_open_init_fixture(Ctx::WithClient, Msg::NoVersion); - conn_open_init_validate(&fxt, Expect::Success); - let expected_version = ValidationContext::get_compatible_versions(&fxt.ctx.clone()); - conn_open_init_execute(&mut fxt, Expect::Success, expected_version); - } - #[test] - fn conn_open_init_incompatible_version() { - let fxt = conn_open_init_fixture(Ctx::WithClient, Msg::BadVersion); - conn_open_init_validate(&fxt, Expect::Failure(None)); - } - - #[test] - fn conn_open_init_with_counterparty_conn_id() { - let mut fxt = conn_open_init_fixture(Ctx::WithClient, Msg::WithCounterpartyConnId); - conn_open_init_validate(&fxt, Expect::Success); - let expected_version = vec![fxt.msg.version.clone().unwrap()]; - conn_open_init_execute(&mut fxt, Expect::Success, expected_version); - } -} diff --git a/crates/ibc/src/core/ics03_connection/handler/conn_open_try.rs b/crates/ibc/src/core/ics03_connection/handler/conn_open_try.rs index cad91660a..965cd4b0d 100644 --- a/crates/ibc/src/core/ics03_connection/handler/conn_open_try.rs +++ b/crates/ibc/src/core/ics03_connection/handler/conn_open_try.rs @@ -202,150 +202,3 @@ impl LocalVars { }) } } - -#[cfg(test)] -mod tests { - use test_log::test; - - use super::*; - use crate::core::events::IbcEvent; - use crate::core::ics03_connection::connection::State; - use crate::core::ics03_connection::handler::test_util::{Expect, Fixture}; - use crate::core::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; - use crate::core::ics24_host::identifier::ChainId; - use crate::mock::context::MockContext; - use crate::mock::host::HostType; - use crate::Height; - - enum Ctx { - Default, - WithClient, - } - - enum Msg { - Default, - HeightAdvanced, - HeightOld, - ProofHeightMissing, - } - - fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { - let max_history_size = 5; - let client_cons_state_height = 10; - let host_chain_height = Height::new(0, 35).unwrap(); - let pruned_height = host_chain_height - .sub(max_history_size + 1) - .unwrap() - .revision_height(); - - let msg = match msg_variant { - Msg::Default => MsgConnectionOpenTry::new_dummy( - client_cons_state_height, - host_chain_height.revision_height(), - ), - Msg::HeightAdvanced => MsgConnectionOpenTry::new_dummy( - client_cons_state_height, - host_chain_height.increment().revision_height(), - ), - Msg::HeightOld => { - MsgConnectionOpenTry::new_dummy(client_cons_state_height, pruned_height) - } - Msg::ProofHeightMissing => MsgConnectionOpenTry::new_dummy( - client_cons_state_height - 1, - host_chain_height.revision_height(), - ), - }; - - let ctx_new = MockContext::new( - ChainId::new("mockgaia-0").unwrap(), - HostType::Mock, - max_history_size, - host_chain_height, - ); - let ctx = match ctx_variant { - Ctx::Default => MockContext::default(), - Ctx::WithClient => ctx_new.with_client( - &msg.client_id_on_b, - Height::new(0, client_cons_state_height).unwrap(), - ), - }; - Fixture { ctx, msg } - } - - fn conn_open_try_validate(fxt: &Fixture, expect: Expect) { - let res = validate(&fxt.ctx, fxt.msg.clone()); - let err_msg = fxt.generate_error_msg(&expect, "validation", &res); - match expect { - Expect::Failure(_) => { - assert!(res.is_err(), "{err_msg}") - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}"); - } - } - } - - fn conn_open_try_execute(fxt: &mut Fixture, expect: Expect) { - let res = execute(&mut fxt.ctx, fxt.msg.clone()); - let err_msg = fxt.generate_error_msg(&expect, "execution", &res); - match expect { - Expect::Failure(_) => { - assert!(res.is_err(), "{err_msg}") - } - Expect::Success => { - assert!(res.is_ok(), "{err_msg}"); - - assert_eq!(fxt.ctx.connection_counter().unwrap(), 1); - - assert_eq!(fxt.ctx.events.len(), 2); - - assert!(matches!( - fxt.ctx.events[0], - IbcEvent::Message(MessageEvent::Connection) - )); - let event = &fxt.ctx.events[1]; - assert!(matches!(event, &IbcEvent::OpenTryConnection(_))); - - let conn_open_try_event = match event { - IbcEvent::OpenTryConnection(e) => e, - _ => unreachable!(), - }; - let conn_end = - ValidationContext::connection_end(&fxt.ctx, conn_open_try_event.conn_id_on_b()) - .unwrap(); - assert_eq!(conn_end.state().clone(), State::TryOpen); - } - } - } - - #[test] - fn conn_open_try_healthy() { - let mut fxt = conn_open_try_fixture(Ctx::WithClient, Msg::Default); - conn_open_try_validate(&fxt, Expect::Success); - conn_open_try_execute(&mut fxt, Expect::Success); - } - - #[test] - fn conn_open_try_height_advanced() { - let fxt = conn_open_try_fixture(Ctx::WithClient, Msg::HeightAdvanced); - conn_open_try_validate(&fxt, Expect::Failure(None)); - } - - #[test] - fn conn_open_try_height_old() { - let fxt = conn_open_try_fixture(Ctx::WithClient, Msg::HeightOld); - conn_open_try_validate(&fxt, Expect::Failure(None)); - } - - #[test] - fn conn_open_try_proof_height_missing() { - let fxt = conn_open_try_fixture(Ctx::WithClient, Msg::ProofHeightMissing); - conn_open_try_validate(&fxt, Expect::Failure(None)); - } - - #[test] - fn conn_open_try_no_client() { - let fxt = conn_open_try_fixture(Ctx::Default, Msg::Default); - conn_open_try_validate(&fxt, Expect::Failure(None)); - } -} diff --git a/crates/ibc/src/core/ics03_connection/msgs.rs b/crates/ibc/src/core/ics03_connection/msgs.rs index 0de5e6153..f80617d82 100644 --- a/crates/ibc/src/core/ics03_connection/msgs.rs +++ b/crates/ibc/src/core/ics03_connection/msgs.rs @@ -36,27 +36,3 @@ pub enum ConnectionMsg { OpenAck(MsgConnectionOpenAck), OpenConfirm(MsgConnectionOpenConfirm), } - -#[cfg(test)] -pub mod test_util { - - use ibc_proto::ibc::core::commitment::v1::MerklePrefix; - use ibc_proto::ibc::core::connection::v1::Counterparty as RawCounterparty; - - use crate::core::ics24_host::identifier::{ClientId, ConnectionId}; - use crate::prelude::*; - - pub fn get_dummy_raw_counterparty(conn_id: Option) -> RawCounterparty { - let connection_id = match conn_id { - Some(id) => ConnectionId::new(id).to_string(), - None => "".to_string(), - }; - RawCounterparty { - client_id: ClientId::default().to_string(), - connection_id, - prefix: Some(MerklePrefix { - key_prefix: b"ibc".to_vec(), - }), - } - } -} diff --git a/crates/ibc/src/core/ics03_connection/msgs/conn_open_ack.rs b/crates/ibc/src/core/ics03_connection/msgs/conn_open_ack.rs index ae8a7f07b..cbdbbfcba 100644 --- a/crates/ibc/src/core/ics03_connection/msgs/conn_open_ack.rs +++ b/crates/ibc/src/core/ics03_connection/msgs/conn_open_ack.rs @@ -130,66 +130,13 @@ impl From for RawMsgConnectionOpenAck { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::client::v1::Height as RawHeight; - use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck; - - use super::MsgConnectionOpenAck; - use crate::core::ics02_client::height::Height; - use crate::core::ics03_connection::version::Version; - use crate::core::ics24_host::identifier::ConnectionId; - use crate::mock::client_state::MockClientState; - use crate::mock::header::MockHeader; - use crate::prelude::*; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Testing-specific helper methods. - impl MsgConnectionOpenAck { - /// Returns a new `MsgConnectionOpenAck` with dummy values. - pub fn new_dummy(proof_height: u64, consensus_height: u64) -> Self { - MsgConnectionOpenAck::try_from(get_dummy_raw_msg_conn_open_ack( - proof_height, - consensus_height, - )) - .unwrap() - } - } - - pub fn get_dummy_raw_msg_conn_open_ack( - proof_height: u64, - consensus_height: u64, - ) -> RawMsgConnectionOpenAck { - let client_state_height = Height::new(0, consensus_height).unwrap(); - RawMsgConnectionOpenAck { - connection_id: ConnectionId::new(0).to_string(), - counterparty_connection_id: ConnectionId::new(1).to_string(), - proof_try: get_dummy_proof(), - proof_height: Some(RawHeight { - revision_number: 0, - revision_height: proof_height, - }), - proof_consensus: get_dummy_proof(), - consensus_height: Some(RawHeight { - revision_number: 0, - revision_height: consensus_height, - }), - client_state: Some(MockClientState::new(MockHeader::new(client_state_height)).into()), - proof_client: get_dummy_proof(), - version: Some(Version::default().into()), - signer: get_dummy_bech32_account(), - host_consensus_state_proof: vec![], - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::client::v1::Height; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck; + use ibc_testkit::utils::core::connection::dummy_raw_msg_conn_open_ack; use test_log::test; - use crate::core::ics03_connection::msgs::conn_open_ack::test_util::get_dummy_raw_msg_conn_open_ack; use crate::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck; use crate::prelude::*; @@ -202,7 +149,7 @@ mod tests { want_pass: bool, } - let default_ack_msg = get_dummy_raw_msg_conn_open_ack(5, 5); + let default_ack_msg = dummy_raw_msg_conn_open_ack(5, 5); let tests: Vec = vec![ Test { @@ -268,7 +215,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_conn_open_ack(5, 6); + let raw = dummy_raw_msg_conn_open_ack(5, 6); let msg = MsgConnectionOpenAck::try_from(raw.clone()).unwrap(); let raw_back = RawMsgConnectionOpenAck::from(msg.clone()); let msg_back = MsgConnectionOpenAck::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics03_connection/msgs/conn_open_confirm.rs b/crates/ibc/src/core/ics03_connection/msgs/conn_open_confirm.rs index 45a5f6f7d..5a1200c78 100644 --- a/crates/ibc/src/core/ics03_connection/msgs/conn_open_confirm.rs +++ b/crates/ibc/src/core/ics03_connection/msgs/conn_open_confirm.rs @@ -72,43 +72,13 @@ impl From for RawMsgConnectionOpenConfirm { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::client::v1::Height; - use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; - - use super::MsgConnectionOpenConfirm; - use crate::prelude::*; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Testing-specific helper methods. - impl MsgConnectionOpenConfirm { - /// Returns a new `MsgConnectionOpenConfirm` with dummy values. - pub fn new_dummy() -> Self { - MsgConnectionOpenConfirm::try_from(get_dummy_raw_msg_conn_open_confirm()).unwrap() - } - } - - pub fn get_dummy_raw_msg_conn_open_confirm() -> RawMsgConnectionOpenConfirm { - RawMsgConnectionOpenConfirm { - connection_id: "srcconnection".to_string(), - proof_ack: get_dummy_proof(), - proof_height: Some(Height { - revision_number: 0, - revision_height: 10, - }), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::client::v1::Height; use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; + use ibc_testkit::utils::core::connection::dummy_raw_msg_conn_open_confirm; use test_log::test; - use crate::core::ics03_connection::msgs::conn_open_confirm::test_util::get_dummy_raw_msg_conn_open_confirm; use crate::core::ics03_connection::msgs::conn_open_confirm::MsgConnectionOpenConfirm; use crate::prelude::*; @@ -121,7 +91,7 @@ mod tests { want_pass: bool, } - let default_ack_msg = get_dummy_raw_msg_conn_open_confirm(); + let default_ack_msg = dummy_raw_msg_conn_open_confirm(); let tests: Vec = vec![ Test { name: "Good parameters".to_string(), @@ -167,7 +137,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_conn_open_confirm(); + let raw = dummy_raw_msg_conn_open_confirm(); let msg = MsgConnectionOpenConfirm::try_from(raw.clone()).unwrap(); let raw_back = RawMsgConnectionOpenConfirm::from(msg.clone()); let msg_back = MsgConnectionOpenConfirm::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics03_connection/msgs/conn_open_init.rs b/crates/ibc/src/core/ics03_connection/msgs/conn_open_init.rs index 37afaa9b4..249076364 100644 --- a/crates/ibc/src/core/ics03_connection/msgs/conn_open_init.rs +++ b/crates/ibc/src/core/ics03_connection/msgs/conn_open_init.rs @@ -128,83 +128,17 @@ impl From for RawMsgConnectionOpenInit { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::connection::v1::{ - MsgConnectionOpenInit as RawMsgConnectionOpenInit, Version as RawVersion, - }; - - use crate::core::ics03_connection::connection::Counterparty; - use crate::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; - use crate::core::ics03_connection::msgs::test_util::get_dummy_raw_counterparty; - use crate::core::ics03_connection::version::Version; - use crate::core::ics24_host::identifier::ClientId; - use crate::prelude::*; - use crate::test_utils::get_dummy_bech32_account; - - /// Extends the implementation with additional helper methods. - impl MsgConnectionOpenInit { - /// Returns a new `MsgConnectionOpenInit` with dummy values. - pub fn new_dummy() -> Self { - MsgConnectionOpenInit::try_from(get_dummy_raw_msg_conn_open_init()).unwrap() - } - - /// Setter for `client_id`. Amenable to chaining, since it consumes the input message. - pub fn with_client_id(self, client_id: ClientId) -> Self { - MsgConnectionOpenInit { - client_id_on_a: client_id, - ..self - } - } - - /// Setter for `counterparty`. Amenable to chaining, since it consumes the input message.\ - pub fn with_counterparty_conn_id(self, counterparty_conn_id: u64) -> Self { - let counterparty = - Counterparty::try_from(get_dummy_raw_counterparty(Some(counterparty_conn_id))) - .unwrap(); - MsgConnectionOpenInit { - counterparty, - ..self - } - } - - pub fn with_version(self, identifier: Option<&str>) -> Self { - let version = match identifier { - Some(v) => Version::try_from(RawVersion { - identifier: v.to_string(), - features: vec![], - }) - .unwrap() - .into(), - None => None, - }; - MsgConnectionOpenInit { version, ..self } - } - } - - /// Returns a dummy message, for testing only. - /// Other unit tests may import this if they depend on a MsgConnectionOpenInit. - pub fn get_dummy_raw_msg_conn_open_init() -> RawMsgConnectionOpenInit { - RawMsgConnectionOpenInit { - client_id: ClientId::default().to_string(), - counterparty: Some(get_dummy_raw_counterparty(None)), - version: Some(Version::default().into()), - delay_period: 0, - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::connection::v1::{ Counterparty as RawCounterparty, MsgConnectionOpenInit as RawMsgConnectionOpenInit, }; + use ibc_testkit::utils::core::connection::{ + dummy_raw_counterparty_conn, dummy_raw_msg_conn_open_init, + }; use test_log::test; use super::MsgConnectionOpenInit; - use crate::core::ics03_connection::msgs::conn_open_init::test_util::get_dummy_raw_msg_conn_open_init; - use crate::core::ics03_connection::msgs::test_util::get_dummy_raw_counterparty; use crate::prelude::*; #[test] @@ -216,7 +150,7 @@ mod tests { want_pass: bool, } - let default_init_msg = get_dummy_raw_msg_conn_open_init(); + let default_init_msg = dummy_raw_msg_conn_open_init(); let tests: Vec = vec![ Test { @@ -239,7 +173,7 @@ mod tests { connection_id: "abcdefghijksdffjssdkflweldflsfladfsfwjkrekcmmsdfsdfjflddmnopqrstu" .to_string(), - ..get_dummy_raw_counterparty(None) + ..dummy_raw_counterparty_conn(None) }), ..default_init_msg }, @@ -265,7 +199,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_conn_open_init(); + let raw = dummy_raw_msg_conn_open_init(); let msg = MsgConnectionOpenInit::try_from(raw.clone()).unwrap(); let raw_back = RawMsgConnectionOpenInit::from(msg.clone()); let msg_back = MsgConnectionOpenInit::try_from(raw_back.clone()).unwrap(); @@ -274,7 +208,7 @@ mod tests { // Check if handler sets counterparty connection id to `None` // in case relayer passes `MsgConnectionOpenInit` message with it set to `Some(_)`. - let raw_with_counterpary_conn_id_some = get_dummy_raw_msg_conn_open_init(); + let raw_with_counterpary_conn_id_some = dummy_raw_msg_conn_open_init(); let msg_with_counterpary_conn_id_some = MsgConnectionOpenInit::try_from(raw_with_counterpary_conn_id_some).unwrap(); let raw_with_counterpary_conn_id_some_back = @@ -299,7 +233,7 @@ mod tests { #[cfg(feature = "borsh")] #[test] fn test_borsh() { - let mut raw = get_dummy_raw_msg_conn_open_init(); + let mut raw = dummy_raw_msg_conn_open_init(); raw.delay_period = u64::MAX; let msg = MsgConnectionOpenInit::try_from(raw.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics03_connection/msgs/conn_open_try.rs b/crates/ibc/src/core/ics03_connection/msgs/conn_open_try.rs index 09dc56b4b..ab778f2e3 100644 --- a/crates/ibc/src/core/ics03_connection/msgs/conn_open_try.rs +++ b/crates/ibc/src/core/ics03_connection/msgs/conn_open_try.rs @@ -48,7 +48,7 @@ pub struct MsgConnectionOpenTry { #[deprecated(since = "0.22.0")] /// Only kept here for proper conversion to/from the raw type - previous_connection_id: String, + pub previous_connection_id: String, } impl Msg for MsgConnectionOpenTry { @@ -242,89 +242,18 @@ impl From for RawMsgConnectionOpenTry { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::client::v1::Height as RawHeight; - use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenTry as RawMsgConnectionOpenTry; - - use crate::core::ics02_client::height::Height; - use crate::core::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; - use crate::core::ics03_connection::msgs::test_util::get_dummy_raw_counterparty; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics24_host::identifier::{ClientId, ConnectionId}; - use crate::mock::client_state::MockClientState; - use crate::mock::header::MockHeader; - use crate::prelude::*; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Testing-specific helper methods. - impl MsgConnectionOpenTry { - /// Returns a new `MsgConnectionOpenTry` with dummy values. - pub fn new_dummy(proof_height: u64, consensus_height: u64) -> Self { - MsgConnectionOpenTry::try_from(get_dummy_raw_msg_conn_open_try( - proof_height, - consensus_height, - )) - .unwrap() - } - /// Setter for `client_id`. - pub fn with_client_id(self, client_id: ClientId) -> MsgConnectionOpenTry { - MsgConnectionOpenTry { - client_id_on_b: client_id, - ..self - } - } - } - - /// Returns a dummy `RawMsgConnectionOpenTry` with parametrized heights. The parameter - /// `proof_height` represents the height, on the source chain, at which this chain produced the - /// proof. Parameter `consensus_height` represents the height of destination chain which a - /// client on the source chain stores. - pub fn get_dummy_raw_msg_conn_open_try( - proof_height: u64, - consensus_height: u64, - ) -> RawMsgConnectionOpenTry { - let client_state_height = Height::new(0, consensus_height).unwrap(); - - #[allow(deprecated)] - RawMsgConnectionOpenTry { - client_id: ClientId::default().to_string(), - previous_connection_id: ConnectionId::default().to_string(), - client_state: Some(MockClientState::new(MockHeader::new(client_state_height)).into()), - counterparty: Some(get_dummy_raw_counterparty(Some(0))), - delay_period: 0, - counterparty_versions: get_compatible_versions() - .iter() - .map(|v| v.clone().into()) - .collect(), - proof_init: get_dummy_proof(), - proof_height: Some(RawHeight { - revision_number: 0, - revision_height: proof_height, - }), - proof_consensus: get_dummy_proof(), - consensus_height: Some(RawHeight { - revision_number: 0, - revision_height: consensus_height, - }), - proof_client: get_dummy_proof(), - signer: get_dummy_bech32_account(), - host_consensus_state_proof: vec![], - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::client::v1::Height; use ibc_proto::ibc::core::connection::v1::{ Counterparty as RawCounterparty, MsgConnectionOpenTry as RawMsgConnectionOpenTry, }; + use ibc_testkit::utils::core::connection::{ + dummy_raw_counterparty_conn, dummy_raw_msg_conn_open_try, + }; use test_log::test; - use crate::core::ics03_connection::msgs::conn_open_try::test_util::get_dummy_raw_msg_conn_open_try; use crate::core::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; - use crate::core::ics03_connection::msgs::test_util::get_dummy_raw_counterparty; use crate::prelude::*; #[test] @@ -336,7 +265,7 @@ mod tests { want_pass: bool, } - let default_try_msg = get_dummy_raw_msg_conn_open_try(10, 34); + let default_try_msg = dummy_raw_msg_conn_open_try(10, 34); let tests: Vec = vec![ @@ -360,7 +289,7 @@ mod tests { connection_id: "abcdasdfasdfsdfasfdwefwfsdfsfsfasfwewvxcvdvwgadvaadsefghijklmnopqrstu" .to_string(), - ..get_dummy_raw_counterparty(Some(0)) + ..dummy_raw_counterparty_conn(Some(0)) }), ..default_try_msg.clone() }, @@ -372,7 +301,7 @@ mod tests { raw: RawMsgConnectionOpenTry { counterparty: Some(RawCounterparty { client_id: "ClientId_".to_string(), - ..get_dummy_raw_counterparty(Some(0)) + ..dummy_raw_counterparty_conn(Some(0)) }), ..default_try_msg.clone() }, @@ -438,7 +367,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_conn_open_try(10, 34); + let raw = dummy_raw_msg_conn_open_try(10, 34); let msg = MsgConnectionOpenTry::try_from(raw.clone()).unwrap(); let raw_back = RawMsgConnectionOpenTry::from(msg.clone()); let msg_back = MsgConnectionOpenTry::try_from(raw_back.clone()).unwrap(); @@ -450,7 +379,7 @@ mod tests { #[cfg(feature = "borsh")] #[test] fn test_borsh() { - let mut raw = get_dummy_raw_msg_conn_open_try(10, 34); + let mut raw = dummy_raw_msg_conn_open_try(10, 34); raw.delay_period = u64::MAX; let msg = MsgConnectionOpenTry::try_from(raw.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics04_channel/channel.rs b/crates/ibc/src/core/ics04_channel/channel.rs index 2b1c7c766..49717cde4 100644 --- a/crates/ibc/src/core/ics04_channel/channel.rs +++ b/crates/ibc/src/core/ics04_channel/channel.rs @@ -338,7 +338,7 @@ pub(crate) fn verify_connection_hops_length( derive(borsh::BorshSerialize, borsh::BorshDeserialize) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Counterparty { pub port_id: PortId, pub channel_id: Option, @@ -571,54 +571,20 @@ impl Display for State { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::{ - Channel as RawChannel, Counterparty as RawCounterparty, - }; - - use crate::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; - use crate::prelude::*; - - /// Returns a dummy `RawCounterparty`, for testing only! - /// Can be optionally parametrized with a specific channel identifier. - pub fn get_dummy_raw_counterparty(channel_id: String) -> RawCounterparty { - RawCounterparty { - port_id: PortId::default().to_string(), - channel_id, - } - } - - /// Returns a dummy `RawChannel`, for testing only! - pub fn get_dummy_raw_channel_end(state: i32, channel_id: Option) -> RawChannel { - let channel_id = match channel_id { - Some(id) => ChannelId::new(id).to_string(), - None => "".to_string(), - }; - RawChannel { - state, - ordering: 2, - counterparty: Some(get_dummy_raw_counterparty(channel_id)), - connection_hops: vec![ConnectionId::default().to_string()], - version: "".to_string(), // The version is not validated. - } - } -} - #[cfg(test)] mod tests { use core::str::FromStr; use ibc_proto::ibc::core::channel::v1::Channel as RawChannel; + use ibc_testkit::utils::core::channel::dummy_raw_channel_end; use test_log::test; - use crate::core::ics04_channel::channel::test_util::get_dummy_raw_channel_end; use crate::core::ics04_channel::channel::ChannelEnd; use crate::prelude::*; #[test] fn channel_end_try_from_raw() { - let raw_channel_end = get_dummy_raw_channel_end(2, Some(0)); + let raw_channel_end = dummy_raw_channel_end(2, Some(0)); let empty_raw_channel_end = RawChannel { counterparty: None, diff --git a/crates/ibc/src/core/ics04_channel/commitment.rs b/crates/ibc/src/core/ics04_channel/commitment.rs index 8903f310a..67f8fbae6 100644 --- a/crates/ibc/src/core/ics04_channel/commitment.rs +++ b/crates/ibc/src/core/ics04_channel/commitment.rs @@ -81,7 +81,7 @@ impl From> for AcknowledgementCommitment { /// `{revision_number: 0, revision_height: 0}` to be consistent with ibc-go, /// where this value is used to mean "no timeout height": /// -pub(crate) fn compute_packet_commitment( +pub fn compute_packet_commitment( packet_data: &[u8], timeout_height: &TimeoutHeight, timeout_timestamp: &Timestamp, @@ -97,7 +97,7 @@ pub(crate) fn compute_packet_commitment( } /// Compute the commitment for an acknowledgement. -pub(crate) fn compute_ack_commitment(ack: &Acknowledgement) -> AcknowledgementCommitment { +pub fn compute_ack_commitment(ack: &Acknowledgement) -> AcknowledgementCommitment { hash(ack.as_ref()).to_vec().into() } diff --git a/crates/ibc/src/core/ics04_channel/handler/acknowledgement.rs b/crates/ibc/src/core/ics04_channel/handler/acknowledgement.rs index 71b31bb78..4680e86d2 100644 --- a/crates/ibc/src/core/ics04_channel/handler/acknowledgement.rs +++ b/crates/ibc/src/core/ics04_channel/handler/acknowledgement.rs @@ -213,268 +213,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use rstest::*; - use test_log::test; - - use super::*; - use crate::applications::transfer::MODULE_ID_STR; - use crate::core::ics02_client::height::Height; - use crate::core::ics02_client::ClientExecutionContext; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; - use crate::core::ics04_channel::commitment::PacketCommitment; - use crate::core::ics04_channel::msgs::acknowledgement::test_util::get_dummy_raw_msg_acknowledgement; - use crate::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; - use crate::core::router::{ModuleId, Router}; - use crate::core::timestamp::{Timestamp, ZERO_DURATION}; - use crate::mock::context::MockContext; - use crate::mock::router::MockRouter; - use crate::test_utils::DummyTransferModule; - - struct Fixture { - ctx: MockContext, - router: MockRouter, - client_height: Height, - module_id: ModuleId, - msg: MsgAcknowledgement, - packet_commitment: PacketCommitment, - conn_end_on_a: ConnectionEnd, - chan_end_on_a_ordered: ChannelEnd, - chan_end_on_a_unordered: ChannelEnd, - } - - #[fixture] - fn fixture() -> Fixture { - let client_height = Height::new(0, 2).unwrap(); - let ctx = MockContext::default().with_client(&ClientId::default(), client_height); - let mut router = MockRouter::default(); - - let module_id: ModuleId = ModuleId::new(MODULE_ID_STR.to_string()); - let module = DummyTransferModule::new(); - router.add_route(module_id.clone(), module).unwrap(); - - let msg = MsgAcknowledgement::try_from(get_dummy_raw_msg_acknowledgement( - client_height.revision_height(), - )) - .unwrap(); - - let packet = msg.packet.clone(); - - let packet_commitment = compute_packet_commitment( - &packet.data, - &packet.timeout_height_on_b, - &packet.timeout_timestamp_on_b, - ); - - let chan_end_on_a_unordered = ChannelEnd::new( - State::Open, - Order::Unordered, - Counterparty::new(packet.port_id_on_b, Some(packet.chan_id_on_b)), - vec![ConnectionId::default()], - Version::new("ics20-1".to_string()), - ) - .unwrap(); - - let mut chan_end_on_a_ordered = chan_end_on_a_unordered.clone(); - chan_end_on_a_ordered.ordering = Order::Ordered; - - let conn_end_on_a = ConnectionEnd::new( - ConnectionState::Open, - ClientId::default(), - ConnectionCounterparty::new( - ClientId::default(), - Some(ConnectionId::default()), - Default::default(), - ), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - Fixture { - ctx, - router, - client_height, - module_id, - msg, - packet_commitment, - conn_end_on_a, - chan_end_on_a_unordered, - chan_end_on_a_ordered, - } - } - - #[rstest] - fn ack_fail_no_channel(fixture: Fixture) { - let Fixture { ctx, msg, .. } = fixture; - - let res = validate(&ctx, &msg); - - assert!( - res.is_err(), - "Validation fails because no channel exists in the context" - ) - } - - /// NO-OP case - #[rstest] - fn ack_success_no_packet_commitment(fixture: Fixture) { - let Fixture { - ctx, - msg, - conn_end_on_a, - chan_end_on_a_unordered, - client_height, - .. - } = fixture; - let ctx = ctx - .with_client(&ClientId::default(), client_height) - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_unordered, - ) - .with_connection(ConnectionId::default(), conn_end_on_a); - - let res = validate(&ctx, &msg); - - assert!( - res.is_ok(), - "Validation should succeed when no packet commitment is present" - ) - } - - #[rstest] - fn ack_success_happy_path(fixture: Fixture) { - let Fixture { - ctx, - msg, - packet_commitment, - conn_end_on_a, - chan_end_on_a_unordered, - client_height, - .. - } = fixture; - let mut ctx: MockContext = ctx - .with_client(&ClientId::default(), client_height) - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_unordered, - ) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_packet_commitment( - msg.packet.port_id_on_a.clone(), - msg.packet.chan_id_on_a.clone(), - msg.packet.seq_on_a, - packet_commitment, - ); - ctx.get_client_execution_context() - .store_update_time( - ClientId::default(), - client_height, - Timestamp::from_nanoseconds(1000).unwrap(), - ) - .unwrap(); - ctx.get_client_execution_context() - .store_update_height( - ClientId::default(), - client_height, - Height::new(0, 4).unwrap(), - ) - .unwrap(); - - let res = validate(&ctx, &msg); - - assert!( - res.is_ok(), - "Happy path: validation should succeed. err: {res:?}" - ) - } - - #[rstest] - fn ack_unordered_chan_execute(fixture: Fixture) { - let Fixture { - ctx, - mut router, - module_id, - msg, - packet_commitment, - conn_end_on_a, - chan_end_on_a_unordered, - .. - } = fixture; - let mut ctx = ctx - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_unordered, - ) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_packet_commitment( - msg.packet.port_id_on_a.clone(), - msg.packet.chan_id_on_a.clone(), - msg.packet.seq_on_a, - packet_commitment, - ); - - let module = router.get_route_mut(&module_id).unwrap(); - let res = acknowledgement_packet_execute(&mut ctx, module, msg); - - assert!(res.is_ok()); - - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::AcknowledgePacket(_))); - } - - #[rstest] - fn ack_ordered_chan_execute(fixture: Fixture) { - let Fixture { - ctx, - mut router, - module_id, - msg, - packet_commitment, - conn_end_on_a, - chan_end_on_a_ordered, - .. - } = fixture; - let mut ctx = ctx - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_ordered, - ) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_packet_commitment( - msg.packet.port_id_on_a.clone(), - msg.packet.chan_id_on_a.clone(), - msg.packet.seq_on_a, - packet_commitment, - ); - - let module = router.get_route_mut(&module_id).unwrap(); - let res = acknowledgement_packet_execute(&mut ctx, module, msg); - - assert!(res.is_ok()); - - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::AcknowledgePacket(_))); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_close_confirm.rs b/crates/ibc/src/core/ics04_channel/handler/chan_close_confirm.rs index 717220339..28ce154d9 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_close_confirm.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_close_confirm.rs @@ -163,138 +163,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::applications::transfer::MODULE_ID_STR; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::msgs::test_util::get_dummy_raw_counterparty; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::channel::{ - ChannelEnd, Counterparty, Order, State as ChannelState, - }; - use crate::core::ics04_channel::msgs::chan_close_confirm::test_util::get_dummy_raw_msg_chan_close_confirm; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ClientId, ConnectionId}; - use crate::core::router::{ModuleId, Router}; - use crate::core::timestamp::ZERO_DURATION; - use crate::mock::client_state::client_type as mock_client_type; - use crate::mock::context::MockContext; - use crate::mock::router::MockRouter; - use crate::test_utils::DummyTransferModule; - - #[test] - fn test_chan_close_confirm_validate() { - let client_id = ClientId::new(mock_client_type(), 24).unwrap(); - let conn_id = ConnectionId::new(2); - let default_context = MockContext::default(); - let client_consensus_state_height = default_context.host_height().unwrap(); - - let conn_end = ConnectionEnd::new( - ConnectionState::Open, - client_id.clone(), - ConnectionCounterparty::try_from(get_dummy_raw_counterparty(Some(0))).unwrap(), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - let msg_chan_close_confirm = MsgChannelCloseConfirm::try_from( - get_dummy_raw_msg_chan_close_confirm(client_consensus_state_height.revision_height()), - ) - .unwrap(); - - let chan_end = ChannelEnd::new( - ChannelState::Open, - Order::default(), - Counterparty::new( - msg_chan_close_confirm.port_id_on_b.clone(), - Some(msg_chan_close_confirm.chan_id_on_b.clone()), - ), - vec![conn_id.clone()], - Version::default(), - ) - .unwrap(); - - let context = default_context - .with_client(&client_id, client_consensus_state_height) - .with_connection(conn_id, conn_end) - .with_channel( - msg_chan_close_confirm.port_id_on_b.clone(), - msg_chan_close_confirm.chan_id_on_b.clone(), - chan_end, - ); - - let res = validate(&context, &msg_chan_close_confirm); - assert!( - res.is_ok(), - "Validation expected to succeed (happy path). Error: {res:?}" - ); - } - - #[test] - fn test_chan_close_confirm_execute() { - let client_id = ClientId::new(mock_client_type(), 24).unwrap(); - let conn_id = ConnectionId::new(2); - let default_context = MockContext::default(); - let client_consensus_state_height = default_context.host_height().unwrap(); - - let conn_end = ConnectionEnd::new( - ConnectionState::Open, - client_id.clone(), - ConnectionCounterparty::try_from(get_dummy_raw_counterparty(Some(0))).unwrap(), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - let msg_chan_close_confirm = MsgChannelCloseConfirm::try_from( - get_dummy_raw_msg_chan_close_confirm(client_consensus_state_height.revision_height()), - ) - .unwrap(); - - let chan_end = ChannelEnd::new( - ChannelState::Open, - Order::default(), - Counterparty::new( - msg_chan_close_confirm.port_id_on_b.clone(), - Some(msg_chan_close_confirm.chan_id_on_b.clone()), - ), - vec![conn_id.clone()], - Version::default(), - ) - .unwrap(); - - let mut context = default_context - .with_client(&client_id, client_consensus_state_height) - .with_connection(conn_id, conn_end) - .with_channel( - msg_chan_close_confirm.port_id_on_b.clone(), - msg_chan_close_confirm.chan_id_on_b.clone(), - chan_end, - ); - let mut router = MockRouter::default(); - - let module_id = ModuleId::new(MODULE_ID_STR.to_string()); - router - .add_route(module_id.clone(), DummyTransferModule::new()) - .unwrap(); - - let module = router.get_route_mut(&module_id).unwrap(); - let res = chan_close_confirm_execute(&mut context, module, msg_chan_close_confirm); - assert!(res.is_ok(), "Execution success: happy path"); - - assert_eq!(context.events.len(), 2); - assert!(matches!( - context.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!( - context.events[1], - IbcEvent::CloseConfirmChannel(_) - )); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_close_init.rs b/crates/ibc/src/core/ics04_channel/handler/chan_close_init.rs index 174397672..b5856e9f6 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_close_init.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_close_init.rs @@ -121,139 +121,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::applications::transfer::MODULE_ID_STR; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::msgs::test_util::get_dummy_raw_counterparty; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::channel::{ - ChannelEnd, Counterparty, Order, State as ChannelState, - }; - use crate::core::ics04_channel::msgs::chan_close_init::test_util::get_dummy_raw_msg_chan_close_init; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ClientId, ConnectionId}; - use crate::core::router::{ModuleId, Router}; - use crate::core::timestamp::ZERO_DURATION; - use crate::mock::client_state::client_type as mock_client_type; - use crate::mock::context::MockContext; - use crate::mock::router::MockRouter; - use crate::test_utils::DummyTransferModule; - - #[test] - fn test_chan_close_init_validate() { - let client_id = ClientId::new(mock_client_type(), 24).unwrap(); - let conn_id = ConnectionId::new(2); - - let conn_end = ConnectionEnd::new( - ConnectionState::Open, - client_id.clone(), - ConnectionCounterparty::try_from(get_dummy_raw_counterparty(Some(0))).unwrap(), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - let msg_chan_close_init = - MsgChannelCloseInit::try_from(get_dummy_raw_msg_chan_close_init()).unwrap(); - - let chan_end = ChannelEnd::new( - ChannelState::Open, - Order::default(), - Counterparty::new( - msg_chan_close_init.port_id_on_a.clone(), - Some(msg_chan_close_init.chan_id_on_a.clone()), - ), - vec![conn_id.clone()], - Version::default(), - ) - .unwrap(); - - let context = { - let default_context = MockContext::default(); - let client_consensus_state_height = default_context.host_height().unwrap(); - - default_context - .with_client(&client_id, client_consensus_state_height) - .with_connection(conn_id, conn_end) - .with_channel( - msg_chan_close_init.port_id_on_a.clone(), - msg_chan_close_init.chan_id_on_a.clone(), - chan_end, - ) - }; - - let res = validate(&context, &msg_chan_close_init); - assert!( - res.is_ok(), - "Validation expected to succeed (happy path). Error: {res:?}" - ); - } - - #[test] - fn test_chan_close_init_execute() { - let client_id = ClientId::new(mock_client_type(), 24).unwrap(); - let conn_id = ConnectionId::new(2); - - let conn_end = ConnectionEnd::new( - ConnectionState::Open, - client_id.clone(), - ConnectionCounterparty::try_from(get_dummy_raw_counterparty(Some(0))).unwrap(), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - let msg_chan_close_init = - MsgChannelCloseInit::try_from(get_dummy_raw_msg_chan_close_init()).unwrap(); - - let chan_end = ChannelEnd::new( - ChannelState::Open, - Order::default(), - Counterparty::new( - msg_chan_close_init.port_id_on_a.clone(), - Some(msg_chan_close_init.chan_id_on_a.clone()), - ), - vec![conn_id.clone()], - Version::default(), - ) - .unwrap(); - - let mut context = { - let default_context = MockContext::default(); - let client_consensus_state_height = default_context.host_height().unwrap(); - - default_context - .with_client(&client_id, client_consensus_state_height) - .with_connection(conn_id, conn_end) - .with_channel( - msg_chan_close_init.port_id_on_a.clone(), - msg_chan_close_init.chan_id_on_a.clone(), - chan_end, - ) - }; - - let mut router = MockRouter::default(); - router - .add_route( - ModuleId::new(MODULE_ID_STR.to_string()), - DummyTransferModule::new(), - ) - .unwrap(); - let module_id = ModuleId::new(MODULE_ID_STR.to_string()); - let module = router.get_route_mut(&module_id).unwrap(); - let res = chan_close_init_execute(&mut context, module, msg_chan_close_init); - assert!(res.is_ok(), "Execution happy path"); - - assert_eq!(context.events.len(), 2); - assert!(matches!( - context.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(context.events[1], IbcEvent::CloseInitChannel(_))); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_open_ack.rs b/crates/ibc/src/core/ics04_channel/handler/chan_open_ack.rs index eb6ac5074..4267437d2 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_open_ack.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_open_ack.rs @@ -159,239 +159,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - - use rstest::*; - use test_log::test; - - use super::*; - use crate::applications::transfer::MODULE_ID_STR; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::msgs::test_util::get_dummy_raw_counterparty; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; - use crate::core::ics04_channel::msgs::chan_open_ack::test_util::get_dummy_raw_msg_chan_open_ack; - use crate::core::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; - use crate::core::ics24_host::identifier::{ClientId, ConnectionId}; - use crate::core::router::{ModuleId, Router}; - use crate::core::timestamp::ZERO_DURATION; - use crate::mock::client_state::client_type as mock_client_type; - use crate::mock::context::MockContext; - use crate::mock::router::MockRouter; - use crate::test_utils::DummyTransferModule; - use crate::Height; - - pub struct Fixture { - pub context: MockContext, - pub router: MockRouter, - pub module_id: ModuleId, - pub msg: MsgChannelOpenAck, - pub client_id_on_a: ClientId, - pub conn_id_on_a: ConnectionId, - pub conn_end_on_a: ConnectionEnd, - pub chan_end_on_a: ChannelEnd, - pub proof_height: u64, - } - - #[fixture] - fn fixture() -> Fixture { - let proof_height = 10; - let context = MockContext::default(); - - let module_id = ModuleId::new(MODULE_ID_STR.to_string()); - let mut router = MockRouter::default(); - router - .add_route(module_id.clone(), DummyTransferModule::new()) - .unwrap(); - - let client_id_on_a = ClientId::new(mock_client_type(), 45).unwrap(); - let conn_id_on_a = ConnectionId::new(2); - let conn_end_on_a = ConnectionEnd::new( - ConnectionState::Open, - client_id_on_a.clone(), - ConnectionCounterparty::try_from(get_dummy_raw_counterparty(Some(0))).unwrap(), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - let msg = - MsgChannelOpenAck::try_from(get_dummy_raw_msg_chan_open_ack(proof_height)).unwrap(); - - let chan_end_on_a = ChannelEnd::new( - State::Init, - Order::Unordered, - Counterparty::new(msg.port_id_on_a.clone(), Some(msg.chan_id_on_b.clone())), - vec![conn_id_on_a.clone()], - msg.version_on_b.clone(), - ) - .unwrap(); - - Fixture { - context, - router, - module_id, - msg, - client_id_on_a, - conn_id_on_a, - conn_end_on_a, - chan_end_on_a, - proof_height, - } - } - - #[rstest] - fn chan_open_ack_fail_no_channel(fixture: Fixture) { - let Fixture { - context, - msg, - client_id_on_a, - conn_id_on_a, - conn_end_on_a, - proof_height, - .. - } = fixture; - let context = context - .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_a, conn_end_on_a); - - let res = validate(&context, &msg); - - assert!( - res.is_err(), - "Validation fails because no channel exists in the context" - ) - } - - #[rstest] - fn chan_open_ack_fail_channel_wrong_state(fixture: Fixture) { - let Fixture { - context, - msg, - client_id_on_a, - conn_id_on_a, - conn_end_on_a, - proof_height, - .. - } = fixture; - - let wrong_chan_end = ChannelEnd::new( - State::Open, - Order::Unordered, - Counterparty::new(msg.port_id_on_a.clone(), Some(msg.chan_id_on_b.clone())), - vec![conn_id_on_a.clone()], - msg.version_on_b.clone(), - ) - .unwrap(); - let context = context - .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_a, conn_end_on_a) - .with_channel( - msg.port_id_on_a.clone(), - msg.chan_id_on_a.clone(), - wrong_chan_end, - ); - - let res = validate(&context, &msg); - - assert!( - res.is_err(), - "Validation fails because channel is in the wrong state" - ) - } - - #[rstest] - fn chan_open_ack_fail_no_connection(fixture: Fixture) { - let Fixture { - context, - msg, - client_id_on_a, - chan_end_on_a, - proof_height, - .. - } = fixture; - - let context = context - .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) - .with_channel( - msg.port_id_on_a.clone(), - msg.chan_id_on_a.clone(), - chan_end_on_a, - ); - - let res = validate(&context, &msg); - - assert!( - res.is_err(), - "Validation fails because no connection exists in the context" - ) - } - - #[rstest] - fn chan_open_ack_happy_path(fixture: Fixture) { - let Fixture { - context, - msg, - client_id_on_a, - conn_id_on_a, - conn_end_on_a, - chan_end_on_a, - proof_height, - .. - } = fixture; - - let context = context - .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_a, conn_end_on_a) - .with_channel( - msg.port_id_on_a.clone(), - msg.chan_id_on_a.clone(), - chan_end_on_a, - ); - - let res = validate(&context, &msg); - - assert!(res.is_ok(), "Validation happy path") - } - - #[rstest] - fn chan_open_ack_execute_happy_path(fixture: Fixture) { - let Fixture { - context, - mut router, - module_id, - msg, - client_id_on_a, - conn_id_on_a, - conn_end_on_a, - chan_end_on_a, - proof_height, - .. - } = fixture; - - let mut context = context - .with_client(&client_id_on_a, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_a, conn_end_on_a) - .with_channel( - msg.port_id_on_a.clone(), - msg.chan_id_on_a.clone(), - chan_end_on_a, - ); - - let module = router.get_route_mut(&module_id).unwrap(); - let res = chan_open_ack_execute(&mut context, module, msg); - - assert!(res.is_ok(), "Execution happy path"); - - assert_eq!(context.events.len(), 2); - assert!(matches!( - context.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(context.events[1], IbcEvent::OpenAckChannel(_))); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_open_confirm.rs b/crates/ibc/src/core/ics04_channel/handler/chan_open_confirm.rs index 8fff0a6a9..7e5ecf93b 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_open_confirm.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_open_confirm.rs @@ -165,213 +165,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use rstest::*; - use test_log::test; - - use super::*; - use crate::applications::transfer::MODULE_ID_STR; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::msgs::test_util::get_dummy_raw_counterparty; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; - use crate::core::ics04_channel::msgs::chan_open_confirm::test_util::get_dummy_raw_msg_chan_open_confirm; - use crate::core::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId}; - use crate::core::router::{ModuleId, Router}; - use crate::core::timestamp::ZERO_DURATION; - use crate::mock::client_state::client_type as mock_client_type; - use crate::mock::context::MockContext; - use crate::mock::router::MockRouter; - use crate::test_utils::DummyTransferModule; - use crate::Height; - - pub struct Fixture { - pub context: MockContext, - pub router: MockRouter, - pub module_id: ModuleId, - pub msg: MsgChannelOpenConfirm, - pub client_id_on_b: ClientId, - pub conn_id_on_b: ConnectionId, - pub conn_end_on_b: ConnectionEnd, - pub chan_end_on_b: ChannelEnd, - pub proof_height: u64, - } - - #[fixture] - fn fixture() -> Fixture { - let proof_height = 10; - let context = MockContext::default(); - - let module_id: ModuleId = ModuleId::new(MODULE_ID_STR.to_string()); - let mut router = MockRouter::default(); - router - .add_route(module_id.clone(), DummyTransferModule::new()) - .unwrap(); - - let client_id_on_b = ClientId::new(mock_client_type(), 45).unwrap(); - let conn_id_on_b = ConnectionId::new(2); - let conn_end_on_b = ConnectionEnd::new( - ConnectionState::Open, - client_id_on_b.clone(), - ConnectionCounterparty::try_from(get_dummy_raw_counterparty(Some(0))).unwrap(), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - let msg = - MsgChannelOpenConfirm::try_from(get_dummy_raw_msg_chan_open_confirm(proof_height)) - .unwrap(); - - let chan_end_on_b = ChannelEnd::new( - State::TryOpen, - Order::Unordered, - Counterparty::new(msg.port_id_on_b.clone(), Some(ChannelId::default())), - vec![conn_id_on_b.clone()], - Version::default(), - ) - .unwrap(); - - Fixture { - context, - router, - module_id, - msg, - client_id_on_b, - conn_id_on_b, - conn_end_on_b, - chan_end_on_b, - proof_height, - } - } - - #[rstest] - fn chan_open_confirm_fail_no_channel(fixture: Fixture) { - let Fixture { - context, - msg, - client_id_on_b, - conn_id_on_b, - conn_end_on_b, - proof_height, - .. - } = fixture; - let context = context - .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_b, conn_end_on_b); - - let res = validate(&context, &msg); - - assert!( - res.is_err(), - "Validation fails because no channel exists in the context" - ) - } - - #[rstest] - fn chan_open_confirm_fail_channel_wrong_state(fixture: Fixture) { - let Fixture { - context, - msg, - client_id_on_b, - conn_id_on_b, - conn_end_on_b, - proof_height, - .. - } = fixture; - - let wrong_chan_end = ChannelEnd::new( - State::Init, - Order::Unordered, - Counterparty::new(msg.port_id_on_b.clone(), Some(ChannelId::default())), - vec![conn_id_on_b.clone()], - Version::default(), - ) - .unwrap(); - let context = context - .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_b, conn_end_on_b) - .with_channel( - msg.port_id_on_b.clone(), - ChannelId::default(), - wrong_chan_end, - ); - - let res = validate(&context, &msg); - - assert!( - res.is_err(), - "Validation fails because channel is in the wrong state" - ) - } - - #[rstest] - fn chan_open_confirm_validate_happy_path(fixture: Fixture) { - let Fixture { - context, - msg, - client_id_on_b, - conn_id_on_b, - conn_end_on_b, - chan_end_on_b, - proof_height, - .. - } = fixture; - - let context = context - .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_b, conn_end_on_b) - .with_channel( - msg.port_id_on_b.clone(), - ChannelId::default(), - chan_end_on_b, - ); - - let res = validate(&context, &msg); - - assert!(res.is_ok(), "Validation happy path") - } - - #[rstest] - fn chan_open_confirm_execute_happy_path(fixture: Fixture) { - let Fixture { - context, - mut router, - module_id, - msg, - client_id_on_b, - conn_id_on_b, - conn_end_on_b, - chan_end_on_b, - proof_height, - .. - } = fixture; - - let mut context = context - .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_b, conn_end_on_b) - .with_channel( - msg.port_id_on_b.clone(), - ChannelId::default(), - chan_end_on_b, - ); - - let module = router.get_route_mut(&module_id).unwrap(); - let res = chan_open_confirm_execute(&mut context, module, msg); - - assert!(res.is_ok(), "Execution happy path"); - - assert_eq!(context.events.len(), 2); - assert!(matches!( - context.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(context.events[1], IbcEvent::OpenConfirmChannel(_))); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_open_init.rs b/crates/ibc/src/core/ics04_channel/handler/chan_open_init.rs index cab961ed4..17dc77df3 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_open_init.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_open_init.rs @@ -136,129 +136,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use rstest::*; - use test_log::test; - - use super::*; - use crate::applications::transfer::MODULE_ID_STR; - use crate::clients::ics07_tendermint::client_type as tm_client_type; - use crate::core::ics02_client::height::Height; - use crate::core::ics03_connection::connection::{ConnectionEnd, State as ConnectionState}; - use crate::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::handler::chan_open_init::validate; - use crate::core::ics04_channel::msgs::chan_open_init::test_util::get_dummy_raw_msg_chan_open_init; - use crate::core::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; - use crate::core::ics24_host::identifier::{ClientId, ConnectionId}; - use crate::core::router::{ModuleId, Router}; - use crate::mock::context::MockContext; - use crate::mock::router::MockRouter; - use crate::test_utils::DummyTransferModule; - - pub struct Fixture { - pub ctx: MockContext, - pub router: MockRouter, - pub module_id: ModuleId, - pub msg: MsgChannelOpenInit, - } - - #[fixture] - fn fixture() -> Fixture { - let msg = MsgChannelOpenInit::try_from(get_dummy_raw_msg_chan_open_init(None)).unwrap(); - - let default_ctx = MockContext::default(); - let module_id: ModuleId = ModuleId::new(MODULE_ID_STR.to_string()); - let mut router = MockRouter::default(); - router - .add_route(module_id.clone(), DummyTransferModule::new()) - .unwrap(); - - let msg_conn_init = MsgConnectionOpenInit::new_dummy(); - - let client_id_on_a = ClientId::new(tm_client_type(), 0).unwrap(); - let client_height = Height::new(0, 10).unwrap(); - - let conn_end_on_a = ConnectionEnd::new( - ConnectionState::Init, - msg_conn_init.client_id_on_a.clone(), - msg_conn_init.counterparty.clone(), - get_compatible_versions(), - msg_conn_init.delay_period, - ) - .unwrap(); - - let ctx = default_ctx - .with_client(&client_id_on_a, client_height) - .with_connection(ConnectionId::default(), conn_end_on_a); - - Fixture { - ctx, - router, - module_id, - msg, - } - } - - #[rstest] - fn chan_open_init_fail_no_connection(fixture: Fixture) { - let Fixture { msg, .. } = fixture; - - let res = validate(&MockContext::default(), &msg); - - assert!( - res.is_err(), - "Validation fails because no connection exists in the context" - ) - } - - #[rstest] - fn chan_open_init_validate_happy_path(fixture: Fixture) { - let Fixture { ctx, msg, .. } = fixture; - - let res = validate(&ctx, &msg); - - assert!(res.is_ok(), "Validation succeeds; good parameters") - } - - #[rstest] - fn chan_open_init_validate_counterparty_chan_id_set(fixture: Fixture) { - let Fixture { ctx, .. } = fixture; - - let msg = MsgChannelOpenInit::try_from(get_dummy_raw_msg_chan_open_init(None)).unwrap(); - - let res = validate(&ctx, &msg); - - assert!( - res.is_ok(), - "Validation succeeds even if counterparty channel id is set by relayer" - ) - } - - #[rstest] - fn chan_open_init_execute_happy_path(fixture: Fixture) { - let Fixture { - mut ctx, - mut router, - module_id, - msg, - } = fixture; - let module = router.get_route_mut(&module_id).unwrap(); - - let res = chan_open_init_execute(&mut ctx, module, msg); - - assert!(res.is_ok(), "Execution succeeds; good parameters"); - - assert_eq!(ctx.channel_counter().unwrap(), 1); - - assert_eq!(ctx.events.len(), 2); - - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::OpenInitChannel(_))); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/chan_open_try.rs b/crates/ibc/src/core/ics04_channel/handler/chan_open_try.rs index f140a0832..ff27b0eff 100644 --- a/crates/ibc/src/core/ics04_channel/handler/chan_open_try.rs +++ b/crates/ibc/src/core/ics04_channel/handler/chan_open_try.rs @@ -183,168 +183,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use rstest::*; - use test_log::test; - - use super::*; - use crate::applications::transfer::MODULE_ID_STR; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::msgs::test_util::get_dummy_raw_counterparty; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::msgs::chan_open_try::test_util::get_dummy_raw_msg_chan_open_try; - use crate::core::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; - use crate::core::ics24_host::identifier::{ClientId, ConnectionId}; - use crate::core::router::{ModuleId, Router}; - use crate::core::timestamp::ZERO_DURATION; - use crate::mock::client_state::client_type as mock_client_type; - use crate::mock::context::MockContext; - use crate::mock::router::MockRouter; - use crate::test_utils::DummyTransferModule; - use crate::Height; - - pub struct Fixture { - pub ctx: MockContext, - pub router: MockRouter, - pub module_id: ModuleId, - pub msg: MsgChannelOpenTry, - pub client_id_on_b: ClientId, - pub conn_id_on_b: ConnectionId, - pub conn_end_on_b: ConnectionEnd, - pub proof_height: u64, - } - - #[fixture] - fn fixture() -> Fixture { - let proof_height = 10; - let conn_id_on_b = ConnectionId::new(2); - let client_id_on_b = ClientId::new(mock_client_type(), 45).unwrap(); - - // This is the connection underlying the channel we're trying to open. - let conn_end_on_b = ConnectionEnd::new( - ConnectionState::Open, - client_id_on_b.clone(), - ConnectionCounterparty::try_from(get_dummy_raw_counterparty(Some(0))).unwrap(), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - // We're going to test message processing against this message. - // Note: we make the counterparty's channel_id `None`. - let mut msg = - MsgChannelOpenTry::try_from(get_dummy_raw_msg_chan_open_try(proof_height)).unwrap(); - - let hops = vec![conn_id_on_b.clone()]; - msg.connection_hops_on_b = hops; - - let ctx = MockContext::default(); - - let module_id: ModuleId = ModuleId::new(MODULE_ID_STR.to_string()); - let mut router = MockRouter::default(); - router - .add_route(module_id.clone(), DummyTransferModule::new()) - .unwrap(); - - Fixture { - ctx, - router, - module_id, - msg, - client_id_on_b, - conn_id_on_b, - conn_end_on_b, - proof_height, - } - } - - #[rstest] - fn chan_open_try_fail_no_connection(fixture: Fixture) { - let Fixture { ctx, msg, .. } = fixture; - - let res = validate(&ctx, &msg); - - assert!( - res.is_err(), - "Validation fails because no connection exists in the context" - ) - } - - #[rstest] - fn chan_open_try_fail_no_client_state(fixture: Fixture) { - let Fixture { - ctx, - msg, - conn_id_on_b, - conn_end_on_b, - .. - } = fixture; - let ctx = ctx.with_connection(conn_id_on_b, conn_end_on_b); - - let res = validate(&ctx, &msg); - - assert!( - res.is_err(), - "Validation fails because the context has no client state" - ) - } - - #[rstest] - fn chan_open_try_validate_happy_path(fixture: Fixture) { - let Fixture { - ctx, - msg, - client_id_on_b, - conn_id_on_b, - conn_end_on_b, - proof_height, - .. - } = fixture; - - let ctx = ctx - .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_b, conn_end_on_b); - - let res = validate(&ctx, &msg); - - assert!(res.is_ok(), "Validation success: happy path") - } - - #[rstest] - fn chan_open_try_execute_happy_path(fixture: Fixture) { - let Fixture { - ctx, - mut router, - module_id, - msg, - client_id_on_b, - conn_id_on_b, - conn_end_on_b, - proof_height, - .. - } = fixture; - - let mut ctx = ctx - .with_client(&client_id_on_b, Height::new(0, proof_height).unwrap()) - .with_connection(conn_id_on_b, conn_end_on_b); - - let module = router.get_route_mut(&module_id).unwrap(); - let res = chan_open_try_execute(&mut ctx, module, msg); - - assert!(res.is_ok(), "Execution success: happy path"); - - assert_eq!(ctx.channel_counter().unwrap(), 1); - - assert_eq!(ctx.events.len(), 2); - - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::OpenTryChannel(_))); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/recv_packet.rs b/crates/ibc/src/core/ics04_channel/handler/recv_packet.rs index 1ff78ccd2..f99d184a1 100644 --- a/crates/ibc/src/core/ics04_channel/handler/recv_packet.rs +++ b/crates/ibc/src/core/ics04_channel/handler/recv_packet.rs @@ -279,246 +279,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use rstest::*; - use test_log::test; - - use super::*; - use crate::applications::transfer::MODULE_ID_STR; - use crate::core::ics02_client::ClientExecutionContext; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; - use crate::core::ics04_channel::msgs::recv_packet::test_util::get_dummy_raw_msg_recv_packet; - use crate::core::ics04_channel::msgs::recv_packet::MsgRecvPacket; - use crate::core::ics04_channel::packet::Packet; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; - use crate::core::router::{ModuleId, Router}; - use crate::core::timestamp::{Timestamp, ZERO_DURATION}; - use crate::mock::context::MockContext; - use crate::mock::ics18_relayer::context::RelayerContext; - use crate::mock::router::MockRouter; - use crate::test_utils::{get_dummy_account_id, DummyTransferModule}; - use crate::Height; - - pub struct Fixture { - pub context: MockContext, - pub router: MockRouter, - pub module_id: ModuleId, - pub client_height: Height, - pub host_height: Height, - pub msg: MsgRecvPacket, - pub conn_end_on_b: ConnectionEnd, - pub chan_end_on_b: ChannelEnd, - } - - #[fixture] - fn fixture() -> Fixture { - let context = MockContext::default(); - - let module_id: ModuleId = ModuleId::new(MODULE_ID_STR.to_string()); - let mut router = MockRouter::default(); - router - .add_route(module_id.clone(), DummyTransferModule::new()) - .unwrap(); - - let host_height = context.query_latest_height().unwrap().increment(); - - let client_height = host_height.increment(); - - let msg = MsgRecvPacket::try_from(get_dummy_raw_msg_recv_packet( - client_height.revision_height(), - )) - .unwrap(); - - let packet = msg.packet.clone(); - - let chan_end_on_b = ChannelEnd::new( - State::Open, - Order::default(), - Counterparty::new(packet.port_id_on_a, Some(packet.chan_id_on_a)), - vec![ConnectionId::default()], - Version::new("ics20-1".to_string()), - ) - .unwrap(); - - let conn_end_on_b = ConnectionEnd::new( - ConnectionState::Open, - ClientId::default(), - ConnectionCounterparty::new( - ClientId::default(), - Some(ConnectionId::default()), - Default::default(), - ), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - Fixture { - context, - router, - module_id, - client_height, - host_height, - msg, - conn_end_on_b, - chan_end_on_b, - } - } - - #[rstest] - fn recv_packet_fail_no_channel(fixture: Fixture) { - let Fixture { context, msg, .. } = fixture; - - let res = validate(&context, &msg); - - assert!( - res.is_err(), - "Validation fails because no channel exists in the context" - ) - } - - #[rstest] - fn recv_packet_validate_happy_path(fixture: Fixture) { - let Fixture { - context, - msg, - conn_end_on_b, - chan_end_on_b, - client_height, - host_height, - .. - } = fixture; - - let packet = &msg.packet; - let mut context = context - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_b) - .with_channel( - packet.port_id_on_b.clone(), - packet.chan_id_on_b.clone(), - chan_end_on_b, - ) - .with_send_sequence( - packet.port_id_on_b.clone(), - packet.chan_id_on_b.clone(), - 1.into(), - ) - .with_height(host_height) - // This `with_recv_sequence` is required for ordered channels - .with_recv_sequence( - packet.port_id_on_b.clone(), - packet.chan_id_on_b.clone(), - packet.seq_on_a, - ); - - context - .get_client_execution_context() - .store_update_time( - ClientId::default(), - client_height, - Timestamp::from_nanoseconds(1000).unwrap(), - ) - .unwrap(); - context - .get_client_execution_context() - .store_update_height( - ClientId::default(), - client_height, - Height::new(0, 5).unwrap(), - ) - .unwrap(); - - let res = validate(&context, &msg); - - assert!( - res.is_ok(), - "Happy path: validation should succeed. err: {res:?}" - ) - } - - #[rstest] - fn recv_packet_timeout_expired(fixture: Fixture) { - let Fixture { - context, - msg, - conn_end_on_b, - chan_end_on_b, - client_height, - host_height, - .. - } = fixture; - - let packet_old = Packet { - seq_on_a: 1.into(), - port_id_on_a: PortId::default(), - chan_id_on_a: ChannelId::default(), - port_id_on_b: PortId::default(), - chan_id_on_b: ChannelId::default(), - data: Vec::new(), - timeout_height_on_b: client_height.into(), - timeout_timestamp_on_b: Timestamp::from_nanoseconds(1).unwrap(), - }; - - let msg_packet_old = MsgRecvPacket::new( - packet_old, - msg.proof_commitment_on_a.clone(), - msg.proof_height_on_a, - get_dummy_account_id(), - ); - let context = context - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_b) - .with_channel(PortId::default(), ChannelId::default(), chan_end_on_b) - .with_send_sequence(PortId::default(), ChannelId::default(), 1.into()) - .with_height(host_height); - - let res = validate(&context, &msg_packet_old); - - assert!( - res.is_err(), - "recv_packet validation should fail when the packet has timed out" - ) - } - - #[rstest] - fn recv_packet_execute_happy_path(fixture: Fixture) { - let Fixture { - context, - mut router, - module_id, - msg, - conn_end_on_b, - chan_end_on_b, - client_height, - .. - } = fixture; - let mut ctx = context - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_b) - .with_channel(PortId::default(), ChannelId::default(), chan_end_on_b); - - let module = router.get_route_mut(&module_id).unwrap(); - let res = recv_packet_execute(&mut ctx, module, msg); - - assert!(res.is_ok()); - - assert_eq!(ctx.events.len(), 4); - assert!(matches!( - &ctx.events[0], - &IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(&ctx.events[1], &IbcEvent::ReceivePacket(_))); - assert!(matches!( - &ctx.events[2], - &IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(&ctx.events[3], &IbcEvent::WriteAcknowledgement(_))); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/send_packet.rs b/crates/ibc/src/core/ics04_channel/handler/send_packet.rs index 023bcd88f..4a9134f57 100644 --- a/crates/ibc/src/core/ics04_channel/handler/send_packet.rs +++ b/crates/ibc/src/core/ics04_channel/handler/send_packet.rs @@ -137,194 +137,3 @@ pub fn send_packet_execute( Ok(()) } - -#[cfg(test)] -mod tests { - use core::ops::Add; - use core::time::Duration; - - use test_log::test; - - use super::*; - use crate::core::events::IbcEvent; - use crate::core::ics02_client::height::Height; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; - use crate::core::ics04_channel::handler::send_packet::send_packet; - use crate::core::ics04_channel::packet::test_utils::get_dummy_raw_packet; - use crate::core::ics04_channel::packet::Packet; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; - use crate::core::timestamp::{Timestamp, ZERO_DURATION}; - use crate::mock::context::MockContext; - - #[test] - fn send_packet_processing() { - struct Test { - name: String, - ctx: MockContext, - packet: Packet, - want_pass: bool, - } - - let context = MockContext::default(); - - let chan_end_on_a = ChannelEnd::new( - State::Open, - Order::default(), - Counterparty::new(PortId::default(), Some(ChannelId::default())), - vec![ConnectionId::default()], - Version::new("ics20-1".to_string()), - ) - .unwrap(); - - let conn_end_on_a = ConnectionEnd::new( - ConnectionState::Open, - ClientId::default(), - ConnectionCounterparty::new( - ClientId::default(), - Some(ConnectionId::default()), - Default::default(), - ), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - let timestamp_future = Timestamp::now().add(Duration::from_secs(10)).unwrap(); - let timestamp_ns_past = 1; - - let timeout_height_future = 10; - - let mut packet: Packet = - get_dummy_raw_packet(timeout_height_future, timestamp_future.nanoseconds()) - .try_into() - .unwrap(); - packet.seq_on_a = 1.into(); - packet.data = vec![0]; - - let mut packet_with_timestamp_old: Packet = - get_dummy_raw_packet(timeout_height_future, timestamp_ns_past) - .try_into() - .unwrap(); - packet_with_timestamp_old.seq_on_a = 1.into(); - packet_with_timestamp_old.data = vec![0]; - - let client_raw_height = 5; - let packet_timeout_equal_client_height: Packet = - get_dummy_raw_packet(client_raw_height, timestamp_future.nanoseconds()) - .try_into() - .unwrap(); - let packet_timeout_one_before_client_height: Packet = - get_dummy_raw_packet(client_raw_height - 1, timestamp_future.nanoseconds()) - .try_into() - .unwrap(); - - let client_height = Height::new(0, client_raw_height).unwrap(); - - let tests: Vec = vec![ - Test { - name: "Processing fails because no channel exists in the context".to_string(), - ctx: context.clone(), - packet: packet.clone(), - want_pass: false, - }, - Test { - name: "Good parameters".to_string(), - ctx: context - .clone() - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_a.clone()) - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a.clone(), - ) - .with_send_sequence(PortId::default(), ChannelId::default(), 1.into()), - packet, - want_pass: true, - }, - Test { - name: "Packet timeout height same as destination chain height".to_string(), - ctx: context - .clone() - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_a.clone()) - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a.clone(), - ) - .with_send_sequence(PortId::default(), ChannelId::default(), 1.into()), - packet: packet_timeout_equal_client_height, - want_pass: true, - }, - Test { - name: "Packet timeout height one more than destination chain height".to_string(), - ctx: context - .clone() - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_a.clone()) - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a.clone(), - ) - .with_send_sequence(PortId::default(), ChannelId::default(), 1.into()), - packet: packet_timeout_one_before_client_height, - want_pass: false, - }, - Test { - name: "Packet timeout due to timestamp".to_string(), - ctx: context - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_channel(PortId::default(), ChannelId::default(), chan_end_on_a) - .with_send_sequence(PortId::default(), ChannelId::default(), 1.into()), - packet: packet_with_timestamp_old, - want_pass: false, - }, - ] - .into_iter() - .collect(); - - for mut test in tests { - let res = send_packet(&mut test.ctx, test.packet.clone()); - // Additionally check the events and the output objects in the result. - match res { - Ok(()) => { - assert!( - test.want_pass, - "send_packet: test passed but was supposed to fail for test: {}, \nparams {:?} {:?}", - test.name, - test.packet.clone(), - test.ctx.clone() - ); - - assert!(!test.ctx.events.is_empty()); // Some events must exist. - - assert_eq!(test.ctx.events.len(), 2); - assert!(matches!( - &test.ctx.events[0], - &IbcEvent::Message(MessageEvent::Channel) - )); - // TODO: The object in the output is a PacketResult what can we check on it? - assert!(matches!(&test.ctx.events[1], &IbcEvent::SendPacket(_))); - } - Err(e) => { - assert!( - !test.want_pass, - "send_packet: did not pass test: {}, \nparams {:?} {:?} error: {:?}", - test.name, - test.packet.clone(), - test.ctx.clone(), - e, - ); - } - } - } - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/timeout.rs b/crates/ibc/src/core/ics04_channel/handler/timeout.rs index 26b3623a0..5235f9a8c 100644 --- a/crates/ibc/src/core/ics04_channel/handler/timeout.rs +++ b/crates/ibc/src/core/ics04_channel/handler/timeout.rs @@ -266,429 +266,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use rstest::*; - - use super::*; - use crate::applications::transfer::MODULE_ID_STR; - use crate::core::ics02_client::height::Height; - use crate::core::ics02_client::ClientExecutionContext; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; - use crate::core::ics04_channel::commitment::PacketCommitment; - use crate::core::ics04_channel::handler::timeout::validate; - use crate::core::ics04_channel::msgs::timeout::test_util::get_dummy_raw_msg_timeout; - use crate::core::ics04_channel::msgs::timeout::MsgTimeout; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; - use crate::core::router::{ModuleId, Router}; - use crate::core::timestamp::{Timestamp, ZERO_DURATION}; - use crate::mock::context::MockContext; - use crate::mock::router::MockRouter; - use crate::test_utils::DummyTransferModule; - - struct Fixture { - ctx: MockContext, - pub router: MockRouter, - client_height: Height, - module_id: ModuleId, - msg: MsgTimeout, - packet_commitment: PacketCommitment, - conn_end_on_a: ConnectionEnd, - chan_end_on_a_ordered: ChannelEnd, - chan_end_on_a_unordered: ChannelEnd, - } - - #[fixture] - fn fixture() -> Fixture { - let client_height = Height::new(0, 2).unwrap(); - let ctx = MockContext::default().with_client(&ClientId::default(), client_height); - - let client_height = Height::new(0, 2).unwrap(); - - let module_id: ModuleId = ModuleId::new(MODULE_ID_STR.to_string()); - let mut router = MockRouter::default(); - router - .add_route(module_id.clone(), DummyTransferModule::new()) - .unwrap(); - - let msg_proof_height = 2; - let msg_timeout_height = 5; - let timeout_timestamp = Timestamp::now().nanoseconds(); - - let msg = MsgTimeout::try_from(get_dummy_raw_msg_timeout( - msg_proof_height, - msg_timeout_height, - timeout_timestamp, - )) - .unwrap(); - - let packet = msg.packet.clone(); - - let packet_commitment = compute_packet_commitment( - &msg.packet.data, - &msg.packet.timeout_height_on_b, - &msg.packet.timeout_timestamp_on_b, - ); - - let chan_end_on_a_unordered = ChannelEnd::new( - State::Open, - Order::Unordered, - Counterparty::new(packet.port_id_on_b.clone(), Some(packet.chan_id_on_b)), - vec![ConnectionId::default()], - Version::new("ics20-1".to_string()), - ) - .unwrap(); - - let mut chan_end_on_a_ordered = chan_end_on_a_unordered.clone(); - chan_end_on_a_ordered.ordering = Order::Ordered; - - let conn_end_on_a = ConnectionEnd::new( - ConnectionState::Open, - ClientId::default(), - ConnectionCounterparty::new( - ClientId::default(), - Some(ConnectionId::default()), - Default::default(), - ), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - Fixture { - ctx, - router, - client_height, - module_id, - msg, - packet_commitment, - conn_end_on_a, - chan_end_on_a_ordered, - chan_end_on_a_unordered, - } - } - - #[rstest] - fn timeout_fail_no_channel(fixture: Fixture) { - let Fixture { - ctx, - msg, - client_height, - .. - } = fixture; - let ctx = ctx.with_client(&ClientId::default(), client_height); - let res = validate(&ctx, &msg); - - assert!( - res.is_err(), - "Validation fails because no channel exists in the context" - ) - } - - #[rstest] - fn timeout_fail_no_consensus_state_for_height(fixture: Fixture) { - let Fixture { - ctx, - msg, - chan_end_on_a_unordered, - conn_end_on_a, - packet_commitment, - .. - } = fixture; - - let packet = msg.packet.clone(); - - let ctx = ctx - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_unordered, - ) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_packet_commitment( - packet.port_id_on_a, - packet.chan_id_on_a, - packet.seq_on_a, - packet_commitment, - ); - - let res = validate(&ctx, &msg); - - assert!( - res.is_err(), - "Validation fails because the client does not have a consensus state for the required height" - ) - } - - #[rstest] - fn timeout_fail_proof_timeout_not_reached(fixture: Fixture) { - let Fixture { - ctx, - mut msg, - chan_end_on_a_unordered, - conn_end_on_a, - client_height, - .. - } = fixture; - - // timeout timestamp has not reached yet - let timeout_timestamp_on_b = - (msg.packet.timeout_timestamp_on_b + core::time::Duration::new(10, 0)).unwrap(); - msg.packet.timeout_timestamp_on_b = timeout_timestamp_on_b; - let packet_commitment = compute_packet_commitment( - &msg.packet.data, - &msg.packet.timeout_height_on_b, - &msg.packet.timeout_timestamp_on_b, - ); - - let packet = msg.packet.clone(); - - let mut ctx = ctx - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_unordered, - ) - .with_packet_commitment( - packet.port_id_on_a, - packet.chan_id_on_a, - packet.seq_on_a, - packet_commitment, - ); - - ctx.store_update_time( - ClientId::default(), - client_height, - Timestamp::from_nanoseconds(5).unwrap(), - ) - .unwrap(); - ctx.store_update_height( - ClientId::default(), - client_height, - Height::new(0, 4).unwrap(), - ) - .unwrap(); - - let res = validate(&ctx, &msg); - - assert!( - res.is_err(), - "Validation should fail because the timeout height was reached, but the timestamp hasn't been reached. Both the height and timestamp need to be reached for the packet to be considered timed out" - ) - } - - /// NO-OP case - #[rstest] - fn timeout_success_no_packet_commitment(fixture: Fixture) { - let Fixture { - ctx, - msg, - conn_end_on_a, - chan_end_on_a_unordered, - .. - } = fixture; - let ctx = ctx - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_unordered, - ) - .with_connection(ConnectionId::default(), conn_end_on_a); - - let res = validate(&ctx, &msg); - - assert!( - res.is_ok(), - "Validation should succeed when no packet commitment is present" - ) - } - - #[rstest] - fn timeout_unordered_channel_validate(fixture: Fixture) { - let Fixture { - ctx, - msg, - chan_end_on_a_unordered, - conn_end_on_a, - packet_commitment, - client_height, - .. - } = fixture; - - let packet = msg.packet.clone(); - - let mut ctx = ctx - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_unordered, - ) - .with_packet_commitment( - packet.port_id_on_a, - packet.chan_id_on_a, - packet.seq_on_a, - packet_commitment, - ); - - ctx.get_client_execution_context() - .store_update_time( - ClientId::default(), - client_height, - Timestamp::from_nanoseconds(1000).unwrap(), - ) - .unwrap(); - ctx.get_client_execution_context() - .store_update_height( - ClientId::default(), - client_height, - Height::new(0, 5).unwrap(), - ) - .unwrap(); - - let res = validate(&ctx, &msg); - - assert!(res.is_ok(), "Good parameters for unordered channels") - } - - #[rstest] - fn timeout_ordered_channel_validate(fixture: Fixture) { - let Fixture { - ctx, - msg, - chan_end_on_a_ordered, - conn_end_on_a, - packet_commitment, - client_height, - .. - } = fixture; - - let packet = msg.packet.clone(); - - let mut ctx = ctx - .with_client(&ClientId::default(), client_height) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_ordered, - ) - .with_packet_commitment( - packet.port_id_on_a, - packet.chan_id_on_a, - packet.seq_on_a, - packet_commitment, - ); - - ctx.store_update_time( - ClientId::default(), - client_height, - Timestamp::from_nanoseconds(1000).unwrap(), - ) - .unwrap(); - ctx.store_update_height( - ClientId::default(), - client_height, - Height::new(0, 4).unwrap(), - ) - .unwrap(); - - let res = validate(&ctx, &msg); - - assert!(res.is_ok(), "Good parameters for unordered channels") - } - - #[rstest] - fn timeout_unordered_chan_execute(fixture: Fixture) { - let Fixture { - ctx, - mut router, - module_id, - msg, - packet_commitment, - conn_end_on_a, - chan_end_on_a_unordered, - .. - } = fixture; - let mut ctx = ctx - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_unordered, - ) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_packet_commitment( - msg.packet.port_id_on_a.clone(), - msg.packet.chan_id_on_a.clone(), - msg.packet.seq_on_a, - packet_commitment, - ); - - let module = router.get_route_mut(&module_id).unwrap(); - let res = timeout_packet_execute(&mut ctx, module, TimeoutMsgType::Timeout(msg)); - - assert!(res.is_ok()); - - // Unordered channels only emit one event - assert_eq!(ctx.events.len(), 2); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::TimeoutPacket(_))); - } - - #[rstest] - fn timeout_ordered_chan_execute(fixture: Fixture) { - let Fixture { - ctx, - mut router, - module_id, - msg, - packet_commitment, - conn_end_on_a, - chan_end_on_a_ordered, - .. - } = fixture; - let mut ctx = ctx - .with_channel( - PortId::default(), - ChannelId::default(), - chan_end_on_a_ordered, - ) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_packet_commitment( - msg.packet.port_id_on_a.clone(), - msg.packet.chan_id_on_a.clone(), - msg.packet.seq_on_a, - packet_commitment, - ); - - let module = router.get_route_mut(&module_id).unwrap(); - let res = timeout_packet_execute(&mut ctx, module, TimeoutMsgType::Timeout(msg)); - - assert!(res.is_ok()); - - // Ordered channels emit 2 events - assert_eq!(ctx.events.len(), 4); - assert!(matches!( - ctx.events[0], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[1], IbcEvent::TimeoutPacket(_))); - assert!(matches!( - ctx.events[2], - IbcEvent::Message(MessageEvent::Channel) - )); - assert!(matches!(ctx.events[3], IbcEvent::ChannelClosed(_))); - } -} diff --git a/crates/ibc/src/core/ics04_channel/handler/timeout_on_close.rs b/crates/ibc/src/core/ics04_channel/handler/timeout_on_close.rs index 509d1380c..cb0a9b40a 100644 --- a/crates/ibc/src/core/ics04_channel/handler/timeout_on_close.rs +++ b/crates/ibc/src/core/ics04_channel/handler/timeout_on_close.rs @@ -170,166 +170,3 @@ where Ok(()) } - -#[cfg(test)] -mod tests { - use rstest::*; - - use crate::core::ics02_client::ClientExecutionContext; - use crate::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnectionCounterparty, State as ConnectionState, - }; - use crate::core::ics03_connection::version::get_compatible_versions; - use crate::core::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; - use crate::core::ics04_channel::commitment::{compute_packet_commitment, PacketCommitment}; - use crate::core::ics04_channel::handler::timeout_on_close::validate; - use crate::core::ics04_channel::msgs::timeout_on_close::test_util::get_dummy_raw_msg_timeout_on_close; - use crate::core::ics04_channel::msgs::timeout_on_close::MsgTimeoutOnClose; - use crate::core::ics04_channel::Version; - use crate::core::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; - use crate::core::timestamp::{Timestamp, ZERO_DURATION}; - use crate::core::ExecutionContext; - use crate::mock::context::MockContext; - use crate::prelude::*; - use crate::Height; - - pub struct Fixture { - pub context: MockContext, - pub msg: MsgTimeoutOnClose, - pub packet_commitment: PacketCommitment, - pub conn_end_on_a: ConnectionEnd, - pub chan_end_on_a: ChannelEnd, - } - - #[fixture] - fn fixture() -> Fixture { - let client_height = Height::new(0, 2).unwrap(); - let context = MockContext::default().with_client(&ClientId::default(), client_height); - - let height = 2; - let timeout_timestamp = 5; - - let msg = MsgTimeoutOnClose::try_from(get_dummy_raw_msg_timeout_on_close( - height, - timeout_timestamp, - )) - .unwrap(); - - let packet = msg.packet.clone(); - - let packet_commitment = compute_packet_commitment( - &msg.packet.data, - &msg.packet.timeout_height_on_b, - &msg.packet.timeout_timestamp_on_b, - ); - - let chan_end_on_a = ChannelEnd::new( - State::Open, - Order::Ordered, - Counterparty::new(packet.port_id_on_b.clone(), Some(packet.chan_id_on_b)), - vec![ConnectionId::default()], - Version::new("ics20-1".to_string()), - ) - .unwrap(); - - let conn_end_on_a = ConnectionEnd::new( - ConnectionState::Open, - ClientId::default(), - ConnectionCounterparty::new( - ClientId::default(), - Some(ConnectionId::default()), - Default::default(), - ), - get_compatible_versions(), - ZERO_DURATION, - ) - .unwrap(); - - Fixture { - context, - msg, - packet_commitment, - conn_end_on_a, - chan_end_on_a, - } - } - - #[rstest] - fn timeout_on_close_fail_no_channel(fixture: Fixture) { - let Fixture { context, msg, .. } = fixture; - - let res = validate(&context, &msg); - - assert!( - res.is_err(), - "Validation fails because no channel exists in the context" - ) - } - - /// NO-OP case - #[rstest] - fn timeout_on_close_success_no_packet_commitment(fixture: Fixture) { - let Fixture { - context, - msg, - conn_end_on_a, - chan_end_on_a, - .. - } = fixture; - let context = context - .with_channel(PortId::default(), ChannelId::default(), chan_end_on_a) - .with_connection(ConnectionId::default(), conn_end_on_a); - - let res = validate(&context, &msg); - - assert!( - res.is_ok(), - "Validation should succeed when no packet commitment is present" - ) - } - - #[rstest] - fn timeout_on_close_success_happy_path(fixture: Fixture) { - let Fixture { - context, - msg, - packet_commitment, - conn_end_on_a, - chan_end_on_a, - .. - } = fixture; - let mut context = context - .with_channel(PortId::default(), ChannelId::default(), chan_end_on_a) - .with_connection(ConnectionId::default(), conn_end_on_a) - .with_packet_commitment( - msg.packet.port_id_on_a.clone(), - msg.packet.chan_id_on_a.clone(), - msg.packet.seq_on_a, - packet_commitment, - ); - - context - .get_client_execution_context() - .store_update_time( - ClientId::default(), - Height::new(0, 2).unwrap(), - Timestamp::from_nanoseconds(5000).unwrap(), - ) - .unwrap(); - context - .get_client_execution_context() - .store_update_height( - ClientId::default(), - Height::new(0, 2).unwrap(), - Height::new(0, 5).unwrap(), - ) - .unwrap(); - - let res = validate(&context, &msg); - - assert!( - res.is_ok(), - "Happy path: validation should succeed. err: {res:?}" - ) - } -} diff --git a/crates/ibc/src/core/ics04_channel/msgs.rs b/crates/ibc/src/core/ics04_channel/msgs.rs index 392a0c988..4b8c39bda 100644 --- a/crates/ibc/src/core/ics04_channel/msgs.rs +++ b/crates/ibc/src/core/ics04_channel/msgs.rs @@ -3,16 +3,16 @@ use crate::prelude::*; -pub(crate) mod acknowledgement; -pub(crate) mod chan_close_confirm; -pub(crate) mod chan_close_init; -pub(crate) mod chan_open_ack; -pub(crate) mod chan_open_confirm; -pub(crate) mod chan_open_init; -pub(crate) mod chan_open_try; -pub(crate) mod recv_packet; -pub(crate) mod timeout; -pub(crate) mod timeout_on_close; +pub mod acknowledgement; +pub mod chan_close_confirm; +pub mod chan_close_init; +pub mod chan_open_ack; +pub mod chan_open_confirm; +pub mod chan_open_init; +pub mod chan_open_try; +pub mod recv_packet; +pub mod timeout; +pub mod timeout_on_close; // Opening handshake messages. // Packet specific messages. diff --git a/crates/ibc/src/core/ics04_channel/msgs/acknowledgement.rs b/crates/ibc/src/core/ics04_channel/msgs/acknowledgement.rs index 7612f34a0..7195be630 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/acknowledgement.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/acknowledgement.rs @@ -76,49 +76,16 @@ impl From for RawMsgAcknowledgement { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::{ - MsgAcknowledgement as RawMsgAcknowledgement, Packet as RawPacket, - }; - use ibc_proto::ibc::core::client::v1::Height as RawHeight; - - use crate::core::ics04_channel::packet::test_utils::get_dummy_raw_packet; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Returns a dummy `RawMsgAcknowledgement`, for testing only! - /// The `height` parametrizes both the proof height as well as the timeout height. - pub fn get_dummy_raw_msg_acknowledgement(height: u64) -> RawMsgAcknowledgement { - get_dummy_raw_msg_ack_with_packet(get_dummy_raw_packet(height, 1), height) - } - - pub fn get_dummy_raw_msg_ack_with_packet( - packet: RawPacket, - height: u64, - ) -> RawMsgAcknowledgement { - RawMsgAcknowledgement { - packet: Some(packet), - acknowledgement: get_dummy_proof(), - proof_acked: get_dummy_proof(), - proof_height: Some(RawHeight { - revision_number: 0, - revision_height: height, - }), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod test { use ibc_proto::ibc::core::channel::v1::MsgAcknowledgement as RawMsgAcknowledgement; + use ibc_testkit::utils::core::channel::dummy_raw_msg_acknowledgement; + use ibc_testkit::utils::core::signer::dummy_bech32_account; use test_log::test; use crate::core::ics04_channel::error::PacketError; - use crate::core::ics04_channel::msgs::acknowledgement::test_util::get_dummy_raw_msg_acknowledgement; use crate::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; use crate::prelude::*; - use crate::test_utils::get_dummy_bech32_account; #[test] fn msg_acknowledgment_try_from_raw() { @@ -129,7 +96,7 @@ mod test { } let height = 50; - let default_raw_msg = get_dummy_raw_msg_acknowledgement(height); + let default_raw_msg = dummy_raw_msg_acknowledgement(height); let tests: Vec = vec![ Test { @@ -156,7 +123,7 @@ mod test { Test { name: "Empty signer".to_string(), raw: RawMsgAcknowledgement { - signer: get_dummy_bech32_account(), + signer: dummy_bech32_account(), ..default_raw_msg.clone() }, want_pass: true, diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_close_confirm.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_close_confirm.rs index d183db53d..d9c9867c1 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_close_confirm.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_close_confirm.rs @@ -72,36 +72,12 @@ impl From for RawMsgChannelCloseConfirm { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::MsgChannelCloseConfirm as RawMsgChannelCloseConfirm; - use ibc_proto::ibc::core::client::v1::Height; - - use crate::core::ics24_host::identifier::{ChannelId, PortId}; - use crate::prelude::*; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Returns a dummy `RawMsgChannelCloseConfirm`, for testing only! - pub fn get_dummy_raw_msg_chan_close_confirm(proof_height: u64) -> RawMsgChannelCloseConfirm { - RawMsgChannelCloseConfirm { - port_id: PortId::default().to_string(), - channel_id: ChannelId::default().to_string(), - proof_init: get_dummy_proof(), - proof_height: Some(Height { - revision_number: 0, - revision_height: proof_height, - }), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::channel::v1::MsgChannelCloseConfirm as RawMsgChannelCloseConfirm; use ibc_proto::ibc::core::client::v1::Height; + use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_close_confirm; - use crate::core::ics04_channel::msgs::chan_close_confirm::test_util::get_dummy_raw_msg_chan_close_confirm; use crate::core::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; use crate::prelude::*; @@ -114,7 +90,7 @@ mod tests { } let proof_height = 10; - let default_raw_msg = get_dummy_raw_msg_chan_close_confirm(proof_height); + let default_raw_msg = dummy_raw_msg_chan_close_confirm(proof_height); let tests: Vec = vec![ Test { @@ -205,7 +181,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_chan_close_confirm(19); + let raw = dummy_raw_msg_chan_close_confirm(19); let msg = MsgChannelCloseConfirm::try_from(raw.clone()).unwrap(); let raw_back = RawMsgChannelCloseConfirm::from(msg.clone()); let msg_back = MsgChannelCloseConfirm::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_close_init.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_close_init.rs index 7f0c1e04f..dbbea054b 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_close_init.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_close_init.rs @@ -57,30 +57,12 @@ impl From for RawMsgChannelCloseInit { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::MsgChannelCloseInit as RawMsgChannelCloseInit; - - use crate::core::ics24_host::identifier::{ChannelId, PortId}; - use crate::prelude::*; - use crate::test_utils::get_dummy_bech32_account; - - /// Returns a dummy `RawMsgChannelCloseInit`, for testing only! - pub fn get_dummy_raw_msg_chan_close_init() -> RawMsgChannelCloseInit { - RawMsgChannelCloseInit { - port_id: PortId::default().to_string(), - channel_id: ChannelId::default().to_string(), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::channel::v1::MsgChannelCloseInit as RawMsgChannelCloseInit; + use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_close_init; use test_log::test; - use crate::core::ics04_channel::msgs::chan_close_init::test_util::get_dummy_raw_msg_chan_close_init; use crate::core::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; use crate::prelude::*; @@ -92,7 +74,7 @@ mod tests { want_pass: bool, } - let default_raw_msg = get_dummy_raw_msg_chan_close_init(); + let default_raw_msg = dummy_raw_msg_chan_close_init(); let tests: Vec = vec![ Test { @@ -168,7 +150,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_chan_close_init(); + let raw = dummy_raw_msg_chan_close_init(); let msg = MsgChannelCloseInit::try_from(raw.clone()).unwrap(); let raw_back = RawMsgChannelCloseInit::from(msg.clone()); let msg_back = MsgChannelCloseInit::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_open_ack.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_open_ack.rs index 3b230d1c9..65bbb1f4b 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_open_ack.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_open_ack.rs @@ -77,39 +77,13 @@ impl From for RawMsgChannelOpenAck { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::MsgChannelOpenAck as RawMsgChannelOpenAck; - use ibc_proto::ibc::core::client::v1::Height; - - use crate::core::ics24_host::identifier::{ChannelId, PortId}; - use crate::prelude::*; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Returns a dummy `RawMsgChannelOpenAck`, for testing only! - pub fn get_dummy_raw_msg_chan_open_ack(proof_height: u64) -> RawMsgChannelOpenAck { - RawMsgChannelOpenAck { - port_id: PortId::default().to_string(), - channel_id: ChannelId::default().to_string(), - counterparty_channel_id: ChannelId::default().to_string(), - counterparty_version: "".to_string(), - proof_try: get_dummy_proof(), - proof_height: Some(Height { - revision_number: 0, - revision_height: proof_height, - }), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::channel::v1::MsgChannelOpenAck as RawMsgChannelOpenAck; use ibc_proto::ibc::core::client::v1::Height; + use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_open_ack; use test_log::test; - use crate::core::ics04_channel::msgs::chan_open_ack::test_util::get_dummy_raw_msg_chan_open_ack; use crate::core::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; use crate::prelude::*; @@ -122,7 +96,7 @@ mod tests { } let proof_height = 20; - let default_raw_msg = get_dummy_raw_msg_chan_open_ack(proof_height); + let default_raw_msg = dummy_raw_msg_chan_open_ack(proof_height); let tests: Vec = vec![ Test { @@ -265,7 +239,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_chan_open_ack(100); + let raw = dummy_raw_msg_chan_open_ack(100); let msg = MsgChannelOpenAck::try_from(raw.clone()).unwrap(); let raw_back = RawMsgChannelOpenAck::from(msg.clone()); let msg_back = MsgChannelOpenAck::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_open_confirm.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_open_confirm.rs index 86e208e77..01a03e16e 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_open_confirm.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_open_confirm.rs @@ -72,37 +72,13 @@ impl From for RawMsgChannelOpenConfirm { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::MsgChannelOpenConfirm as RawMsgChannelOpenConfirm; - use ibc_proto::ibc::core::client::v1::Height; - - use crate::core::ics24_host::identifier::{ChannelId, PortId}; - use crate::prelude::*; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Returns a dummy `RawMsgChannelOpenConfirm`, for testing only! - pub fn get_dummy_raw_msg_chan_open_confirm(proof_height: u64) -> RawMsgChannelOpenConfirm { - RawMsgChannelOpenConfirm { - port_id: PortId::default().to_string(), - channel_id: ChannelId::default().to_string(), - proof_ack: get_dummy_proof(), - proof_height: Some(Height { - revision_number: 0, - revision_height: proof_height, - }), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::channel::v1::MsgChannelOpenConfirm as RawMsgChannelOpenConfirm; use ibc_proto::ibc::core::client::v1::Height; + use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_open_confirm; use test_log::test; - use crate::core::ics04_channel::msgs::chan_open_confirm::test_util::get_dummy_raw_msg_chan_open_confirm; use crate::core::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; use crate::prelude::*; @@ -115,7 +91,7 @@ mod tests { } let proof_height = 78; - let default_raw_msg = get_dummy_raw_msg_chan_open_confirm(proof_height); + let default_raw_msg = dummy_raw_msg_chan_open_confirm(proof_height); let tests: Vec = vec![ Test { @@ -210,7 +186,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_chan_open_confirm(19); + let raw = dummy_raw_msg_chan_open_confirm(19); let msg = MsgChannelOpenConfirm::try_from(raw.clone()).unwrap(); let raw_back = RawMsgChannelOpenConfirm::from(msg.clone()); let msg_back = MsgChannelOpenConfirm::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_open_init.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_open_init.rs index b16c27525..c9bc91718 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_open_init.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_open_init.rs @@ -91,33 +91,12 @@ impl From for RawMsgChannelOpenInit { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::MsgChannelOpenInit as RawMsgChannelOpenInit; - - use crate::core::ics04_channel::channel::test_util::get_dummy_raw_channel_end; - use crate::core::ics24_host::identifier::PortId; - use crate::prelude::*; - use crate::test_utils::get_dummy_bech32_account; - - /// Returns a dummy `RawMsgChannelOpenInit`, for testing only! - pub fn get_dummy_raw_msg_chan_open_init( - counterparty_channel_id: Option, - ) -> RawMsgChannelOpenInit { - RawMsgChannelOpenInit { - port_id: PortId::default().to_string(), - channel: Some(get_dummy_raw_channel_end(1, counterparty_channel_id)), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::channel::v1::MsgChannelOpenInit as RawMsgChannelOpenInit; + use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_open_init; use test_log::test; - use crate::core::ics04_channel::msgs::chan_open_init::test_util::get_dummy_raw_msg_chan_open_init; use crate::core::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; use crate::prelude::*; @@ -129,7 +108,7 @@ mod tests { want_pass: bool, } - let default_raw_init_msg = get_dummy_raw_msg_chan_open_init(None); + let default_raw_init_msg = dummy_raw_msg_chan_open_init(None); let tests: Vec = vec![ Test { @@ -174,7 +153,7 @@ mod tests { #[test] fn to_and_from() { // Check if raw and domain types are equal after conversions - let raw = get_dummy_raw_msg_chan_open_init(None); + let raw = dummy_raw_msg_chan_open_init(None); let msg = MsgChannelOpenInit::try_from(raw.clone()).unwrap(); let raw_back = RawMsgChannelOpenInit::from(msg.clone()); let msg_back = MsgChannelOpenInit::try_from(raw_back.clone()).unwrap(); @@ -183,7 +162,7 @@ mod tests { // Check if handler sets counterparty channel id to `None` // in case relayer passes `MsgChannelOpenInit` message with it set to `Some(_)` - let raw_with_counterpary_chan_id_some = get_dummy_raw_msg_chan_open_init(None); + let raw_with_counterpary_chan_id_some = dummy_raw_msg_chan_open_init(None); let msg_with_counterpary_chan_id_some = MsgChannelOpenInit::try_from(raw_with_counterpary_chan_id_some).unwrap(); let raw_with_counterpary_chan_id_some_back = diff --git a/crates/ibc/src/core/ics04_channel/msgs/chan_open_try.rs b/crates/ibc/src/core/ics04_channel/msgs/chan_open_try.rs index 3eca4a0c4..ae9c60fc2 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/chan_open_try.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/chan_open_try.rs @@ -128,41 +128,13 @@ impl From for RawMsgChannelOpenTry { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; - use ibc_proto::ibc::core::client::v1::Height; - - use crate::core::ics04_channel::channel::test_util::get_dummy_raw_channel_end; - use crate::core::ics24_host::identifier::PortId; - use crate::prelude::*; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Returns a dummy `RawMsgChannelOpenTry`, for testing only! - pub fn get_dummy_raw_msg_chan_open_try(proof_height: u64) -> RawMsgChannelOpenTry { - #[allow(deprecated)] - RawMsgChannelOpenTry { - port_id: PortId::default().to_string(), - previous_channel_id: "".to_string(), - channel: Some(get_dummy_raw_channel_end(2, Some(0))), - counterparty_version: "".to_string(), - proof_init: get_dummy_proof(), - proof_height: Some(Height { - revision_number: 0, - revision_height: proof_height, - }), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::channel::v1::MsgChannelOpenTry as RawMsgChannelOpenTry; use ibc_proto::ibc::core::client::v1::Height; + use ibc_testkit::utils::core::channel::dummy_raw_msg_chan_open_try; use test_log::test; - use crate::core::ics04_channel::msgs::chan_open_try::test_util::get_dummy_raw_msg_chan_open_try; use crate::core::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; use crate::prelude::*; @@ -175,7 +147,7 @@ mod tests { } let proof_height = 10; - let default_raw_msg = get_dummy_raw_msg_chan_open_try(proof_height); + let default_raw_msg = dummy_raw_msg_chan_open_try(proof_height); let tests: Vec = vec![ Test { @@ -270,7 +242,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_chan_open_try(10); + let raw = dummy_raw_msg_chan_open_try(10); let msg = MsgChannelOpenTry::try_from(raw.clone()).unwrap(); let raw_back = RawMsgChannelOpenTry::from(msg.clone()); let msg_back = MsgChannelOpenTry::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics04_channel/msgs/recv_packet.rs b/crates/ibc/src/core/ics04_channel/msgs/recv_packet.rs index a39f1ec10..91bebda00 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/recv_packet.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/recv_packet.rs @@ -74,67 +74,16 @@ impl From for RawMsgRecvPacket { } } -#[cfg(test)] -pub mod test_util { - use core::ops::Add; - use core::time::Duration; - - use ibc_proto::ibc::core::channel::v1::MsgRecvPacket as RawMsgRecvPacket; - use ibc_proto::ibc::core::client::v1::Height as RawHeight; - - use super::MsgRecvPacket; - use crate::core::ics04_channel::packet::test_utils::get_dummy_raw_packet; - use crate::core::ics04_channel::packet::Packet; - use crate::core::ics23_commitment::commitment::CommitmentProofBytes; - use crate::core::timestamp::Timestamp; - use crate::signer::Signer; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - impl MsgRecvPacket { - pub fn new( - packet: Packet, - proof_commitment_on_a: CommitmentProofBytes, - proof_height_on_a: crate::Height, - signer: Signer, - ) -> MsgRecvPacket { - Self { - packet, - proof_commitment_on_a, - proof_height_on_a, - signer, - } - } - } - - /// Returns a dummy `RawMsgRecvPacket`, for testing only! The `height` parametrizes both the - /// proof height as well as the timeout height. - pub fn get_dummy_raw_msg_recv_packet(height: u64) -> RawMsgRecvPacket { - let timestamp = Timestamp::now().add(Duration::from_secs(9)); - RawMsgRecvPacket { - packet: Some(get_dummy_raw_packet( - height, - timestamp.unwrap().nanoseconds(), - )), - proof_commitment: get_dummy_proof(), - proof_height: Some(RawHeight { - revision_number: 0, - revision_height: height, - }), - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod test { use ibc_proto::ibc::core::channel::v1::MsgRecvPacket as RawMsgRecvPacket; + use ibc_testkit::utils::core::channel::dummy_raw_msg_recv_packet; + use ibc_testkit::utils::core::signer::dummy_bech32_account; use test_log::test; use crate::core::ics04_channel::error::PacketError; - use crate::core::ics04_channel::msgs::recv_packet::test_util::get_dummy_raw_msg_recv_packet; use crate::core::ics04_channel::msgs::recv_packet::MsgRecvPacket; use crate::prelude::*; - use crate::test_utils::get_dummy_bech32_account; #[test] fn msg_recv_packet_try_from_raw() { @@ -145,7 +94,7 @@ mod test { } let height = 20; - let default_raw_msg = get_dummy_raw_msg_recv_packet(height); + let default_raw_msg = dummy_raw_msg_recv_packet(height); let tests: Vec = vec![ Test { name: "Good parameters".to_string(), @@ -171,7 +120,7 @@ mod test { Test { name: "Empty signer".to_string(), raw: RawMsgRecvPacket { - signer: get_dummy_bech32_account(), + signer: dummy_bech32_account(), ..default_raw_msg }, want_pass: true, @@ -194,7 +143,7 @@ mod test { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_recv_packet(15); + let raw = dummy_raw_msg_recv_packet(15); let msg = MsgRecvPacket::try_from(raw.clone()).unwrap(); let raw_back = RawMsgRecvPacket::from(msg.clone()); let msg_back = MsgRecvPacket::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics04_channel/msgs/timeout.rs b/crates/ibc/src/core/ics04_channel/msgs/timeout.rs index 6f854d70a..55dd4bc62 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/timeout.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/timeout.rs @@ -77,44 +77,16 @@ impl From for RawMsgTimeout { } } -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::MsgTimeout as RawMsgTimeout; - use ibc_proto::ibc::core::client::v1::Height as RawHeight; - - use crate::core::ics04_channel::packet::test_utils::get_dummy_raw_packet; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Returns a dummy `RawMsgTimeout`, for testing only! - /// The `height` parametrizes both the proof height as well as the timeout height. - pub fn get_dummy_raw_msg_timeout( - proof_height: u64, - timeout_height: u64, - timeout_timestamp: u64, - ) -> RawMsgTimeout { - RawMsgTimeout { - packet: Some(get_dummy_raw_packet(timeout_height, timeout_timestamp)), - proof_unreceived: get_dummy_proof(), - proof_height: Some(RawHeight { - revision_number: 0, - revision_height: proof_height, - }), - next_sequence_recv: 1, - signer: get_dummy_bech32_account(), - } - } -} - #[cfg(test)] mod test { use ibc_proto::ibc::core::channel::v1::MsgTimeout as RawMsgTimeout; + use ibc_testkit::utils::core::channel::dummy_raw_msg_timeout; + use ibc_testkit::utils::core::signer::dummy_bech32_account; use test_log::test; use crate::core::ics04_channel::error::PacketError; - use crate::core::ics04_channel::msgs::timeout::test_util::get_dummy_raw_msg_timeout; use crate::core::ics04_channel::msgs::timeout::MsgTimeout; use crate::prelude::*; - use crate::test_utils::get_dummy_bech32_account; #[test] fn msg_timeout_try_from_raw() { @@ -128,7 +100,7 @@ mod test { let timeout_height = proof_height; let timeout_timestamp = 0; let default_raw_msg = - get_dummy_raw_msg_timeout(proof_height, timeout_height, timeout_timestamp); + dummy_raw_msg_timeout(proof_height, timeout_height, timeout_timestamp); let tests: Vec = vec![ Test { @@ -163,7 +135,7 @@ mod test { Test { name: "Empty signer".to_string(), raw: RawMsgTimeout { - signer: get_dummy_bech32_account(), + signer: dummy_bech32_account(), ..default_raw_msg }, want_pass: true, @@ -186,7 +158,8 @@ mod test { #[test] fn to_and_from() { - let raw = get_dummy_raw_msg_timeout(15, 20, 0); + let dummy_raw_msg_timeout = dummy_raw_msg_timeout(15, 20, 0); + let raw = dummy_raw_msg_timeout; let msg = MsgTimeout::try_from(raw.clone()).unwrap(); let raw_back = RawMsgTimeout::from(msg.clone()); let msg_back = MsgTimeout::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics04_channel/msgs/timeout_on_close.rs b/crates/ibc/src/core/ics04_channel/msgs/timeout_on_close.rs index 74bd6de8c..c590e2bf2 100644 --- a/crates/ibc/src/core/ics04_channel/msgs/timeout_on_close.rs +++ b/crates/ibc/src/core/ics04_channel/msgs/timeout_on_close.rs @@ -86,9 +86,9 @@ impl From for RawMsgTimeoutOnClose { #[cfg(test)] mod tests { use ibc_proto::ibc::core::channel::v1::MsgTimeoutOnClose as RawMsgTimeoutOnClose; + use ibc_testkit::utils::core::channel::dummy_raw_msg_timeout_on_close; use test_log::test; - use crate::core::ics04_channel::msgs::timeout_on_close::test_util::get_dummy_raw_msg_timeout_on_close; use crate::core::ics04_channel::msgs::timeout_on_close::MsgTimeoutOnClose; use crate::prelude::*; @@ -96,7 +96,7 @@ mod tests { fn msg_timeout_on_close_try_from_raw() { let height = 50; let timeout_timestamp = 5; - let raw = get_dummy_raw_msg_timeout_on_close(height, timeout_timestamp); + let raw = dummy_raw_msg_timeout_on_close(height, timeout_timestamp); let msg = MsgTimeoutOnClose::try_from(raw.clone()).unwrap(); let raw_back = RawMsgTimeoutOnClose::from(msg); @@ -113,7 +113,7 @@ mod tests { let height = 50; let timeout_timestamp = 5; - let default_raw_msg = get_dummy_raw_msg_timeout_on_close(height, timeout_timestamp); + let default_raw_msg = dummy_raw_msg_timeout_on_close(height, timeout_timestamp); let tests: Vec = vec![ Test { @@ -169,31 +169,3 @@ mod tests { } } } - -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::channel::v1::MsgTimeoutOnClose as RawMsgTimeoutOnClose; - use ibc_proto::ibc::core::client::v1::Height as RawHeight; - - use crate::core::ics04_channel::packet::test_utils::get_dummy_raw_packet; - use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof}; - - /// Returns a dummy `RawMsgTimeoutOnClose`, for testing only! - /// The `height` parametrizes both the proof height as well as the timeout height. - pub fn get_dummy_raw_msg_timeout_on_close( - height: u64, - timeout_timestamp: u64, - ) -> RawMsgTimeoutOnClose { - RawMsgTimeoutOnClose { - packet: Some(get_dummy_raw_packet(height, timeout_timestamp)), - proof_unreceived: get_dummy_proof(), - proof_close: get_dummy_proof(), - proof_height: Some(RawHeight { - revision_number: 0, - revision_height: height, - }), - next_sequence_recv: 1, - signer: get_dummy_bech32_account(), - } - } -} diff --git a/crates/ibc/src/core/ics04_channel/packet.rs b/crates/ibc/src/core/ics04_channel/packet.rs index b1914dcbf..eba1ca8e9 100644 --- a/crates/ibc/src/core/ics04_channel/packet.rs +++ b/crates/ibc/src/core/ics04_channel/packet.rs @@ -129,7 +129,7 @@ impl core::fmt::Display for Sequence { )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Default, Hash, PartialEq, Eq)] +#[derive(Clone, Hash, PartialEq, Eq)] pub struct Packet { pub seq_on_a: Sequence, pub port_id_on_a: PortId, @@ -304,7 +304,7 @@ impl From for RawPacket { )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, Default, Hash, PartialEq, Eq)] +#[derive(Clone, Hash, PartialEq, Eq)] pub struct PacketState { pub port_id: PortId, pub chan_id: ChannelId, @@ -372,39 +372,13 @@ impl From for RawPacketState { } } -#[cfg(test)] -pub mod test_utils { - use ibc_proto::ibc::core::channel::v1::Packet as RawPacket; - use ibc_proto::ibc::core::client::v1::Height as RawHeight; - - use crate::core::ics24_host::identifier::{ChannelId, PortId}; - use crate::prelude::*; - - /// Returns a dummy `RawPacket`, for testing only! - pub fn get_dummy_raw_packet(timeout_height: u64, timeout_timestamp: u64) -> RawPacket { - RawPacket { - sequence: 1, - source_port: PortId::default().to_string(), - source_channel: ChannelId::default().to_string(), - destination_port: PortId::default().to_string(), - destination_channel: ChannelId::default().to_string(), - data: vec![0], - timeout_height: Some(RawHeight { - revision_number: 0, - revision_height: timeout_height, - }), - timeout_timestamp, - } - } -} - #[cfg(test)] mod tests { use ibc_proto::ibc::core::channel::v1::Packet as RawPacket; use ibc_proto::ibc::core::client::v1::Height as RawHeight; + use ibc_testkit::utils::core::channel::dummy_raw_packet; use test_log::test; - use crate::core::ics04_channel::packet::test_utils::get_dummy_raw_packet; use crate::core::ics04_channel::packet::Packet; use crate::prelude::*; @@ -417,10 +391,10 @@ mod tests { } let proof_height = 10; - let default_raw_packet = get_dummy_raw_packet(proof_height, 1000); - let raw_packet_no_timeout_or_timestamp = get_dummy_raw_packet(10, 0); + let default_raw_packet = dummy_raw_packet(proof_height, 1000); + let raw_packet_no_timeout_or_timestamp = dummy_raw_packet(10, 0); - let mut raw_packet_invalid_timeout_height = get_dummy_raw_packet(0, 10); + let mut raw_packet_invalid_timeout_height = dummy_raw_packet(0, 10); raw_packet_invalid_timeout_height.timeout_height = Some(RawHeight { revision_number: 1, revision_height: 0, @@ -581,7 +555,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_packet(15, 0); + let raw = dummy_raw_packet(15, 0); let msg = Packet::try_from(raw.clone()).unwrap(); let raw_back = RawPacket::from(msg.clone()); let msg_back = Packet::try_from(raw_back.clone()).unwrap(); diff --git a/crates/ibc/src/core/ics23_commitment/commitment.rs b/crates/ibc/src/core/ics23_commitment/commitment.rs index 46c4d8150..556f58458 100644 --- a/crates/ibc/src/core/ics23_commitment/commitment.rs +++ b/crates/ibc/src/core/ics23_commitment/commitment.rs @@ -198,20 +198,3 @@ impl serde::Serialize for CommitmentPrefix { format!("{self:?}").serialize(serializer) } } - -#[cfg(test)] -pub mod test_util { - use ibc_proto::ibc::core::commitment::v1::MerkleProof as RawMerkleProof; - use ibc_proto::ics23::CommitmentProof; - - use super::CommitmentProofBytes; - use crate::prelude::*; - - /// Returns a dummy `CommitmentProofBytes`, for testing only! - pub fn get_dummy_commitment_proof_bytes() -> CommitmentProofBytes { - let parsed = CommitmentProof { proof: None }; - let mproofs: Vec = vec![parsed]; - let raw_mp = RawMerkleProof { proofs: mproofs }; - raw_mp.try_into().unwrap() - } -} diff --git a/crates/ibc/src/core/ics24_host/identifier.rs b/crates/ibc/src/core/ics24_host/identifier.rs index a822d5715..8b7943847 100644 --- a/crates/ibc/src/core/ics24_host/identifier.rs +++ b/crates/ibc/src/core/ics24_host/identifier.rs @@ -15,7 +15,6 @@ use crate::prelude::*; const CONNECTION_ID_PREFIX: &str = "connection"; const CHANNEL_ID_PREFIX: &str = "channel"; -const DEFAULT_PORT_ID: &str = "defaultPort"; const TRANSFER_PORT_ID: &str = "transfer"; /// Defines the domain type for chain identifiers. @@ -416,12 +415,6 @@ impl AsRef for PortId { } } -impl Default for PortId { - fn default() -> Self { - Self(DEFAULT_PORT_ID.to_string()) - } -} - #[cfg_attr( feature = "parity-scale-codec", derive( diff --git a/crates/ibc/src/core/ics24_host/path.rs b/crates/ibc/src/core/ics24_host/path.rs index c01eb5797..6630abbf0 100644 --- a/crates/ibc/src/core/ics24_host/path.rs +++ b/crates/ibc/src/core/ics24_host/path.rs @@ -933,22 +933,22 @@ mod tests { #[test] fn test_parse_ports_fn() { - let path = "ports/defaultPort"; + let path = "ports/transfer"; let components: Vec<&str> = path.split('/').collect(); assert_eq!( parse_ports(&components), - Some(Path::Ports(PortPath(PortId::default()))), + Some(Path::Ports(PortPath(PortId::transfer()))), ); } #[test] fn ports_path_parses() { - let path = "ports/defaultPort"; + let path = "ports/transfer"; let path = Path::from_str(path); assert!(path.is_ok()); - assert_eq!(path.unwrap(), Path::Ports(PortPath(PortId::default()))); + assert_eq!(path.unwrap(), Path::Ports(PortPath(PortId::transfer()))); } #[test] @@ -991,13 +991,13 @@ mod tests { #[test] fn test_parse_channel_ends_fn() { - let path = "channelEnds/ports/defaultPort/channels/channel-0"; + let path = "channelEnds/ports/transfer/channels/channel-0"; let components: Vec<&str> = path.split('/').collect(); assert_eq!( parse_channel_ends(&components), Some(Path::ChannelEnd(ChannelEndPath( - PortId::default(), + PortId::transfer(), ChannelId::default() ))), ); @@ -1005,47 +1005,47 @@ mod tests { #[test] fn channel_ends_path_parses() { - let path = "channelEnds/ports/defaultPort/channels/channel-0"; + let path = "channelEnds/ports/transfer/channels/channel-0"; let path = Path::from_str(path); assert!(path.is_ok()); assert_eq!( path.unwrap(), - Path::ChannelEnd(ChannelEndPath(PortId::default(), ChannelId::default())), + Path::ChannelEnd(ChannelEndPath(PortId::transfer(), ChannelId::default())), ); } #[test] fn test_parse_seqs_fn() { - let path = "nextSequenceSend/ports/defaultPort/channels/channel-0"; + let path = "nextSequenceSend/ports/transfer/channels/channel-0"; let components: Vec<&str> = path.split('/').collect(); assert_eq!( parse_seqs(&components), Some(Path::SeqSend(SeqSendPath( - PortId::default(), + PortId::transfer(), ChannelId::default() ))), ); - let path = "nextSequenceRecv/ports/defaultPort/channels/channel-0"; + let path = "nextSequenceRecv/ports/transfer/channels/channel-0"; let components: Vec<&str> = path.split('/').collect(); assert_eq!( parse_seqs(&components), Some(Path::SeqRecv(SeqRecvPath( - PortId::default(), + PortId::transfer(), ChannelId::default() ))), ); - let path = "nextSequenceAck/ports/defaultPort/channels/channel-0"; + let path = "nextSequenceAck/ports/transfer/channels/channel-0"; let components: Vec<&str> = path.split('/').collect(); assert_eq!( parse_seqs(&components), Some(Path::SeqAck(SeqAckPath( - PortId::default(), + PortId::transfer(), ChannelId::default() ))), ); @@ -1053,49 +1053,49 @@ mod tests { #[test] fn sequence_send_path_parses() { - let path = "nextSequenceSend/ports/defaultPort/channels/channel-0"; + let path = "nextSequenceSend/ports/transfer/channels/channel-0"; let path = Path::from_str(path); assert!(path.is_ok()); assert_eq!( path.unwrap(), - Path::SeqSend(SeqSendPath(PortId::default(), ChannelId::default())), + Path::SeqSend(SeqSendPath(PortId::transfer(), ChannelId::default())), ); } #[test] fn sequence_recv_path_parses() { - let path = "nextSequenceRecv/ports/defaultPort/channels/channel-0"; + let path = "nextSequenceRecv/ports/transfer/channels/channel-0"; let path = Path::from_str(path); assert!(path.is_ok()); assert_eq!( path.unwrap(), - Path::SeqRecv(SeqRecvPath(PortId::default(), ChannelId::default())), + Path::SeqRecv(SeqRecvPath(PortId::transfer(), ChannelId::default())), ); } #[test] fn sequence_ack_path_parses() { - let path = "nextSequenceAck/ports/defaultPort/channels/channel-0"; + let path = "nextSequenceAck/ports/transfer/channels/channel-0"; let path = Path::from_str(path); assert!(path.is_ok()); assert_eq!( path.unwrap(), - Path::SeqAck(SeqAckPath(PortId::default(), ChannelId::default())), + Path::SeqAck(SeqAckPath(PortId::transfer(), ChannelId::default())), ); } #[test] fn test_parse_commitments_fn() { - let path = "commitments/ports/defaultPort/channels/channel-0/sequences/0"; + let path = "commitments/ports/transfer/channels/channel-0/sequences/0"; let components: Vec<&str> = path.split('/').collect(); assert_eq!( parse_commitments(&components), Some(Path::Commitment(CommitmentPath { - port_id: PortId::default(), + port_id: PortId::transfer(), channel_id: ChannelId::default(), sequence: Sequence::default(), })), @@ -1104,14 +1104,14 @@ mod tests { #[test] fn commitments_path_parses() { - let path = "commitments/ports/defaultPort/channels/channel-0/sequences/0"; + let path = "commitments/ports/transfer/channels/channel-0/sequences/0"; let path = Path::from_str(path); assert!(path.is_ok()); assert_eq!( path.unwrap(), Path::Commitment(CommitmentPath { - port_id: PortId::default(), + port_id: PortId::transfer(), channel_id: ChannelId::default(), sequence: Sequence::default(), }), @@ -1120,13 +1120,13 @@ mod tests { #[test] fn test_parse_acks_fn() { - let path = "acks/ports/defaultPort/channels/channel-0/sequences/0"; + let path = "acks/ports/transfer/channels/channel-0/sequences/0"; let components: Vec<&str> = path.split('/').collect(); assert_eq!( parse_acks(&components), Some(Path::Ack(AckPath { - port_id: PortId::default(), + port_id: PortId::transfer(), channel_id: ChannelId::default(), sequence: Sequence::default(), })), @@ -1135,14 +1135,14 @@ mod tests { #[test] fn acks_path_parses() { - let path = "acks/ports/defaultPort/channels/channel-0/sequences/0"; + let path = "acks/ports/transfer/channels/channel-0/sequences/0"; let path = Path::from_str(path); assert!(path.is_ok()); assert_eq!( path.unwrap(), Path::Ack(AckPath { - port_id: PortId::default(), + port_id: PortId::transfer(), channel_id: ChannelId::default(), sequence: Sequence::default(), }), @@ -1151,13 +1151,13 @@ mod tests { #[test] fn test_parse_receipts_fn() { - let path = "receipts/ports/defaultPort/channels/channel-0/sequences/0"; + let path = "receipts/ports/transfer/channels/channel-0/sequences/0"; let components: Vec<&str> = path.split('/').collect(); assert_eq!( parse_receipts(&components), Some(Path::Receipt(ReceiptPath { - port_id: PortId::default(), + port_id: PortId::transfer(), channel_id: ChannelId::default(), sequence: Sequence::default(), })), @@ -1166,14 +1166,14 @@ mod tests { #[test] fn receipts_path_parses() { - let path = "receipts/ports/defaultPort/channels/channel-0/sequences/0"; + let path = "receipts/ports/transfer/channels/channel-0/sequences/0"; let path = Path::from_str(path); assert!(path.is_ok()); assert_eq!( path.unwrap(), Path::Receipt(ReceiptPath { - port_id: PortId::default(), + port_id: PortId::transfer(), channel_id: ChannelId::default(), sequence: Sequence::default(), }), diff --git a/crates/ibc/src/lib.rs b/crates/ibc/src/lib.rs index c1a9d323f..16318bdfb 100644 --- a/crates/ibc/src/lib.rs +++ b/crates/ibc/src/lib.rs @@ -55,11 +55,6 @@ pub mod clients; pub mod core; pub mod hosts; -#[cfg(any(test, feature = "mocks"))] -pub mod mock; -#[cfg(any(test, feature = "mocks"))] -pub mod test_utils; // Context mock, the underlying host chain, and client types: for testing all handlers. - pub mod prelude; mod signer; pub mod utils; @@ -71,7 +66,7 @@ mod serializers; pub mod proto { pub use ibc_proto::google::protobuf::Any; pub use ibc_proto::ibc::apps::transfer; - pub use ibc_proto::ibc::core; pub use ibc_proto::ibc::lightclients::tendermint; - pub use ibc_proto::Protobuf; + pub use ibc_proto::ibc::{core, mock}; + pub use ibc_proto::{ics23, Protobuf}; } diff --git a/crates/ibc/src/mock/ics18_relayer/mod.rs b/crates/ibc/src/mock/ics18_relayer/mod.rs deleted file mode 100644 index 8a7f14e5b..000000000 --- a/crates/ibc/src/mock/ics18_relayer/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! ICS 18: Relayer contains utilities for testing `ibc` against the Hermes relayer. - -pub mod context; -pub mod error; diff --git a/crates/ibc/src/mock/mod.rs b/crates/ibc/src/mock/mod.rs deleted file mode 100644 index 9492569c2..000000000 --- a/crates/ibc/src/mock/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Implementation of mocks for context, host chain, and client. - -#[cfg(any(test, feature = "mocks"))] -pub mod client_state; -#[cfg(any(test, feature = "mocks"))] -pub mod consensus_state; -#[cfg(any(test, feature = "mocks"))] -pub mod context; -#[cfg(any(test, feature = "mocks"))] -pub mod header; -#[cfg(any(test, feature = "mocks"))] -pub mod host; -#[cfg(any(test, feature = "mocks"))] -pub mod ics18_relayer; -#[cfg(any(test, feature = "mocks"))] -pub mod misbehaviour; -#[cfg(any(test, feature = "mocks"))] -pub mod router; diff --git a/crates/ibc/src/mock/router.rs b/crates/ibc/src/mock/router.rs deleted file mode 100644 index 7cfaf3829..000000000 --- a/crates/ibc/src/mock/router.rs +++ /dev/null @@ -1,56 +0,0 @@ -use alloc::collections::BTreeMap; -use alloc::sync::Arc; - -use crate::core::ics24_host::identifier::PortId; -use crate::core::router::{Module, ModuleId, Router}; -use crate::prelude::*; - -#[derive(Default)] -pub struct MockRouter { - router: BTreeMap>, - - /// Maps ports to the the module that owns it - pub port_to_module: BTreeMap, -} - -impl MockRouter { - pub fn add_route( - &mut self, - module_id: ModuleId, - module: impl Module + 'static, - ) -> Result<(), String> { - match self.router.insert(module_id, Arc::new(module)) { - None => Ok(()), - Some(_) => Err("Duplicate module_id".to_owned()), - } - } - - pub fn scope_port_to_module(&mut self, port_id: PortId, module_id: ModuleId) { - self.port_to_module.insert(port_id, module_id); - } -} - -impl Router for MockRouter { - fn get_route(&self, module_id: &ModuleId) -> Option<&dyn Module> { - self.router.get(module_id).map(Arc::as_ref) - } - fn get_route_mut(&mut self, module_id: &ModuleId) -> Option<&mut dyn Module> { - // NOTE: The following: - - // self.router.get_mut(module_id).and_then(Arc::get_mut) - - // doesn't work due to a compiler bug. So we expand it out manually. - - match self.router.get_mut(module_id) { - Some(arc_mod) => match Arc::get_mut(arc_mod) { - Some(m) => Some(m), - None => None, - }, - None => None, - } - } - - fn lookup_module(&self, port_id: &PortId) -> Option { - self.port_to_module.get(port_id).cloned() - } -} diff --git a/crates/ibc/src/test_utils.rs b/crates/ibc/src/test_utils.rs deleted file mode 100644 index 6a13ff639..000000000 --- a/crates/ibc/src/test_utils.rs +++ /dev/null @@ -1,222 +0,0 @@ -use subtle_encoding::bech32; - -use crate::applications::transfer::context::{ - cosmos_adr028_escrow_address, TokenTransferExecutionContext, TokenTransferValidationContext, -}; -use crate::applications::transfer::error::TokenTransferError; -use crate::applications::transfer::PrefixedCoin; -use crate::core::ics04_channel::acknowledgement::Acknowledgement; -use crate::core::ics04_channel::channel::{Counterparty, Order}; -use crate::core::ics04_channel::error::{ChannelError, PacketError}; -use crate::core::ics04_channel::packet::Packet; -use crate::core::ics04_channel::Version; -use crate::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; -use crate::core::router::{Module, ModuleExtras}; -use crate::prelude::*; -use crate::signer::Signer; - -pub fn get_dummy_proof() -> Vec { - "Y29uc2Vuc3VzU3RhdGUvaWJjb25lY2xpZW50LzIy" - .as_bytes() - .to_vec() -} - -pub fn get_dummy_account_id() -> Signer { - "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C" - .to_string() - .into() -} - -pub fn get_dummy_bech32_account() -> String { - "cosmos1wxeyh7zgn4tctjzs0vtqpc6p5cxq5t2muzl7ng".to_string() -} - -pub fn get_dummy_transfer_module() -> DummyTransferModule { - DummyTransferModule -} -#[derive(Debug)] -pub struct DummyTransferModule; - -impl DummyTransferModule { - pub fn new() -> Self { - Self - } -} - -impl Default for DummyTransferModule { - fn default() -> Self { - Self::new() - } -} - -impl Module for DummyTransferModule { - fn on_chan_open_init_validate( - &self, - _order: Order, - _connection_hops: &[ConnectionId], - _port_id: &PortId, - _channel_id: &ChannelId, - _counterparty: &Counterparty, - version: &Version, - ) -> Result { - Ok(version.clone()) - } - - fn on_chan_open_init_execute( - &mut self, - _order: Order, - _connection_hops: &[ConnectionId], - _port_id: &PortId, - _channel_id: &ChannelId, - _counterparty: &Counterparty, - version: &Version, - ) -> Result<(ModuleExtras, Version), ChannelError> { - Ok((ModuleExtras::empty(), version.clone())) - } - - fn on_chan_open_try_validate( - &self, - _order: Order, - _connection_hops: &[ConnectionId], - _port_id: &PortId, - _channel_id: &ChannelId, - _counterparty: &Counterparty, - counterparty_version: &Version, - ) -> Result { - Ok(counterparty_version.clone()) - } - - fn on_chan_open_try_execute( - &mut self, - _order: Order, - _connection_hops: &[ConnectionId], - _port_id: &PortId, - _channel_id: &ChannelId, - _counterparty: &Counterparty, - counterparty_version: &Version, - ) -> Result<(ModuleExtras, Version), ChannelError> { - Ok((ModuleExtras::empty(), counterparty_version.clone())) - } - - fn on_recv_packet_execute( - &mut self, - _packet: &Packet, - _relayer: &Signer, - ) -> (ModuleExtras, Acknowledgement) { - ( - ModuleExtras::empty(), - Acknowledgement::try_from(vec![1u8]).expect("Never fails"), - ) - } - - fn on_timeout_packet_validate( - &self, - _packet: &Packet, - _relayer: &Signer, - ) -> Result<(), PacketError> { - Ok(()) - } - - fn on_timeout_packet_execute( - &mut self, - _packet: &Packet, - _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { - (ModuleExtras::empty(), Ok(())) - } - - fn on_acknowledgement_packet_validate( - &self, - _packet: &Packet, - _acknowledgement: &Acknowledgement, - _relayer: &Signer, - ) -> Result<(), PacketError> { - Ok(()) - } - - fn on_acknowledgement_packet_execute( - &mut self, - _packet: &Packet, - _acknowledgement: &Acknowledgement, - _relayer: &Signer, - ) -> (ModuleExtras, Result<(), PacketError>) { - (ModuleExtras::empty(), Ok(())) - } -} - -impl TokenTransferValidationContext for DummyTransferModule { - type AccountId = Signer; - - fn get_port(&self) -> Result { - Ok(PortId::transfer()) - } - - fn get_escrow_account( - &self, - port_id: &PortId, - channel_id: &ChannelId, - ) -> Result { - let addr = cosmos_adr028_escrow_address(port_id, channel_id); - Ok(bech32::encode("cosmos", addr).into()) - } - - fn can_send_coins(&self) -> Result<(), TokenTransferError> { - Ok(()) - } - - fn can_receive_coins(&self) -> Result<(), TokenTransferError> { - Ok(()) - } - - fn send_coins_validate( - &self, - _from_account: &Self::AccountId, - _to_account: &Self::AccountId, - _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { - Ok(()) - } - - fn mint_coins_validate( - &self, - _account: &Self::AccountId, - _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { - Ok(()) - } - - fn burn_coins_validate( - &self, - _account: &Self::AccountId, - _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { - Ok(()) - } -} - -impl TokenTransferExecutionContext for DummyTransferModule { - fn send_coins_execute( - &mut self, - _from_account: &Self::AccountId, - _to_account: &Self::AccountId, - _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { - Ok(()) - } - - fn mint_coins_execute( - &mut self, - _account: &Self::AccountId, - _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { - Ok(()) - } - - fn burn_coins_execute( - &mut self, - _account: &Self::AccountId, - _coin: &PrefixedCoin, - ) -> Result<(), TokenTransferError> { - Ok(()) - } -} diff --git a/crates/ibc/src/utils/mod.rs b/crates/ibc/src/utils/mod.rs index b4f1b623d..f0eeeadba 100644 --- a/crates/ibc/src/utils/mod.rs +++ b/crates/ibc/src/utils/mod.rs @@ -1,4 +1,3 @@ //! Various utilities used internally - pub(crate) mod macros; pub(crate) mod pretty;