diff --git a/.github/workflows/basic.yaml b/.github/workflows/basic.yaml index 330d4be48..a9e699f1c 100644 --- a/.github/workflows/basic.yaml +++ b/.github/workflows/basic.yaml @@ -20,7 +20,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.73.0 + toolchain: 1.75.0 override: true - name: Install protoc @@ -52,7 +52,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.73.0 + toolchain: 1.75.0 target: wasm32-unknown-unknown override: true @@ -88,7 +88,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.73.0 + toolchain: 1.75.0 override: true components: rustfmt, clippy @@ -123,7 +123,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: -- -D warnings -A deprecated + args: --all-targets -- -D warnings -A deprecated - name: Check Diff # fails if any changes not committed diff --git a/.github/workflows/codecov.yaml b/.github/workflows/codecov.yaml index 71b40b36e..3536f0b8a 100644 --- a/.github/workflows/codecov.yaml +++ b/.github/workflows/codecov.yaml @@ -19,7 +19,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.73.0 + toolchain: 1.75.0 override: true - name: Install protoc diff --git a/.github/workflows/dependabot-vulns-to-slack.yaml b/.github/workflows/dependabot-vulns-to-slack.yaml new file mode 100644 index 000000000..6599b4976 --- /dev/null +++ b/.github/workflows/dependabot-vulns-to-slack.yaml @@ -0,0 +1,16 @@ +name: Send vulnerabilities found by Dependabot to Slack + +on: + schedule: + - cron: '0 0 * * 0' + + +jobs: + Notify-Vulnerabilites: + runs-on: ubuntu-latest + steps: + - name: Notify Vulnerabilities + uses: kunalnagarco/action-cve@v1.7.15 + with: + token: ${{ secrets.SLACK_PERSONAL_ACCESS_TOKEN }} + slack_webhook: ${{ secrets.SLACK_CORE_WEBHOOK }} diff --git a/Cargo.lock b/Cargo.lock index 6d8f3afa7..cade1a4ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,9 +180,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -222,8 +222,8 @@ dependencies = [ "axelar-wasm-std 0.1.0", "base64 0.21.7", "bcs", - "borsh 1.3.1", - "clap 4.5.1", + "borsh 1.4.0", + "clap 4.5.4", "config", "connection-router-api", "cosmos-sdk-proto 0.16.0", @@ -250,6 +250,7 @@ dependencies = [ "mockall", "move-core-types", "multisig 0.1.0", + "num-traits", "prost 0.11.9", "rand 0.8.5", "random-string", @@ -257,7 +258,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "serde_with 3.6.1", + "serde_with 3.7.0", "service-registry 0.1.0", "sha3 0.10.8", "solana-account-decoder", @@ -396,9 +397,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "ark-bls12-381" @@ -496,7 +497,7 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint 0.4.4", "num-traits", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -569,7 +570,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -645,7 +646,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", "synstructure", @@ -657,7 +658,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -695,9 +696,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c" +checksum = "86a9249d1447a85f95810c620abea82e001fe58a31713fcce614caf52499f905" dependencies = [ "brotli", "flate2", @@ -733,9 +734,9 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -745,13 +746,13 @@ source = "git+https://github.com/mystenmark/async-task?rev=4e45b26e11126b191701b [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -782,9 +783,9 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -795,9 +796,20 @@ checksum = "7460f7dd8e100147b82a63afca1a20eb6c231ee36b90ba7272e14951cb58af59" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "axelar-message-primitives" +version = "0.1.0" +source = "git+ssh://git@github.com/eigerco/solana-axelar.git?branch=poc#f6f328fd17c39ba09e53219dd837b91483d10a66" +dependencies = [ + "borsh 1.4.0", + "connection-router 0.1.0 (git+https://github.com/axelarnetwork/axelar-amplifier?rev=c0904e1c72c99a174a93b5f6c18b30f52d20b71f)", + "hex", + "solana-program", +] [[package]] name = "axelar-wasm-std" @@ -853,7 +865,7 @@ dependencies = [ "error-stack", "quote 1.0.35", "report 0.1.0", - "syn 2.0.52", + "syn 2.0.57", "thiserror", ] @@ -866,7 +878,7 @@ dependencies = [ "error-stack", "quote 1.0.35", "report 0.1.0 (git+https://github.com/axelarnetwork/axelar-amplifier?rev=c0904e1c72c99a174a93b5f6c18b30f52d20b71f)", - "syn 2.0.52", + "syn 2.0.57", "thiserror", ] @@ -925,9 +937,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -1041,13 +1053,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.16", - "proc-macro2 1.0.78", + "prettyplease 0.2.17", + "proc-macro2 1.0.79", "quote 1.0.35", "regex", "rustc-hash", "shlex", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -1056,7 +1068,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164" dependencies = [ - "bs58 0.5.0", + "bs58 0.5.1", "hmac 0.12.1", "k256", "once_cell", @@ -1106,9 +1118,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" dependencies = [ "serde", ] @@ -1179,9 +1191,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ "arrayref", "arrayvec", @@ -1254,12 +1266,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "bnum" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" - [[package]] name = "bnum" version = "0.10.0" @@ -1292,11 +1298,11 @@ dependencies = [ [[package]] name = "borsh" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58b559fd6448c6e2fd0adb5720cd98a2506594cafa4737ff98c396f3e82f667" +checksum = "0901fc8eb0aca4c83be0106d6f2db17d86a08dfc2c25f0e84464bf381158add6" dependencies = [ - "borsh-derive 1.3.1", + "borsh-derive 1.4.0", "cfg_aliases", ] @@ -1309,7 +1315,7 @@ dependencies = [ "borsh-derive-internal 0.9.3", "borsh-schema-derive-internal 0.9.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "syn 1.0.109", ] @@ -1322,21 +1328,21 @@ dependencies = [ "borsh-derive-internal 0.10.3", "borsh-schema-derive-internal 0.10.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "syn 1.0.109", ] [[package]] name = "borsh-derive" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aadb5b6ccbd078890f6d7003694e33816e6b784358f18e15e7e6d9f065a57cd" +checksum = "51670c3aa053938b0ee3bd67c3817e471e626151131b934038e83c5bf8de48f5" dependencies = [ "once_cell", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", "syn_derive", ] @@ -1346,7 +1352,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -1357,7 +1363,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -1368,7 +1374,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -1379,16 +1385,16 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "brotli" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1413,9 +1419,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "sha2 0.10.8", "tinyvec", @@ -1443,9 +1449,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.3" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bv" @@ -1471,22 +1477,22 @@ checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" [[package]] name = "bytemuck" -version = "1.14.3" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -1497,9 +1503,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ "serde", ] @@ -1546,9 +1552,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -1578,9 +1584,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ "jobserver", "libc", @@ -1609,9 +1615,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1685,9 +1691,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -1695,9 +1701,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -1707,14 +1713,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.78", + "heck 0.5.0", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -1768,7 +1774,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" dependencies = [ - "bs58 0.5.0", + "bs58 0.5.1", "coins-core", "digest 0.10.7", "hmac 0.12.1", @@ -1802,7 +1808,7 @@ checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ "base64 0.21.7", "bech32", - "bs58 0.5.0", + "bs58 0.5.1", "digest 0.10.7", "generic-array", "hex", @@ -1988,9 +1994,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbd12d49ab0eaf8193ba9175e45f56bbc2e4b27d57b8cfe62aa47942a46b9a9" +checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" dependencies = [ "cfg-if", "cpufeatures", @@ -2108,9 +2114,9 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6aa9f904de106fa16443ad14ec2abe75e94ba003bb61c681c0e43d4c58d2a" +checksum = "9934c79e58d9676edfd592557dee765d2a6ef54c09d5aa2edb06156b00148966" dependencies = [ "digest 0.10.7", "ecdsa", @@ -2148,20 +2154,20 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5d803bea6bd9ed61bd1ee0b4a2eb09ee20dbb539cc6e0b8795614d20952ebb1" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "cosmwasm-std" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad011ae7447188e26e4a7dbca2fcd0fc186aa21ae5c86df0503ea44c78f9e469" +checksum = "ef8666e572a3a2519010dde88c04d16e9339ae751b56b2bb35081fe3f7d6be74" dependencies = [ "base64 0.21.7", "bech32", - "bnum 0.8.1", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -2339,9 +2345,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -2485,7 +2491,7 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "strsim 0.10.0", "syn 1.0.109", @@ -2499,10 +2505,10 @@ checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "strsim 0.10.0", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -2524,7 +2530,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -2618,9 +2624,9 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -2639,7 +2645,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4255bb7dd538590188bd0aea52e48bd699b19bd90b0d069ec2ced8461fe23273" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -2656,7 +2662,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -2677,7 +2683,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -2699,7 +2705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "rustc_version", "syn 1.0.109", @@ -2798,9 +2804,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -2821,9 +2827,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -3027,9 +3033,9 @@ dependencies = [ [[package]] name = "enr" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe81b5c06ecfdbc71dd845216f225f53b62a10cb8a16c946836a3467f701d05b" +checksum = "2a3d8dc56e02f954cac8eb489772c552c473346fc34f67412bb6244fd647f7e4" dependencies = [ "base64 0.21.7", "bytes", @@ -3057,7 +3063,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f16ef37b2a9b242295d61a154ee91ae884afff6b8b933b486b12481cc58310ca" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -3077,21 +3083,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] name = "enum_dispatch" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -3216,9 +3222,9 @@ dependencies = [ [[package]] name = "ethers" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c7cd562832e2ff584fa844cd2f6e5d4f35bbe11b28c7c9b8df957b2e1d0c701" +checksum = "816841ea989f0c69e459af1cf23a6b0033b19a55424a1ea3a30099becdb8dec0" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -3232,9 +3238,9 @@ dependencies = [ [[package]] name = "ethers-addressbook" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35dc9a249c066d17e8947ff52a4116406163cf92c7f0763cb8c001760b26403f" +checksum = "5495afd16b4faa556c3bba1f21b98b4983e53c1755022377051a975c3b021759" dependencies = [ "ethers-core", "once_cell", @@ -3244,9 +3250,9 @@ dependencies = [ [[package]] name = "ethers-contract" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43304317c7f776876e47f2f637859f6d0701c1ec7930a150f169d5fbe7d76f5a" +checksum = "6fceafa3578c836eeb874af87abacfb041f92b4da0a78a5edd042564b8ecdaaa" dependencies = [ "const-hex", "ethers-contract-abigen", @@ -3263,9 +3269,9 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f96502317bf34f6d71a3e3d270defaa9485d754d789e15a8e04a84161c95eb" +checksum = "04ba01fbc2331a38c429eb95d4a570166781f14290ef9fdb144278a90b5a739b" dependencies = [ "Inflector", "const-hex", @@ -3273,39 +3279,39 @@ dependencies = [ "ethers-core", "ethers-etherscan", "eyre", - "prettyplease 0.2.16", - "proc-macro2 1.0.78", + "prettyplease 0.2.17", + "proc-macro2 1.0.79", "quote 1.0.35", "regex", "reqwest", "serde", "serde_json", - "syn 2.0.52", - "toml 0.8.10", + "syn 2.0.57", + "toml 0.8.12", "walkdir", ] [[package]] name = "ethers-contract-derive" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "452ff6b0a64507ce8d67ffd48b1da3b42f03680dcf5382244e9c93822cbbf5de" +checksum = "87689dcabc0051cde10caaade298f9e9093d65f6125c14575db3fd8c669a168f" dependencies = [ "Inflector", "const-hex", "ethers-contract-abigen", "ethers-core", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "serde_json", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] name = "ethers-core" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab3cef6cc1c9fd7f787043c81ad3052eff2b96a3878ef1526aa446311bdbfc9" +checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" dependencies = [ "arrayvec", "bytes", @@ -3323,8 +3329,8 @@ dependencies = [ "rlp", "serde", "serde_json", - "strum 0.25.0", - "syn 2.0.52", + "strum 0.26.2", + "syn 2.0.57", "tempfile", "thiserror", "tiny-keccak", @@ -3333,9 +3339,9 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d45b981f5fa769e1d0343ebc2a44cfa88c9bc312eb681b676318b40cef6fb1" +checksum = "e79e5973c26d4baf0ce55520bd732314328cabe53193286671b47144145b9649" dependencies = [ "chrono", "ethers-core", @@ -3349,9 +3355,9 @@ dependencies = [ [[package]] name = "ethers-middleware" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145211f34342487ef83a597c1e69f0d3e01512217a7c72cc8a25931854c7dca0" +checksum = "48f9fdf09aec667c099909d91908d5eaf9be1bd0e2500ba4172c1d28bfaa43de" dependencies = [ "async-trait", "auto_impl", @@ -3376,9 +3382,9 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb6b15393996e3b8a78ef1332d6483c11d839042c17be58decc92fa8b1c3508a" +checksum = "6434c9a33891f1effc9c75472e12666db2fa5a0fec4b29af6221680a6fe83ab2" dependencies = [ "async-trait", "auto_impl", @@ -3413,9 +3419,9 @@ dependencies = [ [[package]] name = "ethers-signers" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b125a103b56aef008af5d5fb48191984aa326b50bfd2557d231dc499833de3" +checksum = "228875491c782ad851773b652dd8ecac62cda8571d3bc32a5853644dd26766c2" dependencies = [ "async-trait", "coins-bip32", @@ -3432,9 +3438,9 @@ dependencies = [ [[package]] name = "ethers-solc" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d21df08582e0a43005018a858cc9b465c5fff9cf4056651be64f844e57d1f55f" +checksum = "66244a771d9163282646dbeffe0e6eca4dda4146b6498644e678ac6089b11edd" dependencies = [ "cfg-if", "const-hex", @@ -3496,7 +3502,7 @@ dependencies = [ "quote 1.0.35", "serde", "serde_json", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -3571,7 +3577,7 @@ version = "0.1.3" source = "git+https://github.com/MystenLabs/fastcrypto?rev=69180dc7275f5f0efb69e11e9d03f6db338d1dd6#69180dc7275f5f0efb69e11e9d03f6db338d1dd6" dependencies = [ "convert_case 0.6.0", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -3641,9 +3647,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fdlimit" @@ -3684,16 +3690,16 @@ dependencies = [ "num-bigint 0.3.3", "num-integer", "num-traits", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "fiat-crypto" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" [[package]] name = "fixed-hash" @@ -3882,9 +3888,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -4076,13 +4082,15 @@ dependencies = [ [[package]] name = "gmp-gateway" version = "0.1.0" -source = "git+ssh://git@github.com/eigerco/solana-axelar.git?branch=poc#725a33c31be2dd249252770450e647ca77c986ce" +source = "git+ssh://git@github.com/eigerco/solana-axelar.git?branch=poc#f6f328fd17c39ba09e53219dd837b91483d10a66" dependencies = [ + "anyhow", + "axelar-message-primitives", "base64 0.21.7", "bcs", "bimap", - "bnum 0.10.0", - "borsh 1.3.1", + "bnum", + "borsh 1.4.0", "bytemuck", "connection-router 0.1.0 (git+https://github.com/axelarnetwork/axelar-amplifier?rev=c0904e1c72c99a174a93b5f6c18b30f52d20b71f)", "cosmwasm-std", @@ -4126,9 +4134,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -4136,7 +4144,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.5", + "indexmap 2.2.6", "slab", "tokio", "tokio-util 0.7.10", @@ -4251,6 +4259,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -4578,7 +4592,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -4602,9 +4616,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -4636,9 +4650,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.36.1" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7c22c4d34ef4788c351e971c52bfdfe7ea2766f8c5466bc175dd46e52ac22e" +checksum = "3eab73f58e59ca6526037208f0e98851159ec1633cf17b6cd2e1f2c3fd5d53cc" dependencies = [ "console", "lazy_static", @@ -4647,7 +4661,6 @@ dependencies = [ "pest_derive", "serde", "similar", - "yaml-rust", ] [[package]] @@ -4750,9 +4763,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" @@ -4823,9 +4836,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", "ecdsa", @@ -4913,13 +4926,12 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", - "redox_syscall", ] [[package]] @@ -5036,9 +5048,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.15" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" dependencies = [ "cc", "pkg-config", @@ -5137,9 +5149,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -5161,9 +5173,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -5245,11 +5257,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] +[[package]] +name = "monitoring" +version = "0.1.0" +dependencies = [ + "axelar-wasm-std 0.1.0", + "axelar-wasm-std-derive 0.1.0", + "connection-router-api", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-multi-test", + "cw-storage-plus 1.2.0", + "error-stack", + "integration-tests", + "multisig 0.1.0", + "report 0.1.0", +] + [[package]] name = "move-abigen" version = "0.1.0" @@ -5355,7 +5385,7 @@ source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6a dependencies = [ "anyhow", "bcs", - "clap 4.5.1", + "clap 4.5.4", "codespan-reporting", "hex", "move-binary-format", @@ -5402,7 +5432,7 @@ source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6a dependencies = [ "anyhow", "bcs", - "clap 4.5.1", + "clap 4.5.4", "codespan", "colored", "move-binary-format", @@ -5421,7 +5451,7 @@ source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6a dependencies = [ "anyhow", "bcs", - "clap 4.5.1", + "clap 4.5.4", "colored", "hex", "move-binary-format", @@ -5526,7 +5556,7 @@ version = "0.1.0" source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" dependencies = [ "anyhow", - "clap 4.5.1", + "clap 4.5.4", "colored", "move-abigen", "move-binary-format", @@ -5559,7 +5589,7 @@ source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6a dependencies = [ "enum-compat-util", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -5653,7 +5683,7 @@ version = "0.1.0" source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=1a52783d6600ecc22e15253a982f77881bd47c77#1a52783d6600ecc22e15253a982f77881bd47c77" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -5707,7 +5737,7 @@ checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro-error", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", "synstructure", @@ -5859,7 +5889,7 @@ dependencies = [ "tap", "tokio", "tracing", - "uuid 1.7.0", + "uuid 1.8.0", "workspace-hack", ] @@ -5912,7 +5942,7 @@ name = "mysten-util-mem-derive" version = "0.1.0" source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "syn 1.0.109", "synstructure", "workspace-hack", @@ -5991,9 +6021,9 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nexus-gateway" @@ -6012,6 +6042,7 @@ dependencies = [ "schemars", "serde", "thiserror", + "voting-verifier 0.1.0", ] [[package]] @@ -6163,7 +6194,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -6174,9 +6205,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -6268,9 +6299,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.1.3", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -6280,9 +6311,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -6341,7 +6372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -6393,9 +6424,9 @@ checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -6471,7 +6502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" dependencies = [ "proc-macro-crate 1.1.3", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -6483,7 +6514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ "proc-macro-crate 2.0.0", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -6612,7 +6643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" dependencies = [ "peg-runtime", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", ] @@ -6693,9 +6724,9 @@ checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -6726,7 +6757,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.2.5", + "indexmap 2.2.6", ] [[package]] @@ -6767,9 +6798,9 @@ checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ "phf_generator", "phf_shared 0.11.2", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -6805,16 +6836,16 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -6879,9 +6910,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "platforms" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "polyval" @@ -6967,18 +6998,18 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "syn 1.0.109", ] [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ - "proc-macro2 1.0.78", - "syn 2.0.52", + "proc-macro2 1.0.79", + "syn 2.0.57", ] [[package]] @@ -7060,7 +7091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", "version_check", @@ -7072,7 +7103,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "version_check", ] @@ -7088,9 +7119,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -7098,9 +7129,9 @@ dependencies = [ [[package]] name = "program-utils" version = "0.1.0" -source = "git+ssh://git@github.com/eigerco/solana-axelar.git?branch=poc#725a33c31be2dd249252770450e647ca77c986ce" +source = "git+ssh://git@github.com/eigerco/solana-axelar.git?branch=poc#f6f328fd17c39ba09e53219dd837b91483d10a66" dependencies = [ - "borsh 1.3.1", + "borsh 1.4.0", "solana-program", "thiserror", ] @@ -7139,7 +7170,7 @@ checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.2", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand 0.8.5", @@ -7222,7 +7253,7 @@ checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -7235,7 +7266,7 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -7248,9 +7279,9 @@ checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" dependencies = [ "anyhow", "itertools 0.11.0", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -7286,9 +7317,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -7361,7 +7392,7 @@ version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", ] [[package]] @@ -7476,9 +7507,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -7524,9 +7555,9 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a25d631e41bfb5fdcde1d4e2215f62f7f0afa3ff11e26563765bd6ea1d229aeb" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -7558,9 +7589,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom 0.2.12", "libredox", @@ -7582,16 +7613,16 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -7612,9 +7643,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "report" @@ -7641,11 +7672,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.24" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "async-compression 0.4.6", + "async-compression 0.4.7", "base64 0.21.7", "bytes", "encoding_rs", @@ -7785,7 +7816,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -7911,11 +7942,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -8034,9 +8065,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +checksum = "788745a868b0e751750388f4e6546eb921ef714a4317fa6954f7cde114eb2eb7" dependencies = [ "cfg-if", "derive_more", @@ -8046,12 +8077,12 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +checksum = "7dc2f4e8bc344b9fc3d5f74f72c2e55bfc38d28dc2ebc69c194a3df424e4d9ac" dependencies = [ "proc-macro-crate 1.1.3", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -8084,7 +8115,7 @@ version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "serde_derive_internals", "syn 1.0.109", @@ -8111,9 +8142,9 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -8184,9 +8215,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -8197,9 +8228,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -8289,9 +8320,9 @@ version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -8300,18 +8331,18 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -8319,9 +8350,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -8333,9 +8364,9 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -8377,19 +8408,19 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.6.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" +checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" dependencies = [ "base64 0.21.7", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.5", + "indexmap 2.2.6", "serde", "serde_derive", "serde_json", - "serde_with_macros 3.6.1", + "serde_with_macros 3.7.0", "time", ] @@ -8400,21 +8431,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling 0.20.8", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] name = "serde_with_macros" -version = "3.6.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" +checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" dependencies = [ "darling 0.20.8", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -8608,9 +8639,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "simple_asn1" @@ -8651,9 +8682,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snap" @@ -8683,9 +8714,9 @@ dependencies = [ [[package]] name = "solana-account-decoder" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e769cfd7410944d41208631c6c69f8d7a8fa92cc5af85593ce7c6984375c6335" +checksum = "6c5f49893e9e7cd1a45869e03ad6d767666544e47aa39cff5bcfd10c9b156177" dependencies = [ "Inflector", "base64 0.21.7", @@ -8708,9 +8739,9 @@ dependencies = [ [[package]] name = "solana-clap-utils" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5cdcbc655b99c18cf86a17a6080de9fc7370ea3824d26ef0d174e903eccfe9a" +checksum = "ce8528b2853d64e0b27889948e9f41b264d758074be9d9b8ba4e0ddce7c476dd" dependencies = [ "chrono", "clap 2.34.0", @@ -8725,16 +8756,16 @@ dependencies = [ [[package]] name = "solana-client" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80529b6cf2be6337ab4711bb0f7f2b5618301b6f14698d670215a51e5944cb17" +checksum = "914b2b8e68c4b57a916400a77a66078a96b2914d7b218572ef8dff5a84e48ea2" dependencies = [ "async-trait", "bincode", "dashmap", "futures", "futures-util", - "indexmap 2.2.5", + "indexmap 2.2.6", "indicatif", "log", "quinn", @@ -8758,9 +8789,9 @@ dependencies = [ [[package]] name = "solana-config-program" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d61ec5e55f26061a56fd4bf6484c5fa1a9a8a3d3b2188b4983369df2a564a70" +checksum = "617228f9569238e544b526c422b632de368a5ce748a7af04f7f762bd937f42f3" dependencies = [ "bincode", "chrono", @@ -8772,15 +8803,15 @@ dependencies = [ [[package]] name = "solana-connection-cache" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370c11d0787f203231c4a28972f55826fe38175c8961e240001a3a3de24d17e3" +checksum = "cec06c0772e0ebc54d22626ddb433eb798404fdc21b0175ac504f5fff4e50612" dependencies = [ "async-trait", "bincode", "crossbeam-channel", "futures-util", - "indexmap 2.2.5", + "indexmap 2.2.6", "log", "rand 0.8.5", "rayon", @@ -8794,9 +8825,9 @@ dependencies = [ [[package]] name = "solana-frozen-abi" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e608aeeb42922e803589258af0028e2c9e70fbfb04e1f1ff6c5f07019c2af95" +checksum = "cc46edad65d122c8b8634aa9ad11276a804c1aa2404755577fdfdea67484b620" dependencies = [ "block-buffer 0.10.4", "bs58 0.4.0", @@ -8819,21 +8850,21 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cc7a55ad8a177287f3abb1be371b2a252d4c474d71c2e3983a7dff7db15d3f2" +checksum = "c71c603d2203da423cfd8862572ffca7165268e76cab181035f50d106c3710eb" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "rustc_version", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] name = "solana-logger" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f507f00c12d7309475fde8bfc58502ed87bd4024769760223cc9c03dd5aa53" +checksum = "f5b3eef85d82c2c3030acb1d3272d77984d118d2a026d9b1a2cecc6e4c6602eb" dependencies = [ "env_logger", "lazy_static", @@ -8842,9 +8873,9 @@ dependencies = [ [[package]] name = "solana-measure" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9671ea55a8613da1fa3bfcbf211cc19881b08097db2a6b6c188579c4bb82c660" +checksum = "bd072023ab870dcb7d524980d9481317bfd29c1dfd74a067747c8d1d249d1075" dependencies = [ "log", "solana-sdk", @@ -8852,9 +8883,9 @@ dependencies = [ [[package]] name = "solana-metrics" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f136e251c6eb16898947d6128cd3fbc5aa4b25362f1537cb00e0987123dd495" +checksum = "6f594ca2a2698983b515580925d10be20b079617154bbed07d6cd455d1661b90" dependencies = [ "crossbeam-channel", "gethostname", @@ -8867,9 +8898,9 @@ dependencies = [ [[package]] name = "solana-net-utils" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c3bf1d905b81cdf4e0a9c81448ddd777367c9e3227025ed8f1a1aa9b417fc0" +checksum = "e632ad2071b8c911baf873403273c574bb11da95947e2f56db4e53ef06b771fe" dependencies = [ "bincode", "clap 3.2.25", @@ -8889,9 +8920,9 @@ dependencies = [ [[package]] name = "solana-perf" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330eba34f8d5e711453f7ba28382898e27353cac43b0e9b47113a1d3b3acfd73" +checksum = "e3012434718bf3e76ef46cbf3be98a2de877afe7bff6571232d64cc67dce219a" dependencies = [ "ahash 0.8.11", "bincode", @@ -8918,9 +8949,9 @@ dependencies = [ [[package]] name = "solana-program" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fad730d66a6f33ef5bb180def74a3d84e7487b829a8f67ff58570332481f6c" +checksum = "760e9e923050f30f03a159aec9ba1fe09ae7c7494ebd8ba74dc5b7429b11085b" dependencies = [ "ark-bn254", "ark-ec", @@ -8928,11 +8959,11 @@ dependencies = [ "ark-serialize", "base64 0.21.7", "bincode", - "bitflags 2.4.2", + "bitflags 2.5.0", "blake3", "borsh 0.10.3", "borsh 0.9.3", - "borsh 1.3.1", + "borsh 1.4.0", "bs58 0.4.0", "bv", "bytemuck", @@ -8948,7 +8979,7 @@ dependencies = [ "libsecp256k1 0.6.0", "light-poseidon", "log", - "memoffset 0.9.0", + "memoffset 0.9.1", "num-bigint 0.4.4", "num-derive 0.4.2", "num-traits", @@ -8973,9 +9004,9 @@ dependencies = [ [[package]] name = "solana-program-runtime" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ea72e967256cbeb1279b7e83bdee17fa9ff641676dde07e34a98842d1d9562d" +checksum = "a61b65be846413bc504ecae468b6a3fa91b1b37631c80074c41ada8cdc36d165" dependencies = [ "base64 0.21.7", "bincode", @@ -9001,9 +9032,9 @@ dependencies = [ [[package]] name = "solana-pubsub-client" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c001d326505e0ad178db0b3b81e81b9d0a3247d5582fa3818c563352d8b401" +checksum = "4173029a0e3a20930791886dce92c1a6d293a59754e418ac68547f3e14a8f025" dependencies = [ "crossbeam-channel", "futures-util", @@ -9026,9 +9057,9 @@ dependencies = [ [[package]] name = "solana-quic-client" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60019bc9e7817ddef328ad7ffb0c3a5ea63e2afcbee4897bff1e7a1fd1e1a55a" +checksum = "8ae1b9224efc32579fe61a9e06dc8efb2a3cd167c458091cc093086304441285" dependencies = [ "async-mutex", "async-trait", @@ -9053,9 +9084,9 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "652cd60beb6e0006c4b5c7b449b0593e09e8340966414eff9b30543aeae4c761" +checksum = "9460642bcb89090dcc15ecf11b6c4a1994c538ba49a4c5d99023365600631009" dependencies = [ "lazy_static", "num_cpus", @@ -9063,9 +9094,9 @@ dependencies = [ [[package]] name = "solana-remote-wallet" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed53b5cf416c93a9f233ee95c6c2242941de8a606230262a16ca650b2bc519d5" +checksum = "c3de3b65d0fdbf0b2de6dc9aecac03d79ea7ef11c29efbdd67bf17a13d061a95" dependencies = [ "console", "dialoguer", @@ -9082,9 +9113,9 @@ dependencies = [ [[package]] name = "solana-rpc-client" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a03ef7807e31203d07aa9f3baf76d8f98d9ded2e88aab9044f42a5c068e19b1e" +checksum = "b27d16eaebd5889c4dd873d715be0bad4651a0bc2509ba99ec6fc7d3b3416278" dependencies = [ "async-trait", "base64 0.21.7", @@ -9108,9 +9139,9 @@ dependencies = [ [[package]] name = "solana-rpc-client-api" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f5d71639e7600d23558f29414772f20334af5ce0d1977aaac09b5d23e2b06a1" +checksum = "d82323f7b130105a4789101b5e7d97142110774ee06aa84346effa661898d407" dependencies = [ "base64 0.21.7", "bs58 0.4.0", @@ -9130,9 +9161,9 @@ dependencies = [ [[package]] name = "solana-rpc-client-nonce-utils" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf7e20e6b3874a8da9db9b50db917b868797b5841776b269184deb2f521296a" +checksum = "092867e337f366d1b719d28b4e02cffff4734cbbf9945daf5f172715db378e2c" dependencies = [ "clap 2.34.0", "solana-clap-utils", @@ -9143,15 +9174,15 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3612d0b4c2120c2a4cef6bac8af469908575319249e1a5c0de5a487fb7610f" +checksum = "1788023924ebb072288868f8b4b72f5459c1ff653238d769700da9c8043a8aea" dependencies = [ "assert_matches", "base64 0.21.7", "bincode", - "bitflags 2.4.2", - "borsh 1.3.1", + "bitflags 2.5.0", + "borsh 1.4.0", "bs58 0.4.0", "bytemuck", "byteorder", @@ -9198,15 +9229,15 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df952c230e35ba179882cef765655b171b8f99f512f04fc4a84800fa43cfe592" +checksum = "6f3b24f46820e8912b81719a828a3d05f4fbd2f6afdc13826b0327df065ab795" dependencies = [ "bs58 0.4.0", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "rustversion", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -9217,16 +9248,16 @@ checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" [[package]] name = "solana-streamer" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a852027d8b095aaf6931cab230562118bdf71e0b38a17d073f9a808aebcba9" +checksum = "7403972b4233b87598c0a8e8fac26db549f8cb18076d385b8ef8deb1e36a6bc3" dependencies = [ "async-channel", "bytes", "crossbeam-channel", "futures-util", "histogram", - "indexmap 2.2.5", + "indexmap 2.2.6", "itertools 0.10.5", "libc", "log", @@ -9249,9 +9280,9 @@ dependencies = [ [[package]] name = "solana-thin-client" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "104cda4f0cc977db2474728413253a35bd283492546d57bfcc6eddb2f9432a4a" +checksum = "f63740969c229c77306024ed3ad809fabc0a1269cfe75a9c982c7cbd20c38d26" dependencies = [ "bincode", "log", @@ -9264,14 +9295,14 @@ dependencies = [ [[package]] name = "solana-tpu-client" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0c2cd930f1ef2ced8f73d67543d04ab69f6f0b4b0733e8ef4c973022c0a9dc" +checksum = "0f910ac2556498a889d393309110bf4ec7607208104b40260df822e925fce933" dependencies = [ "async-trait", "bincode", "futures-util", - "indexmap 2.2.5", + "indexmap 2.2.6", "indicatif", "log", "rayon", @@ -9288,9 +9319,9 @@ dependencies = [ [[package]] name = "solana-transaction-status" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7fed91baf079b21ea8b9a70ab61910f2a539e6d08740f0f6f6c1f90784472b" +checksum = "a5fa023f9a09216e809bf28c1dc16c8dfce726dfa64133f9016e8a1f01267f39" dependencies = [ "Inflector", "base64 0.21.7", @@ -9313,9 +9344,9 @@ dependencies = [ [[package]] name = "solana-udp-client" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f0fa2c7d277206afcad3e13472379cf7e7c804d02de0fb0bd61730a72df1fef" +checksum = "0e15df3333f480774a47da0d3af3e7b9c78a3417f949bb99ed7a16dd33a906d4" dependencies = [ "async-trait", "solana-connection-cache", @@ -9328,9 +9359,9 @@ dependencies = [ [[package]] name = "solana-version" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30194d1b6a9311f9d92e68fd4355e075169fbc19794e3cef19b45829fa83c0bd" +checksum = "99147bd36e5ccc2e046bc3ef8af9517da1f34c255536e3aeaf0abc383a9f37ed" dependencies = [ "log", "rustc_version", @@ -9344,9 +9375,9 @@ dependencies = [ [[package]] name = "solana-vote-program" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2880e99200ac244ffd63ff7b978f989d9b42a16d2639dce0bf7db1f029c07050" +checksum = "c6071a7f57e03c9d1b55ae4a96fbc55b0df0f97c3153df1dc0c9521ea823bb2b" dependencies = [ "bincode", "log", @@ -9366,9 +9397,9 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "1.18.4" +version = "1.18.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd13a3e898ab98d3a3cb781935f2d0298ba85d11d589b82be5537d6498bf42d" +checksum = "76cba1f80c9001dc788655f2d7d5671af55c7a0a49b95de819f2fc45d8b803b0" dependencies = [ "aes-gcm-siv", "base64 0.21.7", @@ -9503,7 +9534,7 @@ checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" dependencies = [ "quote 1.0.35", "spl-discriminator-syn", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -9512,10 +9543,10 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18fea7be851bd98d10721782ea958097c03a0c2a07d8d4997041d0ece6319a63" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "sha2 0.10.8", - "syn 2.0.52", + "syn 2.0.57", "thiserror", ] @@ -9560,10 +9591,10 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1845dfe71fd68f70382232742e758557afe973ae19e6c06807b2c30f5d5cb474" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "sha2 0.10.8", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -9730,6 +9761,15 @@ dependencies = [ "strum_macros 0.25.3", ] +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros 0.26.2", +] + [[package]] name = "strum_macros" version = "0.24.3" @@ -9737,7 +9777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "rustversion", "syn 1.0.109", @@ -9750,10 +9790,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "rustversion", - "syn 2.0.52", + "syn 2.0.57", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.79", + "quote 1.0.35", + "rustversion", + "syn 2.0.57", ] [[package]] @@ -9894,10 +9947,10 @@ version = "0.7.0" source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" dependencies = [ "msim-macros", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "sui-enum-compat-util", - "syn 2.0.52", + "syn 2.0.57", "workspace-hack", ] @@ -9906,7 +9959,7 @@ name = "sui-protocol-config" version = "0.1.0" source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "insta", "schemars", "serde", @@ -9921,7 +9974,7 @@ name = "sui-protocol-config-macros" version = "0.1.0" source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", "workspace-hack", @@ -10038,18 +10091,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.52" +version = "2.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "unicode-ident", ] @@ -10061,9 +10114,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -10078,7 +10131,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", "unicode-xid 0.2.4", @@ -10124,7 +10177,7 @@ checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -10142,7 +10195,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.1", + "fastrand 2.0.2", "rustix", "windows-sys 0.52.0", ] @@ -10349,22 +10402,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -10483,9 +10536,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -10515,9 +10568,9 @@ name = "tokio-macros" version = "2.1.0" source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -10526,9 +10579,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -10554,9 +10607,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -10621,14 +10674,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.10" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.6", + "toml_edit 0.22.9", ] [[package]] @@ -10646,7 +10699,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "toml_datetime", "winnow 0.5.40", ] @@ -10657,18 +10710,18 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.6" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -10744,7 +10797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" dependencies = [ "prettyplease 0.1.25", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "prost-build", "quote 1.0.35", "syn 1.0.109", @@ -10811,7 +10864,7 @@ dependencies = [ "tower-layer", "tower-service", "tracing", - "uuid 1.7.0", + "uuid 1.8.0", ] [[package]] @@ -10844,9 +10897,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -10917,7 +10970,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -11159,9 +11212,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom 0.2.12", "rand 0.8.5", @@ -11182,7 +11235,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d44690c645190cfce32f91a1582281654b2338c6073fa250b0949fd25c55b32" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", "syn 1.0.109", ] @@ -11345,9 +11398,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", "wasm-bindgen-shared", ] @@ -11379,9 +11432,9 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -11450,9 +11503,9 @@ dependencies = [ [[package]] name = "whoami" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ "redox_syscall", "wasite", @@ -11762,9 +11815,9 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -11782,9 +11835,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.79", "quote 1.0.35", - "syn 2.0.52", + "syn 2.0.57", ] [[package]] @@ -11828,9 +11881,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index cd60e7149..476599bc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,11 @@ members = ["ampd", "contracts/*", "integration-tests", "packages/*"] resolver = "2" [workspace.package] -rust-version = "1.73.0" +rust-version = "1.75.0" # be sure there is an optimizer release supporting this version before updating. See https://github.com/CosmWasm/optimizer [workspace.dependencies] connection-router = { version = "^0.1.0", path = "contracts/connection-router" } -cosmwasm-std = "1.3.3" +cosmwasm-std = "1.5.3" cosmwasm-schema = "1.3.3" cosmwasm-storage = "1.3.3" cw-storage-plus = "1.1.0" @@ -19,8 +19,10 @@ axelar-wasm-std-derive = { version = "^0.1.0", path = "packages/axelar-wasm-std- integration-tests = { version = "^0.1.0", path = "integration-tests" } itertools = "0.11.0" voting-verifier = { version = "^0.1.0", path = "contracts/voting-verifier" } +monitoring = { version = "^0.1.0", path = "contracts/monitoring" } multisig = { version = "^0.1.0", path = "contracts/multisig" } multisig-prover = { version = "^0.1.0", path = "contracts/multisig-prover" } +num-traits = { version = "0.2.14", default-features = false } service-registry = { version = "^0.1.0", path = "contracts/service-registry" } aggregate-verifier = { version = "^0.1.0", path = "contracts/aggregate-verifier" } gateway = { version = "^0.1.0", path = "contracts/gateway" } @@ -36,6 +38,9 @@ schemars = "0.8.10" sha3 = { version = "0.10.8", default-features = false, features = [] } signature-verifier-api = { version = "^0.1.0", path = "packages/signature-verifier-api" } +[workspace.lints.clippy] +arithmetic_side_effects = "deny" + [profile.release] opt-level = 3 debug = false diff --git a/ampd/Cargo.lock b/ampd/Cargo.lock index b861b75a5..aac71e8a7 100644 --- a/ampd/Cargo.lock +++ b/ampd/Cargo.lock @@ -5669,4 +5669,4 @@ dependencies = [ "cc", "libc", "pkg-config", -] +] \ No newline at end of file diff --git a/ampd/Cargo.toml b/ampd/Cargo.toml index e03c180a3..e70b1ee4d 100644 --- a/ampd/Cargo.toml +++ b/ampd/Cargo.toml @@ -16,7 +16,7 @@ config = "0.13.2" connection-router-api = { workspace = true } cosmos-sdk-proto = "0.16.0" cosmrs = { version = "0.14.0", features = ["cosmwasm"] } -cosmwasm-std = { version = "1.2.1", features = ["stargate"] } +cosmwasm-std = { workspace = true, features = ["stargate"] } deref-derive = "0.1.0" derive_builder = "0.12.0" dirs = "5.0.1" @@ -34,6 +34,7 @@ k256 = { version = "0.13.1", features = ["ecdsa"] } mockall = "0.11.3" move-core-types = { git = "https://github.com/mystenlabs/sui", tag = "mainnet-v1.14.2" } multisig = { workspace = true } +num-traits = { workspace = true } prost = "0.11.9" report = { workspace = true } reqwest = { version = "0.11.24", default-features = false } @@ -100,3 +101,5 @@ tonic-build = "0.8.3" aes-gcm-siv = { git = "https://github.com/RustCrypto/AEADs", rev = "6105d7a5591aefa646a95d12b5e8d3f55a9214ef" } curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek", rev = "8274d5cbb6fc3f38cdc742b4798173895cd2a290" } ahash = "=0.8.6" +[lints] +workspace = true diff --git a/ampd/Dockerfile b/ampd/Dockerfile index f13a38005..51155bf99 100644 --- a/ampd/Dockerfile +++ b/ampd/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.74-bookworm as builder +FROM rust:1.75-bookworm as builder RUN apt-get update && apt-get install -y clang protobuf-compiler WORKDIR /ampd diff --git a/ampd/README.md b/ampd/README.md index a4443ac01..87861cb68 100644 --- a/ampd/README.md +++ b/ampd/README.md @@ -103,22 +103,32 @@ chain_rpc_url = "https://api.avax-test.network/ext/bc/C/rpc" ``` +By default, ampd loads the config file from `~/.ampd/config.toml` when running any command. +This can be overridden by passing `--config [path]`. + +### Prerequisite: tofnd +Ampd needs access to a running tofnd instance in order to onboard as a worker +or run the daemon. See the [tofnd repository](https://github.com/axelarnetwork/tofnd) for more info. + ### Worker Onboarding Prior to running the ampd daemon, workers need to perform the following onboarding steps. -1. Determine your worker address: `ampd --config config.toml worker-address` +1. Determine your worker address: `ampd worker-address` 2. Fund your worker address. This can be achieved in a number of ways and is dependent on the environment (mainnet, testnet or devnet). -3. Bond your worker: `ampd --config config.toml bond-worker [service name] [amount] [denom]` +3. Bond your worker: `ampd bond-worker [service name] [amount] [denom]` -4. Register your public key: `ampd --config config.toml register-public-key` +4. Register your public key: `ampd register-public-key` 5. Authorize your worker. This is dependent on the environment, and can be done via governance, or by the network operators. +6. Register support for desired chains. This enables ampd to participate in voting and signing for the specified chains. Multiple chain names can be passed, separated by a space. +`ampd register-chain-support [service name] [chains]...` + ### Run the daemon -`ampd --config config.toml --state state.json` +`ampd` -The state file will be created if it doesn't yet exist. +A state file will be created if it doesn't yet exist. The default location of the state file is `~/.ampd/state.json`, which can be overridden by passing `--state [path]`. -#### Default paths -Note, `--config` or `--state` may be omitted in any command, in which case the default path of `~/.ampd/config.toml` or `~/.ampd/state.json` will be used. +### Help +For more info about the available commands and options, run `ampd --help`. diff --git a/ampd/src/asyncutil/future.rs b/ampd/src/asyncutil/future.rs index 9b693a740..5e0eb672a 100644 --- a/ampd/src/asyncutil/future.rs +++ b/ampd/src/asyncutil/future.rs @@ -59,7 +59,7 @@ where cx: &mut Context<'_>, error: Err, ) -> Poll> { - self.err_count += 1; + self.err_count = self.err_count.saturating_add(1); match self.policy { RetryPolicy::RepeatConstant { diff --git a/ampd/src/asyncutil/task.rs b/ampd/src/asyncutil/task.rs index 77a9b7b5c..1eae600d4 100644 --- a/ampd/src/asyncutil/task.rs +++ b/ampd/src/asyncutil/task.rs @@ -90,7 +90,7 @@ where token.cancel(); info!( "shutting down sub-tasks ({}/{})", - total_task_count - running_tasks.len(), + total_task_count.saturating_sub(running_tasks.len()), total_task_count ); diff --git a/ampd/src/block_height_monitor.rs b/ampd/src/block_height_monitor.rs index afdec3292..5ed7e38f4 100644 --- a/ampd/src/block_height_monitor.rs +++ b/ampd/src/block_height_monitor.rs @@ -114,16 +114,16 @@ mod tests { let monitor = BlockHeightMonitor::connect(mock_client) .await .unwrap() - .poll_interval(poll_interval.clone()); + .poll_interval(poll_interval); let exit_token = token.clone(); let latest_block_height = monitor.latest_block_height(); let handle = tokio::spawn(async move { monitor.run(exit_token).await }); - let mut prev_height = latest_block_height.borrow().clone(); + let mut prev_height = *latest_block_height.borrow(); for _ in 1..10 { time::sleep(poll_interval * 2).await; - let next_height = latest_block_height.borrow().clone(); + let next_height = *latest_block_height.borrow(); assert!(next_height > prev_height); prev_height = next_height; } diff --git a/ampd/src/broadcaster.rs b/ampd/src/broadcaster.rs index bb6b5c58c..f2eaa4cb4 100644 --- a/ampd/src/broadcaster.rs +++ b/ampd/src/broadcaster.rs @@ -1,4 +1,5 @@ use std::convert::TryInto; +use std::ops::Mul; use std::thread; use std::time::Duration; @@ -16,6 +17,7 @@ use error_stack::{FutureExt, Report, Result, ResultExt}; use futures::TryFutureExt; use k256::sha2::{Digest, Sha256}; use mockall::automock; +use num_traits::cast; use serde::{Deserialize, Serialize}; use thiserror::Error; use tonic::Status; @@ -40,6 +42,8 @@ pub enum Error { TxBuilding, #[error("failed to estimate gas")] GasEstimation, + #[error("failed to estimate fee")] + FeeEstimation, #[error("broadcast failed")] Broadcast, #[error("failed to confirm tx inclusion in block")] @@ -165,16 +169,17 @@ where .change_context(Error::TxBuilding)?; self.estimate_gas(sim_tx).await.map(|gas| { - let gas_adj = (gas as f64 * self.config.gas_adjustment) as u64; + let gas_adj = gas as f64 * self.config.gas_adjustment; - Fee::from_amount_and_gas( + Ok(Fee::from_amount_and_gas( Coin { - amount: (gas_adj as f64 * self.config.gas_price.amount).ceil() as u128, + amount: cast((gas_adj.mul(self.config.gas_price.amount)).ceil()) + .ok_or(Error::FeeEstimation)?, denom: self.config.gas_price.denom.clone().into(), }, - gas_adj, - ) - }) + cast::(gas_adj).ok_or(Error::FeeEstimation)?, + )) + })? } } @@ -199,7 +204,7 @@ where async fn confirm_tx(&mut self, tx_hash: &str) -> Result<(), Error> { let mut result: Result<(), Status> = Ok(()); - for i in 0..self.config.tx_fetch_max_retries + 1 { + for i in 0..self.config.tx_fetch_max_retries.saturating_add(1) { if i > 0 { thread::sleep(self.config.tx_fetch_interval) } diff --git a/ampd/src/broadcaster/tx.rs b/ampd/src/broadcaster/tx.rs index b4773adfb..831e70cad 100644 --- a/ampd/src/broadcaster/tx.rs +++ b/ampd/src/broadcaster/tx.rs @@ -144,7 +144,7 @@ mod tests { let hash = hasher.finalize(); let priv_key = ecdsa::SigningKey::from_bytes(&priv_key_bytes).unwrap(); - let (signature, _) = priv_key.sign_prehash_recoverable(&hash.to_vec()).unwrap(); + let (signature, _) = priv_key.sign_prehash_recoverable(&hash).unwrap(); Result::<_, Error>::Ok(signature.to_vec()) }) diff --git a/ampd/src/config.rs b/ampd/src/config.rs index 9b5e1b798..b9b625562 100644 --- a/ampd/src/config.rs +++ b/ampd/src/config.rs @@ -1,6 +1,7 @@ -use serde::{Deserialize, Serialize}; use std::time::Duration; +use serde::{Deserialize, Serialize}; + use crate::broadcaster; use crate::commands::ServiceRegistryConfig; use crate::handlers::{self, config::deserialize_handler_configs}; @@ -48,7 +49,9 @@ mod tests { use cosmrs::AccountId; - use crate::evm::ChainName; + use connection_router_api::ChainName; + + use crate::evm::finalizer::Finalization; use crate::handlers::config::Chain; use crate::handlers::config::Config as HandlerConfig; use crate::types::TMAddress; @@ -261,7 +264,8 @@ mod tests { handlers: vec![ HandlerConfig::EvmMsgVerifier { chain: Chain { - name: ChainName::Ethereum, + name: ChainName::from_str("Ethereum").unwrap(), + finalization: Finalization::RPCFinalizedBlock, rpc_url: Url::from_str("http://127.0.0.1").unwrap(), }, rpc_timeout: Some(Duration::from_secs(3)), @@ -274,7 +278,8 @@ mod tests { AccountId::new("axelar", &[0u8; 32]).unwrap(), ), chain: Chain { - name: ChainName::Other("Fantom".to_string()), + name: ChainName::from_str("Fantom").unwrap(), + finalization: Finalization::ConfirmationHeight, rpc_url: Url::from_str("http://127.0.0.1").unwrap(), }, rpc_timeout: Some(Duration::from_secs(3)), diff --git a/ampd/src/evm/finalizer.rs b/ampd/src/evm/finalizer.rs index ef6ef31c7..9e0551a2f 100644 --- a/ampd/src/evm/finalizer.rs +++ b/ampd/src/evm/finalizer.rs @@ -1,8 +1,8 @@ -use crate::evm::ChainName; use async_trait::async_trait; use error_stack::{self, Report, ResultExt}; use ethers::types::U64; use mockall::automock; +use serde::{Deserialize, Serialize}; use super::error::Error; use crate::evm::json_rpc::EthereumClient; @@ -15,8 +15,15 @@ pub trait Finalizer: Send + Sync { async fn latest_finalized_block_height(&self) -> Result; } +#[derive(Debug, Deserialize, Serialize, PartialEq, Default)] +pub enum Finalization { + #[default] + RPCFinalizedBlock, + ConfirmationHeight, +} + pub fn pick<'a, C, H>( - chain: &'a ChainName, + finalizer_type: &'a Finalization, rpc_client: &'a C, confirmation_height: H, ) -> Box @@ -24,30 +31,33 @@ where C: EthereumClient + Send + Sync, H: Into, { - match chain { - ChainName::Ethereum => Box::new(EthereumFinalizer::new(rpc_client)), - ChainName::Other(_) => Box::new(PoWFinalizer::new(rpc_client, confirmation_height)), + match finalizer_type { + Finalization::RPCFinalizedBlock => Box::new(RPCFinalizer::new(rpc_client)), + Finalization::ConfirmationHeight => Box::new(ConfirmationHeightFinalizer::new( + rpc_client, + confirmation_height, + )), } } -pub struct EthereumFinalizer<'a, C> +pub struct RPCFinalizer<'a, C> where C: EthereumClient, { rpc_client: &'a C, } -impl<'a, C> EthereumFinalizer<'a, C> +impl<'a, C> RPCFinalizer<'a, C> where C: EthereumClient, { pub fn new(rpc_client: &'a C) -> Self { - EthereumFinalizer { rpc_client } + RPCFinalizer { rpc_client } } } #[async_trait] -impl<'a, C> Finalizer for EthereumFinalizer<'a, C> +impl<'a, C> Finalizer for RPCFinalizer<'a, C> where C: EthereumClient + Send + Sync, { @@ -61,7 +71,7 @@ where } } -pub struct PoWFinalizer<'a, C> +pub struct ConfirmationHeightFinalizer<'a, C> where C: EthereumClient, { @@ -69,7 +79,7 @@ where confirmation_height: U64, } -impl<'a, C> PoWFinalizer<'a, C> +impl<'a, C> ConfirmationHeightFinalizer<'a, C> where C: EthereumClient, { @@ -77,7 +87,7 @@ where where H: Into, { - PoWFinalizer { + ConfirmationHeightFinalizer { rpc_client, confirmation_height: confirmation_height.into(), } @@ -85,7 +95,7 @@ where } #[async_trait] -impl<'a, C> Finalizer for PoWFinalizer<'a, C> +impl<'a, C> Finalizer for ConfirmationHeightFinalizer<'a, C> where C: EthereumClient + Send + Sync, { @@ -96,15 +106,22 @@ where .await .change_context(Error::JsonRPC)?; - Ok(block_number - self.confirmation_height + 1) + // order of operations is important here when saturating, otherwise the finalization window could be cut short + // if we add 1 afterwards + Ok(block_number + .saturating_add(U64::from(1)) + .saturating_sub(self.confirmation_height)) } } #[cfg(test)] mod tests { - use crate::evm::finalizer::{Finalizer, PoWFinalizer}; + use crate::evm::finalizer::{pick, ConfirmationHeightFinalizer, Finalization, Finalizer}; use crate::evm::json_rpc::MockEthereumClient; - use ethers::types::U64; + use ethers::{ + abi::Hash, + types::{Block, U64}, + }; use tokio::test; #[test] @@ -116,7 +133,7 @@ mod tests { .returning(move || Ok(block_number)); assert_eq!( block_number, - PoWFinalizer::new(&rpc_client, 1) + ConfirmationHeightFinalizer::new(&rpc_client, 1) .latest_finalized_block_height() .await .unwrap() @@ -129,7 +146,7 @@ mod tests { .returning(move || Ok(block_number)); assert_eq!( block_number + 1, - PoWFinalizer::new(&rpc_client, 0) + ConfirmationHeightFinalizer::new(&rpc_client, 0) .latest_finalized_block_height() .await .unwrap() @@ -142,7 +159,7 @@ mod tests { .returning(move || Ok(block_number)); assert_eq!( block_number - 1, - PoWFinalizer::new(&rpc_client, 2) + ConfirmationHeightFinalizer::new(&rpc_client, 2) .latest_finalized_block_height() .await .unwrap() @@ -155,10 +172,49 @@ mod tests { .returning(move || Ok(block_number)); assert_eq!( U64::from(1), - PoWFinalizer::new(&rpc_client, block_number) + ConfirmationHeightFinalizer::new(&rpc_client, block_number) .latest_finalized_block_height() .await .unwrap() ); } + + #[test] + async fn pick_should_work_for_ethereum_finalizer() { + let mut rpc_client = MockEthereumClient::new(); + let mut block = Block::::default(); + let block_number: U64 = 10.into(); + block.number = Some(block_number); + + rpc_client + .expect_finalized_block() + .returning(move || Ok(block.clone())); + + let finalizer = pick(&Finalization::RPCFinalizedBlock, &rpc_client, 1); + assert_eq!( + finalizer.latest_finalized_block_height().await.unwrap(), + block_number + ); + } + + #[test] + async fn pick_should_work_for_pow_finalizer() { + let mut rpc_client = MockEthereumClient::new(); + let block_number: U64 = 10.into(); + let pow_confirmation_height = 6; + + rpc_client + .expect_block_number() + .returning(move || Ok(block_number)); + + let finalizer = pick( + &Finalization::ConfirmationHeight, + &rpc_client, + pow_confirmation_height, + ); + assert_eq!( + finalizer.latest_finalized_block_height().await.unwrap(), + block_number - U64::from(pow_confirmation_height - 1) + ); + } } diff --git a/ampd/src/evm/mod.rs b/ampd/src/evm/mod.rs index 3de002e1f..0703e6e08 100644 --- a/ampd/src/evm/mod.rs +++ b/ampd/src/evm/mod.rs @@ -1,50 +1,4 @@ -use std::fmt::Display; - -use enum_display_derive::Display; -use serde::{Deserialize, Serialize}; - pub mod error; pub mod finalizer; pub mod json_rpc; pub mod verifier; - -#[derive(Debug, Deserialize, Serialize, PartialEq, Hash, Eq, Clone, Display)] -pub enum ChainName { - Ethereum, - #[serde(untagged)] - Other(String), -} - -impl PartialEq for ChainName { - fn eq(&self, other: &connection_router_api::ChainName) -> bool { - self.to_string().eq_ignore_ascii_case(other.as_ref()) - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use connection_router_api::ChainName; - - use crate::evm; - - #[test] - fn chain_name_partial_eq() { - let chain_name = evm::ChainName::Ethereum; - - assert_eq!(chain_name, ChainName::from_str("Ethereum").unwrap()); - assert_eq!(chain_name, ChainName::from_str("ETHEREUM").unwrap()); - assert_eq!(chain_name, ChainName::from_str("ethereum").unwrap()); - assert_eq!(chain_name, ChainName::from_str("ethEReum").unwrap()); - assert_ne!(chain_name, ChainName::from_str("Ethereum-1").unwrap()); - - let chain_name = evm::ChainName::Other("avalanche".into()); - - assert_eq!(chain_name, ChainName::from_str("Avalanche").unwrap()); - assert_eq!(chain_name, ChainName::from_str("AVALANCHE").unwrap()); - assert_eq!(chain_name, ChainName::from_str("avalanche").unwrap()); - assert_eq!(chain_name, ChainName::from_str("avaLAnche").unwrap()); - assert_ne!(chain_name, ChainName::from_str("Avalanche-2").unwrap()); - } -} diff --git a/ampd/src/evm/verifier.rs b/ampd/src/evm/verifier.rs index 05543b4d8..71924e110 100644 --- a/ampd/src/evm/verifier.rs +++ b/ampd/src/evm/verifier.rs @@ -2,7 +2,8 @@ use axelar_wasm_std::voting::Vote; use ethers::abi::{encode, Token}; use ethers::contract::EthLogDecode; use ethers::prelude::abigen; -use ethers::types::{Log, TransactionReceipt}; +use ethers::types::{Log, TransactionReceipt, H256}; +use num_traits::cast; use crate::handlers::evm_verify_msg::Message; use crate::handlers::evm_verify_worker_set::WorkerSetConfirmation; @@ -12,31 +13,30 @@ abigen!(IAxelarGateway, "src/evm/abi/IAxelarGateway.json"); struct IAxelarGatewayEventsWithLog<'a>(&'a Log, IAxelarGatewayEvents); -impl PartialEq<&Message> for IAxelarGatewayEventsWithLog<'_> { - fn eq(&self, msg: &&Message) -> bool { - let IAxelarGatewayEventsWithLog(log, event) = self; +impl PartialEq> for &Message { + fn eq(&self, other: &IAxelarGatewayEventsWithLog<'_>) -> bool { + let IAxelarGatewayEventsWithLog(log, event) = other; match event { IAxelarGatewayEvents::ContractCallFilter(event) => { - log.transaction_hash == Some(msg.tx_id) - && log.log_index == Some(msg.event_index.into()) - && event.sender == msg.source_address - && msg.destination_chain == event.destination_chain - && event.destination_contract_address == msg.destination_address - && event.payload_hash == msg.payload_hash.as_bytes() + log.transaction_hash == Some(self.tx_id) + && event.sender == self.source_address + && self.destination_chain == event.destination_chain + && event.destination_contract_address == self.destination_address + && event.payload_hash == self.payload_hash.as_bytes() } _ => false, } } } -impl PartialEq<&WorkerSetConfirmation> for IAxelarGatewayEventsWithLog<'_> { - fn eq(&self, worker_set: &&WorkerSetConfirmation) -> bool { - let IAxelarGatewayEventsWithLog(log, event) = self; +impl PartialEq> for &WorkerSetConfirmation { + fn eq(&self, other: &IAxelarGatewayEventsWithLog<'_>) -> bool { + let IAxelarGatewayEventsWithLog(log, event) = other; match event { IAxelarGatewayEvents::OperatorshipTransferredFilter(event) => { - let (operators, weights): (Vec<_>, Vec<_>) = worker_set + let (operators, weights): (Vec<_>, Vec<_>) = self .operators .weights_by_addresses .iter() @@ -48,13 +48,12 @@ impl PartialEq<&WorkerSetConfirmation> for IAxelarGatewayEventsWithLog<'_> { }) .unzip(); - log.transaction_hash == Some(worker_set.tx_id) - && log.log_index == Some(worker_set.event_index.into()) + log.transaction_hash == Some(self.tx_id) && event.new_operators_data == encode(&[ Token::Array(operators), Token::Array(weights), - Token::Uint(worker_set.operators.threshold.as_ref().to_owned()), + Token::Uint(self.operators.threshold.as_ref().to_owned()), ]) } _ => false, @@ -62,15 +61,20 @@ impl PartialEq<&WorkerSetConfirmation> for IAxelarGatewayEventsWithLog<'_> { } } +fn has_failed(tx_receipt: &TransactionReceipt) -> bool { + tx_receipt.status == Some(0u64.into()) +} + fn get_event<'a>( gateway_address: &EVMAddress, tx_receipt: &'a TransactionReceipt, - log_index: u64, + log_index: u32, ) -> Option> { + let log_index: usize = cast(log_index).expect("log_index must be a valid usize"); + tx_receipt .logs - .iter() - .find(|log| log.log_index == Some(log_index.into())) + .get(log_index) .filter(|log| log.address == *gateway_address) .and_then( |log| match IAxelarGatewayEvents::decode_log(&log.clone().into()) { @@ -80,30 +84,50 @@ fn get_event<'a>( ) } -pub fn verify_message( +fn verify<'a, V>( gateway_address: &EVMAddress, - tx_receipt: &TransactionReceipt, - msg: &Message, -) -> Vote { - match get_event(gateway_address, tx_receipt, msg.event_index) { - Some(event) if tx_receipt.transaction_hash == msg.tx_id && event == msg => { + tx_receipt: &'a TransactionReceipt, + to_verify: V, + expected_transaction_hash: H256, + expected_event_index: u32, +) -> Vote +where + V: PartialEq>, +{ + if has_failed(tx_receipt) { + return Vote::FailedOnChain; + } + + match get_event(gateway_address, tx_receipt, expected_event_index) { + Some(event) + if tx_receipt.transaction_hash == expected_transaction_hash && to_verify == event => + { Vote::SucceededOnChain } _ => Vote::NotFound, } } +pub fn verify_message( + gateway_address: &EVMAddress, + tx_receipt: &TransactionReceipt, + msg: &Message, +) -> Vote { + verify(gateway_address, tx_receipt, msg, msg.tx_id, msg.event_index) +} + pub fn verify_worker_set( gateway_address: &EVMAddress, tx_receipt: &TransactionReceipt, worker_set: &WorkerSetConfirmation, ) -> Vote { - match get_event(gateway_address, tx_receipt, worker_set.event_index) { - Some(event) if tx_receipt.transaction_hash == worker_set.tx_id && event == worker_set => { - Vote::SucceededOnChain - } - _ => Vote::NotFound, - } + verify( + gateway_address, + tx_receipt, + worker_set, + worker_set.tx_id, + worker_set.event_index, + ) } #[cfg(test)] @@ -133,6 +157,18 @@ mod tests { ); } + #[test] + fn should_not_verify_worker_set_if_tx_failed() { + let (gateway_address, mut tx_receipt, worker_set) = + get_matching_worker_set_and_tx_receipt(); + + tx_receipt.status = Some(0u64.into()); + assert_eq!( + verify_worker_set(&gateway_address, &tx_receipt, &worker_set), + Vote::FailedOnChain + ); + } + #[test] fn should_not_verify_worker_set_if_gateway_address_does_not_match() { let (_, tx_receipt, worker_set) = get_matching_worker_set_and_tx_receipt(); @@ -199,6 +235,17 @@ mod tests { ); } + #[test] + fn should_not_verify_msg_if_tx_failed() { + let (gateway_address, mut tx_receipt, msg) = get_matching_msg_and_tx_receipt(); + + tx_receipt.status = Some(0u64.into()); + assert_eq!( + verify_message(&gateway_address, &tx_receipt, &msg), + Vote::FailedOnChain + ); + } + #[test] fn should_not_verify_msg_if_gateway_address_does_not_match() { let (_, tx_receipt, msg) = get_matching_msg_and_tx_receipt(); @@ -255,7 +302,7 @@ mod tests { fn get_matching_worker_set_and_tx_receipt( ) -> (EVMAddress, TransactionReceipt, WorkerSetConfirmation) { let tx_id = Hash::random(); - let log_index = 999; + let log_index = 1; let gateway_address = EVMAddress::random(); let worker_set = WorkerSetConfirmation { @@ -296,6 +343,7 @@ mod tests { }; let tx_receipt = TransactionReceipt { transaction_hash: tx_id, + status: Some(1u64.into()), logs: vec![Log::default(), log, Log::default()], ..Default::default() }; @@ -305,7 +353,7 @@ mod tests { fn get_matching_msg_and_tx_receipt() -> (EVMAddress, TransactionReceipt, Message) { let tx_id = Hash::random(); - let log_index = 999; + let log_index = 1; let gateway_address = EVMAddress::random(); let msg = Message { @@ -334,6 +382,7 @@ mod tests { }; let tx_receipt = TransactionReceipt { transaction_hash: tx_id, + status: Some(1u64.into()), logs: vec![Log::default(), log, Log::default()], ..Default::default() }; diff --git a/ampd/src/handlers/config.rs b/ampd/src/handlers/config.rs index 214113c8e..738fb790c 100644 --- a/ampd/src/handlers/config.rs +++ b/ampd/src/handlers/config.rs @@ -5,14 +5,17 @@ use serde::de::{self, Deserializer}; use serde::{Deserialize, Serialize}; use serde_with::with_prefix; -use crate::evm::ChainName; +use crate::evm::finalizer::Finalization; use crate::types::TMAddress; use crate::url::Url; +use connection_router_api::ChainName; #[derive(Debug, Deserialize, Serialize, PartialEq)] pub struct Chain { pub name: ChainName, pub rpc_url: Url, + #[serde(default)] + pub finalization: Finalization, } #[derive(Debug, Deserialize, Serialize, PartialEq)] @@ -228,3 +231,20 @@ where Ok(configs) } + +#[cfg(test)] +mod tests { + + use crate::{evm::finalizer::Finalization, handlers::config::Chain}; + + #[test] + fn finalizer_should_default_to_ethereum() { + let chain_config_toml = " + name = 'polygon' + rpc_url = 'http://127.0.0.1/' + "; + + let chain_config: Chain = toml::from_str(chain_config_toml).unwrap(); + assert_eq!(chain_config.finalization, Finalization::RPCFinalizedBlock); + } +} diff --git a/ampd/src/handlers/evm_verify_msg.rs b/ampd/src/handlers/evm_verify_msg.rs index 03019d9d6..c7271b86e 100644 --- a/ampd/src/handlers/evm_verify_msg.rs +++ b/ampd/src/handlers/evm_verify_msg.rs @@ -12,15 +12,17 @@ use tracing::{info, info_span}; use valuable::Valuable; use axelar_wasm_std::voting::{PollId, Vote}; -use connection_router_api::ID_SEPARATOR; +use connection_router_api::ChainName; use events::Error::EventTypeMismatch; use events_derive::try_from; +use voting_verifier::events::construct_message_id; use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; +use crate::evm::finalizer; +use crate::evm::finalizer::Finalization; use crate::evm::json_rpc::EthereumClient; use crate::evm::verifier::verify_message; -use crate::evm::{finalizer, ChainName}; use crate::handlers::errors::Error; use crate::handlers::errors::Error::DeserializeEvent; use crate::queue::queued_broadcaster::BroadcasterClient; @@ -31,7 +33,7 @@ type Result = error_stack::Result; #[derive(Deserialize, Debug)] pub struct Message { pub tx_id: Hash, - pub event_index: u64, + pub event_index: u32, pub destination_address: String, pub destination_chain: connection_router_api::ChainName, pub source_address: EVMAddress, @@ -58,6 +60,7 @@ where worker: TMAddress, voting_verifier: TMAddress, chain: ChainName, + finalizer_type: Finalization, rpc_client: C, broadcast_client: B, latest_block_height: Receiver, @@ -72,6 +75,7 @@ where worker: TMAddress, voting_verifier: TMAddress, chain: ChainName, + finalizer_type: Finalization, rpc_client: C, broadcast_client: B, latest_block_height: Receiver, @@ -80,6 +84,7 @@ where worker, voting_verifier, chain, + finalizer_type, rpc_client, broadcast_client, latest_block_height, @@ -95,7 +100,7 @@ where T: IntoIterator, { let latest_finalized_block_height = - finalizer::pick(&self.chain, &self.rpc_client, confirmation_height) + finalizer::pick(&self.finalizer_type, &self.rpc_client, confirmation_height) .latest_finalized_block_height() .await .change_context(Error::Finalizer)?; @@ -190,12 +195,7 @@ where let source_chain_str: String = source_chain.into(); let message_ids = messages .iter() - .map(|message| { - format!( - "0x{:x}{}{}", - message.tx_id, ID_SEPARATOR, message.event_index - ) - }) + .map(|message| construct_message_id(message.tx_id.into(), message.event_index)) .collect::>(); let votes = info_span!( "verify messages from an EVM chain", @@ -231,6 +231,7 @@ where #[cfg(test)] mod tests { use std::convert::TryInto; + use std::str::FromStr; use base64::engine::general_purpose::STANDARD; use base64::Engine; @@ -238,23 +239,23 @@ mod tests { use error_stack::{Report, Result}; use ethers::providers::ProviderError; use tendermint::abci; + use tokio::sync::watch; + use tokio::test as async_test; + use connection_router_api::ChainName; use events::Error::{DeserializationFailed, EventTypeMismatch}; use events::Event; - use tokio::sync::watch; use voting_verifier::events::{PollMetadata, PollStarted, TxEventConfirmation}; use crate::event_processor::EventHandler; + use crate::evm::finalizer::Finalization; use crate::evm::json_rpc::MockEthereumClient; - use crate::evm::ChainName; use crate::queue::queued_broadcaster::MockBroadcasterClient; use crate::types::{EVMAddress, Hash, TMAddress}; use crate::PREFIX; use super::PollStartedEvent; - use tokio::test as async_test; - fn get_poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { PollStarted::Messages { metadata: PollMetadata { @@ -378,7 +379,8 @@ mod tests { let handler = super::Handler::new( worker, voting_verifier, - ChainName::Ethereum, + ChainName::from_str("ethereum").unwrap(), + Finalization::RPCFinalizedBlock, rpc_client, broadcast_client, rx, @@ -414,9 +416,8 @@ mod tests { fn participants(n: u8, worker: Option) -> Vec { (0..n) - .into_iter() .map(|_| TMAddress::random(PREFIX)) - .chain(worker.into_iter()) + .chain(worker) .collect() } } diff --git a/ampd/src/handlers/evm_verify_worker_set.rs b/ampd/src/handlers/evm_verify_worker_set.rs index a3b9422e6..186d88d0a 100644 --- a/ampd/src/handlers/evm_verify_worker_set.rs +++ b/ampd/src/handlers/evm_verify_worker_set.rs @@ -1,5 +1,6 @@ use std::convert::TryInto; +use async_trait::async_trait; use cosmrs::cosmwasm::MsgExecuteContract; use error_stack::ResultExt; use ethers::types::{TransactionReceipt, U64}; @@ -8,17 +9,17 @@ use tokio::sync::watch::Receiver; use tracing::{info, info_span}; use valuable::Valuable; -use async_trait::async_trait; +use axelar_wasm_std::voting::{PollId, Vote}; +use connection_router_api::ChainName; use events::Error::EventTypeMismatch; use events_derive::try_from; - -use axelar_wasm_std::voting::{PollId, Vote}; -use connection_router_api::ID_SEPARATOR; +use voting_verifier::events::construct_message_id; use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; +use crate::evm::finalizer::Finalization; use crate::evm::verifier::verify_worker_set; -use crate::evm::{finalizer, json_rpc::EthereumClient, ChainName}; +use crate::evm::{finalizer, json_rpc::EthereumClient}; use crate::handlers::errors::Error; use crate::queue::queued_broadcaster::BroadcasterClient; use crate::types::{EVMAddress, Hash, TMAddress, U256}; @@ -34,7 +35,7 @@ pub struct Operators { #[derive(Deserialize, Debug)] pub struct WorkerSetConfirmation { pub tx_id: Hash, - pub event_index: u64, + pub event_index: u32, pub operators: Operators, } @@ -58,6 +59,7 @@ where worker: TMAddress, voting_verifier: TMAddress, chain: ChainName, + finalizer_type: Finalization, rpc_client: C, broadcast_client: B, latest_block_height: Receiver, @@ -72,6 +74,7 @@ where worker: TMAddress, voting_verifier: TMAddress, chain: ChainName, + finalizer_type: Finalization, rpc_client: C, broadcast_client: B, latest_block_height: Receiver, @@ -80,6 +83,7 @@ where worker, voting_verifier, chain, + finalizer_type, rpc_client, broadcast_client, latest_block_height, @@ -92,7 +96,7 @@ where confirmation_height: u64, ) -> Result> { let latest_finalized_block_height = - finalizer::pick(&self.chain, &self.rpc_client, confirmation_height) + finalizer::pick(&self.finalizer_type, &self.rpc_client, confirmation_height) .latest_finalized_block_height() .await .change_context(Error::Finalizer)?; @@ -184,10 +188,7 @@ where "verify a new worker set for an EVM chain", poll_id = poll_id.to_string(), source_chain = source_chain.to_string(), - id = format!( - "0x{:x}{}{}", - worker_set.tx_id, ID_SEPARATOR, worker_set.event_index - ) + id = construct_message_id(worker_set.tx_id.into(), worker_set.event_index) ) .in_scope(|| { info!("ready to verify a new worker set in poll"); @@ -209,30 +210,30 @@ where #[cfg(test)] mod tests { - use std::convert::TryInto; + use std::{convert::TryInto, str::FromStr}; use base64::engine::general_purpose::STANDARD; use base64::Engine; + use cosmwasm_std::HexBinary; + use error_stack::{Report, Result}; use ethers::providers::ProviderError; use tendermint::abci; + use tokio::{sync::watch, test as async_test}; use axelar_wasm_std::operators::Operators; - use cosmwasm_std::HexBinary; + use connection_router_api::ChainName; use events::Event; use voting_verifier::events::{PollMetadata, PollStarted, WorkerSetConfirmation}; use crate::{ event_processor::EventHandler, - evm::{json_rpc::MockEthereumClient, ChainName}, + evm::{finalizer::Finalization, json_rpc::MockEthereumClient}, handlers::evm_verify_worker_set::PollStartedEvent, queue::queued_broadcaster::MockBroadcasterClient, types::{EVMAddress, Hash, TMAddress}, PREFIX, }; - use error_stack::{Report, Result}; - use tokio::{sync::watch, test as async_test}; - #[test] fn should_deserialize_correct_event() { let event: Event = get_event( @@ -268,7 +269,8 @@ mod tests { let handler = super::Handler::new( worker, voting_verifier, - ChainName::Ethereum, + ChainName::from_str("ethereum").unwrap(), + Finalization::RPCFinalizedBlock, rpc_client, broadcast_client, rx, @@ -343,9 +345,8 @@ mod tests { fn participants(n: u8, worker: Option) -> Vec { (0..n) - .into_iter() .map(|_| TMAddress::random(PREFIX)) - .chain(worker.into_iter()) + .chain(worker) .collect() } } diff --git a/ampd/src/handlers/sui_verify_msg.rs b/ampd/src/handlers/sui_verify_msg.rs index b2a731c01..8980fd8b8 100644 --- a/ampd/src/handlers/sui_verify_msg.rs +++ b/ampd/src/handlers/sui_verify_msg.rs @@ -6,12 +6,12 @@ use cosmrs::cosmwasm::MsgExecuteContract; use error_stack::ResultExt; use serde::Deserialize; use sui_types::base_types::{SuiAddress, TransactionDigest}; +use tokio::sync::watch::Receiver; +use tracing::info; use axelar_wasm_std::voting::{PollId, Vote}; use events::{Error::EventTypeMismatch, Event}; use events_derive::try_from; -use tokio::sync::watch::Receiver; -use tracing::info; use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; @@ -25,7 +25,7 @@ type Result = error_stack::Result; #[derive(Deserialize, Debug)] pub struct Message { pub tx_id: TransactionDigest, - pub event_index: u64, + pub event_index: u32, pub destination_address: String, pub destination_chain: connection_router_api::ChainName, pub source_address: SuiAddress, @@ -161,13 +161,13 @@ mod tests { use cosmwasm_std; use error_stack::{Report, Result}; use ethers::providers::ProviderError; - use events::Event; use sui_types::base_types::{SuiAddress, TransactionDigest}; use tokio::sync::watch; use tokio::test as async_test; + + use events::Event; use voting_verifier::events::{PollMetadata, PollStarted, TxEventConfirmation}; - use super::PollStartedEvent; use crate::event_processor::EventHandler; use crate::handlers::{errors::Error, tests::get_event}; use crate::queue::queued_broadcaster; @@ -175,6 +175,8 @@ mod tests { use crate::sui::json_rpc::MockSuiClient; use crate::types::{EVMAddress, Hash, TMAddress}; + use super::PollStartedEvent; + const PREFIX: &str = "axelar"; #[test] @@ -381,9 +383,8 @@ mod tests { fn participants(n: u8, worker: Option) -> Vec { (0..n) - .into_iter() .map(|_| TMAddress::random(PREFIX)) - .chain(worker.into_iter()) + .chain(worker) .collect() } } diff --git a/ampd/src/handlers/sui_verify_worker_set.rs b/ampd/src/handlers/sui_verify_worker_set.rs index bac852da2..cb13aede9 100644 --- a/ampd/src/handlers/sui_verify_worker_set.rs +++ b/ampd/src/handlers/sui_verify_worker_set.rs @@ -2,19 +2,19 @@ use std::convert::TryInto; use async_trait::async_trait; use cosmrs::cosmwasm::MsgExecuteContract; +use cosmwasm_std::HexBinary; +use cosmwasm_std::Uint128; use error_stack::ResultExt; use serde::Deserialize; use sui_types::base_types::{SuiAddress, TransactionDigest}; use tokio::sync::watch::Receiver; use tracing::{info, info_span}; +use valuable::Valuable; use axelar_wasm_std::voting::{PollId, Vote}; -use connection_router_api::ID_SEPARATOR; -use cosmwasm_std::HexBinary; -use cosmwasm_std::Uint128; use events::{Error::EventTypeMismatch, Event}; use events_derive::try_from; -use valuable::Valuable; +use voting_verifier::events::construct_message_id; use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; @@ -33,7 +33,7 @@ pub struct Operators { #[derive(Deserialize, Debug)] pub struct WorkerSetConfirmation { pub tx_id: TransactionDigest, - pub event_index: u64, + pub event_index: u32, pub operators: Operators, } @@ -145,10 +145,7 @@ where let vote = info_span!( "verify a new worker set for Sui", poll_id = poll_id.to_string(), - id = format!( - "0x{:x}{}{}", - worker_set.tx_id, ID_SEPARATOR, worker_set.event_index - ) + id = construct_message_id(worker_set.tx_id.into(), worker_set.event_index) ) .in_scope(|| { let vote = transaction_block.map_or(Vote::NotFound, |tx_receipt| { @@ -171,30 +168,28 @@ where mod tests { use std::convert::TryInto; - use axelar_wasm_std::operators::Operators; use cosmwasm_std::HexBinary; use error_stack::{Report, Result}; use ethers::providers::ProviderError; - use events::Event; use sui_types::base_types::{SuiAddress, TransactionDigest}; use tokio::sync::watch; + use tokio::test as async_test; + + use axelar_wasm_std::operators::Operators; + use events::Event; use voting_verifier::events::{PollMetadata, PollStarted, WorkerSetConfirmation}; - use super::PollStartedEvent; use crate::event_processor::EventHandler; use crate::queue::queued_broadcaster::MockBroadcasterClient; use crate::sui::json_rpc::MockSuiClient; use crate::PREFIX; use crate::{handlers::tests::get_event, types::TMAddress}; - use tokio::test as async_test; + use super::PollStartedEvent; #[test] fn should_deserialize_worker_set_poll_started_event() { - let participants = (0..5) - .into_iter() - .map(|_| TMAddress::random(PREFIX)) - .collect(); + let participants = (0..5).map(|_| TMAddress::random(PREFIX)).collect(); let event: Result = get_event( worker_set_poll_started_event(participants, 100), diff --git a/ampd/src/lib.rs b/ampd/src/lib.rs index 33e4cbbdb..7d15166c1 100644 --- a/ampd/src/lib.rs +++ b/ampd/src/lib.rs @@ -210,6 +210,7 @@ where worker.clone(), cosmwasm_contract, chain.name, + chain.finalization, json_rpc::Client::new_http( &chain.rpc_url, reqwest::ClientBuilder::new() @@ -233,6 +234,7 @@ where worker.clone(), cosmwasm_contract, chain.name, + chain.finalization, json_rpc::Client::new_http( &chain.rpc_url, reqwest::ClientBuilder::new() diff --git a/ampd/src/queue/msg_queue.rs b/ampd/src/queue/msg_queue.rs index 55171f4e8..b9d98a51d 100644 --- a/ampd/src/queue/msg_queue.rs +++ b/ampd/src/queue/msg_queue.rs @@ -1,4 +1,12 @@ use cosmrs::{Any, Gas}; +use error_stack::Result; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("overflow in gas cost calculation")] + GasCostOverflow, +} #[derive(Default)] pub struct MsgQueue { @@ -7,9 +15,14 @@ pub struct MsgQueue { } impl MsgQueue { - pub fn push(&mut self, msg: Any, gas_cost: Gas) { + pub fn push(&mut self, msg: Any, gas_cost: Gas) -> Result<(), Error> { self.msgs.push(msg); - self.gas_cost += gas_cost; + self.gas_cost = self + .gas_cost + .checked_add(gas_cost) + .ok_or(Error::GasCostOverflow)?; + + Ok(()) } pub fn pop_all(&mut self) -> Vec { @@ -40,7 +53,7 @@ mod test { fn msg_queue_push_should_work() { let mut queue = MsgQueue::default(); for gas_cost in 1..5 { - queue.push(dummy_msg(), gas_cost); + queue.push(dummy_msg(), gas_cost).unwrap(); } assert_eq!(queue.gas_cost(), 10); @@ -51,7 +64,7 @@ mod test { fn msg_queue_pop_all_should_work() { let mut queue = MsgQueue::default(); for gas_cost in 1..5 { - queue.push(dummy_msg(), gas_cost); + queue.push(dummy_msg(), gas_cost).unwrap(); } assert_eq!(queue.pop_all().len(), 4); diff --git a/ampd/src/queue/queued_broadcaster.rs b/ampd/src/queue/queued_broadcaster.rs index 9c8e19fc6..0f7db30d7 100644 --- a/ampd/src/queue/queued_broadcaster.rs +++ b/ampd/src/queue/queued_broadcaster.rs @@ -22,6 +22,8 @@ pub enum Error { Broadcast, #[error("failed encoding message to Protobuf Any {0}")] Proto(String), + #[error("failed to queue message")] + Queue, } pub struct QueuedBroadcasterDriver { @@ -120,13 +122,13 @@ where Some(msg) => { let fee = broadcaster.estimate_fee(vec![msg.clone()]).await.change_context(Error::EstimateFee)?; - if fee.gas_limit + queue.gas_cost() >= self.batch_gas_limit { + if fee.gas_limit.saturating_add(queue.gas_cost()) >= self.batch_gas_limit { broadcast_all(&mut queue, &mut broadcaster).await?; interval.reset(); } let message_type = msg.type_url.clone(); - queue.push(msg, fee.gas_limit); + queue.push(msg, fee.gas_limit).change_context(Error::Queue)?; info!( message_type, queue_size = queue.len(), diff --git a/ampd/src/state.rs b/ampd/src/state.rs index ad2e6891a..74978e994 100644 --- a/ampd/src/state.rs +++ b/ampd/src/state.rs @@ -124,13 +124,15 @@ fn ensure_parent_dirs_exist(path: impl AsRef) -> Result<(), Error> { #[cfg(test)] mod tests { - use crate::state; - use ecdsa::signature::rand_core::OsRng; use std::panic::UnwindSafe; use std::path::{Path, PathBuf}; use std::{fs, panic}; + + use ecdsa::signature::rand_core::OsRng; use tokio::sync::mpsc; + use crate::state; + use super::{State, StateUpdater}; fn run_test(state_path: impl AsRef, test: T) @@ -143,6 +145,7 @@ mod tests { assert!(result.is_ok()) } + #[allow(clippy::field_reassign_with_default)] // State has private fields, so using the object initializer is not possible #[test] fn can_load_and_flush_state() { let path = PathBuf::from("./state_subfolder/can_load_and_flush_state.json"); diff --git a/ampd/src/sui/verifier.rs b/ampd/src/sui/verifier.rs index a61198877..425fa6626 100644 --- a/ampd/src/sui/verifier.rs +++ b/ampd/src/sui/verifier.rs @@ -99,7 +99,7 @@ pub fn verify_message( transaction_block: &SuiTransactionBlockResponse, message: &Message, ) -> Vote { - match find_event(transaction_block, message.event_index) { + match find_event(transaction_block, message.event_index as u64) { Some(event) if transaction_block.digest == message.tx_id && event.type_ == EventType::ContractCall.struct_tag(gateway_address) @@ -116,7 +116,7 @@ pub fn verify_worker_set( transaction_block: &SuiTransactionBlockResponse, worker_set: &WorkerSetConfirmation, ) -> Vote { - match find_event(transaction_block, worker_set.event_index) { + match find_event(transaction_block, worker_set.event_index as u64) { Some(event) if transaction_block.digest == worker_set.tx_id && event.type_ @@ -165,7 +165,7 @@ mod tests { fn should_not_verify_msg_if_event_index_does_not_match() { let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_block(); - msg.event_index = rand::random::(); + msg.event_index = rand::random::(); assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -240,7 +240,7 @@ mod tests { let msg = Message { tx_id: TransactionDigest::random(), - event_index: rand::random::(), + event_index: rand::random::(), source_address: SuiAddress::random_for_testing_only(), destination_chain: rand_chain_name(), destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), @@ -260,7 +260,7 @@ mod tests { let event = SuiEvent { id: EventID { tx_digest: msg.tx_id, - event_seq: msg.event_index, + event_seq: msg.event_index as u64, }, package_id: gateway_address.into(), transaction_module: "gateway".parse().unwrap(), @@ -294,7 +294,7 @@ mod tests { let worker_set_confirmation = WorkerSetConfirmation { tx_id: TransactionDigest::random(), - event_index: rand::random::(), + event_index: rand::random::(), operators: Operators { weights_by_addresses: vec![ ( @@ -374,7 +374,7 @@ mod tests { let event = SuiEvent { id: EventID { tx_digest: worker_set_confirmation.tx_id, - event_seq: worker_set_confirmation.event_index, + event_seq: worker_set_confirmation.event_index as u64, }, package_id: gateway_address.into(), transaction_module: "gateway".parse().unwrap(), diff --git a/ampd/src/tests/config_template.toml b/ampd/src/tests/config_template.toml index af625706f..33ecfb8f0 100644 --- a/ampd/src/tests/config_template.toml +++ b/ampd/src/tests/config_template.toml @@ -16,8 +16,9 @@ broadcast_interval = '5s' [[handlers]] type = 'EvmMsgVerifier' cosmwasm_contract = 'axelar1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqecnww6' -chain_name = 'Ethereum' +chain_name = 'ethereum' chain_rpc_url = 'http://127.0.0.1/' +chain_finalization = 'RPCFinalizedBlock' [handlers.rpc_timeout] secs = 3 @@ -26,8 +27,9 @@ nanos = 0 [[handlers]] type = 'EvmWorkerSetVerifier' cosmwasm_contract = 'axelar1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqecnww6' -chain_name = 'Fantom' +chain_name = 'fantom' chain_rpc_url = 'http://127.0.0.1/' +chain_finalization = 'ConfirmationHeight' [handlers.rpc_timeout] secs = 3 diff --git a/ampd/src/tofnd/grpc.rs b/ampd/src/tofnd/grpc.rs index ea68ac48c..2907f621b 100644 --- a/ampd/src/tofnd/grpc.rs +++ b/ampd/src/tofnd/grpc.rs @@ -8,12 +8,13 @@ use mockall::automock; use tokio::sync::Mutex; use tonic::{transport::Channel, Status}; +use crate::{types::PublicKey, url::Url}; + use super::proto::{ keygen_response::KeygenResponse, multisig_client, sign_response::SignResponse, KeygenRequest, SignRequest, }; use super::{error::Error, error::TofndError, MessageDigest, Signature}; -use crate::{types::PublicKey, url::Url}; type Result = error_stack::Result; @@ -214,12 +215,12 @@ mod tests { thread_rng().fill_bytes(&mut hash); let priv_key = SigningKey::::random(&mut OsRng); - let (signature, _) = priv_key.sign_prehash_recoverable(&hash.as_ref()).unwrap(); + let (signature, _) = priv_key.sign_prehash_recoverable(hash.as_ref()).unwrap(); let mut client = MockEcdsaClient::new(); client .expect_sign() - .returning(move |_, _, _| Ok(signature.to_vec().into())); + .returning(move |_, _, _| Ok(signature.to_vec())); let digest: MessageDigest = rand::random::<[u8; 32]>().into(); @@ -247,7 +248,7 @@ mod tests { let mut client = MockEcdsaClient::new(); client .expect_sign() - .returning(move |_, _, _| Ok(signature.to_vec().into())); + .returning(move |_, _, _| Ok(signature.to_vec())); let client = SharableEcdsaClient::new(client); diff --git a/ampd/src/types.rs b/ampd/src/types.rs index febf78254..eb2c128fd 100644 --- a/ampd/src/types.rs +++ b/ampd/src/types.rs @@ -7,8 +7,6 @@ use cosmwasm_std::Uint256; use ethers::types::{Address, H256}; use serde::{Deserialize, Serialize}; -mod tests; - pub type EVMAddress = Address; pub type Hash = H256; pub type PublicKey = crypto::PublicKey; @@ -63,3 +61,21 @@ impl fmt::Display for TMAddress { self.0.fmt(f) } } + +#[cfg(test)] +pub mod test_utils { + use ecdsa::SigningKey; + use rand::rngs::OsRng; + + use crate::types::{PublicKey, TMAddress}; + + impl TMAddress { + pub fn random(prefix: &str) -> Self { + Self( + PublicKey::from(SigningKey::random(&mut OsRng).verifying_key()) + .account_id(prefix) + .expect("failed to convert to account identifier"), + ) + } + } +} diff --git a/ampd/src/types/tests.rs b/ampd/src/types/tests.rs deleted file mode 100644 index 8a63456cf..000000000 --- a/ampd/src/types/tests.rs +++ /dev/null @@ -1,17 +0,0 @@ -#[cfg(test)] -mod tests { - use ecdsa::SigningKey; - use rand::rngs::OsRng; - - use crate::types::{PublicKey, TMAddress}; - - impl TMAddress { - pub fn random(prefix: &str) -> Self { - Self( - PublicKey::from(SigningKey::random(&mut OsRng).verifying_key()) - .account_id(prefix) - .expect("failed to convert to account identifier"), - ) - } - } -} diff --git a/contracts/aggregate-verifier/Cargo.toml b/contracts/aggregate-verifier/Cargo.toml index 93b91163f..37509ad12 100644 --- a/contracts/aggregate-verifier/Cargo.toml +++ b/contracts/aggregate-verifier/Cargo.toml @@ -53,3 +53,6 @@ voting-verifier = { workspace = true, features = ["library"] } [dev-dependencies] cw-multi-test = "0.15.1" integration-tests = { workspace = true } + +[lints] +workspace = true diff --git a/contracts/aggregate-verifier/src/client.rs b/contracts/aggregate-verifier/src/client.rs index 16b4142b4..d06fb662f 100644 --- a/contracts/aggregate-verifier/src/client.rs +++ b/contracts/aggregate-verifier/src/client.rs @@ -1,7 +1,7 @@ use axelar_wasm_std::utils::TryMapExt; use axelar_wasm_std::{FnExt, VerificationStatus}; use connection_router_api::{CrossChainId, Message}; -use cosmwasm_std::{to_binary, Addr, QuerierWrapper, QueryRequest, WasmMsg, WasmQuery}; +use cosmwasm_std::{to_json_binary, Addr, QuerierWrapper, QueryRequest, WasmMsg, WasmQuery}; use error_stack::{Result, ResultExt}; use serde::de::DeserializeOwned; use std::collections::HashMap; @@ -15,7 +15,7 @@ impl Verifier<'_> { fn execute(&self, msg: &crate::msg::ExecuteMsg) -> WasmMsg { WasmMsg::Execute { contract_addr: self.address.to_string(), - msg: to_binary(msg).expect("msg should always be serializable"), + msg: to_json_binary(msg).expect("msg should always be serializable"), funds: vec![], } } @@ -24,7 +24,7 @@ impl Verifier<'_> { self.querier .query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: self.address.to_string(), - msg: to_binary(&msg).expect("msg should always be serializable"), + msg: to_json_binary(&msg).expect("msg should always be serializable"), })) .change_context(Error::QueryVerifier) } @@ -90,7 +90,7 @@ pub enum Error { #[cfg(test)] mod tests { use axelar_wasm_std::VerificationStatus; - use connection_router_api::CrossChainId; + use connection_router_api::{CrossChainId, CHAIN_NAME_DELIMITER}; use cosmwasm_std::testing::MockQuerier; use std::str::FromStr; @@ -120,7 +120,12 @@ mod tests { fn verifier_returns_error_on_return_type_mismatch() { let mut querier = MockQuerier::default(); querier.update_wasm(|_| { - Ok(to_binary(&CrossChainId::from_str("eth:0x1234").unwrap()).into()).into() + Ok(to_json_binary( + &CrossChainId::from_str(format!("eth{}0x1234", CHAIN_NAME_DELIMITER).as_str()) + .unwrap(), + ) + .into()) + .into() }); let verifier = Verifier { diff --git a/contracts/aggregate-verifier/src/contract.rs b/contracts/aggregate-verifier/src/contract.rs index d238c4c2c..6b5c303c9 100644 --- a/contracts/aggregate-verifier/src/contract.rs +++ b/contracts/aggregate-verifier/src/contract.rs @@ -3,8 +3,8 @@ use connection_router_api::CrossChainId; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - from_binary, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, QueryRequest, Reply, Response, - StdResult, WasmQuery, + from_json, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, QueryRequest, Reply, + Response, StdResult, WasmQuery, }; use cw_utils::{parse_reply_execute_data, MsgExecuteContractResponse}; @@ -45,7 +45,7 @@ pub fn execute( } pub mod execute { - use cosmwasm_std::{to_binary, SubMsg, WasmMsg}; + use cosmwasm_std::{to_json_binary, SubMsg, WasmMsg}; use connection_router_api::Message; @@ -59,7 +59,7 @@ pub mod execute { Ok(Response::new().add_submessage(SubMsg::reply_on_success( WasmMsg::Execute { contract_addr: verifier.to_string(), - msg: to_binary(&voting_msg::ExecuteMsg::VerifyMessages { messages: msgs })?, + msg: to_json_binary(&voting_msg::ExecuteMsg::VerifyMessages { messages: msgs })?, funds: vec![], }, VERIFY_REPLY, @@ -79,7 +79,7 @@ pub fn reply( match parse_reply_execute_data(reply) { Ok(MsgExecuteContractResponse { data: Some(data) }) => { // check format of data - let _: Vec<(CrossChainId, VerificationStatus)> = from_binary(&data)?; + let _: Vec<(CrossChainId, VerificationStatus)> = from_json(&data)?; // only one verifier, so just return the response as is Ok(Response::new().set_data(data)) @@ -102,7 +102,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { let verifier = CONFIG.load(deps.storage)?.verifier; deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: verifier.to_string(), - msg: to_binary(&voting_msg::QueryMsg::GetMessagesStatus { messages })?, + msg: to_json_binary(&voting_msg::QueryMsg::GetMessagesStatus { messages })?, })) } } diff --git a/contracts/aggregate-verifier/tests/mock.rs b/contracts/aggregate-verifier/tests/mock.rs index 2e241238b..3aea3f03f 100644 --- a/contracts/aggregate-verifier/tests/mock.rs +++ b/contracts/aggregate-verifier/tests/mock.rs @@ -2,7 +2,7 @@ use aggregate_verifier::error::ContractError; use axelar_wasm_std::VerificationStatus; use connection_router_api::{CrossChainId, Message}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{to_binary, Addr, DepsMut, Env, MessageInfo, Response}; +use cosmwasm_std::{to_json_binary, Addr, DepsMut, Env, MessageInfo, Response}; use cw_multi_test::{App, ContractWrapper, Executor}; use cw_storage_plus::Map; @@ -33,7 +33,7 @@ pub fn mock_verifier_execute( None => res.push((m.cc_id, VerificationStatus::None)), } } - Ok(Response::new().set_data(to_binary(&res)?)) + Ok(Response::new().set_data(to_json_binary(&res)?)) } MockVotingVerifierExecuteMsg::MessagesVerified { messages } => { for m in messages { @@ -64,19 +64,17 @@ pub fn make_mock_voting_verifier(app: &mut App) -> Addr { |_, _, _, _: MockVotingVerifierInstantiateMsg| { Ok::(Response::new()) }, - |_, _, _: aggregate_verifier::msg::QueryMsg| to_binary(&()), + |_, _, _: aggregate_verifier::msg::QueryMsg| to_json_binary(&()), ); let code_id = app.store_code(Box::new(code)); - let contract_address = app - .instantiate_contract( - code_id, - Addr::unchecked("voting_verifier"), - &MockVotingVerifierInstantiateMsg {}, - &[], - "Contract", - None, - ) - .unwrap(); - contract_address + app.instantiate_contract( + code_id, + Addr::unchecked("voting_verifier"), + &MockVotingVerifierInstantiateMsg {}, + &[], + "Contract", + None, + ) + .unwrap() } diff --git a/contracts/aggregate-verifier/tests/test.rs b/contracts/aggregate-verifier/tests/test.rs index 410c988d3..ffa6a2131 100644 --- a/contracts/aggregate-verifier/tests/test.rs +++ b/contracts/aggregate-verifier/tests/test.rs @@ -1,12 +1,14 @@ use aggregate_verifier::msg::ExecuteMsg; use axelar_wasm_std::VerificationStatus; -use connection_router_api::{CrossChainId, Message, ID_SEPARATOR}; -use cosmwasm_std::from_binary; +use connection_router_api::{CrossChainId, Message}; +use cosmwasm_std::from_json; use cosmwasm_std::Addr; -use cw_multi_test::{App, Executor}; +use cw_multi_test::App; + use integration_tests::contract::Contract; use crate::mock::{make_mock_voting_verifier, mark_messages_as_verified}; + pub mod mock; mod test_utils; @@ -14,7 +16,7 @@ fn generate_messages(count: usize) -> Vec { let mut msgs = vec![]; for x in 0..count { let src_chain = "mock-chain"; - let id = format!("tx_hash{}{}", ID_SEPARATOR, x); + let id = format!("tx_hash{}", x); msgs.push(Message { cc_id: CrossChainId { chain: src_chain.parse().unwrap(), @@ -46,7 +48,7 @@ fn verify_messages_empty() { &ExecuteMsg::VerifyMessages { messages: vec![] }, ) .unwrap(); - let ret: Vec<(CrossChainId, VerificationStatus)> = from_binary(&res.data.unwrap()).unwrap(); + let ret: Vec<(CrossChainId, VerificationStatus)> = from_json(res.data.unwrap()).unwrap(); assert_eq!(ret, vec![]); } @@ -70,7 +72,7 @@ fn verify_messages_not_verified() { }, ) .unwrap(); - let ret: Vec<(CrossChainId, VerificationStatus)> = from_binary(&res.data.unwrap()).unwrap(); + let ret: Vec<(CrossChainId, VerificationStatus)> = from_json(res.data.unwrap()).unwrap(); assert_eq!( ret, messages @@ -102,7 +104,7 @@ fn verify_messages_verified() { }, ) .unwrap(); - let ret: Vec<(CrossChainId, VerificationStatus)> = from_binary(&res.data.unwrap()).unwrap(); + let ret: Vec<(CrossChainId, VerificationStatus)> = from_json(res.data.unwrap()).unwrap(); assert_eq!( ret, messages @@ -135,17 +137,13 @@ fn verify_messages_mixed_status() { }, ) .unwrap(); - let ret: Vec<(CrossChainId, VerificationStatus)> = from_binary(&res.data.unwrap()).unwrap(); + let ret: Vec<(CrossChainId, VerificationStatus)> = from_json(res.data.unwrap()).unwrap(); assert_eq!( ret, messages .iter() .map(|msg| { - if verified - .iter() - .find(|verified_msg| *verified_msg == msg) - .is_some() - { + if verified.iter().any(|verified_msg| verified_msg == msg) { (msg.cc_id.clone(), VerificationStatus::SucceededOnChain) } else { (msg.cc_id.clone(), VerificationStatus::None) diff --git a/contracts/connection-router/Cargo.toml b/contracts/connection-router/Cargo.toml index 0145b92bc..ef9eae1cc 100644 --- a/contracts/connection-router/Cargo.toml +++ b/contracts/connection-router/Cargo.toml @@ -58,3 +58,6 @@ cw-multi-test = "0.15.1" hex = { version = "0.4.3", default-features = false } integration-tests = { workspace = true } rand = "0.8.5" + +[lints] +workspace = true diff --git a/contracts/connection-router/src/contract.rs b/contracts/connection-router/src/contract.rs index 68b4f488c..0d1b44504 100644 --- a/contracts/connection-router/src/contract.rs +++ b/contracts/connection-router/src/contract.rs @@ -1,7 +1,8 @@ -use connection_router_api::msg::{ExecuteMsg, QueryMsg}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; +use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; + +use connection_router_api::msg::{ExecuteMsg, QueryMsg}; use crate::events::RouterInstantiated; use crate::msg::InstantiateMsg; @@ -107,7 +108,7 @@ pub fn query( msg: QueryMsg, ) -> Result { match msg { - QueryMsg::GetChainInfo(chain) => to_binary(&query::get_chain_info(deps, chain)?), + QueryMsg::GetChainInfo(chain) => to_json_binary(&query::get_chain_info(deps, chain)?), } .map_err(axelar_wasm_std::ContractError::from) } diff --git a/contracts/connection-router/src/contract/execute.rs b/contracts/connection-router/src/contract/execute.rs index 54143c777..861a98e70 100644 --- a/contracts/connection-router/src/contract/execute.rs +++ b/contracts/connection-router/src/contract/execute.rs @@ -1,13 +1,16 @@ use std::vec; +use cosmwasm_std::{to_json_binary, Addr, DepsMut, MessageInfo, Response, StdResult, WasmMsg}; +use error_stack::report; +use itertools::Itertools; + use axelar_wasm_std::flagset::FlagSet; use connection_router_api::error::Error; use connection_router_api::{ChainEndpoint, ChainName, Gateway, GatewayDirection, Message}; -use cosmwasm_std::{to_binary, Addr, DepsMut, MessageInfo, Response, StdResult, WasmMsg}; -use error_stack::report; -use itertools::Itertools; -use crate::events::{ChainFrozen, ChainRegistered, GatewayInfo, GatewayUpgraded, MessageRouted}; +use crate::events::{ + ChainFrozen, ChainRegistered, ChainUnfrozen, GatewayInfo, GatewayUpgraded, MessageRouted, +}; use crate::state::{chain_endpoints, Store, CONFIG}; use super::Contract; @@ -78,9 +81,16 @@ pub fn freeze_chain( Ok(chain) } })?; - Ok(Response::new().add_event(ChainFrozen { name: chain }.into())) + Ok(Response::new().add_event( + ChainFrozen { + name: chain, + direction, + } + .into(), + )) } +#[allow(clippy::arithmetic_side_effects)] // flagset operations don't cause under/overflows pub fn unfreeze_chain( deps: DepsMut, chain: ChainName, @@ -93,7 +103,13 @@ pub fn unfreeze_chain( Ok(chain) } })?; - Ok(Response::new().add_event(ChainFrozen { name: chain }.into())) + Ok(Response::new().add_event( + ChainUnfrozen { + name: chain, + direction, + } + .into(), + )) } pub fn require_admin(deps: &DepsMut, info: MessageInfo) -> Result<(), Error> { @@ -174,7 +190,7 @@ where Ok(WasmMsg::Execute { contract_addr: gateway.to_string(), - msg: to_binary(&gateway_api::msg::ExecuteMsg::RouteMessages( + msg: to_json_binary(&gateway_api::msg::ExecuteMsg::RouteMessages( msgs.cloned().collect(), )) .expect("must serialize message"), @@ -191,32 +207,39 @@ where #[cfg(test)] mod test { + use cosmwasm_std::Addr; + use mockall::predicate; + use rand::{Rng, RngCore}; + use axelar_wasm_std::flagset::FlagSet; use connection_router_api::error::Error; use connection_router_api::{ ChainEndpoint, ChainName, CrossChainId, Gateway, GatewayDirection, Message, }; - use cosmwasm_std::Addr; - use mockall::predicate; - use rand::{Rng, RngCore}; + use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::Storage; + use crate::events::{ChainFrozen, ChainUnfrozen}; + use crate::state::chain_endpoints; use crate::{ contract::Contract, state::{Config, MockStore, ID_SEPARATOR}, }; + use super::{freeze_chain, unfreeze_chain}; + fn rand_message(source_chain: ChainName, destination_chain: ChainName) -> Message { let mut bytes = [0; 32]; rand::thread_rng().fill_bytes(&mut bytes); - let tx_id = hex::encode(&bytes); + let tx_id = hex::encode(bytes); let mut bytes = [0; 20]; rand::thread_rng().fill_bytes(&mut bytes); - let source_address = format!("0x{}", hex::encode(&bytes)).try_into().unwrap(); + let source_address = format!("0x{}", hex::encode(bytes)).try_into().unwrap(); let mut bytes = [0; 20]; rand::thread_rng().fill_bytes(&mut bytes); - let destination_address = format!("0x{}", hex::encode(&bytes)).try_into().unwrap(); + let destination_address = format!("0x{}", hex::encode(bytes)).try_into().unwrap(); let mut payload_hash = [0; 32]; rand::thread_rng().fill_bytes(&mut payload_hash); @@ -597,4 +620,136 @@ mod test { ) .is_ok_and(|res| { res.messages.len() == 1 })); } + + #[test] + fn multiple_freeze_unfreeze_causes_no_arithmetic_side_effect() { + let mut deps = mock_dependencies(); + let chain: ChainName = "ethereum".parse().unwrap(); + + chain_endpoints() + .save( + deps.as_mut().storage, + chain.clone(), + &ChainEndpoint { + name: chain.clone(), + gateway: Gateway { + address: Addr::unchecked("gateway"), + }, + frozen_status: FlagSet::from(GatewayDirection::None), + }, + ) + .unwrap(); + + // freezing twice produces same result + freeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Incoming).unwrap(); + freeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Incoming).unwrap(); + + assert_chain_endpoint_frozen_status( + deps.as_mut().storage, + chain.clone(), + FlagSet::from(GatewayDirection::Incoming), + ); + + freeze_chain( + deps.as_mut(), + chain.clone(), + GatewayDirection::Bidirectional, + ) + .unwrap(); + freeze_chain( + deps.as_mut(), + chain.clone(), + GatewayDirection::Bidirectional, + ) + .unwrap(); + + assert_chain_endpoint_frozen_status( + deps.as_mut().storage, + chain.clone(), + FlagSet::from(GatewayDirection::Bidirectional), + ); + + // unfreezing twice produces same result + unfreeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Outgoing).unwrap(); + unfreeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Outgoing).unwrap(); + + assert_chain_endpoint_frozen_status( + deps.as_mut().storage, + chain.clone(), + FlagSet::from(GatewayDirection::Incoming), + ); + + unfreeze_chain( + deps.as_mut(), + chain.clone(), + GatewayDirection::Bidirectional, + ) + .unwrap(); + unfreeze_chain( + deps.as_mut(), + chain.clone(), + GatewayDirection::Bidirectional, + ) + .unwrap(); + + assert_chain_endpoint_frozen_status( + deps.as_mut().storage, + chain.clone(), + FlagSet::from(GatewayDirection::None), + ); + } + + #[test] + fn freezing_unfreezing_chain_emits_correct_event() { + let mut deps = mock_dependencies(); + let chain: ChainName = "ethereum".parse().unwrap(); + + chain_endpoints() + .save( + deps.as_mut().storage, + chain.clone(), + &ChainEndpoint { + name: chain.clone(), + gateway: Gateway { + address: Addr::unchecked("gateway"), + }, + frozen_status: FlagSet::from(GatewayDirection::None), + }, + ) + .unwrap(); + + let res = freeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Incoming).unwrap(); + + assert_eq!(res.events.len(), 1); + assert!(res.events.contains( + &ChainFrozen { + name: chain.clone(), + direction: GatewayDirection::Incoming, + } + .into() + )); + + let res = unfreeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Incoming).unwrap(); + + assert_eq!(res.events.len(), 1); + assert!(res.events.contains( + &ChainUnfrozen { + name: chain.clone(), + direction: GatewayDirection::Incoming, + } + .into() + )); + } + + fn assert_chain_endpoint_frozen_status( + storage: &dyn Storage, + chain: ChainName, + expected: FlagSet, + ) { + let status = chain_endpoints() + .load(storage, chain.clone()) + .unwrap() + .frozen_status; + assert_eq!(status, expected); + } } diff --git a/contracts/connection-router/src/events.rs b/contracts/connection-router/src/events.rs index 6c6c0091a..264263ac1 100644 --- a/contracts/connection-router/src/events.rs +++ b/contracts/connection-router/src/events.rs @@ -1,4 +1,5 @@ -use connection_router_api::{ChainName, Message}; +use axelar_wasm_std::event; +use connection_router_api::{ChainName, GatewayDirection, Message}; use cosmwasm_std::{Addr, Attribute, Event}; pub struct RouterInstantiated { @@ -31,10 +32,12 @@ pub struct GatewayUnfrozen { pub struct ChainFrozen { pub name: ChainName, + pub direction: GatewayDirection, } pub struct ChainUnfrozen { pub name: ChainName, + pub direction: GatewayDirection, } pub struct MessageRouted { @@ -89,13 +92,23 @@ impl From for Event { impl From for Event { fn from(other: ChainFrozen) -> Self { - Event::new("chain_frozen").add_attribute("name", other.name) + Event::new("chain_frozen") + .add_attribute("name", other.name) + .add_attribute( + "direction", + event::attribute_value(&other.direction).expect("failed to serialize direction"), + ) } } impl From for Event { fn from(other: ChainUnfrozen) -> Self { - Event::new("chain_unfrozen").add_attribute("name", other.name) + Event::new("chain_unfrozen") + .add_attribute("name", other.name) + .add_attribute( + "direction", + event::attribute_value(&other.direction).expect("failed to serialize direction"), + ) } } diff --git a/contracts/connection-router/tests/mock.rs b/contracts/connection-router/tests/mock.rs index 22541610f..18fbcb472 100644 --- a/contracts/connection-router/tests/mock.rs +++ b/contracts/connection-router/tests/mock.rs @@ -1,7 +1,9 @@ use connection_router_api::error::Error; use connection_router_api::{CrossChainId, Message}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{ + to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, +}; use cw_multi_test::{App, ContractWrapper, Executor}; use cw_storage_plus::Map; @@ -38,20 +40,19 @@ pub fn mock_gateway_query(deps: Deps, _env: Env, msg: MockGatewayQueryMsg) -> St match msg { MockGatewayQueryMsg::GetOutgoingMessages { ids } => { for id in ids { - match MOCK_GATEWAY_MESSAGES.may_load(deps.storage, id)? { - Some(m) => msgs.push(m), - None => (), + if let Some(m) = MOCK_GATEWAY_MESSAGES.may_load(deps.storage, id)? { + msgs.push(m) } } } } - to_binary(&msgs) + to_json_binary(&msgs) } pub fn get_gateway_messages( app: &mut App, gateway_address: Addr, - msgs: &Vec, + msgs: &[Message], ) -> Vec { app.wrap() .query_wasm_smart( @@ -71,19 +72,17 @@ pub fn make_mock_gateway(app: &mut App) -> Addr { ); let code_id = app.store_code(Box::new(code)); - let contract_address = app - .instantiate_contract( - code_id, - Addr::unchecked("sender"), - &connection_router::msg::InstantiateMsg { - admin_address: Addr::unchecked("admin").to_string(), - governance_address: Addr::unchecked("governance").to_string(), - nexus_gateway: Addr::unchecked("nexus_gateway").to_string(), - }, - &[], - "Contract", - None, - ) - .unwrap(); - contract_address + app.instantiate_contract( + code_id, + Addr::unchecked("sender"), + &connection_router::msg::InstantiateMsg { + admin_address: Addr::unchecked("admin").to_string(), + governance_address: Addr::unchecked("governance").to_string(), + nexus_gateway: Addr::unchecked("nexus_gateway").to_string(), + }, + &[], + "Contract", + None, + ) + .unwrap() } diff --git a/contracts/connection-router/tests/test.rs b/contracts/connection-router/tests/test.rs index 835659fd5..abb24c110 100644 --- a/contracts/connection-router/tests/test.rs +++ b/contracts/connection-router/tests/test.rs @@ -6,7 +6,9 @@ use std::{collections::HashMap, vec}; use connection_router_api::error::Error; use connection_router_api::msg::ExecuteMsg; -use connection_router_api::{ChainName, CrossChainId, GatewayDirection, Message}; +use connection_router_api::{ + ChainName, CrossChainId, GatewayDirection, Message, CHAIN_NAME_DELIMITER, +}; use cosmwasm_std::Addr; use cw_multi_test::App; use integration_tests::contract::Contract; @@ -69,6 +71,7 @@ fn register_chain(config: &mut TestConfig, chain: &Chain) { .unwrap(); } +#[allow(clippy::arithmetic_side_effects)] fn generate_messages( src_chain: &Chain, dest_chain: &Chain, @@ -77,7 +80,7 @@ fn generate_messages( ) -> Vec { let mut msgs = vec![]; for x in 0..count { - *nonce = *nonce + 1; + *nonce += 1; let id = format!("tx_id:{}", nonce); msgs.push(Message { cc_id: CrossChainId { @@ -172,7 +175,7 @@ fn multi_chain_route() { for d in &chains { let mut msgs = vec![]; for s in &chains { - let mut sending = generate_messages(&s, &d, nonce, 50); + let mut sending = generate_messages(s, d, nonce, 50); all_msgs_by_src .entry(s.chain_name.to_string()) @@ -399,12 +402,12 @@ fn upgrade_gateway_outgoing() { .unwrap(); let outgoing_messages = - mock::get_gateway_messages(&mut config.app, new_gateway, &vec![message.clone()]); + mock::get_gateway_messages(&mut config.app, new_gateway, &[message.clone()]); assert_eq!(outgoing_messages.len(), 1); assert_eq!(message.clone(), outgoing_messages[0]); let outgoing_messages = - mock::get_gateway_messages(&mut config.app, polygon.gateway, &vec![message.clone()]); + mock::get_gateway_messages(&mut config.app, polygon.gateway, &[message.clone()]); assert_eq!(outgoing_messages.len(), 0); } @@ -448,7 +451,7 @@ fn upgrade_gateway_incoming() { ); assert!(res.is_ok()); - let messages = mock::get_gateway_messages(&mut config.app, eth.gateway, &vec![message.clone()]); + let messages = mock::get_gateway_messages(&mut config.app, eth.gateway, &[message.clone()]); assert_eq!(messages.len(), 1); assert_eq!(messages[0], message.clone()); } @@ -524,7 +527,7 @@ fn chain_already_registered() { #[test] fn invalid_chain_name() { test_utils::are_contract_err_strings_equal( - ChainName::from_str("bad:").unwrap_err(), + ChainName::from_str(format!("bad{}", CHAIN_NAME_DELIMITER).as_str()).unwrap_err(), Error::InvalidChainName, ); @@ -616,11 +619,8 @@ fn freeze_incoming() { ); assert!(res.is_ok()); - let messages = mock::get_gateway_messages( - &mut config.app, - polygon.gateway.clone(), - &vec![message.clone()], - ); + let messages = + mock::get_gateway_messages(&mut config.app, polygon.gateway.clone(), &[message.clone()]); assert_eq!(&messages[0], message); let res = config.connection_router.execute( @@ -694,8 +694,7 @@ fn freeze_outgoing() { &ExecuteMsg::RouteMessages(vec![message.clone()]), ); assert!(res.is_ok()); - let messages = - mock::get_gateway_messages(&mut config.app, polygon.gateway, &vec![message.clone()]); + let messages = mock::get_gateway_messages(&mut config.app, polygon.gateway, &[message.clone()]); assert_eq!(messages.len(), 1); assert_eq!(messages[0], message.clone()); } @@ -782,7 +781,7 @@ fn freeze_chain() { let outgoing_messages = mock::get_gateway_messages( &mut config.app, polygon.gateway.clone(), - &vec![routed_msg.clone()], + &[routed_msg.clone()], ); assert_eq!(1, outgoing_messages.len()); assert_eq!(routed_msg.clone(), outgoing_messages[0]); diff --git a/contracts/gateway/Cargo.toml b/contracts/gateway/Cargo.toml index 3b3025370..05ef0210e 100644 --- a/contracts/gateway/Cargo.toml +++ b/contracts/gateway/Cargo.toml @@ -55,3 +55,6 @@ thiserror = { workspace = true } [dev-dependencies] cw-multi-test = "0.15.1" + +[lints] +workspace = true diff --git a/contracts/gateway/src/contract.rs b/contracts/gateway/src/contract.rs index 90b178f6d..1f5a907c7 100644 --- a/contracts/gateway/src/contract.rs +++ b/contracts/gateway/src/contract.rs @@ -57,7 +57,7 @@ pub enum Error { mod internal { use aggregate_verifier::client::Verifier; use connection_router_api::client::Router; - use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; + use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; use error_stack::{Result, ResultExt}; use gateway_api::msg::{ExecuteMsg, QueryMsg}; @@ -122,7 +122,7 @@ mod internal { match msg { QueryMsg::GetOutgoingMessages { message_ids } => { let msgs = contract::query::get_outgoing_messages(deps.storage, message_ids)?; - to_binary(&msgs).change_context(Error::SerializeResponse) + to_json_binary(&msgs).change_context(Error::SerializeResponse) } } } diff --git a/contracts/gateway/tests/contract.rs b/contracts/gateway/tests/contract.rs index 069dd80ff..a20ba1663 100644 --- a/contracts/gateway/tests/contract.rs +++ b/contracts/gateway/tests/contract.rs @@ -4,19 +4,19 @@ use std::fs::File; use std::iter; use axelar_wasm_std::{ContractError, VerificationStatus}; -use connection_router_api::{CrossChainId, Message, ID_SEPARATOR}; +use connection_router_api::{CrossChainId, Message}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockQuerier}; +#[cfg(not(feature = "generate_golden_files"))] +use cosmwasm_std::Response; use cosmwasm_std::{ - from_binary, to_binary, Addr, ContractResult, DepsMut, QuerierResult, WasmQuery, + from_json, to_json_binary, Addr, ContractResult, DepsMut, QuerierResult, WasmQuery, }; -use gateway::contract::*; -use gateway::msg::InstantiateMsg; -use gateway_api::msg::{ExecuteMsg, QueryMsg}; use itertools::Itertools; use serde::Serialize; -#[cfg(not(feature = "generate_golden_files"))] -use cosmwasm_std::Response; +use gateway::contract::*; +use gateway::msg::InstantiateMsg; +use gateway_api::msg::{ExecuteMsg, QueryMsg}; #[test] fn instantiate_works() { @@ -145,7 +145,10 @@ fn successful_route_outgoing() { iter::repeat(query(deps.as_ref(), mock_env(), query_msg.clone()).unwrap()) .take(2) .for_each(|response| { - assert_eq!(response, to_binary::>(&vec![]).unwrap()) + assert_eq!( + response, + to_json_binary::>(&vec![]).unwrap() + ) }); // check routing of outgoing messages is idempotent @@ -153,7 +156,7 @@ fn successful_route_outgoing() { execute( deps.as_mut(), mock_env(), - mock_info(&router, &[]), // execute with router as sender + mock_info(router, &[]), // execute with router as sender ExecuteMsg::RouteMessages(msgs.clone()), ) .unwrap(), @@ -169,7 +172,7 @@ fn successful_route_outgoing() { // check all outgoing messages are stored because the router (sender) is implicitly trusted iter::repeat(query(deps.as_ref(), mock_env().clone(), query_msg).unwrap()) .take(2) - .for_each(|response| assert_eq!(response, to_binary(&msgs).unwrap())); + .for_each(|response| assert_eq!(response, to_json_binary(&msgs).unwrap())); } let golden_file = "tests/test_route_outgoing.json"; @@ -293,14 +296,12 @@ fn test_cases_for_correct_verifier() -> ( // no messages test_cases.push(vec![]); - // // one message of each status - for msgs in all_messages.iter() { - test_cases.push(msgs.into_iter().cloned().take(1).collect::>()); - } - // // multiple messages with same status + // one message of each status for msgs in all_messages.iter() { - test_cases.push(msgs.into_iter().cloned().collect()); + test_cases.push(msgs.iter().take(1).cloned().collect::>()); } + // multiple messages with same status + test_cases.append(&mut all_messages.clone()); // multiple messages with multiple statuses test_cases.push(all_messages.into_iter().flatten().collect()); @@ -322,22 +323,18 @@ fn test_cases_for_duplicate_msgs() -> ( .flatten() .collect::>(); - let mut test_cases = vec![]; - - // one duplicate - test_cases.push(duplicate_msgs(all_messages.clone(), 1)); - - // multiple duplicates - test_cases.push(duplicate_msgs(all_messages.clone(), 10)); - - // all duplicates - test_cases.push( + let test_cases = vec![ + // one duplicate + duplicate_msgs(all_messages.clone(), 1), + // multiple duplicates + duplicate_msgs(all_messages.clone(), 10), + // all duplicates all_messages .clone() .into_iter() .chain(all_messages.clone()) .collect::>(), - ); + ]; (test_cases, handler) } @@ -356,9 +353,7 @@ fn generate_msgs(namespace: impl Debug, count: i32) -> Vec { .map(|i| Message { cc_id: CrossChainId { chain: "mock-chain".parse().unwrap(), - id: format!("{:?}{}{}", namespace, ID_SEPARATOR, i) - .parse() - .unwrap(), + id: format!("{:?}{}", namespace, i).parse().unwrap(), }, destination_address: "idc".parse().unwrap(), destination_chain: "mock-chain-2".parse().unwrap(), @@ -368,6 +363,7 @@ fn generate_msgs(namespace: impl Debug, count: i32) -> Vec { .collect() } +#[allow(clippy::arithmetic_side_effects)] fn all_statuses() -> Vec { let statuses = vec![ VerificationStatus::None, @@ -379,21 +375,21 @@ fn all_statuses() -> Vec { ]; // we need to make sure that if the variants change, the tests cover all of them - let mut status_count = 0; + let mut status_count: usize = 0; for status in &statuses { match status { - VerificationStatus::None => status_count += 1, - VerificationStatus::NotFound => status_count += 1, - VerificationStatus::FailedToVerify => status_count += 1, - VerificationStatus::InProgress => status_count += 1, - VerificationStatus::SucceededOnChain => status_count += 1, - VerificationStatus::FailedOnChain => status_count += 1, + VerificationStatus::None + | VerificationStatus::NotFound + | VerificationStatus::FailedToVerify + | VerificationStatus::InProgress + | VerificationStatus::SucceededOnChain + | VerificationStatus::FailedOnChain => status_count += 1, }; } assert_eq!(statuses.len(), status_count); - return statuses; + statuses } fn map_status_by_msg_id( @@ -426,8 +422,8 @@ fn update_query_handler( ) { let handler = move |msg: &WasmQuery| match msg { WasmQuery::Smart { msg, .. } => { - let result = handler(from_binary(msg).expect("should not fail to deserialize")) - .map(|response| to_binary(&response).expect("should not fail to serialize")); + let result = handler(from_json(msg).expect("should not fail to deserialize")) + .map(|response| to_json_binary(&response).expect("should not fail to serialize")); QuerierResult::Ok(ContractResult::from(result)) } diff --git a/contracts/gateway/tests/test_route_incoming.json b/contracts/gateway/tests/test_route_incoming.json index aaf37ec48..77e8f7e1b 100644 --- a/contracts/gateway/tests/test_route_incoming.json +++ b/contracts/gateway/tests/test_route_incoming.json @@ -13,7 +13,7 @@ "wasm": { "execute": { "contract_addr": "router", - "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOjAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn1dfQ==", + "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifV19", "funds": [] } } @@ -29,7 +29,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:0" + "value": "SucceededOnChain0" }, { "key": "source_chain", @@ -65,7 +65,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:0" + "value": "FailedOnChain0" }, { "key": "source_chain", @@ -101,7 +101,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:0" + "value": "NotFound0" }, { "key": "source_chain", @@ -137,7 +137,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:0" + "value": "FailedToVerify0" }, { "key": "source_chain", @@ -173,7 +173,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:0" + "value": "InProgress0" }, { "key": "source_chain", @@ -209,7 +209,7 @@ "attributes": [ { "key": "id", - "value": "None:0" + "value": "None0" }, { "key": "source_chain", @@ -244,7 +244,7 @@ "wasm": { "execute": { "contract_addr": "router", - "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOjAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW46MSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjoyIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOjMifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW46NCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjo1In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOjYifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW46NyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjo4In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOjkifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5In1dfQ==", + "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW4yIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW41In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW44In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19", "funds": [] } } @@ -260,7 +260,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:0" + "value": "SucceededOnChain0" }, { "key": "source_chain", @@ -289,7 +289,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:1" + "value": "SucceededOnChain1" }, { "key": "source_chain", @@ -318,7 +318,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:2" + "value": "SucceededOnChain2" }, { "key": "source_chain", @@ -347,7 +347,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:3" + "value": "SucceededOnChain3" }, { "key": "source_chain", @@ -376,7 +376,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:4" + "value": "SucceededOnChain4" }, { "key": "source_chain", @@ -405,7 +405,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:5" + "value": "SucceededOnChain5" }, { "key": "source_chain", @@ -434,7 +434,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:6" + "value": "SucceededOnChain6" }, { "key": "source_chain", @@ -463,7 +463,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:7" + "value": "SucceededOnChain7" }, { "key": "source_chain", @@ -492,7 +492,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:8" + "value": "SucceededOnChain8" }, { "key": "source_chain", @@ -521,7 +521,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:9" + "value": "SucceededOnChain9" }, { "key": "source_chain", @@ -557,7 +557,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:0" + "value": "FailedOnChain0" }, { "key": "source_chain", @@ -586,7 +586,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:1" + "value": "FailedOnChain1" }, { "key": "source_chain", @@ -615,7 +615,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:2" + "value": "FailedOnChain2" }, { "key": "source_chain", @@ -644,7 +644,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:3" + "value": "FailedOnChain3" }, { "key": "source_chain", @@ -673,7 +673,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:4" + "value": "FailedOnChain4" }, { "key": "source_chain", @@ -702,7 +702,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:5" + "value": "FailedOnChain5" }, { "key": "source_chain", @@ -731,7 +731,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:6" + "value": "FailedOnChain6" }, { "key": "source_chain", @@ -760,7 +760,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:7" + "value": "FailedOnChain7" }, { "key": "source_chain", @@ -789,7 +789,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:8" + "value": "FailedOnChain8" }, { "key": "source_chain", @@ -818,7 +818,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:9" + "value": "FailedOnChain9" }, { "key": "source_chain", @@ -854,7 +854,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:0" + "value": "NotFound0" }, { "key": "source_chain", @@ -883,7 +883,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:1" + "value": "NotFound1" }, { "key": "source_chain", @@ -912,7 +912,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:2" + "value": "NotFound2" }, { "key": "source_chain", @@ -941,7 +941,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:3" + "value": "NotFound3" }, { "key": "source_chain", @@ -970,7 +970,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:4" + "value": "NotFound4" }, { "key": "source_chain", @@ -999,7 +999,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:5" + "value": "NotFound5" }, { "key": "source_chain", @@ -1028,7 +1028,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:6" + "value": "NotFound6" }, { "key": "source_chain", @@ -1057,7 +1057,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:7" + "value": "NotFound7" }, { "key": "source_chain", @@ -1086,7 +1086,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:8" + "value": "NotFound8" }, { "key": "source_chain", @@ -1115,7 +1115,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:9" + "value": "NotFound9" }, { "key": "source_chain", @@ -1151,7 +1151,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:0" + "value": "FailedToVerify0" }, { "key": "source_chain", @@ -1180,7 +1180,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:1" + "value": "FailedToVerify1" }, { "key": "source_chain", @@ -1209,7 +1209,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:2" + "value": "FailedToVerify2" }, { "key": "source_chain", @@ -1238,7 +1238,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:3" + "value": "FailedToVerify3" }, { "key": "source_chain", @@ -1267,7 +1267,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:4" + "value": "FailedToVerify4" }, { "key": "source_chain", @@ -1296,7 +1296,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:5" + "value": "FailedToVerify5" }, { "key": "source_chain", @@ -1325,7 +1325,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:6" + "value": "FailedToVerify6" }, { "key": "source_chain", @@ -1354,7 +1354,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:7" + "value": "FailedToVerify7" }, { "key": "source_chain", @@ -1383,7 +1383,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:8" + "value": "FailedToVerify8" }, { "key": "source_chain", @@ -1412,7 +1412,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:9" + "value": "FailedToVerify9" }, { "key": "source_chain", @@ -1448,7 +1448,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:0" + "value": "InProgress0" }, { "key": "source_chain", @@ -1477,7 +1477,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:1" + "value": "InProgress1" }, { "key": "source_chain", @@ -1506,7 +1506,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:2" + "value": "InProgress2" }, { "key": "source_chain", @@ -1535,7 +1535,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:3" + "value": "InProgress3" }, { "key": "source_chain", @@ -1564,7 +1564,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:4" + "value": "InProgress4" }, { "key": "source_chain", @@ -1593,7 +1593,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:5" + "value": "InProgress5" }, { "key": "source_chain", @@ -1622,7 +1622,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:6" + "value": "InProgress6" }, { "key": "source_chain", @@ -1651,7 +1651,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:7" + "value": "InProgress7" }, { "key": "source_chain", @@ -1680,7 +1680,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:8" + "value": "InProgress8" }, { "key": "source_chain", @@ -1709,7 +1709,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:9" + "value": "InProgress9" }, { "key": "source_chain", @@ -1745,7 +1745,7 @@ "attributes": [ { "key": "id", - "value": "None:0" + "value": "None0" }, { "key": "source_chain", @@ -1774,7 +1774,7 @@ "attributes": [ { "key": "id", - "value": "None:1" + "value": "None1" }, { "key": "source_chain", @@ -1803,7 +1803,7 @@ "attributes": [ { "key": "id", - "value": "None:2" + "value": "None2" }, { "key": "source_chain", @@ -1832,7 +1832,7 @@ "attributes": [ { "key": "id", - "value": "None:3" + "value": "None3" }, { "key": "source_chain", @@ -1861,7 +1861,7 @@ "attributes": [ { "key": "id", - "value": "None:4" + "value": "None4" }, { "key": "source_chain", @@ -1890,7 +1890,7 @@ "attributes": [ { "key": "id", - "value": "None:5" + "value": "None5" }, { "key": "source_chain", @@ -1919,7 +1919,7 @@ "attributes": [ { "key": "id", - "value": "None:6" + "value": "None6" }, { "key": "source_chain", @@ -1948,7 +1948,7 @@ "attributes": [ { "key": "id", - "value": "None:7" + "value": "None7" }, { "key": "source_chain", @@ -1977,7 +1977,7 @@ "attributes": [ { "key": "id", - "value": "None:8" + "value": "None8" }, { "key": "source_chain", @@ -2006,7 +2006,7 @@ "attributes": [ { "key": "id", - "value": "None:9" + "value": "None9" }, { "key": "source_chain", @@ -2041,7 +2041,7 @@ "wasm": { "execute": { "contract_addr": "router", - "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOjAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW46MSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjoyIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOjMifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW46NCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjo1In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOjYifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW46NyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjo4In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOjkifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5In1dfQ==", + "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW4yIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW41In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25DaGFpbjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uQ2hhaW44In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPbkNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19", "funds": [] } } @@ -2057,7 +2057,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:0" + "value": "SucceededOnChain0" }, { "key": "source_chain", @@ -2086,7 +2086,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:1" + "value": "SucceededOnChain1" }, { "key": "source_chain", @@ -2115,7 +2115,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:2" + "value": "SucceededOnChain2" }, { "key": "source_chain", @@ -2144,7 +2144,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:3" + "value": "SucceededOnChain3" }, { "key": "source_chain", @@ -2173,7 +2173,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:4" + "value": "SucceededOnChain4" }, { "key": "source_chain", @@ -2202,7 +2202,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:5" + "value": "SucceededOnChain5" }, { "key": "source_chain", @@ -2231,7 +2231,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:6" + "value": "SucceededOnChain6" }, { "key": "source_chain", @@ -2260,7 +2260,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:7" + "value": "SucceededOnChain7" }, { "key": "source_chain", @@ -2289,7 +2289,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:8" + "value": "SucceededOnChain8" }, { "key": "source_chain", @@ -2318,7 +2318,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:9" + "value": "SucceededOnChain9" }, { "key": "source_chain", @@ -2347,7 +2347,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:0" + "value": "FailedOnChain0" }, { "key": "source_chain", @@ -2376,7 +2376,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:1" + "value": "FailedOnChain1" }, { "key": "source_chain", @@ -2405,7 +2405,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:2" + "value": "FailedOnChain2" }, { "key": "source_chain", @@ -2434,7 +2434,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:3" + "value": "FailedOnChain3" }, { "key": "source_chain", @@ -2463,7 +2463,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:4" + "value": "FailedOnChain4" }, { "key": "source_chain", @@ -2492,7 +2492,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:5" + "value": "FailedOnChain5" }, { "key": "source_chain", @@ -2521,7 +2521,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:6" + "value": "FailedOnChain6" }, { "key": "source_chain", @@ -2550,7 +2550,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:7" + "value": "FailedOnChain7" }, { "key": "source_chain", @@ -2579,7 +2579,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:8" + "value": "FailedOnChain8" }, { "key": "source_chain", @@ -2608,7 +2608,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:9" + "value": "FailedOnChain9" }, { "key": "source_chain", @@ -2637,7 +2637,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:0" + "value": "NotFound0" }, { "key": "source_chain", @@ -2666,7 +2666,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:1" + "value": "NotFound1" }, { "key": "source_chain", @@ -2695,7 +2695,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:2" + "value": "NotFound2" }, { "key": "source_chain", @@ -2724,7 +2724,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:3" + "value": "NotFound3" }, { "key": "source_chain", @@ -2753,7 +2753,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:4" + "value": "NotFound4" }, { "key": "source_chain", @@ -2782,7 +2782,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:5" + "value": "NotFound5" }, { "key": "source_chain", @@ -2811,7 +2811,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:6" + "value": "NotFound6" }, { "key": "source_chain", @@ -2840,7 +2840,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:7" + "value": "NotFound7" }, { "key": "source_chain", @@ -2869,7 +2869,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:8" + "value": "NotFound8" }, { "key": "source_chain", @@ -2898,7 +2898,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:9" + "value": "NotFound9" }, { "key": "source_chain", @@ -2927,7 +2927,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:0" + "value": "FailedToVerify0" }, { "key": "source_chain", @@ -2956,7 +2956,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:1" + "value": "FailedToVerify1" }, { "key": "source_chain", @@ -2985,7 +2985,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:2" + "value": "FailedToVerify2" }, { "key": "source_chain", @@ -3014,7 +3014,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:3" + "value": "FailedToVerify3" }, { "key": "source_chain", @@ -3043,7 +3043,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:4" + "value": "FailedToVerify4" }, { "key": "source_chain", @@ -3072,7 +3072,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:5" + "value": "FailedToVerify5" }, { "key": "source_chain", @@ -3101,7 +3101,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:6" + "value": "FailedToVerify6" }, { "key": "source_chain", @@ -3130,7 +3130,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:7" + "value": "FailedToVerify7" }, { "key": "source_chain", @@ -3159,7 +3159,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:8" + "value": "FailedToVerify8" }, { "key": "source_chain", @@ -3188,7 +3188,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:9" + "value": "FailedToVerify9" }, { "key": "source_chain", @@ -3217,7 +3217,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:0" + "value": "InProgress0" }, { "key": "source_chain", @@ -3246,7 +3246,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:1" + "value": "InProgress1" }, { "key": "source_chain", @@ -3275,7 +3275,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:2" + "value": "InProgress2" }, { "key": "source_chain", @@ -3304,7 +3304,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:3" + "value": "InProgress3" }, { "key": "source_chain", @@ -3333,7 +3333,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:4" + "value": "InProgress4" }, { "key": "source_chain", @@ -3362,7 +3362,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:5" + "value": "InProgress5" }, { "key": "source_chain", @@ -3391,7 +3391,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:6" + "value": "InProgress6" }, { "key": "source_chain", @@ -3420,7 +3420,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:7" + "value": "InProgress7" }, { "key": "source_chain", @@ -3449,7 +3449,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:8" + "value": "InProgress8" }, { "key": "source_chain", @@ -3478,7 +3478,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:9" + "value": "InProgress9" }, { "key": "source_chain", @@ -3507,7 +3507,7 @@ "attributes": [ { "key": "id", - "value": "None:0" + "value": "None0" }, { "key": "source_chain", @@ -3536,7 +3536,7 @@ "attributes": [ { "key": "id", - "value": "None:1" + "value": "None1" }, { "key": "source_chain", @@ -3565,7 +3565,7 @@ "attributes": [ { "key": "id", - "value": "None:2" + "value": "None2" }, { "key": "source_chain", @@ -3594,7 +3594,7 @@ "attributes": [ { "key": "id", - "value": "None:3" + "value": "None3" }, { "key": "source_chain", @@ -3623,7 +3623,7 @@ "attributes": [ { "key": "id", - "value": "None:4" + "value": "None4" }, { "key": "source_chain", @@ -3652,7 +3652,7 @@ "attributes": [ { "key": "id", - "value": "None:5" + "value": "None5" }, { "key": "source_chain", @@ -3681,7 +3681,7 @@ "attributes": [ { "key": "id", - "value": "None:6" + "value": "None6" }, { "key": "source_chain", @@ -3710,7 +3710,7 @@ "attributes": [ { "key": "id", - "value": "None:7" + "value": "None7" }, { "key": "source_chain", @@ -3739,7 +3739,7 @@ "attributes": [ { "key": "id", - "value": "None:8" + "value": "None8" }, { "key": "source_chain", @@ -3768,7 +3768,7 @@ "attributes": [ { "key": "id", - "value": "None:9" + "value": "None9" }, { "key": "source_chain", diff --git a/contracts/gateway/tests/test_route_outgoing.json b/contracts/gateway/tests/test_route_outgoing.json index f3ba61c28..3c18aeb84 100644 --- a/contracts/gateway/tests/test_route_outgoing.json +++ b/contracts/gateway/tests/test_route_outgoing.json @@ -14,7 +14,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:0" + "value": "SucceededOnChain0" }, { "key": "source_chain", @@ -50,7 +50,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:0" + "value": "FailedOnChain0" }, { "key": "source_chain", @@ -86,7 +86,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:0" + "value": "NotFound0" }, { "key": "source_chain", @@ -122,7 +122,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:0" + "value": "FailedToVerify0" }, { "key": "source_chain", @@ -158,7 +158,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:0" + "value": "InProgress0" }, { "key": "source_chain", @@ -194,7 +194,7 @@ "attributes": [ { "key": "id", - "value": "None:0" + "value": "None0" }, { "key": "source_chain", @@ -230,7 +230,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:0" + "value": "SucceededOnChain0" }, { "key": "source_chain", @@ -259,7 +259,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:1" + "value": "SucceededOnChain1" }, { "key": "source_chain", @@ -288,7 +288,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:2" + "value": "SucceededOnChain2" }, { "key": "source_chain", @@ -317,7 +317,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:3" + "value": "SucceededOnChain3" }, { "key": "source_chain", @@ -346,7 +346,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:4" + "value": "SucceededOnChain4" }, { "key": "source_chain", @@ -375,7 +375,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:5" + "value": "SucceededOnChain5" }, { "key": "source_chain", @@ -404,7 +404,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:6" + "value": "SucceededOnChain6" }, { "key": "source_chain", @@ -433,7 +433,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:7" + "value": "SucceededOnChain7" }, { "key": "source_chain", @@ -462,7 +462,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:8" + "value": "SucceededOnChain8" }, { "key": "source_chain", @@ -491,7 +491,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:9" + "value": "SucceededOnChain9" }, { "key": "source_chain", @@ -527,7 +527,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:0" + "value": "FailedOnChain0" }, { "key": "source_chain", @@ -556,7 +556,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:1" + "value": "FailedOnChain1" }, { "key": "source_chain", @@ -585,7 +585,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:2" + "value": "FailedOnChain2" }, { "key": "source_chain", @@ -614,7 +614,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:3" + "value": "FailedOnChain3" }, { "key": "source_chain", @@ -643,7 +643,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:4" + "value": "FailedOnChain4" }, { "key": "source_chain", @@ -672,7 +672,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:5" + "value": "FailedOnChain5" }, { "key": "source_chain", @@ -701,7 +701,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:6" + "value": "FailedOnChain6" }, { "key": "source_chain", @@ -730,7 +730,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:7" + "value": "FailedOnChain7" }, { "key": "source_chain", @@ -759,7 +759,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:8" + "value": "FailedOnChain8" }, { "key": "source_chain", @@ -788,7 +788,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:9" + "value": "FailedOnChain9" }, { "key": "source_chain", @@ -824,7 +824,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:0" + "value": "NotFound0" }, { "key": "source_chain", @@ -853,7 +853,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:1" + "value": "NotFound1" }, { "key": "source_chain", @@ -882,7 +882,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:2" + "value": "NotFound2" }, { "key": "source_chain", @@ -911,7 +911,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:3" + "value": "NotFound3" }, { "key": "source_chain", @@ -940,7 +940,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:4" + "value": "NotFound4" }, { "key": "source_chain", @@ -969,7 +969,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:5" + "value": "NotFound5" }, { "key": "source_chain", @@ -998,7 +998,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:6" + "value": "NotFound6" }, { "key": "source_chain", @@ -1027,7 +1027,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:7" + "value": "NotFound7" }, { "key": "source_chain", @@ -1056,7 +1056,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:8" + "value": "NotFound8" }, { "key": "source_chain", @@ -1085,7 +1085,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:9" + "value": "NotFound9" }, { "key": "source_chain", @@ -1121,7 +1121,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:0" + "value": "FailedToVerify0" }, { "key": "source_chain", @@ -1150,7 +1150,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:1" + "value": "FailedToVerify1" }, { "key": "source_chain", @@ -1179,7 +1179,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:2" + "value": "FailedToVerify2" }, { "key": "source_chain", @@ -1208,7 +1208,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:3" + "value": "FailedToVerify3" }, { "key": "source_chain", @@ -1237,7 +1237,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:4" + "value": "FailedToVerify4" }, { "key": "source_chain", @@ -1266,7 +1266,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:5" + "value": "FailedToVerify5" }, { "key": "source_chain", @@ -1295,7 +1295,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:6" + "value": "FailedToVerify6" }, { "key": "source_chain", @@ -1324,7 +1324,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:7" + "value": "FailedToVerify7" }, { "key": "source_chain", @@ -1353,7 +1353,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:8" + "value": "FailedToVerify8" }, { "key": "source_chain", @@ -1382,7 +1382,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:9" + "value": "FailedToVerify9" }, { "key": "source_chain", @@ -1418,7 +1418,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:0" + "value": "InProgress0" }, { "key": "source_chain", @@ -1447,7 +1447,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:1" + "value": "InProgress1" }, { "key": "source_chain", @@ -1476,7 +1476,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:2" + "value": "InProgress2" }, { "key": "source_chain", @@ -1505,7 +1505,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:3" + "value": "InProgress3" }, { "key": "source_chain", @@ -1534,7 +1534,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:4" + "value": "InProgress4" }, { "key": "source_chain", @@ -1563,7 +1563,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:5" + "value": "InProgress5" }, { "key": "source_chain", @@ -1592,7 +1592,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:6" + "value": "InProgress6" }, { "key": "source_chain", @@ -1621,7 +1621,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:7" + "value": "InProgress7" }, { "key": "source_chain", @@ -1650,7 +1650,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:8" + "value": "InProgress8" }, { "key": "source_chain", @@ -1679,7 +1679,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:9" + "value": "InProgress9" }, { "key": "source_chain", @@ -1715,7 +1715,7 @@ "attributes": [ { "key": "id", - "value": "None:0" + "value": "None0" }, { "key": "source_chain", @@ -1744,7 +1744,7 @@ "attributes": [ { "key": "id", - "value": "None:1" + "value": "None1" }, { "key": "source_chain", @@ -1773,7 +1773,7 @@ "attributes": [ { "key": "id", - "value": "None:2" + "value": "None2" }, { "key": "source_chain", @@ -1802,7 +1802,7 @@ "attributes": [ { "key": "id", - "value": "None:3" + "value": "None3" }, { "key": "source_chain", @@ -1831,7 +1831,7 @@ "attributes": [ { "key": "id", - "value": "None:4" + "value": "None4" }, { "key": "source_chain", @@ -1860,7 +1860,7 @@ "attributes": [ { "key": "id", - "value": "None:5" + "value": "None5" }, { "key": "source_chain", @@ -1889,7 +1889,7 @@ "attributes": [ { "key": "id", - "value": "None:6" + "value": "None6" }, { "key": "source_chain", @@ -1918,7 +1918,7 @@ "attributes": [ { "key": "id", - "value": "None:7" + "value": "None7" }, { "key": "source_chain", @@ -1947,7 +1947,7 @@ "attributes": [ { "key": "id", - "value": "None:8" + "value": "None8" }, { "key": "source_chain", @@ -1976,7 +1976,7 @@ "attributes": [ { "key": "id", - "value": "None:9" + "value": "None9" }, { "key": "source_chain", @@ -2012,7 +2012,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:0" + "value": "SucceededOnChain0" }, { "key": "source_chain", @@ -2041,7 +2041,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:1" + "value": "SucceededOnChain1" }, { "key": "source_chain", @@ -2070,7 +2070,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:2" + "value": "SucceededOnChain2" }, { "key": "source_chain", @@ -2099,7 +2099,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:3" + "value": "SucceededOnChain3" }, { "key": "source_chain", @@ -2128,7 +2128,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:4" + "value": "SucceededOnChain4" }, { "key": "source_chain", @@ -2157,7 +2157,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:5" + "value": "SucceededOnChain5" }, { "key": "source_chain", @@ -2186,7 +2186,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:6" + "value": "SucceededOnChain6" }, { "key": "source_chain", @@ -2215,7 +2215,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:7" + "value": "SucceededOnChain7" }, { "key": "source_chain", @@ -2244,7 +2244,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:8" + "value": "SucceededOnChain8" }, { "key": "source_chain", @@ -2273,7 +2273,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:9" + "value": "SucceededOnChain9" }, { "key": "source_chain", @@ -2302,7 +2302,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:0" + "value": "FailedOnChain0" }, { "key": "source_chain", @@ -2331,7 +2331,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:1" + "value": "FailedOnChain1" }, { "key": "source_chain", @@ -2360,7 +2360,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:2" + "value": "FailedOnChain2" }, { "key": "source_chain", @@ -2389,7 +2389,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:3" + "value": "FailedOnChain3" }, { "key": "source_chain", @@ -2418,7 +2418,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:4" + "value": "FailedOnChain4" }, { "key": "source_chain", @@ -2447,7 +2447,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:5" + "value": "FailedOnChain5" }, { "key": "source_chain", @@ -2476,7 +2476,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:6" + "value": "FailedOnChain6" }, { "key": "source_chain", @@ -2505,7 +2505,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:7" + "value": "FailedOnChain7" }, { "key": "source_chain", @@ -2534,7 +2534,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:8" + "value": "FailedOnChain8" }, { "key": "source_chain", @@ -2563,7 +2563,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:9" + "value": "FailedOnChain9" }, { "key": "source_chain", @@ -2592,7 +2592,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:0" + "value": "NotFound0" }, { "key": "source_chain", @@ -2621,7 +2621,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:1" + "value": "NotFound1" }, { "key": "source_chain", @@ -2650,7 +2650,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:2" + "value": "NotFound2" }, { "key": "source_chain", @@ -2679,7 +2679,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:3" + "value": "NotFound3" }, { "key": "source_chain", @@ -2708,7 +2708,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:4" + "value": "NotFound4" }, { "key": "source_chain", @@ -2737,7 +2737,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:5" + "value": "NotFound5" }, { "key": "source_chain", @@ -2766,7 +2766,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:6" + "value": "NotFound6" }, { "key": "source_chain", @@ -2795,7 +2795,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:7" + "value": "NotFound7" }, { "key": "source_chain", @@ -2824,7 +2824,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:8" + "value": "NotFound8" }, { "key": "source_chain", @@ -2853,7 +2853,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:9" + "value": "NotFound9" }, { "key": "source_chain", @@ -2882,7 +2882,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:0" + "value": "FailedToVerify0" }, { "key": "source_chain", @@ -2911,7 +2911,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:1" + "value": "FailedToVerify1" }, { "key": "source_chain", @@ -2940,7 +2940,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:2" + "value": "FailedToVerify2" }, { "key": "source_chain", @@ -2969,7 +2969,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:3" + "value": "FailedToVerify3" }, { "key": "source_chain", @@ -2998,7 +2998,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:4" + "value": "FailedToVerify4" }, { "key": "source_chain", @@ -3027,7 +3027,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:5" + "value": "FailedToVerify5" }, { "key": "source_chain", @@ -3056,7 +3056,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:6" + "value": "FailedToVerify6" }, { "key": "source_chain", @@ -3085,7 +3085,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:7" + "value": "FailedToVerify7" }, { "key": "source_chain", @@ -3114,7 +3114,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:8" + "value": "FailedToVerify8" }, { "key": "source_chain", @@ -3143,7 +3143,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:9" + "value": "FailedToVerify9" }, { "key": "source_chain", @@ -3172,7 +3172,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:0" + "value": "InProgress0" }, { "key": "source_chain", @@ -3201,7 +3201,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:1" + "value": "InProgress1" }, { "key": "source_chain", @@ -3230,7 +3230,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:2" + "value": "InProgress2" }, { "key": "source_chain", @@ -3259,7 +3259,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:3" + "value": "InProgress3" }, { "key": "source_chain", @@ -3288,7 +3288,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:4" + "value": "InProgress4" }, { "key": "source_chain", @@ -3317,7 +3317,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:5" + "value": "InProgress5" }, { "key": "source_chain", @@ -3346,7 +3346,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:6" + "value": "InProgress6" }, { "key": "source_chain", @@ -3375,7 +3375,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:7" + "value": "InProgress7" }, { "key": "source_chain", @@ -3404,7 +3404,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:8" + "value": "InProgress8" }, { "key": "source_chain", @@ -3433,7 +3433,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:9" + "value": "InProgress9" }, { "key": "source_chain", @@ -3462,7 +3462,7 @@ "attributes": [ { "key": "id", - "value": "None:0" + "value": "None0" }, { "key": "source_chain", @@ -3491,7 +3491,7 @@ "attributes": [ { "key": "id", - "value": "None:1" + "value": "None1" }, { "key": "source_chain", @@ -3520,7 +3520,7 @@ "attributes": [ { "key": "id", - "value": "None:2" + "value": "None2" }, { "key": "source_chain", @@ -3549,7 +3549,7 @@ "attributes": [ { "key": "id", - "value": "None:3" + "value": "None3" }, { "key": "source_chain", @@ -3578,7 +3578,7 @@ "attributes": [ { "key": "id", - "value": "None:4" + "value": "None4" }, { "key": "source_chain", @@ -3607,7 +3607,7 @@ "attributes": [ { "key": "id", - "value": "None:5" + "value": "None5" }, { "key": "source_chain", @@ -3636,7 +3636,7 @@ "attributes": [ { "key": "id", - "value": "None:6" + "value": "None6" }, { "key": "source_chain", @@ -3665,7 +3665,7 @@ "attributes": [ { "key": "id", - "value": "None:7" + "value": "None7" }, { "key": "source_chain", @@ -3694,7 +3694,7 @@ "attributes": [ { "key": "id", - "value": "None:8" + "value": "None8" }, { "key": "source_chain", @@ -3723,7 +3723,7 @@ "attributes": [ { "key": "id", - "value": "None:9" + "value": "None9" }, { "key": "source_chain", diff --git a/contracts/gateway/tests/test_verify.json b/contracts/gateway/tests/test_verify.json index 62cb44dab..93fc403aa 100644 --- a/contracts/gateway/tests/test_verify.json +++ b/contracts/gateway/tests/test_verify.json @@ -14,7 +14,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:0" + "value": "SucceededOnChain0" }, { "key": "source_chain", @@ -50,7 +50,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:0" + "value": "FailedOnChain0" }, { "key": "source_chain", @@ -85,7 +85,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6MCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifV19fQ==", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9XX19", "funds": [] } } @@ -101,7 +101,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:0" + "value": "NotFound0" }, { "key": "source_chain", @@ -136,7 +136,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6MCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifV19fQ==", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnkwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9XX19", "funds": [] } } @@ -152,7 +152,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:0" + "value": "FailedToVerify0" }, { "key": "source_chain", @@ -188,7 +188,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:0" + "value": "InProgress0" }, { "key": "source_chain", @@ -223,7 +223,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTowIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9XX19", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn1dfX0=", "funds": [] } } @@ -239,7 +239,7 @@ "attributes": [ { "key": "id", - "value": "None:0" + "value": "None0" }, { "key": "source_chain", @@ -275,7 +275,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:0" + "value": "SucceededOnChain0" }, { "key": "source_chain", @@ -304,7 +304,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:1" + "value": "SucceededOnChain1" }, { "key": "source_chain", @@ -333,7 +333,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:2" + "value": "SucceededOnChain2" }, { "key": "source_chain", @@ -362,7 +362,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:3" + "value": "SucceededOnChain3" }, { "key": "source_chain", @@ -391,7 +391,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:4" + "value": "SucceededOnChain4" }, { "key": "source_chain", @@ -420,7 +420,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:5" + "value": "SucceededOnChain5" }, { "key": "source_chain", @@ -449,7 +449,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:6" + "value": "SucceededOnChain6" }, { "key": "source_chain", @@ -478,7 +478,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:7" + "value": "SucceededOnChain7" }, { "key": "source_chain", @@ -507,7 +507,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:8" + "value": "SucceededOnChain8" }, { "key": "source_chain", @@ -536,7 +536,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:9" + "value": "SucceededOnChain9" }, { "key": "source_chain", @@ -572,7 +572,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:0" + "value": "FailedOnChain0" }, { "key": "source_chain", @@ -601,7 +601,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:1" + "value": "FailedOnChain1" }, { "key": "source_chain", @@ -630,7 +630,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:2" + "value": "FailedOnChain2" }, { "key": "source_chain", @@ -659,7 +659,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:3" + "value": "FailedOnChain3" }, { "key": "source_chain", @@ -688,7 +688,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:4" + "value": "FailedOnChain4" }, { "key": "source_chain", @@ -717,7 +717,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:5" + "value": "FailedOnChain5" }, { "key": "source_chain", @@ -746,7 +746,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:6" + "value": "FailedOnChain6" }, { "key": "source_chain", @@ -775,7 +775,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:7" + "value": "FailedOnChain7" }, { "key": "source_chain", @@ -804,7 +804,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:8" + "value": "FailedOnChain8" }, { "key": "source_chain", @@ -833,7 +833,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:9" + "value": "FailedOnChain9" }, { "key": "source_chain", @@ -868,7 +868,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6MCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6MSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6MyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6NCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6NiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6NyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6OSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19fQ==", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb3RGb3VuZDEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vdEZvdW5kMiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQzIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb3RGb3VuZDQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vdEZvdW5kNSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb3RGb3VuZDcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vdEZvdW5kOCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9XX19", "funds": [] } } @@ -884,7 +884,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:0" + "value": "NotFound0" }, { "key": "source_chain", @@ -913,7 +913,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:1" + "value": "NotFound1" }, { "key": "source_chain", @@ -942,7 +942,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:2" + "value": "NotFound2" }, { "key": "source_chain", @@ -971,7 +971,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:3" + "value": "NotFound3" }, { "key": "source_chain", @@ -1000,7 +1000,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:4" + "value": "NotFound4" }, { "key": "source_chain", @@ -1029,7 +1029,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:5" + "value": "NotFound5" }, { "key": "source_chain", @@ -1058,7 +1058,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:6" + "value": "NotFound6" }, { "key": "source_chain", @@ -1087,7 +1087,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:7" + "value": "NotFound7" }, { "key": "source_chain", @@ -1116,7 +1116,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:8" + "value": "NotFound8" }, { "key": "source_chain", @@ -1145,7 +1145,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:9" + "value": "NotFound9" }, { "key": "source_chain", @@ -1180,7 +1180,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6MCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6MSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6MyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6NCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6NiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6NyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6OSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19fQ==", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnkwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnkzIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9XX19", "funds": [] } } @@ -1196,7 +1196,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:0" + "value": "FailedToVerify0" }, { "key": "source_chain", @@ -1225,7 +1225,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:1" + "value": "FailedToVerify1" }, { "key": "source_chain", @@ -1254,7 +1254,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:2" + "value": "FailedToVerify2" }, { "key": "source_chain", @@ -1283,7 +1283,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:3" + "value": "FailedToVerify3" }, { "key": "source_chain", @@ -1312,7 +1312,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:4" + "value": "FailedToVerify4" }, { "key": "source_chain", @@ -1341,7 +1341,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:5" + "value": "FailedToVerify5" }, { "key": "source_chain", @@ -1370,7 +1370,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:6" + "value": "FailedToVerify6" }, { "key": "source_chain", @@ -1399,7 +1399,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:7" + "value": "FailedToVerify7" }, { "key": "source_chain", @@ -1428,7 +1428,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:8" + "value": "FailedToVerify8" }, { "key": "source_chain", @@ -1457,7 +1457,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:9" + "value": "FailedToVerify9" }, { "key": "source_chain", @@ -1493,7 +1493,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:0" + "value": "InProgress0" }, { "key": "source_chain", @@ -1522,7 +1522,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:1" + "value": "InProgress1" }, { "key": "source_chain", @@ -1551,7 +1551,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:2" + "value": "InProgress2" }, { "key": "source_chain", @@ -1580,7 +1580,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:3" + "value": "InProgress3" }, { "key": "source_chain", @@ -1609,7 +1609,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:4" + "value": "InProgress4" }, { "key": "source_chain", @@ -1638,7 +1638,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:5" + "value": "InProgress5" }, { "key": "source_chain", @@ -1667,7 +1667,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:6" + "value": "InProgress6" }, { "key": "source_chain", @@ -1696,7 +1696,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:7" + "value": "InProgress7" }, { "key": "source_chain", @@ -1725,7 +1725,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:8" + "value": "InProgress8" }, { "key": "source_chain", @@ -1754,7 +1754,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:9" + "value": "InProgress9" }, { "key": "source_chain", @@ -1789,7 +1789,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTowIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lOjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU6MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTozIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lOjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU6NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTo2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lOjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU6OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTo5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9XX19", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmUxIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lMiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTMifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU0In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lNSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTYifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU3In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lOCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTkifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5In1dfX0=", "funds": [] } } @@ -1805,7 +1805,7 @@ "attributes": [ { "key": "id", - "value": "None:0" + "value": "None0" }, { "key": "source_chain", @@ -1834,7 +1834,7 @@ "attributes": [ { "key": "id", - "value": "None:1" + "value": "None1" }, { "key": "source_chain", @@ -1863,7 +1863,7 @@ "attributes": [ { "key": "id", - "value": "None:2" + "value": "None2" }, { "key": "source_chain", @@ -1892,7 +1892,7 @@ "attributes": [ { "key": "id", - "value": "None:3" + "value": "None3" }, { "key": "source_chain", @@ -1921,7 +1921,7 @@ "attributes": [ { "key": "id", - "value": "None:4" + "value": "None4" }, { "key": "source_chain", @@ -1950,7 +1950,7 @@ "attributes": [ { "key": "id", - "value": "None:5" + "value": "None5" }, { "key": "source_chain", @@ -1979,7 +1979,7 @@ "attributes": [ { "key": "id", - "value": "None:6" + "value": "None6" }, { "key": "source_chain", @@ -2008,7 +2008,7 @@ "attributes": [ { "key": "id", - "value": "None:7" + "value": "None7" }, { "key": "source_chain", @@ -2037,7 +2037,7 @@ "attributes": [ { "key": "id", - "value": "None:8" + "value": "None8" }, { "key": "source_chain", @@ -2066,7 +2066,7 @@ "attributes": [ { "key": "id", - "value": "None:9" + "value": "None9" }, { "key": "source_chain", @@ -2101,7 +2101,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6MCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6MSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6MyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6NCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6NiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6NyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ6OSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6MCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6MSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6MyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6NCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6NiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6NyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk6OSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTowIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lOjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU6MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTozIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lOjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU6NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTo2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lOjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU6OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTo5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9XX19", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb3RGb3VuZDEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vdEZvdW5kMiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQzIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb3RGb3VuZDQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vdEZvdW5kNSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb3RGb3VuZDcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vdEZvdW5kOCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmQ5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5MSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnkyIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTMifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5NCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk1In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTYifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5NyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk4In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTkifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmUwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lMSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTIifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmUzIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lNCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTUifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJOb25lNyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm9uZTgifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6Ik5vbmU5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9XX19", "funds": [] } } @@ -2117,7 +2117,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:0" + "value": "SucceededOnChain0" }, { "key": "source_chain", @@ -2146,7 +2146,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:1" + "value": "SucceededOnChain1" }, { "key": "source_chain", @@ -2175,7 +2175,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:2" + "value": "SucceededOnChain2" }, { "key": "source_chain", @@ -2204,7 +2204,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:3" + "value": "SucceededOnChain3" }, { "key": "source_chain", @@ -2233,7 +2233,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:4" + "value": "SucceededOnChain4" }, { "key": "source_chain", @@ -2262,7 +2262,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:5" + "value": "SucceededOnChain5" }, { "key": "source_chain", @@ -2291,7 +2291,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:6" + "value": "SucceededOnChain6" }, { "key": "source_chain", @@ -2320,7 +2320,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:7" + "value": "SucceededOnChain7" }, { "key": "source_chain", @@ -2349,7 +2349,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:8" + "value": "SucceededOnChain8" }, { "key": "source_chain", @@ -2378,7 +2378,7 @@ "attributes": [ { "key": "id", - "value": "SucceededOnChain:9" + "value": "SucceededOnChain9" }, { "key": "source_chain", @@ -2407,7 +2407,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:0" + "value": "FailedOnChain0" }, { "key": "source_chain", @@ -2436,7 +2436,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:1" + "value": "FailedOnChain1" }, { "key": "source_chain", @@ -2465,7 +2465,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:2" + "value": "FailedOnChain2" }, { "key": "source_chain", @@ -2494,7 +2494,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:3" + "value": "FailedOnChain3" }, { "key": "source_chain", @@ -2523,7 +2523,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:4" + "value": "FailedOnChain4" }, { "key": "source_chain", @@ -2552,7 +2552,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:5" + "value": "FailedOnChain5" }, { "key": "source_chain", @@ -2581,7 +2581,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:6" + "value": "FailedOnChain6" }, { "key": "source_chain", @@ -2610,7 +2610,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:7" + "value": "FailedOnChain7" }, { "key": "source_chain", @@ -2639,7 +2639,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:8" + "value": "FailedOnChain8" }, { "key": "source_chain", @@ -2668,7 +2668,7 @@ "attributes": [ { "key": "id", - "value": "FailedOnChain:9" + "value": "FailedOnChain9" }, { "key": "source_chain", @@ -2697,7 +2697,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:0" + "value": "NotFound0" }, { "key": "source_chain", @@ -2726,7 +2726,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:1" + "value": "NotFound1" }, { "key": "source_chain", @@ -2755,7 +2755,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:2" + "value": "NotFound2" }, { "key": "source_chain", @@ -2784,7 +2784,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:3" + "value": "NotFound3" }, { "key": "source_chain", @@ -2813,7 +2813,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:4" + "value": "NotFound4" }, { "key": "source_chain", @@ -2842,7 +2842,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:5" + "value": "NotFound5" }, { "key": "source_chain", @@ -2871,7 +2871,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:6" + "value": "NotFound6" }, { "key": "source_chain", @@ -2900,7 +2900,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:7" + "value": "NotFound7" }, { "key": "source_chain", @@ -2929,7 +2929,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:8" + "value": "NotFound8" }, { "key": "source_chain", @@ -2958,7 +2958,7 @@ "attributes": [ { "key": "id", - "value": "NotFound:9" + "value": "NotFound9" }, { "key": "source_chain", @@ -2987,7 +2987,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:0" + "value": "FailedToVerify0" }, { "key": "source_chain", @@ -3016,7 +3016,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:1" + "value": "FailedToVerify1" }, { "key": "source_chain", @@ -3045,7 +3045,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:2" + "value": "FailedToVerify2" }, { "key": "source_chain", @@ -3074,7 +3074,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:3" + "value": "FailedToVerify3" }, { "key": "source_chain", @@ -3103,7 +3103,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:4" + "value": "FailedToVerify4" }, { "key": "source_chain", @@ -3132,7 +3132,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:5" + "value": "FailedToVerify5" }, { "key": "source_chain", @@ -3161,7 +3161,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:6" + "value": "FailedToVerify6" }, { "key": "source_chain", @@ -3190,7 +3190,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:7" + "value": "FailedToVerify7" }, { "key": "source_chain", @@ -3219,7 +3219,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:8" + "value": "FailedToVerify8" }, { "key": "source_chain", @@ -3248,7 +3248,7 @@ "attributes": [ { "key": "id", - "value": "FailedToVerify:9" + "value": "FailedToVerify9" }, { "key": "source_chain", @@ -3277,7 +3277,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:0" + "value": "InProgress0" }, { "key": "source_chain", @@ -3306,7 +3306,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:1" + "value": "InProgress1" }, { "key": "source_chain", @@ -3335,7 +3335,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:2" + "value": "InProgress2" }, { "key": "source_chain", @@ -3364,7 +3364,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:3" + "value": "InProgress3" }, { "key": "source_chain", @@ -3393,7 +3393,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:4" + "value": "InProgress4" }, { "key": "source_chain", @@ -3422,7 +3422,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:5" + "value": "InProgress5" }, { "key": "source_chain", @@ -3451,7 +3451,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:6" + "value": "InProgress6" }, { "key": "source_chain", @@ -3480,7 +3480,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:7" + "value": "InProgress7" }, { "key": "source_chain", @@ -3509,7 +3509,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:8" + "value": "InProgress8" }, { "key": "source_chain", @@ -3538,7 +3538,7 @@ "attributes": [ { "key": "id", - "value": "InProgress:9" + "value": "InProgress9" }, { "key": "source_chain", @@ -3567,7 +3567,7 @@ "attributes": [ { "key": "id", - "value": "None:0" + "value": "None0" }, { "key": "source_chain", @@ -3596,7 +3596,7 @@ "attributes": [ { "key": "id", - "value": "None:1" + "value": "None1" }, { "key": "source_chain", @@ -3625,7 +3625,7 @@ "attributes": [ { "key": "id", - "value": "None:2" + "value": "None2" }, { "key": "source_chain", @@ -3654,7 +3654,7 @@ "attributes": [ { "key": "id", - "value": "None:3" + "value": "None3" }, { "key": "source_chain", @@ -3683,7 +3683,7 @@ "attributes": [ { "key": "id", - "value": "None:4" + "value": "None4" }, { "key": "source_chain", @@ -3712,7 +3712,7 @@ "attributes": [ { "key": "id", - "value": "None:5" + "value": "None5" }, { "key": "source_chain", @@ -3741,7 +3741,7 @@ "attributes": [ { "key": "id", - "value": "None:6" + "value": "None6" }, { "key": "source_chain", @@ -3770,7 +3770,7 @@ "attributes": [ { "key": "id", - "value": "None:7" + "value": "None7" }, { "key": "source_chain", @@ -3799,7 +3799,7 @@ "attributes": [ { "key": "id", - "value": "None:8" + "value": "None8" }, { "key": "source_chain", @@ -3828,7 +3828,7 @@ "attributes": [ { "key": "id", - "value": "None:9" + "value": "None9" }, { "key": "source_chain", diff --git a/contracts/monitoring/Cargo.toml b/contracts/monitoring/Cargo.toml new file mode 100644 index 000000000..e27833fd7 --- /dev/null +++ b/contracts/monitoring/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "monitoring" +version = "0.1.0" +rust-version = { workspace = true } +edition = "2021" +description = "Amplifier info aggregation for external use, alongside contract management, instantiation and migration" + +exclude = [ + # Those files are rust-optimizer artifacts. You might want to commit them for convenience, but they should not be part of the source code publication. + "contract.wasm", + "hash.txt", +] +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib", "rlib"] + +[[bin]] +name = "monitoring-schema" +path = "src/bin/schema.rs" + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[package.metadata.scripts] +optimize = """docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.12.6 +""" + +[dependencies] +axelar-wasm-std = { workspace = true } +axelar-wasm-std-derive = { workspace = true } +connection-router-api = { workspace = true } +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +cosmwasm-storage = { workspace = true } +cw-storage-plus = { workspace = true } +error-stack = { workspace = true } +multisig = { workspace = true, features = ["library"] } +report = { workspace = true } + +[dev-dependencies] +cw-multi-test = "0.15.1" +integration-tests = { workspace = true } + +[lints] +workspace = true diff --git a/contracts/monitoring/src/bin/schema.rs b/contracts/monitoring/src/bin/schema.rs new file mode 100644 index 000000000..5b23d2f31 --- /dev/null +++ b/contracts/monitoring/src/bin/schema.rs @@ -0,0 +1,11 @@ +use cosmwasm_schema::write_api; + +use monitoring::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg, + } +} diff --git a/contracts/monitoring/src/contract.rs b/contracts/monitoring/src/contract.rs new file mode 100644 index 000000000..fce2ae13f --- /dev/null +++ b/contracts/monitoring/src/contract.rs @@ -0,0 +1,42 @@ +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::{Config, CONFIG}; +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + CONFIG.save( + deps.storage, + &Config { + governance: deps.api.addr_validate(&msg.governance_address)?, + }, + )?; + Ok(Response::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +#[allow(dead_code)] +pub fn execute( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: ExecuteMsg, +) -> Result { + Ok(Response::new()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +#[allow(dead_code)] +pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::GetActiveVerifiersForChain { chain: _ } => { + todo!() + } + } +} diff --git a/contracts/monitoring/src/lib.rs b/contracts/monitoring/src/lib.rs new file mode 100644 index 000000000..03a7fc2be --- /dev/null +++ b/contracts/monitoring/src/lib.rs @@ -0,0 +1,4 @@ +pub mod contract; +pub mod msg; +pub mod query; +pub mod state; diff --git a/contracts/monitoring/src/msg.rs b/contracts/monitoring/src/msg.rs new file mode 100644 index 000000000..34fbce28b --- /dev/null +++ b/contracts/monitoring/src/msg.rs @@ -0,0 +1,18 @@ +use connection_router_api::ChainName; +use cosmwasm_schema::{cw_serde, QueryResponses}; +use multisig::worker_set::WorkerSet; + +#[cw_serde] +pub struct InstantiateMsg { + pub governance_address: String, +} + +#[cw_serde] +pub enum ExecuteMsg {} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + #[returns(WorkerSet)] + GetActiveVerifiersForChain { chain: ChainName }, +} diff --git a/contracts/monitoring/src/query.rs b/contracts/monitoring/src/query.rs new file mode 100644 index 000000000..0f712b602 --- /dev/null +++ b/contracts/monitoring/src/query.rs @@ -0,0 +1,10 @@ +use connection_router_api::ChainName; +use cosmwasm_std::Deps; +use multisig::worker_set::WorkerSet; + +pub fn chains_active_worker_sets( + _deps: Deps, + _chains: &[ChainName], +) -> Vec<(ChainName, WorkerSet)> { + todo!() +} diff --git a/contracts/monitoring/src/state.rs b/contracts/monitoring/src/state.rs new file mode 100644 index 000000000..38a06b742 --- /dev/null +++ b/contracts/monitoring/src/state.rs @@ -0,0 +1,9 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Addr; +use cw_storage_plus::Item; + +#[cw_serde] +pub struct Config { + pub governance: Addr, +} +pub const CONFIG: Item = Item::new("config"); diff --git a/contracts/multisig-prover/Cargo.toml b/contracts/multisig-prover/Cargo.toml index cd242ccb4..7646e6789 100644 --- a/contracts/multisig-prover/Cargo.toml +++ b/contracts/multisig-prover/Cargo.toml @@ -66,3 +66,6 @@ cw-multi-test = "0.15.1" elliptic-curve = "0.13.5" ethers = "2.0.8" generic-array = "0.14.7" + +[lints] +workspace = true diff --git a/contracts/multisig-prover/src/contract.rs b/contracts/multisig-prover/src/contract.rs index ead593a88..f142b37ab 100644 --- a/contracts/multisig-prover/src/contract.rs +++ b/contracts/multisig-prover/src/contract.rs @@ -1,14 +1,13 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, + to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, }; use crate::{ error::ContractError, execute, - msg::ExecuteMsg, - msg::{InstantiateMsg, QueryMsg}, + msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}, query, reply, state::{Config, CONFIG}, }; @@ -22,14 +21,26 @@ pub fn instantiate( _info: MessageInfo, msg: InstantiateMsg, ) -> Result { + let config = make_config(&deps, msg)?; + CONFIG.save(deps.storage, &config)?; + + Ok(Response::default()) +} + +fn make_config( + deps: &DepsMut, + msg: InstantiateMsg, +) -> Result { let admin = deps.api.addr_validate(&msg.admin_address)?; + let governance = deps.api.addr_validate(&msg.governance_address)?; let gateway = deps.api.addr_validate(&msg.gateway_address)?; let multisig = deps.api.addr_validate(&msg.multisig_address)?; let service_registry = deps.api.addr_validate(&msg.service_registry_address)?; let voting_verifier = deps.api.addr_validate(&msg.voting_verifier_address)?; - let config = Config { + Ok(Config { admin, + governance, gateway, multisig, service_registry, @@ -44,26 +55,23 @@ pub fn instantiate( worker_set_diff_threshold: msg.worker_set_diff_threshold, encoder: msg.encoder, key_type: msg.key_type, - }; - - CONFIG.save(deps.storage, &config)?; - - Ok(Response::default()) + }) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, env: Env, - _info: MessageInfo, + info: MessageInfo, msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::ConstructProof { message_ids } => { - execute::construct_proof(deps, env, message_ids) + ExecuteMsg::ConstructProof { message_ids } => execute::construct_proof(deps, message_ids), + ExecuteMsg::UpdateWorkerSet {} => { + execute::require_admin(&deps, info)?; + execute::update_worker_set(deps, env) } - ExecuteMsg::UpdateWorkerSet {} => execute::update_worker_set(deps, env), - ExecuteMsg::ConfirmWorkerSet {} => execute::confirm_worker_set(deps), + ExecuteMsg::ConfirmWorkerSet {} => execute::confirm_worker_set(deps, info.sender), } .map_err(axelar_wasm_std::ContractError::from) } @@ -86,24 +94,42 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::GetProof { multisig_session_id, - } => to_binary(&query::get_proof(deps, multisig_session_id)?), - QueryMsg::GetWorkerSet {} => to_binary(&query::get_worker_set(deps)?), + } => to_json_binary(&query::get_proof(deps, multisig_session_id)?), + QueryMsg::GetWorkerSet {} => to_json_binary(&query::get_worker_set(deps)?), } } +#[cfg_attr(not(feature = "libary"), entry_point)] +pub fn migrate( + deps: DepsMut, + _env: Env, + msg: MigrateMsg, +) -> Result { + let old_config = CONFIG.load(deps.storage)?; + let governance = deps.api.addr_validate(&msg.governance_address)?; + let new_config = Config { + governance, + ..old_config + }; + CONFIG.save(deps.storage, &new_config)?; + + Ok(Response::default()) +} + #[cfg(test)] mod tests { - use anyhow::Error; - use axelar_wasm_std::Threshold; - use connection_router_api::CrossChainId; use cosmwasm_std::{ testing::{mock_dependencies, mock_env, mock_info}, Addr, Fraction, Uint256, Uint64, }; use cw_multi_test::{AppResponse, Executor}; + + use axelar_wasm_std::Threshold; + use connection_router_api::CrossChainId; use multisig::{msg::Signer, worker_set::WorkerSet}; + use crate::contract::execute::should_update_worker_set; use crate::{ encoding::Encoder, msg::{GetProofResponse, ProofStatus}, @@ -114,8 +140,6 @@ mod tests { }, }; - use crate::contract::execute::should_update_worker_set; - use super::*; const RELAYER: &str = "relayer"; @@ -131,14 +155,14 @@ mod tests { ) } - fn confirm_worker_set(test_case: &mut TestCaseConfig) -> Result { + fn confirm_worker_set( + test_case: &mut TestCaseConfig, + sender: Addr, + ) -> Result { let msg = ExecuteMsg::ConfirmWorkerSet {}; - test_case.app.execute_contract( - test_case.admin.clone(), - test_case.prover_address.clone(), - &msg, - &[], - ) + test_case + .app + .execute_contract(sender, test_case.prover_address.clone(), &msg, &[]) } fn execute_construct_proof( @@ -187,9 +211,11 @@ mod tests { } #[test] + #[allow(clippy::arithmetic_side_effects)] fn test_instantiation() { let instantiator = "instantiator"; let admin = "admin"; + let governance = "governance"; let gateway_address = "gateway_address"; let multisig_address = "multisig_address"; let service_registry_address = "service_registry_address"; @@ -203,13 +229,14 @@ mod tests { .try_into() .unwrap(); let service_name = "service_name"; - for encoding in vec![Encoder::Abi, Encoder::Bcs] { + for encoding in [Encoder::Abi, Encoder::Bcs] { let mut deps = mock_dependencies(); - let info = mock_info(&instantiator, &[]); + let info = mock_info(instantiator, &[]); let env = mock_env(); let msg = InstantiateMsg { admin_address: admin.to_string(), + governance_address: governance.to_string(), gateway_address: gateway_address.to_string(), multisig_address: multisig_address.to_string(), voting_verifier_address: voting_verifier_address.to_string(), @@ -219,7 +246,7 @@ mod tests { service_name: service_name.to_string(), chain_name: "Ethereum".to_string(), worker_set_diff_threshold: 0, - encoder: encoding.clone(), + encoder: encoding, key_type: multisig::key::KeyType::Ecdsa, }; @@ -236,20 +263,18 @@ mod tests { assert_eq!(config.multisig, multisig_address); assert_eq!(config.service_registry, service_registry_address); assert_eq!(config.destination_chain_id, destination_chain_id); - assert_eq!( - config.signing_threshold, - signing_threshold.try_into().unwrap() - ); + assert_eq!(config.signing_threshold, signing_threshold); assert_eq!(config.service_name, service_name); assert_eq!(config.encoder, encoding) } } + #[allow(clippy::arithmetic_side_effects)] fn test_operators_to_worker_set(operators: Vec, nonce: u64) -> WorkerSet { let total_weight: Uint256 = operators .iter() .fold(Uint256::zero(), |acc, x| acc + x.weight); - let quorum = Uint256::try_from(total_weight.mul_ceil(test_data::threshold())).unwrap(); + let quorum = total_weight.mul_ceil(test_data::threshold()); WorkerSet { signers: operators .into_iter() @@ -289,6 +314,25 @@ mod tests { assert_eq!(worker_set, expected_worker_set); } + #[test] + fn test_update_worker_set_from_non_admin_should_fail() { + let mut test_case = setup_test_case(); + let res = test_case.app.execute_contract( + Addr::unchecked("some random address"), + test_case.prover_address.clone(), + &ExecuteMsg::UpdateWorkerSet {}, + &[], + ); + assert!(res.is_err()); + assert_eq!( + res.unwrap_err() + .downcast::() + .unwrap() + .to_string(), + axelar_wasm_std::ContractError::from(ContractError::Unauthorized).to_string() + ); + } + #[test] fn test_update_worker_set_remove_one() { let mut test_case = setup_test_case(); @@ -429,7 +473,7 @@ mod tests { let total_weight: Uint256 = new_worker_set .iter() .fold(Uint256::zero(), |acc, x| acc + x.weight); - let quorum = Uint256::try_from(total_weight.mul_ceil(test_data::threshold())).unwrap(); + let quorum = total_weight.mul_ceil(test_data::threshold()); mocks::voting_verifier::confirm_worker_set( &mut test_case.app, test_case.voting_verifier_address.clone(), @@ -437,7 +481,7 @@ mod tests { quorum, ); - let res = confirm_worker_set(&mut test_case); + let res = confirm_worker_set(&mut test_case, Addr::unchecked("relayer")); assert!(res.is_ok()); let worker_set = query_get_worker_set(&mut test_case); @@ -469,7 +513,7 @@ mod tests { assert!(res.is_ok()); - let res = confirm_worker_set(&mut test_case); + let res = confirm_worker_set(&mut test_case, Addr::unchecked("relayer")); assert!(res.is_err()); assert_eq!( res.unwrap_err() @@ -480,6 +524,39 @@ mod tests { ); } + #[test] + fn test_governance_should_confirm_worker_set_without_verification() { + let mut test_case = setup_test_case(); + let res = execute_update_worker_set(&mut test_case); + + assert!(res.is_ok()); + + let mut new_worker_set = test_data::operators(); + new_worker_set.pop(); + mocks::service_registry::set_active_workers( + &mut test_case.app, + test_case.service_registry_address.clone(), + new_worker_set.clone(), + ); + let res = execute_update_worker_set(&mut test_case); + + assert!(res.is_ok()); + + let governance = test_case.governance.clone(); + let res = confirm_worker_set(&mut test_case, governance); + assert!(res.is_ok()); + + let worker_set = query_get_worker_set(&mut test_case); + assert!(worker_set.is_ok()); + + let worker_set = worker_set.unwrap(); + + let expected_worker_set = + test_operators_to_worker_set(new_worker_set, test_case.app.block_info().height); + + assert_eq!(worker_set, expected_worker_set); + } + #[test] fn test_confirm_worker_set_wrong_set() { let mut test_case = setup_test_case(); @@ -500,7 +577,7 @@ mod tests { let total_weight: Uint256 = new_worker_set .iter() .fold(Uint256::zero(), |acc, x| acc + x.weight); - let quorum = Uint256::try_from(total_weight.mul_ceil(test_data::threshold())).unwrap(); + let quorum = total_weight.mul_ceil(test_data::threshold()); mocks::voting_verifier::confirm_worker_set( &mut test_case.app, test_case.voting_verifier_address.clone(), @@ -510,7 +587,7 @@ mod tests { assert!(res.is_ok()); - let res = confirm_worker_set(&mut test_case); + let res = confirm_worker_set(&mut test_case, Addr::unchecked("relayer")); assert!(res.is_err()); assert_eq!( res.unwrap_err() diff --git a/contracts/multisig-prover/src/encoding/abi.rs b/contracts/multisig-prover/src/encoding/abi.rs index 305bff6d8..9c7e88dfc 100644 --- a/contracts/multisig-prover/src/encoding/abi.rs +++ b/contracts/multisig-prover/src/encoding/abi.rs @@ -166,8 +166,11 @@ fn make_evm_operator(signer: Signer) -> Result { }) } -fn add27(recovery_byte: u8) -> u8 { - recovery_byte + 27 +fn add27(recovery_byte: k256::ecdsa::RecoveryId) -> u8 { + recovery_byte + .to_byte() + .checked_add(27) + .expect("overflow when adding 27 to recovery byte") } pub fn transfer_operatorship_params(worker_set: &WorkerSet) -> Result { @@ -237,13 +240,13 @@ pub fn command_params( #[cfg(test)] mod test { - use connection_router_api::CrossChainId; use elliptic_curve::consts::U32; use ethers::types::Signature as EthersSignature; use generic_array::GenericArray; use hex::FromHex; use k256::ecdsa::Signature as K256Signature; + use connection_router_api::CrossChainId; use multisig::key::KeyType; use crate::{ @@ -254,7 +257,7 @@ mod test { use super::*; - fn decode_command_params<'a>(encoded_params: impl Into>) -> Vec { + fn decode_command_params(encoded_params: impl Into>) -> Vec { ethabi::decode( &[ ParamType::String, @@ -267,9 +270,7 @@ mod test { .unwrap() } - fn decode_operator_transfer_command_params<'a>( - encoded_params: impl Into>, - ) -> Vec { + fn decode_operator_transfer_command_params(encoded_params: impl Into>) -> Vec { ethabi::decode( &[ ParamType::Array(Box::new(ParamType::Address)), @@ -316,7 +317,7 @@ mod test { .for_each(|((id, ty), params)| match (id, ty, params) { (Token::FixedBytes(id), Token::String(ty), Token::Bytes(params)) => { let command = Command { - id: id.to_owned().try_into().unwrap(), + id: id.to_owned().into(), ty: match ty.as_str() { "approveContractCall" => CommandType::ApproveContractCall, "transferOperatorship" => CommandType::TransferOperatorship, @@ -392,8 +393,7 @@ mod test { let tokens = decode_operator_transfer_command_params(res.unwrap()); let mut signers: Vec = new_worker_set.signers.into_values().collect(); signers.sort_by_key(|signer| evm_address(signer.pub_key.as_ref()).unwrap()); - let mut i = 0; - for signer in signers { + for (i, signer) in signers.into_iter().enumerate() { assert_eq!( tokens[0].clone().into_array().unwrap()[i], Token::Address(ethereum_types::Address::from_slice( @@ -409,7 +409,6 @@ mod test { &signer.weight.to_be_bytes() )) ); - i = i + 1; } assert_eq!( tokens[2], @@ -446,7 +445,7 @@ mod test { test_data .commands .into_iter() - .zip(res.data.commands.into_iter()) + .zip(res.data.commands) .for_each(|(expected_command, command)| { assert_eq!(command.id, expected_command.id); assert_eq!(command.ty, expected_command.ty); @@ -489,7 +488,7 @@ mod test { ( Signer { address: op.address, - weight: op.weight.into(), + weight: op.weight, pub_key: op.pub_key, }, op.signature, @@ -529,7 +528,7 @@ mod test { expected_data .commands .into_iter() - .zip(res.commands.into_iter()) + .zip(res.commands) .for_each(|(expected_command, command)| { assert_eq!(command.id, expected_command.id); assert_eq!(command.ty, expected_command.ty); @@ -568,7 +567,7 @@ mod test { ( Signer { address: op.address, - weight: op.weight.into(), + weight: op.weight, pub_key: op.pub_key, }, op.signature, diff --git a/contracts/multisig-prover/src/encoding/bcs.rs b/contracts/multisig-prover/src/encoding/bcs.rs index 3b17c8fa2..b345182d2 100644 --- a/contracts/multisig-prover/src/encoding/bcs.rs +++ b/contracts/multisig-prover/src/encoding/bcs.rs @@ -1,18 +1,15 @@ -use std::convert::identity; - -use axelar_wasm_std::operators::Operators; use bcs::to_bytes; use cosmwasm_std::{HexBinary, Uint256}; - -use crate::error::ContractError; - use itertools::Itertools; +use sha3::{Digest, Keccak256}; + +use axelar_wasm_std::operators::Operators; use multisig::{key::Signature, msg::Signer, worker_set::WorkerSet}; +use crate::error::ContractError; use crate::types::{CommandBatch, Operator}; use super::Data; -use sha3::{Digest, Keccak256}; // TODO: all of the public functions in this file should be moved to a trait, // that has an abi and bcs implementation (and possibly others) @@ -165,7 +162,7 @@ pub fn encode_execute_data( .to_recoverable( command_batch.msg_digest().as_slice(), &signer.pub_key, - identity, + |recovery_id| recovery_id.to_byte(), ) .map(Signature::EcdsaRecoverable) .ok(); @@ -194,12 +191,11 @@ fn u256_to_u64(chain_id: Uint256) -> u64 { #[cfg(test)] mod test { - use std::vec; use axelar_wasm_std::operators::Operators; use bcs::from_bytes; - use connection_router_api::{CrossChainId, Message}; + use connection_router_api::{CrossChainId, Message, CHAIN_NAME_DELIMITER}; use cosmwasm_std::{Addr, HexBinary, Uint256}; use multisig::{ @@ -220,6 +216,7 @@ mod test { }; use super::msg_digest; + #[test] fn test_transfer_operatorship_params() { let worker_set = test_data::new_worker_set(); @@ -318,15 +315,9 @@ mod test { assert!(proof.is_ok()); let proof = proof.unwrap(); - let decoded_proof: Result<(Vec>, Vec, u128, Vec>), _> = - from_bytes(&proof); + let decoded_proof: Result = from_bytes(&proof); assert!(decoded_proof.is_ok()); - let (operators, weights, quorum_decoded, signatures): ( - Vec>, - Vec, - u128, - Vec>, - ) = decoded_proof.unwrap(); + let (operators, weights, quorum_decoded, signatures): Proof = decoded_proof.unwrap(); assert_eq!(operators.len(), signers.len()); assert_eq!(weights.len(), signers.len()); @@ -377,16 +368,11 @@ mod test { #[test] fn test_command_params() { - let res = command_params( - "Ethereum".into(), - "00".into(), - "01".repeat(32).into(), - &[2; 32], - ); + let res = command_params("Ethereum".into(), "00".into(), "01".repeat(32), &[2; 32]); assert!(res.is_ok()); let res = res.unwrap(); - let params = from_bytes(&res.to_vec()); + let params = from_bytes(&res); assert!(params.is_ok()); let (source_chain, source_address, destination_address, payload_hash): ( String, @@ -409,7 +395,7 @@ mod test { #[test] fn test_invalid_destination_address() { let res = command_params("Ethereum".into(), "00".into(), "01".into(), &[2; 32]); - assert!(!res.is_ok()); + assert!(res.is_err()); } #[test] @@ -428,15 +414,14 @@ mod test { params: command_params( source_chain.into(), source_address.into(), - destination_address.clone().into(), + destination_address.clone(), &payload_hash, ) .unwrap(), }], }; let encoded = encode(&data); - let decoded: Result<(u64, Vec<[u8; 32]>, Vec, Vec>), _> = - from_bytes(&encoded.to_vec()); + let decoded: Result = from_bytes(&encoded); assert!(decoded.is_ok()); let (chain_id, command_ids, command_types, params) = decoded.unwrap(); @@ -476,9 +461,11 @@ mod test { #[test] fn test_msg_to_sign() { let mut builder = CommandBatchBuilder::new(1u128.into(), crate::encoding::Encoder::Bcs); - let _ = builder + builder .add_message(Message { - cc_id: "ethereum:foobar:1".parse().unwrap(), + cc_id: format!("ethereum{}id1", CHAIN_NAME_DELIMITER) + .parse() + .unwrap(), destination_address: "0F".repeat(32).parse().unwrap(), destination_chain: "sui".parse().unwrap(), source_address: "0x00".parse().unwrap(), @@ -490,9 +477,11 @@ mod test { assert_eq!(msg.len(), 32); let mut builder = CommandBatchBuilder::new(1u128.into(), crate::encoding::Encoder::Bcs); - let _ = builder + builder .add_message(Message { - cc_id: "ethereum:foobar:2".parse().unwrap(), + cc_id: format!("ethereum{}id2", CHAIN_NAME_DELIMITER) + .parse() + .unwrap(), destination_address: "0A".repeat(32).parse().unwrap(), destination_chain: "sui".parse().unwrap(), source_address: "0x00".parse().unwrap(), @@ -538,7 +527,7 @@ mod test { let command_batch = CommandBatch { message_ids: vec![], id: BatchId::new( - &vec![CrossChainId { + &[CrossChainId { chain: "AXELAR".to_string().try_into().unwrap(), id: "foobar".to_string().try_into().unwrap(), }], @@ -571,4 +560,7 @@ mod test { assert_eq!(encoded.len(), approval.to_vec().len()); assert_eq!(encoded.to_vec(), approval.to_vec()); } + + type Proof = (Vec>, Vec, u128, Vec>); + type DecodedData = (u64, Vec<[u8; 32]>, Vec, Vec>); } diff --git a/contracts/multisig-prover/src/encoding/mod.rs b/contracts/multisig-prover/src/encoding/mod.rs index ca042d0b1..341f881bb 100644 --- a/contracts/multisig-prover/src/encoding/mod.rs +++ b/contracts/multisig-prover/src/encoding/mod.rs @@ -186,7 +186,7 @@ mod test { assert_eq!( res.id, - HexBinary::from_hex("3ee2f8af2201994e3518c9ce6848774785c2eef3bdbf9f954899497616dd59af") + HexBinary::from_hex("01d9a96970a852fe24dad94e05c4c8c5eab33d027cb7d381c2745c390d75e998") .unwrap() ); assert_eq!(res.ty, CommandType::ApproveContractCall); @@ -200,7 +200,7 @@ mod test { assert_eq!( res.id, - HexBinary::from_hex("3ee2f8af2201994e3518c9ce6848774785c2eef3bdbf9f954899497616dd59af") + HexBinary::from_hex("01d9a96970a852fe24dad94e05c4c8c5eab33d027cb7d381c2745c390d75e998") .unwrap() ); assert_eq!(res.ty, CommandType::ApproveContractCall); diff --git a/contracts/multisig-prover/src/execute.rs b/contracts/multisig-prover/src/execute.rs index 2c748ed64..f017afb23 100644 --- a/contracts/multisig-prover/src/execute.rs +++ b/contracts/multisig-prover/src/execute.rs @@ -1,13 +1,16 @@ +use std::collections::BTreeMap; + use cosmwasm_std::{ - to_binary, wasm_execute, Addr, DepsMut, Env, QuerierWrapper, QueryRequest, Response, Storage, - SubMsg, WasmQuery, + to_json_binary, wasm_execute, Addr, DepsMut, Env, MessageInfo, QuerierWrapper, QueryRequest, + Response, Storage, SubMsg, WasmQuery, }; +use itertools::Itertools; use multisig::{key::PublicKey, msg::Signer, worker_set::WorkerSet}; use axelar_wasm_std::{snapshot, VerificationStatus}; use connection_router_api::{ChainName, CrossChainId, Message}; -use service_registry::state::Worker; +use service_registry::state::WeightedWorker; use crate::{ contract::START_MULTISIG_REPLY_ID, @@ -17,9 +20,15 @@ use crate::{ types::{BatchId, WorkersInfo}, }; +pub fn require_admin(deps: &DepsMut, info: MessageInfo) -> Result<(), ContractError> { + match CONFIG.load(deps.storage)?.admin { + admin if admin == info.sender => Ok(()), + _ => Err(ContractError::Unauthorized), + } +} + pub fn construct_proof( deps: DepsMut, - env: Env, message_ids: Vec, ) -> Result { let config = CONFIG.load(deps.storage)?; @@ -35,21 +44,8 @@ pub fn construct_proof( let command_batch = match COMMANDS_BATCH.may_load(deps.storage, &batch_id)? { Some(batch) => batch, None => { - let new_worker_set = get_next_worker_set(&deps, &env, &config)?; let mut builder = CommandBatchBuilder::new(config.destination_chain_id, config.encoder); - if let Some(new_worker_set) = new_worker_set { - match save_next_worker_set(deps.storage, &new_worker_set) { - Ok(()) => { - builder.add_new_worker_set(new_worker_set)?; - } - Err(ContractError::WorkerSetConfirmationInProgress) => {} - Err(other_error) => { - return Err(other_error); - } - } - } - for msg in messages { builder.add_message(msg)?; } @@ -64,7 +60,12 @@ pub fn construct_proof( // keep track of the batch id to use during submessage reply REPLY_BATCH.save(deps.storage, &command_batch.id)?; - let worker_set_id = CURRENT_WORKER_SET.load(deps.storage)?.id(); + let worker_set_id = match CURRENT_WORKER_SET.may_load(deps.storage)? { + Some(worker_set) => worker_set.id(), + None => { + return Err(ContractError::NoWorkerSet); + } + }; let start_sig_msg = multisig::msg::ExecuteMsg::StartSigningSession { worker_set_id, msg: command_batch.msg_digest(), @@ -88,7 +89,7 @@ fn get_messages( let query = gateway_api::msg::QueryMsg::GetOutgoingMessages { message_ids }; let messages: Vec = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: gateway.into(), - msg: to_binary(&query)?, + msg: to_json_binary(&query)?, }))?; assert!( @@ -112,29 +113,30 @@ fn get_workers_info(deps: &DepsMut, config: &Config) -> Result = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: config.service_registry.to_string(), - msg: to_binary(&active_workers_query)?, - }))?; + let workers: Vec = + deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: config.service_registry.to_string(), + msg: to_json_binary(&active_workers_query)?, + }))?; let participants = workers .clone() .into_iter() - .map(service_registry::state::Worker::try_into) - .collect::, _>>()?; + .map(service_registry::state::WeightedWorker::into) + .collect::>(); let snapshot = snapshot::Snapshot::new(config.signing_threshold, participants.clone().try_into()?); let mut pub_keys = vec![]; - for worker in &workers { + for participant in &participants { let pub_key_query = multisig::msg::QueryMsg::GetPublicKey { - worker_address: worker.address.to_string(), + worker_address: participant.address.to_string(), key_type: config.key_type, }; let pub_key: PublicKey = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: config.multisig.to_string(), - msg: to_binary(&pub_key_query)?, + msg: to_json_binary(&pub_key_query)?, }))?; pub_keys.push(pub_key); } @@ -237,22 +239,34 @@ pub fn update_worker_set(deps: DepsMut, env: Env) -> Result Result { - let config = CONFIG.load(deps.storage)?; - - let worker_set = NEXT_WORKER_SET.load(deps.storage)?; - +fn ensure_worker_set_verification( + worker_set: &WorkerSet, + config: &Config, + deps: &DepsMut, +) -> Result<(), ContractError> { let query = voting_verifier::msg::QueryMsg::GetWorkerSetStatus { new_operators: make_operators(worker_set.clone(), config.encoder), }; let status: VerificationStatus = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: config.voting_verifier.to_string(), - msg: to_binary(&query)?, + msg: to_json_binary(&query)?, }))?; if status != VerificationStatus::SucceededOnChain { - return Err(ContractError::WorkerSetNotConfirmed); + Err(ContractError::WorkerSetNotConfirmed) + } else { + Ok(()) + } +} + +pub fn confirm_worker_set(deps: DepsMut, sender: Addr) -> Result { + let config = CONFIG.load(deps.storage)?; + + let worker_set = NEXT_WORKER_SET.load(deps.storage)?; + + if sender != config.governance { + ensure_worker_set_verification(&worker_set, &config, &deps)?; } CURRENT_WORKER_SET.save(deps.storage, &worker_set)?; @@ -270,27 +284,18 @@ pub fn should_update_worker_set( cur_workers: &WorkerSet, max_diff: usize, ) -> bool { - let new_workers_signers = new_workers - .signers - .values() - .cloned() - .collect::>(); - - let cur_workers_signers = cur_workers - .signers - .values() - .cloned() - .collect::>(); - - new_workers_signers - .iter() - .filter(|item| !cur_workers_signers.contains(item)) - .count() - + cur_workers_signers - .iter() - .filter(|item| !new_workers_signers.contains(item)) - .count() - > max_diff + signers_symetric_difference_count(&new_workers.signers, &cur_workers.signers) > max_diff +} + +fn signers_symetric_difference_count( + s1: &BTreeMap, + s2: &BTreeMap, +) -> usize { + signers_difference_count(s1, s2).saturating_add(signers_difference_count(s2, s1)) +} + +fn signers_difference_count(s1: &BTreeMap, s2: &BTreeMap) -> usize { + s1.values().filter(|v| !s2.values().contains(v)).count() } // Returns true if there is a different worker set pending for confirmation, false if there is no diff --git a/contracts/multisig-prover/src/msg.rs b/contracts/multisig-prover/src/msg.rs index 85eea7ee5..18c83be66 100644 --- a/contracts/multisig-prover/src/msg.rs +++ b/contracts/multisig-prover/src/msg.rs @@ -9,6 +9,7 @@ use crate::encoding::{Data, Encoder}; #[cw_serde] pub struct InstantiateMsg { pub admin_address: String, + pub governance_address: String, pub gateway_address: String, pub multisig_address: String, pub service_registry_address: String, @@ -41,6 +42,11 @@ pub enum QueryMsg { GetWorkerSet, } +#[cw_serde] +pub struct MigrateMsg { + pub governance_address: String, +} + #[cw_serde] pub enum ProofStatus { Pending, diff --git a/contracts/multisig-prover/src/query.rs b/contracts/multisig-prover/src/query.rs index 73c876d4e..adfb1e2fa 100644 --- a/contracts/multisig-prover/src/query.rs +++ b/contracts/multisig-prover/src/query.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - to_binary, Deps, QueryRequest, StdError, StdResult, Uint256, Uint64, WasmQuery, + to_json_binary, Deps, QueryRequest, StdError, StdResult, Uint256, Uint64, WasmQuery, }; use itertools::Itertools; @@ -29,7 +29,7 @@ pub fn get_proof(deps: Deps, multisig_session_id: Uint64) -> StdResult { - *acc += signer.weight; + *acc = acc.saturating_add(signer.weight); Some((signer, Some(sig))) } _ => Some((signer, None)), diff --git a/contracts/multisig-prover/src/reply.rs b/contracts/multisig-prover/src/reply.rs index d7a123f59..477648612 100644 --- a/contracts/multisig-prover/src/reply.rs +++ b/contracts/multisig-prover/src/reply.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{from_binary, DepsMut, Reply, Response, Uint64}; +use cosmwasm_std::{from_json, DepsMut, Reply, Response, Uint64}; use cw_utils::{parse_reply_execute_data, MsgExecuteContractResponse}; use crate::state::{COMMANDS_BATCH, CONFIG}; @@ -16,7 +16,7 @@ pub fn start_multisig_reply(deps: DepsMut, reply: Reply) -> Result Addr { + Addr::unchecked("axelar10d07y265gmmuvt4z0w9aw880jnsr700j7v9daj") +} + pub const CONFIG: Item = Item::new("config"); pub const COMMANDS_BATCH: Map<&BatchId, CommandBatch> = Map::new("command_batch"); pub const MULTISIG_SESSION_BATCH: Map = Map::new("multisig_session_batch"); diff --git a/contracts/multisig-prover/src/test/mocks/gateway.rs b/contracts/multisig-prover/src/test/mocks/gateway.rs index 9b6b1a64b..ee012ed3a 100644 --- a/contracts/multisig-prover/src/test/mocks/gateway.rs +++ b/contracts/multisig-prover/src/test/mocks/gateway.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, + to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, }; use gateway::msg::InstantiateMsg; use gateway_api::msg::{ExecuteMsg, QueryMsg}; @@ -28,7 +28,7 @@ pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::GetOutgoingMessages { message_ids: _ } => { let res = test_data::messages(); - to_binary(&res) + to_json_binary(&res) } } } diff --git a/contracts/multisig-prover/src/test/mocks/multisig.rs b/contracts/multisig-prover/src/test/mocks/multisig.rs index 8a8c357f0..a9e337781 100644 --- a/contracts/multisig-prover/src/test/mocks/multisig.rs +++ b/contracts/multisig-prover/src/test/mocks/multisig.rs @@ -1,9 +1,10 @@ use cosmwasm_std::{ - to_binary, Addr, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response, StdError, + to_json_binary, Addr, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response, StdError, StdResult, Uint64, }; use cw_multi_test::{App, Executor}; use cw_storage_plus::Map; + use multisig::key::{KeyType, KeyTyped, PublicKey}; use multisig::{ msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, @@ -36,7 +37,7 @@ pub fn execute( msg: _, sig_verifier: _, chain_name: _, - } => Ok(Response::new().set_data(to_binary(&Uint64::one())?)), + } => Ok(Response::new().set_data(to_json_binary(&Uint64::one())?)), ExecuteMsg::SubmitSignature { session_id: _, signature: _, @@ -68,7 +69,7 @@ pub fn register_pub_keys(app: &mut App, multisig_address: Addr, workers: Vec StdResult { match msg { - QueryMsg::GetMultisig { session_id: _ } => to_binary(&query::query_success()), + QueryMsg::GetMultisig { session_id: _ } => to_json_binary(&query::query_success()), QueryMsg::GetWorkerSet { worker_set_id: _ } => unimplemented!(), QueryMsg::GetPublicKey { worker_address, key_type, - } => to_binary(&get_public_key_query_success( + } => to_json_binary(&get_public_key_query_success( deps, worker_address, key_type, @@ -110,7 +111,7 @@ mod query { ( Signer { address: op.address, - weight: op.weight.into(), + weight: op.weight, pub_key: op.pub_key, }, op.signature, @@ -131,8 +132,6 @@ mod query { worker: String, key_type: KeyType, ) -> PublicKey { - PUB_KEYS - .load(deps.storage, (worker, key_type.clone())) - .unwrap() + PUB_KEYS.load(deps.storage, (worker, key_type)).unwrap() } } diff --git a/contracts/multisig-prover/src/test/mocks/service_registry.rs b/contracts/multisig-prover/src/test/mocks/service_registry.rs index 9a87749df..3134cbc6d 100644 --- a/contracts/multisig-prover/src/test/mocks/service_registry.rs +++ b/contracts/multisig-prover/src/test/mocks/service_registry.rs @@ -1,12 +1,12 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, + to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, }; use cw_multi_test::{App, Executor}; use cw_storage_plus::Map; use service_registry::{ msg::{InstantiateMsg, QueryMsg}, - state::{AuthorizationState, BondingState, Worker}, + state::{AuthorizationState, BondingState, WeightedWorker, Worker, WORKER_WEIGHT}, }; use crate::test::test_data::TestOperator; @@ -82,17 +82,20 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { } => { let workers = get_operators(deps) .into_iter() - .map(|op| Worker { - address: op.address, - bonding_state: BondingState::Bonded { - amount: op.weight.try_into().unwrap(), + .map(|op| WeightedWorker { + worker_info: Worker { + address: op.address, + bonding_state: BondingState::Bonded { + amount: op.weight.try_into().unwrap(), + }, + authorization_state: AuthorizationState::Authorized, + service_name: service_name.clone(), }, - authorization_state: AuthorizationState::Authorized, - service_name: service_name.clone(), + weight: WORKER_WEIGHT, }) - .collect::>(); + .collect::>(); - to_binary(&workers) + to_json_binary(&workers) } QueryMsg::GetService { .. } => todo!(), QueryMsg::GetWorker { .. } => todo!(), diff --git a/contracts/multisig-prover/src/test/mocks/voting_verifier.rs b/contracts/multisig-prover/src/test/mocks/voting_verifier.rs index 85eeced73..fa9d254ff 100644 --- a/contracts/multisig-prover/src/test/mocks/voting_verifier.rs +++ b/contracts/multisig-prover/src/test/mocks/voting_verifier.rs @@ -1,7 +1,7 @@ use axelar_wasm_std::{hash::Hash, operators::Operators, VerificationStatus}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - to_binary, Addr, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response, StdError, + to_json_binary, Addr, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response, StdError, StdResult, Uint256, }; use cw_multi_test::{App, Executor}; @@ -65,7 +65,7 @@ pub fn confirm_worker_set( pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::GetWorkerSetStatus { new_operators } => to_binary( + QueryMsg::GetWorkerSetStatus { new_operators } => to_json_binary( &CONFIRMED_WORKER_SETS .may_load(deps.storage, &new_operators.hash())? .map_or(VerificationStatus::None, |_| { diff --git a/contracts/multisig-prover/src/test/multicontract.rs b/contracts/multisig-prover/src/test/multicontract.rs index e37a2a36b..b27d804a6 100644 --- a/contracts/multisig-prover/src/test/multicontract.rs +++ b/contracts/multisig-prover/src/test/multicontract.rs @@ -4,6 +4,7 @@ use cw_multi_test::{next_block, App, AppBuilder, Contract, ContractWrapper, Exec use super::{mocks, test_data}; pub const INSTANTIATOR: &str = "instantiator"; +pub const GOVERNANCE: &str = "governance"; pub const RELAYER: &str = "relayer"; pub const SIGNATURE_BLOCK_EXPIRY: u64 = 100; @@ -11,6 +12,7 @@ pub const SIGNATURE_BLOCK_EXPIRY: u64 = 100; pub struct TestCaseConfig { pub app: App, pub admin: Addr, + pub governance: Addr, pub prover_address: Addr, pub service_registry_address: Addr, pub voting_verifier_address: Addr, @@ -158,6 +160,7 @@ fn instantiate_prover( let code_id = app.store_code(contract_prover()); let msg = crate::msg::InstantiateMsg { admin_address: INSTANTIATOR.to_string(), + governance_address: GOVERNANCE.to_string(), gateway_address, multisig_address, service_registry_address, @@ -209,6 +212,7 @@ pub fn setup_test_case() -> TestCaseConfig { TestCaseConfig { app, admin: Addr::unchecked(INSTANTIATOR), + governance: Addr::unchecked(GOVERNANCE), prover_address, service_registry_address, voting_verifier_address, diff --git a/contracts/multisig-prover/src/test/test_data.rs b/contracts/multisig-prover/src/test/test_data.rs index a804c37ee..18cce7582 100644 --- a/contracts/multisig-prover/src/test/test_data.rs +++ b/contracts/multisig-prover/src/test/test_data.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use axelar_wasm_std::{nonempty, MajorityThreshold, Threshold}; -use connection_router_api::Message; +use connection_router_api::{CrossChainId, Message}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, HexBinary, Uint256, Uint64}; use multisig::{ @@ -78,9 +78,12 @@ pub fn new_worker_set() -> WorkerSet { pub fn messages() -> Vec { vec![Message { - cc_id: "ganache-1:0xff822c88807859ff226b58e24f24974a70f04b9442501ae38fd665b3c68f3834:0" - .parse() - .unwrap(), + cc_id: CrossChainId { + chain: "ganache-1".parse().unwrap(), + id: "0xff822c88807859ff226b58e24f24974a70f04b9442501ae38fd665b3c68f3834-0" + .parse() + .unwrap(), + }, source_address: "0x52444f1835Adc02086c37Cb226561605e2E1699b" .parse() .unwrap(), @@ -111,19 +114,19 @@ pub fn chain_id_operator_transfer() -> Uint256 { pub fn encoded_data() -> HexBinary { HexBinary::from_hex( - "0000000000000000000000000000000000000000000000000000000000000539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000013ee2f8af2201994e3518c9ce6848774785c2eef3bdbf9f954899497616dd59af000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000013617070726f7665436f6e747261637443616c6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000a4f10f76b86e01b98daf66a3d02a65e14adb07678c3685dc41c2eca11426f8035742fb97ea9f14931152670a5703f18fe8b392f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000967616e616368652d310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a30783532343434663138333541646330323038366333374362323236353631363035653245313639396200000000000000000000000000000000000000000000").unwrap() + "0000000000000000000000000000000000000000000000000000000000000539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000101d9a96970a852fe24dad94e05c4c8c5eab33d027cb7d381c2745c390d75e998000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000013617070726f7665436f6e747261637443616c6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000a4f10f76b86e01b98daf66a3d02a65e14adb07678c3685dc41c2eca11426f8035742fb97ea9f14931152670a5703f18fe8b392f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000967616e616368652d310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a30783532343434663138333541646330323038366333374362323236353631363035653245313639396200000000000000000000000000000000000000000000").unwrap() } pub fn msg_to_sign() -> HexBinary { - HexBinary::from_hex("7140b89fc7207cd96a262ef8e3f9301cdd72549e01894070ce5a7f2d4096819f").unwrap() + HexBinary::from_hex("a3e14793a042e5b8efb6a81a68be1816f74f4fbeb0adc98771001c93cb4bba96").unwrap() } pub fn encoded_proof() -> HexBinary { - HexBinary::from_hex("0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000500000000000000000000000011c67adfe52a3782bd518294188c4aafaaf6cdeb0000000000000000000000004905fd2e40b1a037256e32fe1e4bca41ae510d730000000000000000000000004ef5c8d81b6417fa80c320b5fc1d3900506dff540000000000000000000000006c51eec96bf0a8ec799cdd0bbcb4512f8334afe8000000000000000000000000a6e7b3b7a1af4103f540d05776f7dd200210201f0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000041eff382a60ef4d917ae0bcbd58ab121fbd7bd53b777773bf1da298d3701565a3379a5b741f881ad739e651a544f2331c085c482857856201c49328ae9ba65cf131c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041301ff4127c1c5a865732776769c6227e4978a1c4567bc1d2926cd798422f1f507b10989e173c6ebc23334eb7e3c713e940369c418061d5cad1173882f35dc43d1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041c55581edbf0401d0cd3495522323e45d4521312dafdedf39b4adc8085a3842c74f13c055b72d12ec3afc1e8f9c37b5f660fbefb38165dbe61090923865e158271b00000000000000000000000000000000000000000000000000000000000000").unwrap() + HexBinary::from_hex("000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000050000000000000000000000004ef5c8d81b6417fa80c320b5fc1d3900506dff540000000000000000000000006c51eec96bf0a8ec799cdd0bbcb4512f8334afe80000000000000000000000007aeb4eebf1e8dcde3016d4e1dca52b4538cf7aaf000000000000000000000000c5b95c99d883c3204cfc2e73669ce3aa7437f4a6000000000000000000000000ffffde829096dfe8b833997e939865ff57422ea90000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000004172b242d7247fc31d14ce82b32f3ea911808f6f600f362150f9904c974315942927c25f9388cecdbbb0b3723164eea92206775870cd28e1ffd8f1cb9655fb3c4a1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004186909155a6ba27f173edf15d283da6a0019fb6afe6b223ca68530464813f468f356e70788faf6d1d9ff7bfcfd9021b560d72408bef4c86c66e3a94b9dee0a34a1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000419b2d986652fdebe67554f1b33ae6161b205ea84e0dacb07ffde0889791bcab2e5be3b8229eae01f2c22805c87f15cb7f9642e9cba951489edcac5d12ace399391b00000000000000000000000000000000000000000000000000000000000000").unwrap() } pub fn execute_data() -> HexBinary { - HexBinary::from_hex("09c5eabe000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000007600000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000013ee2f8af2201994e3518c9ce6848774785c2eef3bdbf9f954899497616dd59af000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000013617070726f7665436f6e747261637443616c6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000a4f10f76b86e01b98daf66a3d02a65e14adb07678c3685dc41c2eca11426f8035742fb97ea9f14931152670a5703f18fe8b392f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000967616e616368652d310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078353234343466313833354164633032303836633337436232323635363136303565324531363939620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000500000000000000000000000011c67adfe52a3782bd518294188c4aafaaf6cdeb0000000000000000000000004905fd2e40b1a037256e32fe1e4bca41ae510d730000000000000000000000004ef5c8d81b6417fa80c320b5fc1d3900506dff540000000000000000000000006c51eec96bf0a8ec799cdd0bbcb4512f8334afe8000000000000000000000000a6e7b3b7a1af4103f540d05776f7dd200210201f0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000041eff382a60ef4d917ae0bcbd58ab121fbd7bd53b777773bf1da298d3701565a3379a5b741f881ad739e651a544f2331c085c482857856201c49328ae9ba65cf131c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041301ff4127c1c5a865732776769c6227e4978a1c4567bc1d2926cd798422f1f507b10989e173c6ebc23334eb7e3c713e940369c418061d5cad1173882f35dc43d1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041c55581edbf0401d0cd3495522323e45d4521312dafdedf39b4adc8085a3842c74f13c055b72d12ec3afc1e8f9c37b5f660fbefb38165dbe61090923865e158271b00000000000000000000000000000000000000000000000000000000000000").unwrap() + HexBinary::from_hex("09c5eabe000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000007600000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000101d9a96970a852fe24dad94e05c4c8c5eab33d027cb7d381c2745c390d75e998000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000013617070726f7665436f6e747261637443616c6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000a4f10f76b86e01b98daf66a3d02a65e14adb07678c3685dc41c2eca11426f8035742fb97ea9f14931152670a5703f18fe8b392f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000967616e616368652d310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307835323434346631383335416463303230383663333743623232363536313630356532453136393962000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000050000000000000000000000004ef5c8d81b6417fa80c320b5fc1d3900506dff540000000000000000000000006c51eec96bf0a8ec799cdd0bbcb4512f8334afe80000000000000000000000007aeb4eebf1e8dcde3016d4e1dca52b4538cf7aaf000000000000000000000000c5b95c99d883c3204cfc2e73669ce3aa7437f4a6000000000000000000000000ffffde829096dfe8b833997e939865ff57422ea90000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000004172b242d7247fc31d14ce82b32f3ea911808f6f600f362150f9904c974315942927c25f9388cecdbbb0b3723164eea92206775870cd28e1ffd8f1cb9655fb3c4a1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004186909155a6ba27f173edf15d283da6a0019fb6afe6b223ca68530464813f468f356e70788faf6d1d9ff7bfcfd9021b560d72408bef4c86c66e3a94b9dee0a34a1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000419b2d986652fdebe67554f1b33ae6161b205ea84e0dacb07ffde0889791bcab2e5be3b8229eae01f2c22805c87f15cb7f9642e9cba951489edcac5d12ace399391b00000000000000000000000000000000000000000000000000000000000000").unwrap() } pub fn threshold() -> MajorityThreshold { @@ -155,24 +158,24 @@ pub fn operators() -> Vec { ), ( "axelar10ad5vqhuw2jgp8x6hf59qjjejlna2nh4sfsklc", - "0277b844fb49bf9b5838dc15d79f3c6c7701eb2e0ab066d4aa63ce162b08718d37", - "11C67adfe52a3782bd518294188C4AAfaaF6cDeb", + "0315a4c9807fb3e3eb360c6b2cd09ba9edb28b566aaf986b4e107180d89895d42c", + "7aeB4EEbf1E8DCDE3016d4e1dcA52B4538Cf7aAf", 1u128, - Some("eff382a60ef4d917ae0bcbd58ab121fbd7bd53b777773bf1da298d3701565a3379a5b741f881ad739e651a544f2331c085c482857856201c49328ae9ba65cf13"), + Some("72b242d7247fc31d14ce82b32f3ea911808f6f600f362150f9904c974315942927c25f9388cecdbbb0b3723164eea92206775870cd28e1ffd8f1cb9655fb3c4a1b"), ), ( "axelar14g0tmk5ldxxdqtl0utl69ck43cpcvd0ay4lfyt", - "03a54c7ad1621b57803bc5bcfe826b713bf5c5a15906ccd08f271d58992bee71d6", - "4905FD2e40B1A037256e32fe1e4BCa41AE510d73", + "022ffb2327809de022e5aaa651508d397c10d7a2ce60c9115884a295cbab293530", + "c5b95c99D883c3204CFc2E73669CE3aa7437f4A6", 1u128, - Some("301ff4127c1c5a865732776769c6227e4978a1c4567bc1d2926cd798422f1f507b10989e173c6ebc23334eb7e3c713e940369c418061d5cad1173882f35dc43d"), + Some("86909155a6ba27f173edf15d283da6a0019fb6afe6b223ca68530464813f468f356e70788faf6d1d9ff7bfcfd9021b560d72408bef4c86c66e3a94b9dee0a34a1b"), ), ( "axelar1gwd8wd3qkapk8pnwdu4cchah2sjjws6lx694r6", - "033a9726a6e2fdc308089c6cab1e6fda2e2bddeb2bcf800990e5fd2c05a270c9df", - "a6E7b3b7A1AF4103F540D05776F7dd200210201F", + "028e02adae730573377cd167095c8b4c63dcc4a2095171ffc9538c7bbbaed31fb2", + "ffFfDe829096DfE8b833997E939865FF57422Ea9", 1u128, - Some("c55581edbf0401d0cd3495522323e45d4521312dafdedf39b4adc8085a3842c74f13c055b72d12ec3afc1e8f9c37b5f660fbefb38165dbe61090923865e15827"), + Some("9b2d986652fdebe67554f1b33ae6161b205ea84e0dacb07ffde0889791bcab2e5be3b8229eae01f2c22805c87f15cb7f9642e9cba951489edcac5d12ace399391b"), ), ( "axelar1fcrwupthhxm6zsd7kw00w2fk530p6wtt8mj92l", diff --git a/contracts/multisig-prover/src/types.rs b/contracts/multisig-prover/src/types.rs index 2e94db66f..638004c9c 100644 --- a/contracts/multisig-prover/src/types.rs +++ b/contracts/multisig-prover/src/types.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use axelar_wasm_std::{Participant, Snapshot}; use connection_router_api::CrossChainId; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{from_binary, HexBinary, StdResult, Uint256}; +use cosmwasm_std::{from_json, HexBinary, StdResult, Uint256}; use cw_storage_plus::{Key, KeyDeserialize, PrimaryKey}; use multisig::{ key::{PublicKey, Signature}, @@ -65,7 +65,7 @@ impl KeyDeserialize for BatchId { type Output = BatchId; fn from_vec(value: Vec) -> StdResult { - Ok(from_binary(&value.into()).expect("violated invariant: BatchID is not deserializable")) + Ok(from_json(value).expect("violated invariant: BatchID is not deserializable")) } } diff --git a/contracts/multisig/Cargo.toml b/contracts/multisig/Cargo.toml index 653eb805c..0dda4b076 100644 --- a/contracts/multisig/Cargo.toml +++ b/contracts/multisig/Cargo.toml @@ -62,3 +62,6 @@ thiserror = { workspace = true } [dev-dependencies] cw-multi-test = "0.15.1" + +[lints] +workspace = true diff --git a/contracts/multisig/src/contract.rs b/contracts/multisig/src/contract.rs index fcf1ec9ab..82980ace7 100644 --- a/contracts/multisig/src/contract.rs +++ b/contracts/multisig/src/contract.rs @@ -1,7 +1,7 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Addr, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response, StdResult, + to_json_binary, Addr, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response, StdResult, Uint64, }; @@ -92,14 +92,16 @@ pub fn execute( #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::GetMultisig { session_id } => to_binary(&query::get_multisig(deps, session_id)?), + QueryMsg::GetMultisig { session_id } => { + to_json_binary(&query::get_multisig(deps, session_id)?) + } QueryMsg::GetWorkerSet { worker_set_id } => { - to_binary(&query::get_worker_set(deps, worker_set_id)?) + to_json_binary(&query::get_worker_set(deps, worker_set_id)?) } QueryMsg::GetPublicKey { worker_address, key_type, - } => to_binary(&query::get_public_key( + } => to_json_binary(&query::get_public_key( deps, deps.api.addr_validate(&worker_address)?, key_type, @@ -109,9 +111,17 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { #[cfg(test)] mod tests { - use serde_json::from_str; use std::vec; + use cosmwasm_std::{ + from_json, + testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, + Addr, Empty, OwnedDeps, WasmMsg, + }; + use serde_json::from_str; + + use connection_router_api::ChainName; + use crate::{ key::{KeyType, PublicKey, Signature}, msg::Multisig, @@ -123,12 +133,6 @@ mod tests { }; use super::*; - use connection_router_api::ChainName; - use cosmwasm_std::{ - from_binary, - testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - Addr, Empty, OwnedDeps, Uint256, WasmMsg, - }; const INSTANTIATOR: &str = "inst"; const PROVER: &str = "prover"; @@ -347,11 +351,11 @@ mod tests { let res = query_worker_set(&worker_set_1.id(), deps.as_ref()); assert!(res.is_ok()); - assert_eq!(worker_set_1, from_binary(&res.unwrap()).unwrap()); + assert_eq!(worker_set_1, from_json(res.unwrap()).unwrap()); let res = query_worker_set(&worker_set_2.id(), deps.as_ref()); assert!(res.is_ok()); - assert_eq!(worker_set_2, from_binary(&res.unwrap()).unwrap()); + assert_eq!(worker_set_2, from_json(res.unwrap()).unwrap()); for (key_type, _) in [ (KeyType::Ecdsa, worker_set_1_id), @@ -401,10 +405,10 @@ mod tests { assert_eq!(session.state, MultisigState::Pending); let res = res.unwrap(); - assert_eq!(res.data, Some(to_binary(&session.id).unwrap())); + assert_eq!(res.data, Some(to_json_binary(&session.id).unwrap())); assert_eq!(res.events.len(), 1); - let event = res.events.get(0).unwrap(); + let event = res.events.first().unwrap(); assert_eq!(event.ty, "signing_started".to_string()); assert_eq!( get_event_attribute(event, "session_id").unwrap(), @@ -458,11 +462,11 @@ mod tests { do_start_signing_session(deps.as_mut(), PROVER, worker_set_id, chain_name.clone()) .unwrap(); - let signer = signers.get(0).unwrap().to_owned(); + let signer = signers.first().unwrap().to_owned(); let expected_rewards_msg = WasmMsg::Execute { contract_addr: REWARDS_CONTRACT.to_string(), - msg: to_binary(&rewards::msg::ExecuteMsg::RecordParticipation { + msg: to_json_binary(&rewards::msg::ExecuteMsg::RecordParticipation { chain_name: chain_name.clone(), event_id: session_id.to_string().try_into().unwrap(), worker_address: signer.address.clone().into(), @@ -472,7 +476,7 @@ mod tests { } .into(); - let res = do_sign(deps.as_mut(), mock_env(), Uint64::from(session_id), &signer); + let res = do_sign(deps.as_mut(), mock_env(), session_id, &signer); assert!(res.is_ok()); @@ -496,7 +500,7 @@ mod tests { assert!(res.messages.iter().any(|m| m.msg == expected_rewards_msg)); - let event = res.events.get(0).unwrap(); + let event = res.events.first().unwrap(); assert_eq!(event.ty, "signature_submitted".to_string()); assert_eq!( get_event_attribute(event, "session_id").unwrap(), @@ -524,7 +528,7 @@ mod tests { do_start_signing_session(deps.as_mut(), PROVER, subkey, "mock-chain".parse().unwrap()) .unwrap(); - let signer = signers.get(0).unwrap().to_owned(); + let signer = signers.first().unwrap().to_owned(); do_sign(deps.as_mut(), mock_env(), session_id, &signer).unwrap(); // second signature @@ -578,7 +582,7 @@ mod tests { { do_start_signing_session(deps.as_mut(), PROVER, subkey, chain_name.clone()).unwrap(); - let signer = signers.get(0).unwrap().to_owned(); + let signer = signers.first().unwrap().to_owned(); do_sign(deps.as_mut(), mock_env(), session_id, &signer).unwrap(); // second signature @@ -590,7 +594,7 @@ mod tests { let expected_rewards_msg = WasmMsg::Execute { contract_addr: REWARDS_CONTRACT.to_string(), - msg: to_binary(&rewards::msg::ExecuteMsg::RecordParticipation { + msg: to_json_binary(&rewards::msg::ExecuteMsg::RecordParticipation { chain_name: chain_name.clone(), event_id: session_id.to_string().try_into().unwrap(), worker_address: signer.address.clone().into(), @@ -608,10 +612,7 @@ mod tests { assert_eq!(signatures.len(), 3); assert!(res.messages.iter().any(|m| m.msg == expected_rewards_msg)); - assert!(!res - .events - .iter() - .any(|e| e.ty == "signing_completed".to_string())); // event is not re-emitted + assert!(!res.events.iter().any(|e| e.ty == *"signing_completed")); // event is not re-emitted } } @@ -626,7 +627,7 @@ mod tests { do_start_signing_session(deps.as_mut(), PROVER, subkey, "mock-chain".parse().unwrap()) .unwrap(); - let signer = signers.get(0).unwrap().to_owned(); + let signer = signers.first().unwrap().to_owned(); do_sign(deps.as_mut(), mock_env(), session_id, &signer).unwrap(); // second signature @@ -662,7 +663,7 @@ mod tests { .unwrap(); let invalid_session_id = Uint64::zero(); - let signer = ecdsa_test_data::signers().get(0).unwrap().to_owned(); + let signer = ecdsa_test_data::signers().first().unwrap().to_owned(); let res = do_sign(deps.as_mut(), mock_env(), invalid_session_id, &signer); assert_eq!( @@ -689,7 +690,7 @@ mod tests { deps.as_mut(), mock_env(), session_id, - signers.get(0).unwrap(), + signers.first().unwrap(), ) .unwrap(); @@ -702,7 +703,7 @@ mod tests { let res = query(deps.as_ref(), mock_env(), msg); assert!(res.is_ok()); - let query_res: Multisig = from_binary(&res.unwrap()).unwrap(); + let query_res: Multisig = from_json(&res.unwrap()).unwrap(); let session = SIGNING_SESSIONS .load(deps.as_ref().storage, session_id.into()) .unwrap(); @@ -729,7 +730,7 @@ mod tests { .find(|signer| signer.0.address == worker_set_signer.address) .unwrap(); - assert_eq!(signer.0.weight, Uint256::from(worker_set_signer.weight)); + assert_eq!(signer.0.weight, worker_set_signer.weight); assert_eq!( signer.0.pub_key, worker_set.signers.get(address).unwrap().pub_key @@ -800,7 +801,7 @@ mod tests { for (addr, _, _) in &expected_pub_keys { let res = query_registered_public_key(deps.as_ref(), addr.clone(), key_type); assert!(res.is_ok()); - ret_pub_keys.push(from_binary(&res.unwrap()).unwrap()); + ret_pub_keys.push(from_json(&res.unwrap()).unwrap()); } assert_eq!( expected_pub_keys @@ -855,7 +856,7 @@ mod tests { assert!(res.is_ok()); assert_eq!( PublicKey::try_from((KeyType::Ecdsa, new_pub_key.clone())).unwrap(), - from_binary::(&res.unwrap()).unwrap() + from_json::(&res.unwrap()).unwrap() ); // Register an ED25519 key, it should not affect our ECDSA key @@ -876,14 +877,14 @@ mod tests { assert!(res.is_ok()); assert_eq!( PublicKey::try_from((KeyType::Ed25519, ed25519_pub_key)).unwrap(), - from_binary::(&res.unwrap()).unwrap() + from_json::(&res.unwrap()).unwrap() ); let res = query_registered_public_key(deps.as_ref(), pub_keys[0].0.clone(), KeyType::Ecdsa); assert!(res.is_ok()); assert_eq!( PublicKey::try_from((KeyType::Ecdsa, new_pub_key)).unwrap(), - from_binary::(&res.unwrap()).unwrap() + from_json::(&res.unwrap()).unwrap() ); } diff --git a/contracts/multisig/src/contract/execute.rs b/contracts/multisig/src/contract/execute.rs index 59caf0ea1..c36cbe612 100644 --- a/contracts/multisig/src/contract/execute.rs +++ b/contracts/multisig/src/contract/execute.rs @@ -1,5 +1,5 @@ use connection_router_api::ChainName; -use cosmwasm_std::WasmMsg; +use cosmwasm_std::{OverflowError, OverflowOperation, WasmMsg}; use sha3::{Digest, Keccak256}; use signature_verifier_api::client::SignatureVerifier; @@ -29,12 +29,24 @@ pub fn start_signing_session( let session_id = SIGNING_SESSION_COUNTER.update( deps.storage, |mut counter| -> Result { - counter += Uint64::one(); + counter = counter + .checked_add(Uint64::one()) + .map_err(ContractError::Overflow)?; Ok(counter) }, )?; - let expires_at = env.block.height + config.block_expiry; + let expires_at = env + .block + .height + .checked_add(config.block_expiry) + .ok_or_else(|| { + OverflowError::new( + OverflowOperation::Add, + env.block.height, + config.block_expiry, + ) + })?; let signing_session = SigningSession::new( session_id, @@ -57,7 +69,7 @@ pub fn start_signing_session( }; Ok(Response::new() - .set_data(to_binary(&session_id)?) + .set_data(to_json_binary(&session_id)?) .add_event(event.into())) } @@ -199,7 +211,7 @@ fn signing_response( ) -> Result { let rewards_msg = WasmMsg::Execute { contract_addr: rewards_contract, - msg: to_binary(&rewards::msg::ExecuteMsg::RecordParticipation { + msg: to_json_binary(&rewards::msg::ExecuteMsg::RecordParticipation { chain_name: session.chain_name, event_id: session .id diff --git a/contracts/multisig/src/ed25519.rs b/contracts/multisig/src/ed25519.rs index d5dd1aa17..29c2159a7 100644 --- a/contracts/multisig/src/ed25519.rs +++ b/contracts/multisig/src/ed25519.rs @@ -3,9 +3,35 @@ use crate::ContractError; pub const ED25519_SIGNATURE_LEN: usize = 64; pub fn ed25519_verify(msg_hash: &[u8], sig: &[u8], pub_key: &[u8]) -> Result { - cosmwasm_crypto::ed25519_verify(msg_hash, &sig[0..ED25519_SIGNATURE_LEN], pub_key).map_err( - |e| ContractError::SignatureVerificationFailed { + cosmwasm_crypto::ed25519_verify(msg_hash, sig, pub_key).map_err(|e| { + ContractError::SignatureVerificationFailed { reason: e.to_string(), - }, - ) + } + }) +} + +#[cfg(test)] +mod test { + use cosmwasm_std::HexBinary; + + use crate::test::common::ed25519_test_data; + + use super::*; + + #[test] + fn should_fail_sig_verification_instead_of_truncating() { + let sig_with_extra_byte = ed25519_test_data::signature().to_hex() + "00"; + + let signature = HexBinary::from_hex(&sig_with_extra_byte).unwrap().to_vec(); + let message = ed25519_test_data::message().to_vec(); + let public_key = ed25519_test_data::pub_key().to_vec(); + + let result = ed25519_verify(&message, &signature, &public_key); + assert_eq!( + result.unwrap_err(), + ContractError::SignatureVerificationFailed { + reason: "Invalid signature format".into(), + } + ); + } } diff --git a/contracts/multisig/src/error.rs b/contracts/multisig/src/error.rs index d7c40fc21..7383a6c11 100644 --- a/contracts/multisig/src/error.rs +++ b/contracts/multisig/src/error.rs @@ -1,5 +1,5 @@ use axelar_wasm_std_derive::IntoContractError; -use cosmwasm_std::{StdError, Uint64}; +use cosmwasm_std::{OverflowError, StdError, Uint64}; use thiserror::Error; #[derive(Error, Debug, PartialEq, IntoContractError)] @@ -7,6 +7,9 @@ pub enum ContractError { #[error(transparent)] Std(#[from] StdError), + #[error(transparent)] + Overflow(#[from] OverflowError), + #[error("no active worker set found for {worker_set_id:?}")] NoActiveWorkerSetFound { worker_set_id: String }, diff --git a/contracts/multisig/src/key.rs b/contracts/multisig/src/key.rs index 8f108256a..9804f13f2 100644 --- a/contracts/multisig/src/key.rs +++ b/contracts/multisig/src/key.rs @@ -38,7 +38,7 @@ impl NonRecoverable { &self, msg: &[u8], pub_key: &PublicKey, - recovery_transform: impl FnOnce(u8) -> u8, + recovery_transform: impl FnOnce(k256::ecdsa::RecoveryId) -> u8, ) -> Result { let sig = k256::ecdsa::Signature::from_slice(self.0.as_ref()).map_err(|err| { ContractError::InvalidSignatureFormat { @@ -50,8 +50,8 @@ impl NonRecoverable { .and_then(|k| k256::ecdsa::RecoveryId::trial_recovery_from_prehash(&k, msg, &sig)) .map_err(|err| ContractError::InvalidSignatureFormat { reason: err.to_string(), - })? - .to_byte(); + })?; + let mut recoverable = sig.to_vec(); recoverable.push(recovery_transform(recovery_byte)); @@ -180,7 +180,7 @@ impl Signature { } let res = match self.key_type() { - KeyType::Ecdsa => ecdsa_verify(msg.as_ref(), self, pub_key.as_ref()), + KeyType::Ecdsa => ecdsa_verify(msg.as_ref(), self.as_ref(), pub_key.as_ref()), KeyType::Ed25519 => ed25519_verify(msg.as_ref(), self.as_ref(), pub_key.as_ref()), }?; @@ -395,7 +395,7 @@ mod ecdsa_tests { .unwrap(); let message = MsgToSign::try_from(ecdsa_test_data::message()).unwrap(); let public_key = PublicKey::try_from((KeyType::Ecdsa, ecdsa_test_data::pub_key())).unwrap(); - let result = signature.verify(&message, &public_key); + let result = signature.verify(message, &public_key); assert!(result.is_ok(), "{:?}", result) } @@ -409,7 +409,7 @@ mod ecdsa_tests { let signature: Signature = (KeyType::Ecdsa, invalid_signature).try_into().unwrap(); let message = MsgToSign::try_from(ecdsa_test_data::message()).unwrap(); let public_key = PublicKey::try_from((KeyType::Ecdsa, ecdsa_test_data::pub_key())).unwrap(); - let result = signature.verify(&message, &public_key); + let result = signature.verify(message, &public_key); assert_eq!( result.unwrap_err(), ContractError::SignatureVerificationFailed { @@ -428,7 +428,7 @@ mod ecdsa_tests { let signature: Signature = (KeyType::Ecdsa, invalid_signature).try_into().unwrap(); let message = MsgToSign::try_from(ecdsa_test_data::message()).unwrap(); let public_key = PublicKey::try_from((KeyType::Ecdsa, ecdsa_test_data::pub_key())).unwrap(); - let result = signature.verify(&message, &public_key); + let result = signature.verify(message, &public_key); assert_eq!( result.unwrap_err(), ContractError::SignatureVerificationFailed { @@ -449,7 +449,7 @@ mod ecdsa_tests { .unwrap(); let message = MsgToSign::try_from(ecdsa_test_data::message()).unwrap(); let public_key = PublicKey::try_from((KeyType::Ecdsa, invalid_pub_key)).unwrap(); - let result = signature.verify(&message, &public_key); + let result = signature.verify(message, &public_key); assert_eq!( result.unwrap_err(), ContractError::SignatureVerificationFailed { @@ -535,7 +535,7 @@ mod ed25519_tests { let message = MsgToSign::try_from(ed25519_test_data::message()).unwrap(); let public_key = PublicKey::try_from((KeyType::Ed25519, ed25519_test_data::pub_key())).unwrap(); - let result = signature.verify(&message, &public_key); + let result = signature.verify(message, &public_key); assert!(result.is_ok(), "{:?}", result) } @@ -550,7 +550,7 @@ mod ed25519_tests { let message = MsgToSign::try_from(ed25519_test_data::message()).unwrap(); let public_key = PublicKey::try_from((KeyType::Ed25519, ed25519_test_data::pub_key())).unwrap(); - let result = signature.verify(&message, &public_key); + let result = signature.verify(message, &public_key); assert_eq!( result.unwrap_err(), ContractError::SignatureVerificationFailed { @@ -569,7 +569,7 @@ mod ed25519_tests { Signature::try_from((KeyType::Ed25519, ed25519_test_data::signature())).unwrap(); let message = MsgToSign::try_from(ed25519_test_data::message()).unwrap(); let public_key = PublicKey::Ed25519(invalid_pub_key); - let result = signature.verify(&message, &public_key); + let result = signature.verify(message, &public_key); assert_eq!( result.unwrap_err(), ContractError::SignatureVerificationFailed { diff --git a/contracts/multisig/src/msg.rs b/contracts/multisig/src/msg.rs index 6c500aa1e..04f036fce 100644 --- a/contracts/multisig/src/msg.rs +++ b/contracts/multisig/src/msg.rs @@ -44,11 +44,11 @@ pub enum ExecuteMsg { to sign their own address using the private key */ signed_sender_address: HexBinary, }, - // Authorizes a contract to call StartSigningSession. + // Authorizes a contract to call StartSigningSession. Callable only by governance AuthorizeCaller { contract_address: Addr, }, - // Unauthorizes a contract, so it can no longer call StartSigningSession. + // Unauthorizes a contract, so it can no longer call StartSigningSession. Callable only by governance UnauthorizeCaller { contract_address: Addr, }, diff --git a/contracts/multisig/src/secp256k1.rs b/contracts/multisig/src/secp256k1.rs index 2881bfe4e..c82071575 100644 --- a/contracts/multisig/src/secp256k1.rs +++ b/contracts/multisig/src/secp256k1.rs @@ -1,17 +1,37 @@ use cosmwasm_crypto::secp256k1_verify; -// TODO: Logic specific to secp256k1 will most likely be handled by core in the future. -use crate::key::Signature; use crate::ContractError; -pub fn ecdsa_verify( - msg_hash: &[u8], - sig: &Signature, - pub_key: &[u8], -) -> Result { - secp256k1_verify(msg_hash, sig.as_ref(), pub_key).map_err(|err| { +pub fn ecdsa_verify(msg_hash: &[u8], sig: &[u8], pub_key: &[u8]) -> Result { + secp256k1_verify(msg_hash, sig, pub_key).map_err(|err| { ContractError::SignatureVerificationFailed { reason: err.to_string(), } }) } + +#[cfg(test)] +mod test { + use cosmwasm_std::HexBinary; + + use crate::test::common::ecdsa_test_data; + + use super::*; + + #[test] + fn should_fail_sig_verification_instead_of_truncating() { + let sig_with_extra_byte = ecdsa_test_data::signature().to_hex() + "00"; + + let signature = HexBinary::from_hex(&sig_with_extra_byte).unwrap().to_vec(); + let message = ecdsa_test_data::message().to_vec(); + let public_key = ecdsa_test_data::pub_key().to_vec(); + + let result = ecdsa_verify(&message, &signature, &public_key); + assert_eq!( + result.unwrap_err(), + ContractError::SignatureVerificationFailed { + reason: "Invalid signature format".into(), + } + ); + } +} diff --git a/contracts/multisig/src/signing.rs b/contracts/multisig/src/signing.rs index 888d67c7d..67c6126e3 100644 --- a/contracts/multisig/src/signing.rs +++ b/contracts/multisig/src/signing.rs @@ -135,7 +135,7 @@ fn signers_weight(signatures: &HashMap, worker_set: &WorkerSe mod tests { use cosmwasm_std::{ testing::{MockQuerier, MockStorage}, - to_binary, Addr, HexBinary, QuerierWrapper, + to_json_binary, Addr, HexBinary, QuerierWrapper, }; use crate::{ @@ -279,7 +279,7 @@ mod tests { for verification in [true, false] { let mut querier = MockQuerier::default(); - querier.update_wasm(move |_| Ok(to_binary(&verification).into()).into()); + querier.update_wasm(move |_| Ok(to_json_binary(&verification).into()).into()); let sig_verifier = Some(SignatureVerifier { address: Addr::unchecked("verifier".to_string()), querier: QuerierWrapper::new(&querier), diff --git a/contracts/multisig/src/test/common.rs b/contracts/multisig/src/test/common.rs index 818e8649c..ec716ceed 100644 --- a/contracts/multisig/src/test/common.rs +++ b/contracts/multisig/src/test/common.rs @@ -128,7 +128,8 @@ pub mod ed25519_test_data { } } -pub fn build_worker_set(key_type: KeyType, signers: &Vec) -> WorkerSet { +#[allow(clippy::arithmetic_side_effects)] +pub fn build_worker_set(key_type: KeyType, signers: &[TestSigner]) -> WorkerSet { let mut total_weight = Uint256::zero(); let participants = signers .iter() diff --git a/contracts/nexus-gateway/Cargo.toml b/contracts/nexus-gateway/Cargo.toml index 4b180754e..d6f05ed02 100644 --- a/contracts/nexus-gateway/Cargo.toml +++ b/contracts/nexus-gateway/Cargo.toml @@ -26,3 +26,7 @@ report = { workspace = true } schemars = "0.8.15" serde = { version = "1.0.188", features = ["derive"] } thiserror = { workspace = true } +voting-verifier = { workspace = true, features = ["library"] } + +[lints] +workspace = true diff --git a/contracts/nexus-gateway/src/contract/execute.rs b/contracts/nexus-gateway/src/contract/execute.rs index 9cba415f9..2fa86565c 100644 --- a/contracts/nexus-gateway/src/contract/execute.rs +++ b/contracts/nexus-gateway/src/contract/execute.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{to_binary, Addr, Response, WasmMsg}; +use cosmwasm_std::{to_json_binary, Addr, Response, WasmMsg}; use error_stack::report; use crate::error::ContractError; @@ -32,7 +32,7 @@ where Ok(Response::new().add_message(WasmMsg::Execute { contract_addr: self.config.router.to_string(), - msg: to_binary(&connection_router_api::msg::ExecuteMsg::RouteMessages(msgs)) + msg: to_json_binary(&connection_router_api::msg::ExecuteMsg::RouteMessages(msgs)) .expect("must serialize route-messages message"), funds: vec![], })) @@ -67,11 +67,12 @@ where #[cfg(test)] mod test { + use cosmwasm_std::{from_json, CosmosMsg}; + use hex::decode; + use connection_router_api::CrossChainId; - use cosmwasm_std::{from_binary, CosmosMsg}; use crate::state::{Config, MockStore}; - use hex::decode; use super::*; @@ -165,7 +166,7 @@ mod test { funds, }) => { if let Ok(connection_router_api::msg::ExecuteMsg::RouteMessages(msgs)) = - from_binary(msg) + from_json(msg) { return *contract_addr == Addr::unchecked("router") && msgs.len() == 2 diff --git a/contracts/nexus-gateway/src/nexus.rs b/contracts/nexus-gateway/src/nexus.rs index 7da4d02ac..86b7846da 100644 --- a/contracts/nexus-gateway/src/nexus.rs +++ b/contracts/nexus-gateway/src/nexus.rs @@ -1,5 +1,5 @@ use axelar_wasm_std::nonempty; -use connection_router_api::{Address, ChainName, CrossChainId, ID_SEPARATOR}; +use connection_router_api::{Address, ChainName, CrossChainId}; use cosmwasm_std::{CosmosMsg, CustomMsg}; use error_stack::{Result, ResultExt}; use hex::{FromHex, ToHex}; @@ -7,6 +7,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::error::ContractError; +use voting_verifier::events::TX_HASH_EVENT_INDEX_SEPARATOR; const ZEROX_PREFIX: &str = "0x"; @@ -25,9 +26,13 @@ pub struct Message { impl CustomMsg for Message {} +// it's parsed into u64 instead of u32 (https://github.com/axelarnetwork/axelar-amplifier/blob/bf0b3049c83e540989c7dad1c609c7e2ef6ed2e5/contracts/voting-verifier/src/events.rs#L162) +// here in order to match the message type defined in the nexus module. Changing nexus to use u32 instead is not worth the effort. fn parse_message_id(message_id: &str) -> Result<(nonempty::Vec, u64), ContractError> { // expected format: : - let components = message_id.split(ID_SEPARATOR).collect::>(); + let components = message_id + .split(TX_HASH_EVENT_INDEX_SEPARATOR) + .collect::>(); if components.len() != 2 { return Err(ContractError::InvalidMessageId(message_id.to_string()).into()); diff --git a/contracts/rewards/Cargo.toml b/contracts/rewards/Cargo.toml index e14fce376..23aac0b85 100644 --- a/contracts/rewards/Cargo.toml +++ b/contracts/rewards/Cargo.toml @@ -48,3 +48,6 @@ thiserror = { workspace = true } [dev-dependencies] cw-multi-test = "0.15.1" + +[lints] +workspace = true diff --git a/contracts/rewards/src/contract.rs b/contracts/rewards/src/contract.rs index e449adfa5..7c8883575 100644 --- a/contracts/rewards/src/contract.rs +++ b/contracts/rewards/src/contract.rs @@ -1,19 +1,20 @@ -use itertools::Itertools; - use axelar_wasm_std::nonempty; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{BankMsg, Coin, DepsMut, Env, MessageInfo, Response}; +use cosmwasm_std::{ + to_json_binary, BankMsg, Binary, Coin, Deps, DepsMut, Env, MessageInfo, Response, +}; use error_stack::ResultExt; +use itertools::Itertools; use crate::{ - contract::execute::Contract, error::ContractError, - msg::{ExecuteMsg, InstantiateMsg}, - state::{Config, Epoch, PoolId, StoredParams, CONFIG, PARAMS}, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + state::{self, Config, Epoch, ParamsSnapshot, PoolId, CONFIG, PARAMS}, }; mod execute; +mod query; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -34,9 +35,9 @@ pub fn instantiate( PARAMS.save( deps.storage, - &StoredParams { + &ParamsSnapshot { params: msg.params, - last_updated: Epoch { + created_at: Epoch { epoch_num: 0, block_height_started: env.block.height, }, @@ -64,25 +65,30 @@ pub fn execute( chain_name, contract: info.sender.clone(), }; - Contract::new(deps) - .record_participation(event_id, worker_address, pool_id, env.block.height) - .map_err(axelar_wasm_std::ContractError::from)?; + execute::record_participation( + deps.storage, + event_id, + worker_address, + pool_id, + env.block.height, + ) + .map_err(axelar_wasm_std::ContractError::from)?; Ok(Response::new()) } ExecuteMsg::AddRewards { pool_id } => { deps.api.addr_validate(pool_id.contract.as_str())?; - let mut contract = Contract::new(deps); let amount = info .funds .iter() - .find(|coin| coin.denom == contract.config.rewards_denom) + .find(|coin| coin.denom == state::load_config(deps.storage).rewards_denom) .filter(|_| info.funds.len() == 1) // filter here to make sure expected denom is the only one attached to this message, and other funds aren't silently swallowed .ok_or(ContractError::WrongDenom)? .amount; - contract.add_rewards( + execute::add_rewards( + deps.storage, pool_id, nonempty::Uint128::try_from(amount).change_context(ContractError::ZeroRewards)?, )?; @@ -95,10 +101,9 @@ pub fn execute( } => { deps.api.addr_validate(pool_id.contract.as_str())?; - let mut contract = Contract::new(deps); - let rewards = contract - .distribute_rewards(pool_id, env.block.height, epoch_count) - .map_err(axelar_wasm_std::ContractError::from)?; + let rewards = + execute::distribute_rewards(deps.storage, pool_id, env.block.height, epoch_count) + .map_err(axelar_wasm_std::ContractError::from)?; let msgs = rewards .into_iter() @@ -106,7 +111,7 @@ pub fn execute( .map(|(addr, amount)| BankMsg::Send { to_address: addr.into(), amount: vec![Coin { - denom: contract.config.rewards_denom.clone(), + denom: state::load_config(deps.storage).rewards_denom.clone(), amount, }], }); @@ -114,25 +119,41 @@ pub fn execute( Ok(Response::new().add_messages(msgs)) } ExecuteMsg::UpdateParams { params } => { - Contract::new(deps).update_params(params, env.block.height, info.sender)?; + execute::update_params(deps.storage, params, env.block.height, info.sender)?; Ok(Response::new()) } } } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query( + deps: Deps, + env: Env, + msg: QueryMsg, +) -> Result { + match msg { + QueryMsg::RewardsPool { pool_id } => { + let pool = query::rewards_pool(deps.storage, pool_id, env.block.height)?; + to_json_binary(&pool) + .change_context(ContractError::SerializeResponse) + .map_err(axelar_wasm_std::ContractError::from) + } + } +} + #[cfg(test)] mod tests { use connection_router_api::ChainName; - use cosmwasm_std::{coins, Addr, Binary, BlockInfo, Deps, Env, StdResult, Uint128}; + use cosmwasm_std::{coins, Addr, BlockInfo, Uint128}; use cw_multi_test::{App, ContractWrapper, Executor}; - use crate::msg::{ExecuteMsg, InstantiateMsg, Params, QueryMsg}; + use crate::msg::{ExecuteMsg, InstantiateMsg, Params, QueryMsg, RewardsPool}; use crate::state::PoolId; - use super::{execute, instantiate}; + use super::{execute, instantiate, query}; - /// Tests that the contract entry points (instantiate and execute) work as expected. + /// Tests that the contract entry points (instantiate, query and execute) work as expected. /// Instantiates the contract and calls each of the 4 ExecuteMsg variants. /// Adds rewards to the pool, updates the rewards params, records some participation /// events and then distributes the rewards. @@ -150,17 +171,13 @@ mod tests { .init_balance(storage, &user, coins(100000, AXL_DENOMINATION)) .unwrap() }); - let code = ContractWrapper::new( - execute, - instantiate, - |_deps: Deps, _env: Env, _msg: QueryMsg| -> StdResult { todo!() }, - ); + let code = ContractWrapper::new(execute, instantiate, query); let code_id = app.store_code(Box::new(code)); let governance_address = Addr::unchecked("governance"); let initial_params = Params { epoch_duration: 10u64.try_into().unwrap(), - rewards_per_epoch: Uint128::one().try_into().unwrap(), + rewards_per_epoch: Uint128::from(100u128).try_into().unwrap(), participation_threshold: (1, 2).try_into().unwrap(), }; let contract_address = app @@ -170,11 +187,7 @@ mod tests { &InstantiateMsg { governance_address: governance_address.to_string(), rewards_denom: AXL_DENOMINATION.to_string(), - params: Params { - epoch_duration: 10u64.try_into().unwrap(), - rewards_per_epoch: Uint128::from(100u128).try_into().unwrap(), - participation_threshold: (1, 2).try_into().unwrap(), - }, + params: initial_params.clone(), }, &[], "Contract", @@ -182,27 +195,31 @@ mod tests { ) .unwrap(); + let pool_id = PoolId { + chain_name: chain_name.clone(), + contract: worker_contract.clone(), + }; + + let rewards = 200; let res = app.execute_contract( user.clone(), contract_address.clone(), &ExecuteMsg::AddRewards { - pool_id: PoolId { - chain_name: chain_name.clone(), - contract: worker_contract.clone(), - }, + pool_id: pool_id.clone(), }, - &coins(200, AXL_DENOMINATION), + &coins(rewards, AXL_DENOMINATION), ); assert!(res.is_ok()); + let updated_params = Params { + rewards_per_epoch: Uint128::from(150u128).try_into().unwrap(), + ..initial_params + }; let res = app.execute_contract( governance_address, contract_address.clone(), &ExecuteMsg::UpdateParams { - params: Params { - rewards_per_epoch: Uint128::from(150u128).try_into().unwrap(), - ..initial_params - }, + params: updated_params.clone(), }, &[], ); @@ -232,6 +249,22 @@ mod tests { ); assert!(res.is_ok()); + // check the rewards pool + let res: RewardsPool = app + .wrap() + .query_wasm_smart(contract_address.clone(), &QueryMsg::RewardsPool { pool_id }) + .unwrap(); + assert_eq!( + res, + RewardsPool { + balance: rewards.into(), + epoch_duration: updated_params.epoch_duration.into(), + rewards_per_epoch: updated_params.rewards_per_epoch.into(), + current_epoch_num: 0u64.into(), + last_distribution_epoch: None + } + ); + // need to change the block height, so we can claim rewards let old_height = app.block_info().height; app.set_block(BlockInfo { diff --git a/contracts/rewards/src/contract/execute.rs b/contracts/rewards/src/contract/execute.rs index ae4229276..36ec48f0f 100644 --- a/contracts/rewards/src/contract/execute.rs +++ b/contracts/rewards/src/contract/execute.rs @@ -1,226 +1,190 @@ use std::collections::HashMap; use axelar_wasm_std::{nonempty, FnExt}; -use cosmwasm_std::{Addr, DepsMut, Uint128}; -use error_stack::Result; +use cosmwasm_std::{Addr, OverflowError, OverflowOperation, Storage, Uint128}; +use error_stack::{Report, Result}; use crate::{ error::ContractError, msg::Params, - state::{ - Config, Epoch, EpochTally, Event, PoolId, RewardsStore, StorageState, Store, StoredParams, - CONFIG, - }, + state::{self, Config, Epoch, EpochTally, Event, ParamsSnapshot, PoolId, StorageState}, }; const DEFAULT_EPOCHS_TO_PROCESS: u64 = 10; const EPOCH_PAYOUT_DELAY: u64 = 2; -pub struct Contract -where - S: Store, -{ - pub store: S, - pub config: Config, -} - -impl<'a> Contract> { - pub fn new(deps: DepsMut) -> Contract { - let config = CONFIG.load(deps.storage).expect("couldn't load config"); - Contract { - store: RewardsStore { - storage: deps.storage, - }, - config, - } +fn require_governance(config: Config, sender: Addr) -> Result<(), ContractError> { + if config.governance != sender { + return Err(ContractError::Unauthorized.into()); } + Ok(()) } -#[allow(dead_code)] -impl Contract -where - S: Store, -{ - /// Returns the current epoch. The current epoch is computed dynamically based on the current - /// block height and the epoch duration. If the epoch duration is updated, we store the epoch - /// in which the update occurs as the last checkpoint - fn current_epoch(&self, cur_block_height: u64) -> Result { - let stored_params = self.store.load_params(); - let epoch_duration: u64 = stored_params.params.epoch_duration.into(); - let last_updated_epoch = stored_params.last_updated; - - if cur_block_height < last_updated_epoch.block_height_started { - Err(ContractError::BlockHeightInPast.into()) - } else { - let epochs_elapsed = - (cur_block_height - last_updated_epoch.block_height_started) / epoch_duration; - Ok(Epoch { - epoch_num: last_updated_epoch.epoch_num + epochs_elapsed, - block_height_started: last_updated_epoch.block_height_started - + (epochs_elapsed * epoch_duration), // result is strictly less than cur_block_height, so multiplication is safe - }) - } - } +pub(crate) fn record_participation( + storage: &mut dyn Storage, + event_id: nonempty::String, + worker: Addr, + pool_id: PoolId, + block_height: u64, +) -> Result<(), ContractError> { + let current_params = state::load_params(storage); + let cur_epoch = Epoch::current(¤t_params, block_height)?; + + let event = load_or_store_event(storage, event_id, pool_id.clone(), cur_epoch.epoch_num)?; + + state::load_epoch_tally(storage, pool_id.clone(), event.epoch_num)? + .unwrap_or(EpochTally::new(pool_id, cur_epoch, current_params.params)) + .record_participation(worker) + .then(|mut tally| { + if matches!(event, StorageState::New(_)) { + tally.event_count = tally.event_count.saturating_add(1) + } + state::save_epoch_tally(storage, &tally) + }) +} - fn require_governance(&self, sender: Addr) -> Result<(), ContractError> { - if self.config.governance != sender { - return Err(ContractError::Unauthorized.into()); +fn load_or_store_event( + storage: &mut dyn Storage, + event_id: nonempty::String, + pool_id: PoolId, + cur_epoch_num: u64, +) -> Result, ContractError> { + let event = state::load_event(storage, event_id.to_string(), pool_id.clone())?; + + match event { + None => { + let event = Event::new(event_id, pool_id, cur_epoch_num); + state::save_event(storage, &event)?; + Ok(StorageState::New(event)) } - Ok(()) - } - - pub fn record_participation( - &mut self, - event_id: nonempty::String, - worker: Addr, - pool_id: PoolId, - block_height: u64, - ) -> Result<(), ContractError> { - let cur_epoch = self.current_epoch(block_height)?; - - let event = self.load_or_store_event(event_id, pool_id.clone(), cur_epoch.epoch_num)?; - - self.store - .load_epoch_tally(pool_id.clone(), event.epoch_num)? - .unwrap_or(EpochTally::new( - pool_id, - cur_epoch, - self.store.load_params().params, - )) - .record_participation(worker) - .then(|mut tally| { - if matches!(event, StorageState::New(_)) { - tally.event_count += 1 - } - self.store.save_epoch_tally(&tally) - }) + Some(event) => Ok(StorageState::Existing(event)), } +} - fn load_or_store_event( - &mut self, - event_id: nonempty::String, - pool_id: PoolId, - cur_epoch_num: u64, - ) -> Result, ContractError> { - let event = self - .store - .load_event(event_id.to_string(), pool_id.clone())?; - - match event { - None => { - let event = Event::new(event_id, pool_id, cur_epoch_num); - self.store.save_event(&event)?; - Ok(StorageState::New(event)) - } - Some(event) => Ok(StorageState::Existing(event)), - } +pub(crate) fn distribute_rewards( + storage: &mut dyn Storage, + pool_id: PoolId, + cur_block_height: u64, + epoch_process_limit: Option, +) -> Result, ContractError> { + let epoch_process_limit = epoch_process_limit.unwrap_or(DEFAULT_EPOCHS_TO_PROCESS); + let cur_epoch = Epoch::current(&state::load_params(storage), cur_block_height)?; + + let from = state::load_rewards_watermark(storage, pool_id.clone())? + .map_or(0, |last_processed| last_processed.saturating_add(1)); + + let to = std::cmp::min( + (from.saturating_add(epoch_process_limit)).saturating_sub(1), // for process limit =1 "from" and "to" must be equal + cur_epoch.epoch_num.saturating_sub(EPOCH_PAYOUT_DELAY), + ); + + if to < from || cur_epoch.epoch_num < EPOCH_PAYOUT_DELAY { + return Err(ContractError::NoRewardsToDistribute.into()); } - pub fn distribute_rewards( - &mut self, - pool_id: PoolId, - cur_block_height: u64, - epoch_process_limit: Option, - ) -> Result, ContractError> { - let epoch_process_limit = epoch_process_limit.unwrap_or(DEFAULT_EPOCHS_TO_PROCESS); - let cur_epoch = self.current_epoch(cur_block_height)?; - - let from = self - .store - .load_rewards_watermark(pool_id.clone())? - .map_or(0, |last_processed| last_processed + 1); - - let to = std::cmp::min( - (from + epoch_process_limit).saturating_sub(1), // for process limit =1 "from" and "to" must be equal - cur_epoch.epoch_num.saturating_sub(EPOCH_PAYOUT_DELAY), - ); - - if to < from || cur_epoch.epoch_num < EPOCH_PAYOUT_DELAY { - return Err(ContractError::NoRewardsToDistribute.into()); - } + let rewards = process_rewards_for_epochs(storage, pool_id.clone(), from, to)?; + state::save_rewards_watermark(storage, pool_id, to)?; + Ok(rewards) +} - let rewards = self.process_rewards_for_epochs(pool_id.clone(), from, to)?; - self.store.save_rewards_watermark(pool_id, to)?; - Ok(rewards) - } +fn process_rewards_for_epochs( + storage: &mut dyn Storage, + pool_id: PoolId, + from: u64, + to: u64, +) -> Result, ContractError> { + let rewards = cumulate_rewards(storage, &pool_id, from, to)?; + state::load_rewards_pool_or_new(storage, pool_id.clone())? + .sub_reward(rewards.values().sum())? + .then(|pool| state::save_rewards_pool(storage, &pool))?; + + Ok(rewards) +} - fn process_rewards_for_epochs( - &mut self, - pool_id: PoolId, - from: u64, - to: u64, - ) -> Result, ContractError> { - let rewards = self.cumulate_rewards(&pool_id, from, to); - self.store - .load_rewards_pool(pool_id.clone())? - .sub_reward(rewards.values().sum())? - .then(|pool| self.store.save_rewards_pool(&pool))?; - - Ok(rewards) - } +fn cumulate_rewards( + storage: &mut dyn Storage, + pool_id: &PoolId, + from: u64, + to: u64, +) -> Result, ContractError> { + iterate_epoch_tallies(storage, pool_id, from, to) + .map(|tally| tally.rewards_by_worker()) + .try_fold(HashMap::new(), merge_rewards) +} - fn cumulate_rewards(&mut self, pool_id: &PoolId, from: u64, to: u64) -> HashMap { - self.iterate_epoch_tallies(pool_id, from, to) - .map(|tally| tally.rewards_by_worker()) - .fold(HashMap::new(), merge_rewards) - } +fn iterate_epoch_tallies<'a>( + storage: &'a mut dyn Storage, + pool_id: &'a PoolId, + from: u64, + to: u64, +) -> impl Iterator + 'a { + (from..=to).filter_map(|epoch_num| { + state::load_epoch_tally(storage, pool_id.clone(), epoch_num).unwrap_or_default() + }) +} - fn iterate_epoch_tallies<'a>( - &'a mut self, - pool_id: &'a PoolId, - from: u64, - to: u64, - ) -> impl Iterator + 'a { - (from..=to).filter_map(|epoch_num| { - self.store - .load_epoch_tally(pool_id.clone(), epoch_num) - .unwrap_or_default() +pub(crate) fn update_params( + storage: &mut dyn Storage, + new_params: Params, + block_height: u64, + sender: Addr, +) -> Result<(), ContractError> { + require_governance(state::load_config(storage), sender)?; + let cur_epoch = Epoch::current(&state::load_params(storage), block_height)?; + // If the param update reduces the epoch duration such that the current epoch immediately ends, + // start a new epoch at this block, incrementing the current epoch number by 1. + // This prevents us from jumping forward an arbitrary number of epochs, and maintains consistency for past events. + // (i.e. we are in epoch 0, which started at block 0 and epoch duration is 1000. At epoch 500, the params + // are updated to shorten the epoch duration to 100 blocks. We set the epoch number to 1, to prevent skipping + // epochs 1-4, and so all events prior to the start of epoch 1 have an epoch number of 0) + let should_end = cur_epoch + .block_height_started + .checked_add(u64::from(new_params.epoch_duration)) + .ok_or_else(|| { + OverflowError::new( + OverflowOperation::Add, + cur_epoch.block_height_started, + new_params.epoch_duration, + ) }) - } - - pub fn update_params( - &mut self, - new_params: Params, - block_height: u64, - sender: Addr, - ) -> Result<(), ContractError> { - self.require_governance(sender)?; - let cur_epoch = self.current_epoch(block_height)?; - // If the param update reduces the epoch duration such that the current epoch immediately ends, - // start a new epoch at this block, incrementing the current epoch number by 1. - // This prevents us from jumping forward an arbitrary number of epochs, and maintains consistency for past events. - // (i.e. we are in epoch 0, which started at block 0 and epoch duration is 1000. At epoch 500, the params - // are updated to shorten the epoch duration to 100 blocks. We set the epoch number to 1, to prevent skipping - // epochs 1-4, and so all events prior to the start of epoch 1 have an epoch number of 0) - let should_end = - cur_epoch.block_height_started + u64::from(new_params.epoch_duration) < block_height; - let cur_epoch = if should_end { - Epoch { - block_height_started: block_height, - epoch_num: cur_epoch.epoch_num + 1, - } - } else { - cur_epoch - }; - self.store.save_params(&StoredParams { + .map_err(ContractError::from)? + < block_height; + let cur_epoch = if should_end { + Epoch { + block_height_started: block_height, + epoch_num: cur_epoch + .epoch_num + .checked_add(1) + .expect("epoch number should be strictly smaller than the current block height"), + } + } else { + cur_epoch + }; + state::save_params( + storage, + &ParamsSnapshot { params: new_params, - last_updated: cur_epoch, - })?; - Ok(()) - } - - pub fn add_rewards( - &mut self, - pool_id: PoolId, - amount: nonempty::Uint128, - ) -> Result<(), ContractError> { - let mut pool = self.store.load_rewards_pool(pool_id)?; - pool.balance += Uint128::from(amount); - - self.store.save_rewards_pool(&pool)?; + created_at: cur_epoch, + }, + )?; + Ok(()) +} - Ok(()) - } +pub(crate) fn add_rewards( + storage: &mut dyn Storage, + pool_id: PoolId, + amount: nonempty::Uint128, +) -> Result<(), ContractError> { + let mut pool = state::load_rewards_pool_or_new(storage, pool_id)?; + pool.balance = pool + .balance + .checked_add(Uint128::from(amount)) + .map_err(Into::::into) + .map_err(Report::from)?; + + state::save_rewards_pool(storage, &pool)?; + + Ok(()) } /// Merges rewards_2 into rewards_1. For each (address, amount) pair in rewards_2, @@ -231,55 +195,63 @@ where fn merge_rewards( rewards_1: HashMap, rewards_2: HashMap, -) -> HashMap { +) -> Result, ContractError> { rewards_2 .into_iter() - .fold(rewards_1, |mut rewards, (addr, amt)| { - *rewards.entry(addr).or_default() += amt; - rewards + .try_fold(rewards_1, |mut rewards, (addr, amt)| { + let r = rewards + .entry(addr.clone()) + .or_default() + .checked_add(amt) + .map_err(Into::::into) + .map_err(Report::from)?; + + rewards.insert(addr, r); + + Ok(rewards) }) } #[cfg(test)] mod test { - use std::{ - collections::HashMap, - sync::{Arc, RwLock}, - }; + use super::*; + + use std::collections::HashMap; use axelar_wasm_std::nonempty; use connection_router_api::ChainName; - use cosmwasm_std::{Addr, Uint128, Uint64}; + use cosmwasm_std::{ + testing::{mock_dependencies, MockApi, MockQuerier, MockStorage}, + Addr, OwnedDeps, Uint128, Uint64, + }; use crate::{ error::ContractError, msg::Params, state::{ - self, Config, Epoch, EpochTally, Event, PoolId, RewardsPool, Store, StoredParams, - TallyId, + self, Config, Epoch, EpochTally, Event, ParamsSnapshot, PoolId, RewardsPool, CONFIG, }, }; - use super::Contract; - /// Tests that the current epoch is computed correctly when the expected epoch is the same as the stored epoch #[test] fn current_epoch_same_epoch_is_idempotent() { let cur_epoch_num = 1u64; let block_height_started = 250u64; let epoch_duration = 100u64; - let contract = setup(cur_epoch_num, block_height_started, epoch_duration); - let new_epoch = contract.current_epoch(block_height_started).unwrap(); + let (mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); + let current_params = state::load_params(mock_deps.as_ref().storage); + + let new_epoch = Epoch::current(¤t_params, block_height_started).unwrap(); assert_eq!(new_epoch.epoch_num, cur_epoch_num); assert_eq!(new_epoch.block_height_started, block_height_started); - let new_epoch = contract.current_epoch(block_height_started + 1).unwrap(); + let new_epoch = Epoch::current(¤t_params, block_height_started + 1).unwrap(); assert_eq!(new_epoch.epoch_num, cur_epoch_num); assert_eq!(new_epoch.block_height_started, block_height_started); - let new_epoch = contract - .current_epoch(block_height_started + epoch_duration - 1) - .unwrap(); + let new_epoch = + Epoch::current(¤t_params, block_height_started + epoch_duration - 1).unwrap(); assert_eq!(new_epoch.epoch_num, cur_epoch_num); assert_eq!(new_epoch.block_height_started, block_height_started); } @@ -291,11 +263,11 @@ mod test { let cur_epoch_num = 1u64; let block_height_started = 250u64; let epoch_duration = 100u64; - let contract = setup(cur_epoch_num, block_height_started, epoch_duration); - assert!(contract.current_epoch(block_height_started - 1).is_err()); - assert!(contract - .current_epoch(block_height_started - epoch_duration) - .is_err()); + let (mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); + let current_params = state::load_params(mock_deps.as_ref().storage); + + assert!(Epoch::current(¤t_params, block_height_started - 1).is_err()); + assert!(Epoch::current(¤t_params, block_height_started - epoch_duration).is_err()); } /// Tests that the current epoch is computed correctly when the expected epoch is different from the stored epoch @@ -304,7 +276,7 @@ mod test { let cur_epoch_num = 1u64; let block_height_started = 250u64; let epoch_duration = 100u64; - let contract = setup(cur_epoch_num, block_height_started, epoch_duration); + let (mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); // elements are (height, expected epoch number, expected epoch start) let test_cases = vec![ @@ -331,7 +303,8 @@ mod test { ]; for (height, expected_epoch_num, expected_block_start) in test_cases { - let new_epoch = contract.current_epoch(height).unwrap(); + let new_epoch = + Epoch::current(&state::load_params(mock_deps.as_ref().storage), height).unwrap(); assert_eq!(new_epoch.epoch_num, expected_epoch_num); assert_eq!(new_epoch.block_height_started, expected_block_start); @@ -345,7 +318,7 @@ mod test { let epoch_block_start = 250u64; let epoch_duration = 100u64; - let mut contract = setup(cur_epoch_num, epoch_block_start, epoch_duration); + let (mut mock_deps, _) = setup(cur_epoch_num, epoch_block_start, epoch_duration); let pool_id = PoolId { chain_name: "mock-chain".parse().unwrap(), @@ -364,18 +337,21 @@ mod test { // simulates a worker participating in only part_count events if i < *part_count { let event_id = i.to_string().try_into().unwrap(); - contract - .record_participation(event_id, worker.clone(), pool_id.clone(), cur_height) - .unwrap(); + record_participation( + mock_deps.as_mut().storage, + event_id, + worker.clone(), + pool_id.clone(), + cur_height, + ) + .unwrap(); } } - cur_height = cur_height + 1; + cur_height += 1; } - let tally = contract - .store - .load_epoch_tally(pool_id, cur_epoch_num) - .unwrap(); + let tally = + state::load_epoch_tally(mock_deps.as_ref().storage, pool_id, cur_epoch_num).unwrap(); assert!(tally.is_some()); let tally = tally.unwrap(); @@ -396,7 +372,7 @@ mod test { let block_height_started = 250u64; let epoch_duration = 100u64; - let mut contract = setup(starting_epoch_num, block_height_started, epoch_duration); + let (mut mock_deps, _) = setup(starting_epoch_num, block_height_started, epoch_duration); let pool_id = PoolId { chain_name: "mock-chain".parse().unwrap(), @@ -412,23 +388,29 @@ mod test { let height_at_epoch_end = block_height_started + epoch_duration - 1; // workers participate in consecutive blocks for (i, workers) in workers.iter().enumerate() { - contract - .record_participation( - "some event".to_string().try_into().unwrap(), - workers.clone(), - pool_id.clone(), - height_at_epoch_end + i as u64, - ) - .unwrap(); + record_participation( + mock_deps.as_mut().storage, + "some event".to_string().try_into().unwrap(), + workers.clone(), + pool_id.clone(), + height_at_epoch_end + i as u64, + ) + .unwrap(); } - let cur_epoch = contract.current_epoch(height_at_epoch_end).unwrap(); + let cur_epoch = Epoch::current( + &state::load_params(mock_deps.as_ref().storage), + height_at_epoch_end, + ) + .unwrap(); assert_ne!(starting_epoch_num + 1, cur_epoch.epoch_num); - let tally = contract - .store - .load_epoch_tally(pool_id.clone(), starting_epoch_num) - .unwrap(); + let tally = state::load_epoch_tally( + mock_deps.as_ref().storage, + pool_id.clone(), + starting_epoch_num, + ) + .unwrap(); assert!(tally.is_some()); let tally = tally.unwrap(); @@ -439,10 +421,9 @@ mod test { assert_eq!(tally.participation.get(&w.to_string()), Some(&1)); } - let tally = contract - .store - .load_epoch_tally(pool_id, starting_epoch_num + 1) - .unwrap(); + let tally = + state::load_epoch_tally(mock_deps.as_ref().storage, pool_id, starting_epoch_num + 1) + .unwrap(); assert!(tally.is_none()); } @@ -453,7 +434,7 @@ mod test { let block_height_started = 250u64; let epoch_duration = 100u64; - let mut contract = setup(cur_epoch_num, block_height_started, epoch_duration); + let (mut mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); let mut simulated_participation = HashMap::new(); simulated_participation.insert( @@ -490,21 +471,23 @@ mod test { for (worker, (worker_contract, events_participated)) in &simulated_participation { for i in 0..*events_participated { let event_id = i.to_string().try_into().unwrap(); - contract - .record_participation( - event_id, - worker.clone(), - worker_contract.clone(), - block_height_started, - ) - .unwrap(); + record_participation( + mock_deps.as_mut().storage, + event_id, + worker.clone(), + worker_contract.clone(), + block_height_started, + ) + .unwrap(); } } for (worker, (worker_contract, events_participated)) in simulated_participation { - let tally = contract - .store - .load_epoch_tally(worker_contract.clone(), cur_epoch_num) - .unwrap(); + let tally = state::load_epoch_tally( + mock_deps.as_ref().storage, + worker_contract.clone(), + cur_epoch_num, + ) + .unwrap(); assert!(tally.is_some()); let tally = tally.unwrap(); @@ -520,13 +503,13 @@ mod test { /// Test that rewards parameters are updated correctly. In this test we don't change the epoch duration, so /// that computation of the current epoch is unaffected. #[test] - fn update_params() { + fn successfully_update_params() { let initial_epoch_num = 1u64; let initial_epoch_start = 250u64; let initial_rewards_per_epoch = 100u128; let initial_participation_threshold = (1, 2); let epoch_duration = 100u64; - let mut contract = setup_with_params( + let (mut mock_deps, config) = setup_with_params( initial_epoch_num, initial_epoch_start, epoch_duration, @@ -546,20 +529,22 @@ mod test { }; // the epoch shouldn't change when the params are updated, since we are not changing the epoch duration - let expected_epoch = contract.current_epoch(cur_height).unwrap(); + let expected_epoch = + Epoch::current(&state::load_params(mock_deps.as_ref().storage), cur_height).unwrap(); - contract - .update_params( - new_params.clone(), - cur_height, - contract.config.governance.clone(), - ) - .unwrap(); - let stored = contract.store.load_params(); + update_params( + mock_deps.as_mut().storage, + new_params.clone(), + cur_height, + config.governance.clone(), + ) + .unwrap(); + let stored = state::load_params(mock_deps.as_ref().storage); assert_eq!(stored.params, new_params); // current epoch shouldn't have changed - let cur_epoch = contract.current_epoch(cur_height).unwrap(); + let cur_epoch = + Epoch::current(&state::load_params(mock_deps.as_ref().storage), cur_height).unwrap(); assert_eq!(expected_epoch.epoch_num, cur_epoch.epoch_num); assert_eq!( expected_epoch.block_height_started, @@ -567,7 +552,7 @@ mod test { ); // last updated should be the current epoch - assert_eq!(stored.last_updated, cur_epoch); + assert_eq!(stored.created_at, cur_epoch); } /// Test that rewards parameters cannot be updated by an address other than governance @@ -576,7 +561,7 @@ mod test { let initial_epoch_num = 1u64; let initial_epoch_start = 250u64; let epoch_duration = 100u64; - let mut contract = setup(initial_epoch_num, initial_epoch_start, epoch_duration); + let (mut mock_deps, _) = setup(initial_epoch_num, initial_epoch_start, epoch_duration); let new_params = Params { rewards_per_epoch: cosmwasm_std::Uint128::from(100u128).try_into().unwrap(), @@ -584,7 +569,8 @@ mod test { epoch_duration: epoch_duration.try_into().unwrap(), }; - let res = contract.update_params( + let res = update_params( + mock_deps.as_mut().storage, new_params.clone(), initial_epoch_start, Addr::unchecked("some non governance address"), @@ -602,7 +588,7 @@ mod test { let initial_epoch_num = 1u64; let initial_epoch_start = 250u64; let initial_epoch_duration = 100u64; - let mut contract = setup( + let (mut mock_deps, config) = setup( initial_epoch_num, initial_epoch_start, initial_epoch_duration, @@ -613,36 +599,40 @@ mod test { let cur_height = initial_epoch_start + initial_epoch_duration * epochs_elapsed + 10; // add 10 here just to be a little past the epoch boundary // epoch shouldn't change if we are extending the duration - let epoch_prior_to_update = contract.current_epoch(cur_height).unwrap(); + let initial_params_snapshot = state::load_params(mock_deps.as_ref().storage); + let epoch_prior_to_update = Epoch::current(&initial_params_snapshot, cur_height).unwrap(); let new_epoch_duration = initial_epoch_duration * 2; let new_params = Params { epoch_duration: (new_epoch_duration).try_into().unwrap(), - ..contract.store.load_params().params // keep everything besides epoch duration the same + ..initial_params_snapshot.params // keep everything besides epoch duration the same }; - contract - .update_params( - new_params.clone(), - cur_height, - contract.config.governance.clone(), - ) - .unwrap(); + update_params( + mock_deps.as_mut().storage, + new_params.clone(), + cur_height, + config.governance.clone(), + ) + .unwrap(); + + let updated_params_snapshot = state::load_params(mock_deps.as_ref().storage); // current epoch shouldn't change - let epoch = contract.current_epoch(cur_height).unwrap(); + let epoch = Epoch::current(&updated_params_snapshot, cur_height).unwrap(); assert_eq!(epoch, epoch_prior_to_update); // we increased the epoch duration, so adding the initial epoch duration should leave us in the same epoch - let epoch = contract - .current_epoch(cur_height + initial_epoch_duration) - .unwrap(); + let epoch = Epoch::current( + &updated_params_snapshot, + cur_height + initial_epoch_duration, + ) + .unwrap(); assert_eq!(epoch, epoch_prior_to_update); // check that we can correctly compute the start of the next epoch - let next_epoch = contract - .current_epoch(cur_height + new_epoch_duration) - .unwrap(); + let next_epoch = + Epoch::current(&updated_params_snapshot, cur_height + new_epoch_duration).unwrap(); assert_eq!(next_epoch.epoch_num, epoch_prior_to_update.epoch_num + 1); assert_eq!( next_epoch.block_height_started, @@ -657,7 +647,7 @@ mod test { let initial_epoch_num = 1u64; let initial_epoch_start = 256u64; let initial_epoch_duration = 100u64; - let mut contract = setup( + let (mut mock_deps, config) = setup( initial_epoch_num, initial_epoch_start, initial_epoch_duration, @@ -668,30 +658,33 @@ mod test { let cur_height = initial_epoch_start + initial_epoch_duration * epochs_elapsed; let new_epoch_duration = initial_epoch_duration / 2; - let epoch_prior_to_update = contract.current_epoch(cur_height).unwrap(); + + let initial_params_snapshot = state::load_params(mock_deps.as_ref().storage); + let epoch_prior_to_update = Epoch::current(&initial_params_snapshot, cur_height).unwrap(); // we are shortening the epoch, but not so much it causes the epoch number to change. We want to remain in the same epoch assert!(cur_height - epoch_prior_to_update.block_height_started < new_epoch_duration); let new_params = Params { epoch_duration: new_epoch_duration.try_into().unwrap(), - ..contract.store.load_params().params + ..initial_params_snapshot.params }; - contract - .update_params( - new_params.clone(), - cur_height, - contract.config.governance.clone(), - ) - .unwrap(); + update_params( + mock_deps.as_mut().storage, + new_params.clone(), + cur_height, + config.governance.clone(), + ) + .unwrap(); + + let updated_params_snapshot = state::load_params(mock_deps.as_ref().storage); // current epoch shouldn't have changed - let epoch = contract.current_epoch(cur_height).unwrap(); + let epoch = Epoch::current(&updated_params_snapshot, cur_height).unwrap(); assert_eq!(epoch_prior_to_update, epoch); // adding the new epoch duration should increase the epoch number by 1 - let epoch = contract - .current_epoch(cur_height + new_epoch_duration) - .unwrap(); + let epoch = + Epoch::current(&updated_params_snapshot, cur_height + new_epoch_duration).unwrap(); assert_eq!(epoch.epoch_num, epoch_prior_to_update.epoch_num + 1); assert_eq!( epoch.block_height_started, @@ -706,7 +699,7 @@ mod test { let initial_epoch_num = 1u64; let initial_epoch_start = 250u64; let initial_epoch_duration = 100u64; - let mut contract = setup( + let (mut mock_deps, config) = setup( initial_epoch_num, initial_epoch_start, initial_epoch_duration, @@ -719,29 +712,32 @@ mod test { // simulate progressing far enough into the epoch such that shortening the epoch duration would change the epoch let cur_height = initial_epoch_start + initial_epoch_duration * epochs_elapsed + new_epoch_duration * 2; - let epoch_prior_to_update = contract.current_epoch(cur_height).unwrap(); + + let initial_params_snapshot = state::load_params(mock_deps.as_ref().storage); + let epoch_prior_to_update = Epoch::current(&initial_params_snapshot, cur_height).unwrap(); let new_params = Params { epoch_duration: 10.try_into().unwrap(), - ..contract.store.load_params().params + ..initial_params_snapshot.params }; - contract - .update_params( - new_params.clone(), - cur_height, - contract.config.governance.clone(), - ) - .unwrap(); + update_params( + mock_deps.as_mut().storage, + new_params.clone(), + cur_height, + config.governance.clone(), + ) + .unwrap(); + + let updated_params_snapshot = state::load_params(mock_deps.as_ref().storage); // should be in new epoch now - let epoch = contract.current_epoch(cur_height).unwrap(); + let epoch = Epoch::current(&updated_params_snapshot, cur_height).unwrap(); assert_eq!(epoch.epoch_num, epoch_prior_to_update.epoch_num + 1); assert_eq!(epoch.block_height_started, cur_height); // moving forward the new epoch duration # of blocks should increment the epoch - let epoch = contract - .current_epoch(cur_height + new_epoch_duration) - .unwrap(); + let epoch = + Epoch::current(&updated_params_snapshot, cur_height + new_epoch_duration).unwrap(); assert_eq!(epoch.epoch_num, epoch_prior_to_update.epoch_num + 2); assert_eq!(epoch.block_height_started, cur_height + new_epoch_duration); } @@ -753,29 +749,37 @@ mod test { let block_height_started = 250u64; let epoch_duration = 100u64; - let mut contract = setup(cur_epoch_num, block_height_started, epoch_duration); + let (mut mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); let pool_id = PoolId { chain_name: "mock-chain".parse().unwrap(), contract: Addr::unchecked("some contract"), }; - let pool = contract.store.load_rewards_pool(pool_id.clone()).unwrap(); + let pool = + state::load_rewards_pool_or_new(mock_deps.as_ref().storage, pool_id.clone()).unwrap(); assert!(pool.balance.is_zero()); let initial_amount = Uint128::from(100u128); - contract - .add_rewards(pool_id.clone(), initial_amount.try_into().unwrap()) - .unwrap(); + add_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + initial_amount.try_into().unwrap(), + ) + .unwrap(); - let pool = contract.store.load_rewards_pool(pool_id.clone()).unwrap(); + let pool = + state::load_rewards_pool_or_new(mock_deps.as_ref().storage, pool_id.clone()).unwrap(); assert_eq!(pool.balance, initial_amount); let added_amount = Uint128::from(500u128); - contract - .add_rewards(pool_id.clone(), added_amount.try_into().unwrap()) - .unwrap(); + add_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + added_amount.try_into().unwrap(), + ) + .unwrap(); - let pool = contract.store.load_rewards_pool(pool_id).unwrap(); + let pool = state::load_rewards_pool_or_new(mock_deps.as_ref().storage, pool_id).unwrap(); assert_eq!(pool.balance, initial_amount + added_amount); } @@ -786,7 +790,7 @@ mod test { let block_height_started = 250u64; let epoch_duration = 100u64; - let mut contract = setup(cur_epoch_num, block_height_started, epoch_duration); + let (mut mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); // a vector of (worker contract, rewards amounts) pairs let test_data = vec![ (Addr::unchecked("contract_1"), vec![100, 200, 50]), @@ -803,12 +807,12 @@ mod test { }; for amount in rewards { - contract - .add_rewards( - pool_id.clone(), - cosmwasm_std::Uint128::from(*amount).try_into().unwrap(), - ) - .unwrap(); + add_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + cosmwasm_std::Uint128::from(*amount).try_into().unwrap(), + ) + .unwrap(); } } @@ -818,7 +822,8 @@ mod test { contract: worker_contract.clone(), }; - let pool = contract.store.load_rewards_pool(pool_id).unwrap(); + let pool = + state::load_rewards_pool_or_new(mock_deps.as_ref().storage, pool_id).unwrap(); assert_eq!( pool.balance, cosmwasm_std::Uint128::from(rewards.iter().sum::()) @@ -828,14 +833,14 @@ mod test { /// Tests that rewards are distributed correctly based on participation #[test] - fn distribute_rewards() { + fn successfully_distribute_rewards() { let cur_epoch_num = 0u64; let block_height_started = 0u64; let epoch_duration = 1000u64; let rewards_per_epoch = 100u128; let participation_threshold = (2, 3); - let mut contract = setup_with_params( + let (mut mock_deps, _) = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, @@ -882,10 +887,11 @@ mod test { }; for (worker, events_participated) in worker_participation_per_epoch.clone() { - for epoch in 0..epoch_count { - for event in &events_participated[epoch] { + for (epoch, events) in events_participated.iter().enumerate().take(epoch_count) { + for event in events { let event_id = event.to_string() + &epoch.to_string() + "event"; - let _ = contract.record_participation( + let _ = record_participation( + mock_deps.as_mut().storage, event_id.clone().try_into().unwrap(), worker.clone(), pool_id.clone(), @@ -898,18 +904,19 @@ mod test { // we add 2 epochs worth of rewards. There were 2 epochs of participation, but only 2 epochs where rewards should be given out // These tests we are accounting correctly, and only removing from the pool when we actually give out rewards let rewards_added = 2 * rewards_per_epoch; - let _ = contract.add_rewards( + let _ = add_rewards( + mock_deps.as_mut().storage, pool_id.clone(), Uint128::from(rewards_added).try_into().unwrap(), ); - let rewards_claimed = contract - .distribute_rewards( - pool_id, - block_height_started + epoch_duration * (epoch_count + 2) as u64, - None, - ) - .unwrap(); + let rewards_claimed = distribute_rewards( + mock_deps.as_mut().storage, + pool_id, + block_height_started + epoch_duration * (epoch_count + 2) as u64, + None, + ) + .unwrap(); assert_eq!(rewards_claimed.len(), worker_participation_per_epoch.len()); for (worker, rewards) in expected_rewards_per_worker { @@ -927,7 +934,7 @@ mod test { let rewards_per_epoch = 100u128; let participation_threshold = (1, 2); - let mut contract = setup_with_params( + let (mut mock_deps, _) = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, @@ -942,7 +949,8 @@ mod test { for height in block_height_started..block_height_started + epoch_duration * 9 { let event_id = height.to_string() + "event"; - let _ = contract.record_participation( + let _ = record_participation( + mock_deps.as_mut().storage, event_id.try_into().unwrap(), worker.clone(), pool_id.clone(), @@ -951,7 +959,8 @@ mod test { } let rewards_added = 1000u128; - let _ = contract.add_rewards( + let _ = add_rewards( + mock_deps.as_mut().storage, pool_id.clone(), Uint128::from(rewards_added).try_into().unwrap(), ); @@ -962,9 +971,13 @@ mod test { // distribute 5 epochs worth of rewards let epochs_to_process = 5; - let rewards_claimed = contract - .distribute_rewards(pool_id.clone(), cur_height, Some(epochs_to_process)) - .unwrap(); + let rewards_claimed = distribute_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + cur_height, + Some(epochs_to_process), + ) + .unwrap(); assert_eq!(rewards_claimed.len(), 1); assert!(rewards_claimed.contains_key(&worker)); assert_eq!( @@ -973,9 +986,13 @@ mod test { ); // distribute the remaining epochs worth of rewards - let rewards_claimed = contract - .distribute_rewards(pool_id.clone(), cur_height, None) - .unwrap(); + let rewards_claimed = distribute_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + cur_height, + None, + ) + .unwrap(); assert_eq!(rewards_claimed.len(), 1); assert!(rewards_claimed.contains_key(&worker)); assert_eq!( @@ -996,7 +1013,7 @@ mod test { let rewards_per_epoch = 100u128; let participation_threshold = (8, 10); - let mut contract = setup_with_params( + let (mut mock_deps, _) = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, @@ -1009,7 +1026,8 @@ mod test { contract: Addr::unchecked("worker_contract"), }; - let _ = contract.record_participation( + let _ = record_participation( + mock_deps.as_mut().storage, "event".try_into().unwrap(), worker.clone(), pool_id.clone(), @@ -1017,37 +1035,50 @@ mod test { ); let rewards_added = 1000u128; - let _ = contract.add_rewards( + let _ = add_rewards( + mock_deps.as_mut().storage, pool_id.clone(), Uint128::from(rewards_added).try_into().unwrap(), ); // too early, still in the same epoch - let err = contract - .distribute_rewards(pool_id.clone(), block_height_started, None) - .unwrap_err(); + let err = distribute_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + block_height_started, + None, + ) + .unwrap_err(); assert_eq!(err.current_context(), &ContractError::NoRewardsToDistribute); // next epoch, but still too early to claim rewards - let err = contract - .distribute_rewards(pool_id.clone(), block_height_started + epoch_duration, None) - .unwrap_err(); + let err = distribute_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + block_height_started + epoch_duration, + None, + ) + .unwrap_err(); assert_eq!(err.current_context(), &ContractError::NoRewardsToDistribute); // can claim now, two epochs after participation - let rewards_claimed = contract - .distribute_rewards( - pool_id.clone(), - block_height_started + epoch_duration * 2, - None, - ) - .unwrap(); + let rewards_claimed = distribute_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + block_height_started + epoch_duration * 2, + None, + ) + .unwrap(); assert_eq!(rewards_claimed.len(), 1); // should error if we try again - let err = contract - .distribute_rewards(pool_id, block_height_started + epoch_duration * 2, None) - .unwrap_err(); + let err = distribute_rewards( + mock_deps.as_mut().storage, + pool_id, + block_height_started + epoch_duration * 2, + None, + ) + .unwrap_err(); assert_eq!(err.current_context(), &ContractError::NoRewardsToDistribute); } @@ -1061,7 +1092,7 @@ mod test { let rewards_per_epoch = 100u128; let participation_threshold = (8, 10); - let mut contract = setup_with_params( + let (mut mock_deps, _) = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, @@ -1074,7 +1105,8 @@ mod test { contract: Addr::unchecked("worker_contract"), }; - let _ = contract.record_participation( + let _ = record_participation( + mock_deps.as_mut().storage, "event".try_into().unwrap(), worker.clone(), pool_id.clone(), @@ -1083,31 +1115,37 @@ mod test { // rewards per epoch is 100, we only add 10 let rewards_added = 10u128; - let _ = contract.add_rewards( + let _ = add_rewards( + mock_deps.as_mut().storage, pool_id.clone(), Uint128::from(rewards_added).try_into().unwrap(), ); - let err = contract - .distribute_rewards( - pool_id.clone(), - block_height_started + epoch_duration * 2, - None, - ) - .unwrap_err(); + let err = distribute_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + block_height_started + epoch_duration * 2, + None, + ) + .unwrap_err(); assert_eq!( err.current_context(), &ContractError::PoolBalanceInsufficient ); // add some more rewards let rewards_added = 90u128; - let _ = contract.add_rewards( + let _ = add_rewards( + mock_deps.as_mut().storage, pool_id.clone(), Uint128::from(rewards_added).try_into().unwrap(), ); - let result = - contract.distribute_rewards(pool_id, block_height_started + epoch_duration * 2, None); + let result = distribute_rewards( + mock_deps.as_mut().storage, + pool_id, + block_height_started + epoch_duration * 2, + None, + ); assert!(result.is_ok()); assert_eq!(result.unwrap().len(), 1); } @@ -1121,7 +1159,7 @@ mod test { let rewards_per_epoch = 100u128; let participation_threshold = (8, 10); - let mut contract = setup_with_params( + let (mut mock_deps, _) = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, @@ -1134,7 +1172,8 @@ mod test { contract: Addr::unchecked("worker_contract"), }; - let _ = contract.record_participation( + let _ = record_participation( + mock_deps.as_mut().storage, "event".try_into().unwrap(), worker.clone(), pool_id.clone(), @@ -1142,123 +1181,82 @@ mod test { ); let rewards_added = 1000u128; - let _ = contract.add_rewards( + let _ = add_rewards( + mock_deps.as_mut().storage, pool_id.clone(), Uint128::from(rewards_added).try_into().unwrap(), ); - let rewards_claimed = contract - .distribute_rewards( - pool_id.clone(), - block_height_started + epoch_duration * 2, - None, - ) - .unwrap(); + let rewards_claimed = distribute_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + block_height_started + epoch_duration * 2, + None, + ) + .unwrap(); assert_eq!(rewards_claimed.len(), 1); // try to claim again, shouldn't get an error - let err = contract - .distribute_rewards(pool_id, block_height_started + epoch_duration * 2, None) - .unwrap_err(); + let err = distribute_rewards( + mock_deps.as_mut().storage, + pool_id, + block_height_started + epoch_duration * 2, + None, + ) + .unwrap_err(); assert_eq!(err.current_context(), &ContractError::NoRewardsToDistribute); } - fn create_contract( - params_store: Arc>, - events_store: Arc>>, - tally_store: Arc>>, - rewards_store: Arc>>, - watermark_store: Arc>>, - ) -> Contract { - let mut store = state::MockStore::new(); - let params_store_cloned = params_store.clone(); - store - .expect_load_params() - .returning(move || params_store_cloned.read().unwrap().clone()); - store.expect_save_params().returning(move |new_params| { - let mut params_store = params_store.write().unwrap(); - *params_store = new_params.clone(); - Ok(()) - }); - let events_store_cloned = events_store.clone(); - store.expect_load_event().returning(move |id, pool_id| { - let events_store = events_store_cloned.read().unwrap(); - Ok(events_store.get(&(id, pool_id)).cloned()) - }); - store.expect_save_event().returning(move |event| { - let mut events_store = events_store.write().unwrap(); - events_store.insert( - (event.event_id.clone().into(), event.pool_id.clone()), - event.clone(), - ); - Ok(()) - }); - let tally_store_cloned = tally_store.clone(); - store - .expect_load_epoch_tally() - .returning(move |pool_id, epoch_num| { - let tally_store = tally_store_cloned.read().unwrap(); - let tally_id = TallyId { - pool_id: pool_id.clone(), - epoch_num, - }; - Ok(tally_store.get(&tally_id).cloned()) - }); - store.expect_save_epoch_tally().returning(move |tally| { - let mut tally_store = tally_store.write().unwrap(); - let tally_id = TallyId { - pool_id: tally.pool_id.clone(), - epoch_num: tally.epoch.epoch_num, - }; - tally_store.insert(tally_id, tally.clone()); - Ok(()) + type MockDeps = OwnedDeps; + + fn set_initial_storage( + params_store: ParamsSnapshot, + events_store: Vec, + tally_store: Vec, + rewards_store: Vec, + watermark_store: HashMap, + ) -> (MockDeps, Config) { + let mut deps = mock_dependencies(); + let storage = deps.as_mut().storage; + + state::save_params(storage, ¶ms_store).unwrap(); + + events_store.iter().for_each(|event| { + state::save_event(storage, event).unwrap(); }); - let rewards_store_cloned = rewards_store.clone(); - store.expect_load_rewards_pool().returning(move |pool_id| { - let rewards_store = rewards_store_cloned.read().unwrap(); - Ok(rewards_store.get(&pool_id).cloned().unwrap_or(RewardsPool { - id: pool_id, - balance: Uint128::zero(), - })) + tally_store.iter().for_each(|tally| { + state::save_epoch_tally(storage, tally).unwrap(); }); - store.expect_save_rewards_pool().returning(move |pool| { - let mut rewards_store = rewards_store.write().unwrap(); - rewards_store.insert(pool.id.clone(), pool.clone()); - Ok(()) + + rewards_store.iter().for_each(|pool| { + state::save_rewards_pool(storage, pool).unwrap(); }); - let watermark_store_cloned = watermark_store.clone(); - store - .expect_load_rewards_watermark() - .returning(move |contract| { - let watermark_store = watermark_store_cloned.read().unwrap(); - Ok(watermark_store.get(&contract).cloned()) - }); - store - .expect_save_rewards_watermark() - .returning(move |pool_id, epoch_num| { - let mut watermark_store = watermark_store.write().unwrap(); - watermark_store.insert(pool_id, epoch_num); - Ok(()) + watermark_store + .into_iter() + .for_each(|(pool_id, epoch_num)| { + state::save_rewards_watermark(storage, pool_id, epoch_num).unwrap(); }); - Contract { - store, - config: Config { - governance: Addr::unchecked("governance"), - rewards_denom: "AXL".to_string(), - }, - } + + let config = Config { + governance: Addr::unchecked("governance"), + rewards_denom: "AXL".to_string(), + }; + + CONFIG.save(storage, &config).unwrap(); + + (deps, config) } fn setup_with_stores( - params_store: Arc>, - events_store: Arc>>, - tally_store: Arc>>, - rewards_store: Arc>>, - watermark_store: Arc>>, - ) -> Contract { - create_contract( + params_store: ParamsSnapshot, + events_store: Vec, + tally_store: Vec, + rewards_store: Vec, + watermark_store: HashMap, + ) -> (MockDeps, Config) { + set_initial_storage( params_store, events_store, tally_store, @@ -1273,7 +1271,7 @@ mod test { epoch_duration: u64, rewards_per_epoch: u128, participation_threshold: (u64, u64), - ) -> Contract { + ) -> (MockDeps, Config) { let rewards_per_epoch: nonempty::Uint128 = cosmwasm_std::Uint128::from(rewards_per_epoch) .try_into() .unwrap(); @@ -1282,21 +1280,21 @@ mod test { block_height_started, }; - let stored_params = StoredParams { + let params_snapshot = ParamsSnapshot { params: Params { participation_threshold: participation_threshold.try_into().unwrap(), epoch_duration: epoch_duration.try_into().unwrap(), rewards_per_epoch, }, - last_updated: current_epoch.clone(), + created_at: current_epoch.clone(), }; - let stored_params = Arc::new(RwLock::new(stored_params)); - let rewards_store = Arc::new(RwLock::new(HashMap::new())); - let events_store = Arc::new(RwLock::new(HashMap::new())); - let tally_store = Arc::new(RwLock::new(HashMap::new())); - let watermark_store = Arc::new(RwLock::new(HashMap::new())); + + let rewards_store = Vec::new(); + let events_store = Vec::new(); + let tally_store = Vec::new(); + let watermark_store = HashMap::new(); setup_with_stores( - stored_params, + params_snapshot, events_store, tally_store, rewards_store, @@ -1308,7 +1306,7 @@ mod test { cur_epoch_num: u64, block_height_started: u64, epoch_duration: u64, - ) -> Contract { + ) -> (MockDeps, Config) { let participation_threshold = (1, 2); let rewards_per_epoch = 100u128; setup_with_params( diff --git a/contracts/rewards/src/contract/query.rs b/contracts/rewards/src/contract/query.rs new file mode 100644 index 000000000..2a01fdc15 --- /dev/null +++ b/contracts/rewards/src/contract/query.rs @@ -0,0 +1,237 @@ +use cosmwasm_std::{Storage, Uint64}; +use error_stack::Result; + +use crate::{ + error::ContractError, + msg, + state::{self, Epoch, PoolId}, +}; + +pub fn rewards_pool( + storage: &dyn Storage, + pool_id: PoolId, + block_height: u64, +) -> Result { + let pool = state::load_rewards_pool(storage, pool_id.clone())?; + let current_params = state::load_params(storage); + let cur_epoch = Epoch::current(¤t_params, block_height)?; + + // the params could have been updated since the tally was created. Therefore we use the params from the + // active tally if it exists, otherwise we use the latest stored params. + let params = match state::load_epoch_tally(storage, pool_id.clone(), cur_epoch.epoch_num)? { + Some(epoch_tally) => epoch_tally.params, + None => current_params.params, + }; + + let last_distribution_epoch = + state::load_rewards_watermark(storage, pool_id)?.map(Uint64::from); + + Ok(msg::RewardsPool { + balance: pool.balance, + epoch_duration: params.epoch_duration.into(), + rewards_per_epoch: params.rewards_per_epoch.into(), + current_epoch_num: cur_epoch.epoch_num.into(), + last_distribution_epoch, + }) +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::{testing::mock_dependencies, Addr, Uint128, Uint64}; + + use crate::{ + msg::Params, + state::{EpochTally, ParamsSnapshot, RewardsPool}, + }; + + use super::*; + + fn setup(storage: &mut dyn Storage, initial_balance: Uint128) -> (ParamsSnapshot, PoolId) { + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract"), + }; + + let epoch = Epoch { + epoch_num: 0, + block_height_started: 0, + }; + + let params = Params { + epoch_duration: Uint64::from(100u64).try_into().unwrap(), + rewards_per_epoch: Uint128::from(1000u128).try_into().unwrap(), + participation_threshold: (1, 2).try_into().unwrap(), + }; + let params_snapshot = ParamsSnapshot { + params: params.clone(), + created_at: epoch.clone(), + }; + let rewards_pool = RewardsPool { + id: pool_id.clone(), + balance: initial_balance, + }; + + state::save_params(storage, ¶ms_snapshot).unwrap(); + state::save_rewards_pool(storage, &rewards_pool).unwrap(); + + (params_snapshot, pool_id) + } + + // Should get rewards pool details, when no tally is found then details are loaded from the stored params + #[test] + fn should_get_rewards_pool_with_no_tally() { + let mut deps = mock_dependencies(); + let balance = Uint128::from(1000u128); + let (current_params, pool_id) = setup(deps.as_mut().storage, balance); + + let block_height = 1000; + + let res = rewards_pool(deps.as_mut().storage, pool_id.clone(), block_height).unwrap(); + assert_eq!( + res, + msg::RewardsPool { + balance, + epoch_duration: current_params.params.epoch_duration.into(), + rewards_per_epoch: current_params.params.rewards_per_epoch.into(), + current_epoch_num: Epoch::current(¤t_params, block_height) + .unwrap() + .epoch_num + .into(), + last_distribution_epoch: None, + } + ); + } + + // Should get rewards pool details with watermark + #[test] + fn should_get_rewards_pool_with_watermark() { + let mut deps = mock_dependencies(); + let balance = Uint128::from(1000u128); + let (current_params, pool_id) = setup(deps.as_mut().storage, balance); + + let block_height = 1000; + let last_distribution_epoch = 5u64; + + state::save_rewards_watermark( + deps.as_mut().storage, + pool_id.clone(), + last_distribution_epoch, + ) + .unwrap(); + + let res = rewards_pool(deps.as_mut().storage, pool_id.clone(), block_height).unwrap(); + assert_eq!( + res, + msg::RewardsPool { + balance, + epoch_duration: current_params.params.epoch_duration.into(), + rewards_per_epoch: current_params.params.rewards_per_epoch.into(), + current_epoch_num: Epoch::current(¤t_params, block_height) + .unwrap() + .epoch_num + .into(), + last_distribution_epoch: Some(last_distribution_epoch.into()), + } + ); + } + + // Should get rewards pool details, if there is no tally for current epoch, then details are loaded from the stored params + // ignoring previous epoch tallies + #[test] + fn should_get_rewards_pool_ignoring_old_tallies_details() { + let mut deps = mock_dependencies(); + let balance = Uint128::from(1000u128); + let (current_params, pool_id) = setup(deps.as_mut().storage, balance); + + let old_block_height = 0; + + let tally_params = Params { + epoch_duration: Uint64::from(200u64).try_into().unwrap(), + rewards_per_epoch: Uint128::from(2000u128).try_into().unwrap(), + participation_threshold: (2, 3).try_into().unwrap(), + }; + + state::save_epoch_tally( + deps.as_mut().storage, + &EpochTally::new( + pool_id.clone(), + Epoch::current(¤t_params, old_block_height).unwrap(), + tally_params.clone(), + ), + ) + .unwrap(); + + let cur_block_height = 1000; + let res = rewards_pool(deps.as_mut().storage, pool_id.clone(), cur_block_height).unwrap(); + assert_eq!( + res, + msg::RewardsPool { + balance, + epoch_duration: current_params.params.epoch_duration.into(), + rewards_per_epoch: current_params.params.rewards_per_epoch.into(), + current_epoch_num: Epoch::current(¤t_params, cur_block_height) + .unwrap() + .epoch_num + .into(), + last_distribution_epoch: None, + } + ); + } + + // Should get rewards pool details, if there is a tally for current epoch, then details are loaded from the tally + #[test] + fn should_get_rewards_pool_with_tally_for_current_epoch() { + let mut deps = mock_dependencies(); + let balance = Uint128::from(1000u128); + let (current_params, pool_id) = setup(deps.as_mut().storage, balance); + + let block_height = 1000; + + let tally_params = Params { + epoch_duration: Uint64::from(200u64).try_into().unwrap(), + rewards_per_epoch: Uint128::from(2000u128).try_into().unwrap(), + participation_threshold: (2, 3).try_into().unwrap(), + }; + + state::save_epoch_tally( + deps.as_mut().storage, + &EpochTally::new( + pool_id.clone(), + Epoch::current(¤t_params, block_height).unwrap(), + tally_params.clone(), + ), + ) + .unwrap(); + + let res = rewards_pool(deps.as_mut().storage, pool_id.clone(), block_height).unwrap(); + assert_eq!( + res, + msg::RewardsPool { + balance, + epoch_duration: tally_params.epoch_duration.into(), + rewards_per_epoch: tally_params.rewards_per_epoch.into(), + current_epoch_num: Epoch::current(¤t_params, block_height) + .unwrap() + .epoch_num + .into(), + last_distribution_epoch: None, + } + ); + } + + #[test] + fn should_fail_when_pool_not_found() { + let mut deps = mock_dependencies(); + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract"), + }; + let block_height = 1000; + + let res = rewards_pool(deps.as_mut().storage, pool_id.clone(), block_height); + assert_eq!( + res.unwrap_err().current_context(), + &ContractError::RewardsPoolNotFound + ); + } +} diff --git a/contracts/rewards/src/error.rs b/contracts/rewards/src/error.rs index 862eb2be3..15a51ef38 100644 --- a/contracts/rewards/src/error.rs +++ b/contracts/rewards/src/error.rs @@ -1,4 +1,5 @@ use axelar_wasm_std_derive::IntoContractError; +use cosmwasm_std::OverflowError; use thiserror::Error; #[derive(Error, Debug, PartialEq, IntoContractError)] @@ -27,6 +28,9 @@ pub enum ContractError { #[error("error loading rewards pool")] LoadRewardsPool, + #[error("rewards pool not found")] + RewardsPoolNotFound, + #[error("error loading rewards watermark")] LoadRewardsWatermark, @@ -36,6 +40,9 @@ pub enum ContractError { #[error("specified block has already passed")] BlockHeightInPast, + #[error(transparent)] + Overflow(#[from] OverflowError), + #[error("rewards pool balance insufficient")] PoolBalanceInsufficient, @@ -50,4 +57,7 @@ pub enum ContractError { #[error("rewards amount is zero")] ZeroRewards, + + #[error("failed to serialize the response")] + SerializeResponse, } diff --git a/contracts/rewards/src/msg.rs b/contracts/rewards/src/msg.rs index 5fd361596..dbcd0243e 100644 --- a/contracts/rewards/src/msg.rs +++ b/contracts/rewards/src/msg.rs @@ -1,6 +1,7 @@ use axelar_wasm_std::{nonempty, Threshold}; use connection_router_api::ChainName; use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Uint128, Uint64}; use crate::state::PoolId; @@ -29,7 +30,7 @@ pub struct Params { #[cw_serde] pub enum ExecuteMsg { - /// Log a specific worker as participating in a specific event + /// Log a specific worker as participating in a specific event. Worker weights are ignored /// /// TODO: For batched voting, treating the entire batch as a single event can be problematic. /// A worker may vote correctly for 9 out of 10 messages in a batch, but the worker's participation @@ -46,7 +47,7 @@ pub enum ExecuteMsg { /// Distribute rewards up to epoch T - 2 (i.e. if we are currently in epoch 10, distribute all undistributed rewards for epochs 0-8) and send the required number of tokens to each worker DistributeRewards { pool_id: PoolId, - /// Maximum number of historical epochs for which to distribute rewards, starting with the oldest. + /// Maximum number of historical epochs for which to distribute rewards, starting with the oldest. If not specified, distribute rewards for 10 epochs. epoch_count: Option, }, @@ -60,4 +61,17 @@ pub enum ExecuteMsg { #[cw_serde] #[derive(QueryResponses)] -pub enum QueryMsg {} +pub enum QueryMsg { + /// Gets the rewards pool details for the given `pool_id`` + #[returns(RewardsPool)] + RewardsPool { pool_id: PoolId }, +} + +#[cw_serde] +pub struct RewardsPool { + pub balance: Uint128, + pub epoch_duration: Uint64, + pub rewards_per_epoch: Uint128, + pub current_epoch_num: Uint64, + pub last_distribution_epoch: Option, +} diff --git a/contracts/rewards/src/state.rs b/contracts/rewards/src/state.rs index cd020786b..a2125bd46 100644 --- a/contracts/rewards/src/state.rs +++ b/contracts/rewards/src/state.rs @@ -7,7 +7,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, StdResult, Storage, Uint128}; use cw_storage_plus::{Item, Key, KeyDeserialize, Map, Prefixer, PrimaryKey}; use error_stack::{Result, ResultExt}; -use mockall::automock; use crate::{error::ContractError, msg::Params}; @@ -18,10 +17,10 @@ pub struct Config { } #[cw_serde] -pub struct StoredParams { +pub struct ParamsSnapshot { pub params: Params, /// epoch in which the params were updated - pub last_updated: Epoch, + pub created_at: Epoch, } /// PoolId a unique identifier for a rewards pool @@ -32,6 +31,15 @@ pub struct PoolId { pub contract: Addr, } +impl PoolId { + pub fn new(chain_name: ChainName, contract: Addr) -> Self { + PoolId { + chain_name, + contract, + } + } +} + impl PrimaryKey<'_> for PoolId { type Prefix = ChainName; type SubPrefix = (); @@ -117,7 +125,7 @@ impl EpochTally { pub fn record_participation(mut self, worker: Addr) -> Self { self.participation .entry(worker.to_string()) - .and_modify(|count| *count += 1) + .and_modify(|count| *count = count.saturating_add(1)) .or_insert(1); self } @@ -178,6 +186,39 @@ pub struct Epoch { pub block_height_started: u64, } +impl Epoch { + /// Returns the current epoch. The current epoch is computed dynamically based on the current + /// block height and the epoch duration. If the epoch duration is updated, we store the epoch + /// in which the update occurs as the last checkpoint + pub fn current( + current_params: &ParamsSnapshot, + cur_block_height: u64, + ) -> Result { + let epoch_duration: u64 = current_params.params.epoch_duration.into(); + let last_updated_epoch = ¤t_params.created_at; + + if cur_block_height < last_updated_epoch.block_height_started { + Err(ContractError::BlockHeightInPast.into()) + } else { + let epochs_elapsed = (cur_block_height + .saturating_sub(last_updated_epoch.block_height_started)) + .checked_div(epoch_duration) + .expect("invalid invariant: epoch duration is zero"); + Ok(Epoch { + epoch_num: last_updated_epoch + .epoch_num + .checked_add(epochs_elapsed) + .expect( + "epoch number should be strictly smaller than the current block height", + ), + block_height_started: last_updated_epoch + .block_height_started + .checked_add(epochs_elapsed.saturating_mul(epoch_duration)).expect("start of current epoch should be strictly smaller than the current block height"), + }) + } + } +} + #[cw_serde] pub struct RewardsPool { pub id: PoolId, @@ -185,61 +226,25 @@ pub struct RewardsPool { } impl RewardsPool { - #[allow(dead_code)] - pub fn new(chain_name: ChainName, contract: Addr) -> Self { + pub fn new(id: PoolId) -> Self { RewardsPool { - id: PoolId { - chain_name, - contract, - }, + id, balance: Uint128::zero(), } } pub fn sub_reward(mut self, reward: Uint128) -> Result { - if self.balance < reward { - return Err(ContractError::PoolBalanceInsufficient.into()); - } - self.balance -= reward; + self.balance = self + .balance + .checked_sub(reward) + .map_err(|_| ContractError::PoolBalanceInsufficient)?; Ok(self) } } -#[automock] -pub trait Store { - fn load_params(&self) -> StoredParams; - - fn load_rewards_watermark(&self, pool_id: PoolId) -> Result, ContractError>; - - fn load_event(&self, event_id: String, pool_id: PoolId) - -> Result, ContractError>; - - fn load_epoch_tally( - &self, - pool_id: PoolId, - epoch_num: u64, - ) -> Result, ContractError>; - - fn load_rewards_pool(&self, pool_id: PoolId) -> Result; - - fn save_params(&mut self, params: &StoredParams) -> Result<(), ContractError>; - - fn save_rewards_watermark( - &mut self, - pool_id: PoolId, - epoch_num: u64, - ) -> Result<(), ContractError>; - - fn save_event(&mut self, event: &Event) -> Result<(), ContractError>; - - fn save_epoch_tally(&mut self, tally: &EpochTally) -> Result<(), ContractError>; - - fn save_rewards_pool(&mut self, pool: &RewardsPool) -> Result<(), ContractError>; -} - /// Current rewards parameters, along with when the params were updated -pub const PARAMS: Item = Item::new("params"); +pub const PARAMS: Item = Item::new("params"); /// Maps a (pool id, epoch number) pair to a tally for that epoch and rewards pool const TALLIES: Map = Map::new("tallies"); @@ -256,95 +261,118 @@ const WATERMARKS: Map = Map::new("rewards_watermarks"); pub const CONFIG: Item = Item::new("config"); -pub struct RewardsStore<'a> { - pub storage: &'a mut dyn Storage, +pub(crate) fn load_config(storage: &dyn Storage) -> Config { + CONFIG.load(storage).expect("couldn't load config") } -impl Store for RewardsStore<'_> { - fn load_params(&self) -> StoredParams { - PARAMS.load(self.storage).expect("params should exist") - } +pub(crate) fn load_params(storage: &dyn Storage) -> ParamsSnapshot { + PARAMS.load(storage).expect("params should exist") +} - fn load_rewards_watermark(&self, pool_id: PoolId) -> Result, ContractError> { - WATERMARKS - .may_load(self.storage, pool_id) - .change_context(ContractError::LoadRewardsWatermark) - } +pub(crate) fn load_rewards_watermark( + storage: &dyn Storage, + pool_id: PoolId, +) -> Result, ContractError> { + WATERMARKS + .may_load(storage, pool_id) + .change_context(ContractError::LoadRewardsWatermark) +} - fn load_event( - &self, - event_id: String, - pool_id: PoolId, - ) -> Result, ContractError> { - EVENTS - .may_load(self.storage, (event_id, pool_id)) - .change_context(ContractError::LoadEvent) - } +pub(crate) fn load_event( + storage: &dyn Storage, + event_id: String, + pool_id: PoolId, +) -> Result, ContractError> { + EVENTS + .may_load(storage, (event_id, pool_id)) + .change_context(ContractError::LoadEvent) +} - fn load_epoch_tally( - &self, - pool_id: PoolId, - epoch_num: u64, - ) -> Result, ContractError> { - TALLIES - .may_load(self.storage, TallyId { pool_id, epoch_num }) - .change_context(ContractError::LoadEpochTally) - } +pub(crate) fn load_epoch_tally( + storage: &dyn Storage, + pool_id: PoolId, + epoch_num: u64, +) -> Result, ContractError> { + TALLIES + .may_load(storage, TallyId { pool_id, epoch_num }) + .change_context(ContractError::LoadEpochTally) +} - fn load_rewards_pool(&self, pool_id: PoolId) -> Result { - POOLS - .may_load(self.storage, pool_id.clone()) - .change_context(ContractError::LoadRewardsPool) - .map(|pool| { - pool.unwrap_or(RewardsPool { - id: pool_id, - balance: Uint128::zero(), - }) - }) - } +pub(crate) fn may_load_rewards_pool( + storage: &dyn Storage, + pool_id: PoolId, +) -> Result, ContractError> { + POOLS + .may_load(storage, pool_id.clone()) + .change_context(ContractError::LoadRewardsPool) +} - fn save_params(&mut self, params: &StoredParams) -> Result<(), ContractError> { - PARAMS - .save(self.storage, params) - .change_context(ContractError::SaveParams) - } +pub(crate) fn load_rewards_pool_or_new( + storage: &dyn Storage, + pool_id: PoolId, +) -> Result { + may_load_rewards_pool(storage, pool_id.clone()) + .map(|pool| pool.unwrap_or(RewardsPool::new(pool_id))) +} - fn save_rewards_watermark( - &mut self, - pool_id: PoolId, - epoch_num: u64, - ) -> Result<(), ContractError> { - WATERMARKS - .save(self.storage, pool_id, &epoch_num) - .change_context(ContractError::SaveRewardsWatermark) - } +pub(crate) fn load_rewards_pool( + storage: &dyn Storage, + pool_id: PoolId, +) -> Result { + may_load_rewards_pool(storage, pool_id.clone())? + .ok_or(ContractError::RewardsPoolNotFound.into()) +} - fn save_event(&mut self, event: &Event) -> Result<(), ContractError> { - EVENTS - .save( - self.storage, - (event.event_id.clone().into(), event.pool_id.clone()), - event, - ) - .change_context(ContractError::SaveEvent) - } +pub(crate) fn save_params( + storage: &mut dyn Storage, + params: &ParamsSnapshot, +) -> Result<(), ContractError> { + PARAMS + .save(storage, params) + .change_context(ContractError::SaveParams) +} - fn save_epoch_tally(&mut self, tally: &EpochTally) -> Result<(), ContractError> { - let tally_id = TallyId { - pool_id: tally.pool_id.clone(), - epoch_num: tally.epoch.epoch_num, - }; +pub(crate) fn save_rewards_watermark( + storage: &mut dyn Storage, + pool_id: PoolId, + epoch_num: u64, +) -> Result<(), ContractError> { + WATERMARKS + .save(storage, pool_id, &epoch_num) + .change_context(ContractError::SaveRewardsWatermark) +} - TALLIES - .save(self.storage, tally_id, tally) - .change_context(ContractError::SaveEpochTally) - } +pub(crate) fn save_event(storage: &mut dyn Storage, event: &Event) -> Result<(), ContractError> { + EVENTS + .save( + storage, + (event.event_id.clone().into(), event.pool_id.clone()), + event, + ) + .change_context(ContractError::SaveEvent) +} - fn save_rewards_pool(&mut self, pool: &RewardsPool) -> Result<(), ContractError> { - POOLS - .save(self.storage, pool.id.clone(), pool) - .change_context(ContractError::SaveRewardsPool) - } +pub(crate) fn save_epoch_tally( + storage: &mut dyn Storage, + tally: &EpochTally, +) -> Result<(), ContractError> { + let tally_id = TallyId { + pool_id: tally.pool_id.clone(), + epoch_num: tally.epoch.epoch_num, + }; + + TALLIES + .save(storage, tally_id, tally) + .change_context(ContractError::SaveEpochTally) +} + +pub(crate) fn save_rewards_pool( + storage: &mut dyn Storage, + pool: &RewardsPool, +) -> Result<(), ContractError> { + POOLS + .save(storage, pool.id.clone(), pool) + .change_context(ContractError::SaveRewardsPool) } pub(crate) enum StorageState { @@ -365,9 +393,9 @@ impl Deref for StorageState { #[cfg(test)] mod test { - use super::{Epoch, EpochTally, Event, PoolId, RewardsPool, RewardsStore, Store}; + use super::*; use crate::error::ContractError; - use crate::{msg::Params, state::StoredParams}; + use crate::{msg::Params, state::ParamsSnapshot}; use connection_router_api::ChainName; use cosmwasm_std::{testing::mock_dependencies, Addr, Uint128, Uint64}; use std::collections::HashMap; @@ -458,47 +486,41 @@ mod test { #[test] fn save_and_load_params() { let mut mock_deps = mock_dependencies(); - let mut store = RewardsStore { - storage: &mut mock_deps.storage, - }; - let params = StoredParams { + let params = ParamsSnapshot { params: Params { participation_threshold: (Uint64::new(1), Uint64::new(2)).try_into().unwrap(), epoch_duration: 100u64.try_into().unwrap(), rewards_per_epoch: Uint128::from(1000u128).try_into().unwrap(), }, - last_updated: Epoch { + created_at: Epoch { epoch_num: 1, block_height_started: 1, }, }; // save an initial params, then load it - assert!(store.save_params(¶ms).is_ok()); - let loaded = store.load_params(); + assert!(save_params(mock_deps.as_mut().storage, ¶ms).is_ok()); + let loaded = load_params(mock_deps.as_ref().storage); assert_eq!(loaded, params); // now store a new params, and check that it was updated - let new_params = StoredParams { + let new_params = ParamsSnapshot { params: Params { epoch_duration: 200u64.try_into().unwrap(), ..params.params }, - last_updated: Epoch { + created_at: Epoch { epoch_num: 2, block_height_started: 101, }, }; - assert!(store.save_params(&new_params).is_ok()); - let loaded = store.load_params(); + assert!(save_params(mock_deps.as_mut().storage, &new_params).is_ok()); + let loaded = load_params(mock_deps.as_mut().storage); assert_eq!(loaded, new_params); } #[test] fn save_and_load_rewards_watermark() { let mut mock_deps = mock_dependencies(); - let mut store = RewardsStore { - storage: &mut mock_deps.storage, - }; let epoch = Epoch { epoch_num: 10, block_height_started: 1000, @@ -509,24 +531,29 @@ mod test { }; // should be empty at first - let loaded = store.load_rewards_watermark(pool_id.clone()); + let loaded = load_rewards_watermark(mock_deps.as_ref().storage, pool_id.clone()); assert!(loaded.is_ok()); assert!(loaded.unwrap().is_none()); // save the first watermark - let res = store.save_rewards_watermark(pool_id.clone(), epoch.epoch_num); + let res = + save_rewards_watermark(mock_deps.as_mut().storage, pool_id.clone(), epoch.epoch_num); assert!(res.is_ok()); - let loaded = store.load_rewards_watermark(pool_id.clone()); + let loaded = load_rewards_watermark(mock_deps.as_ref().storage, pool_id.clone()); assert!(loaded.is_ok()); assert!(loaded.as_ref().unwrap().is_some()); assert_eq!(loaded.unwrap().unwrap(), epoch.epoch_num); // now store a new watermark, should overwrite - let res = store.save_rewards_watermark(pool_id.clone(), epoch.epoch_num + 1); + let res = save_rewards_watermark( + mock_deps.as_mut().storage, + pool_id.clone(), + epoch.epoch_num + 1, + ); assert!(res.is_ok()); - let loaded = store.load_rewards_watermark(pool_id); + let loaded = load_rewards_watermark(mock_deps.as_ref().storage, pool_id); assert!(loaded.is_ok()); assert!(loaded.as_ref().unwrap().is_some()); assert_eq!(loaded.unwrap().unwrap(), epoch.epoch_num + 1); @@ -537,15 +564,19 @@ mod test { contract: Addr::unchecked("some other contract"), }; // should be empty at first - let loaded = store.load_rewards_watermark(diff_pool_id.clone()); + let loaded = load_rewards_watermark(mock_deps.as_ref().storage, diff_pool_id.clone()); assert!(loaded.is_ok()); assert!(loaded.unwrap().is_none()); // save the first watermark for this contract - let res = store.save_rewards_watermark(diff_pool_id.clone(), epoch.epoch_num + 7); + let res = save_rewards_watermark( + mock_deps.as_mut().storage, + diff_pool_id.clone(), + epoch.epoch_num + 7, + ); assert!(res.is_ok()); - let loaded = store.load_rewards_watermark(diff_pool_id.clone()); + let loaded = load_rewards_watermark(mock_deps.as_ref().storage, diff_pool_id.clone()); assert!(loaded.is_ok()); assert!(loaded.as_ref().unwrap().is_some()); assert_eq!(loaded.unwrap().unwrap(), epoch.epoch_num + 7); @@ -554,9 +585,6 @@ mod test { #[test] fn save_and_load_event() { let mut mock_deps = mock_dependencies(); - let mut store = RewardsStore { - storage: &mut mock_deps.storage, - }; let event = Event { pool_id: PoolId { @@ -567,11 +595,15 @@ mod test { epoch_num: 2, }; - let res = store.save_event(&event); + let res = save_event(mock_deps.as_mut().storage, &event); assert!(res.is_ok()); // check that we load the event that we just saved - let loaded = store.load_event(event.event_id.clone().into(), event.pool_id.clone()); + let loaded = load_event( + mock_deps.as_ref().storage, + event.event_id.clone().into(), + event.pool_id.clone(), + ); assert!(loaded.is_ok()); assert!(loaded.as_ref().unwrap().is_some()); assert_eq!(loaded.unwrap().unwrap(), event); @@ -581,17 +613,29 @@ mod test { chain_name: "mock-chain".parse().unwrap(), contract: Addr::unchecked("different contract"), }; - let loaded = store.load_event("some other event".into(), diff_pool_id.clone()); + let loaded = load_event( + mock_deps.as_ref().storage, + "some other event".into(), + diff_pool_id.clone(), + ); assert!(loaded.is_ok()); assert!(loaded.unwrap().is_none()); // same event id but different contract address, should still return none - let loaded = store.load_event(event.event_id.clone().into(), diff_pool_id); + let loaded = load_event( + mock_deps.as_ref().storage, + event.event_id.clone().into(), + diff_pool_id, + ); assert!(loaded.is_ok()); assert!(loaded.unwrap().is_none()); // different event id, but same contract address, should still return none - let loaded = store.load_event("some other event".into(), event.pool_id); + let loaded = load_event( + mock_deps.as_ref().storage, + "some other event".into(), + event.pool_id, + ); assert!(loaded.is_ok()); assert!(loaded.unwrap().is_none()); } @@ -599,9 +643,6 @@ mod test { #[test] fn save_and_load_epoch_tally() { let mut mock_deps = mock_dependencies(); - let mut store = RewardsStore { - storage: &mut mock_deps.storage, - }; let epoch_num = 10; let rewards_rate = Uint128::from(100u128).try_into().unwrap(); @@ -625,17 +666,18 @@ mod test { tally = tally.record_participation(Addr::unchecked("worker")); - let res = store.save_epoch_tally(&tally); + let res = save_epoch_tally(mock_deps.as_mut().storage, &tally); assert!(res.is_ok()); // check that we load the tally that we just saved - let loaded = store.load_epoch_tally(pool_id.clone(), epoch_num); + let loaded = load_epoch_tally(mock_deps.as_ref().storage, pool_id.clone(), epoch_num); assert!(loaded.is_ok()); assert!(loaded.as_ref().unwrap().is_some()); assert_eq!(loaded.unwrap().unwrap(), tally); // different contract but same epoch should return none - let loaded = store.load_epoch_tally( + let loaded = load_epoch_tally( + mock_deps.as_ref().storage, PoolId { chain_name: "mock-chain".parse().unwrap(), contract: Addr::unchecked("different contract"), @@ -646,12 +688,12 @@ mod test { assert!(loaded.unwrap().is_none()); // different epoch but same contract should return none - let loaded = store.load_epoch_tally(pool_id.clone(), epoch_num + 1); + let loaded = load_epoch_tally(mock_deps.as_ref().storage, pool_id.clone(), epoch_num + 1); assert!(loaded.is_ok()); assert!(loaded.unwrap().is_none()); // different epoch and different contract should return none - let loaded = store.load_epoch_tally(pool_id.clone(), epoch_num + 1); + let loaded = load_epoch_tally(mock_deps.as_ref().storage, pool_id.clone(), epoch_num + 1); assert!(loaded.is_ok()); assert!(loaded.unwrap().is_none()); } @@ -659,24 +701,28 @@ mod test { #[test] fn save_and_load_rewards_pool() { let mut mock_deps = mock_dependencies(); - let mut store = RewardsStore { - storage: &mut mock_deps.storage, - }; let chain_name: ChainName = "mock-chain".parse().unwrap(); - let pool = RewardsPool::new(chain_name.clone(), Addr::unchecked("some contract")); - let res = store.save_rewards_pool(&pool); + let pool = RewardsPool::new(PoolId::new( + chain_name.clone(), + Addr::unchecked("some contract"), + )); + let res = save_rewards_pool(mock_deps.as_mut().storage, &pool); assert!(res.is_ok()); - let loaded = store.load_rewards_pool(pool.id.clone()); + let loaded = load_rewards_pool_or_new(mock_deps.as_ref().storage, pool.id.clone()); assert!(loaded.is_ok()); assert_eq!(loaded.unwrap(), pool); - let loaded = store.load_rewards_pool(PoolId { - chain_name: chain_name.clone(), - contract: Addr::unchecked("a different contract"), - }); + // return new pool when pool is not found + let loaded = load_rewards_pool_or_new( + mock_deps.as_ref().storage, + PoolId { + chain_name: chain_name.clone(), + contract: Addr::unchecked("a different contract"), + }, + ); assert!(loaded.is_ok()); assert!(loaded.as_ref().unwrap().balance.is_zero()); } diff --git a/contracts/service-registry/Cargo.toml b/contracts/service-registry/Cargo.toml index e59d9d7e5..d5154e6c8 100644 --- a/contracts/service-registry/Cargo.toml +++ b/contracts/service-registry/Cargo.toml @@ -50,3 +50,6 @@ thiserror = { workspace = true } [dev-dependencies] cw-multi-test = "0.15.1" integration-tests = { workspace = true } + +[lints] +workspace = true diff --git a/contracts/service-registry/src/contract.rs b/contracts/service-registry/src/contract.rs index c8f2c950c..19a3d0747 100644 --- a/contracts/service-registry/src/contract.rs +++ b/contracts/service-registry/src/contract.rs @@ -1,7 +1,7 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Addr, BankMsg, Binary, Coin, Deps, DepsMut, Env, MessageInfo, Order, Response, + to_json_binary, Addr, BankMsg, Binary, Coin, Deps, DepsMut, Env, MessageInfo, Order, Response, Uint128, }; @@ -357,14 +357,15 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result to_binary(&query::get_active_workers(deps, service_name, chain_name)?) + } => to_json_binary(&query::get_active_workers(deps, service_name, chain_name)?) .map_err(|err| err.into()), QueryMsg::GetWorker { service_name, worker, - } => to_binary(&query::get_worker(deps, worker, service_name)?).map_err(|err| err.into()), + } => to_json_binary(&query::get_worker(deps, worker, service_name)?) + .map_err(|err| err.into()), QueryMsg::GetService { service_name } => { - to_binary(&query::get_service(deps, service_name)?).map_err(|err| err.into()) + to_json_binary(&query::get_service(deps, service_name)?).map_err(|err| err.into()) } } } @@ -372,7 +373,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result Result, ContractError> { + ) -> Result, ContractError> { let service = SERVICES .may_load(deps.storage, &service_name)? .ok_or(ContractError::ServiceNotFound)?; - let workers = WORKERS_PER_CHAIN + let workers: Vec<_> = WORKERS_PER_CHAIN .prefix((&service_name, &chain_name)) .range(deps.storage, None, None, Order::Ascending) .map(|res| res.and_then(|(addr, _)| WORKERS.load(deps.storage, (&service_name, &addr)))) @@ -396,9 +397,17 @@ pub mod query { _ => false, }) .filter(|worker| worker.authorization_state == AuthorizationState::Authorized) + .map(|worker| WeightedWorker { + worker_info: worker, + weight: WORKER_WEIGHT, // all workers have an identical const weight for now + }) .collect(); - Ok(workers) + if workers.len() < service.min_num_workers.into() { + Err(ContractError::NotEnoughWorkers) + } else { + Ok(workers) + } } pub fn get_worker( diff --git a/contracts/service-registry/src/error.rs b/contracts/service-registry/src/error.rs index 2645430ca..daed547dd 100644 --- a/contracts/service-registry/src/error.rs +++ b/contracts/service-registry/src/error.rs @@ -1,6 +1,6 @@ use axelar_wasm_std::nonempty; use axelar_wasm_std_derive::IntoContractError; -use cosmwasm_std::StdError; +use cosmwasm_std::{OverflowError, StdError}; use thiserror::Error; use crate::state::BondingState; @@ -10,6 +10,9 @@ pub enum ContractError { #[error(transparent)] Std(#[from] StdError), + #[error(transparent)] + Overflow(#[from] OverflowError), + #[error(transparent)] NonEmpty(#[from] nonempty::Error), @@ -27,4 +30,6 @@ pub enum ContractError { WorkerNotFound, #[error("invalid bonding state `{0:?}` for this operation")] InvalidBondingState(BondingState), + #[error("not enough workers")] + NotEnoughWorkers, } diff --git a/contracts/service-registry/src/helpers.rs b/contracts/service-registry/src/helpers.rs index b5fce9b31..7247c3fdf 100644 --- a/contracts/service-registry/src/helpers.rs +++ b/contracts/service-registry/src/helpers.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; +use cosmwasm_std::{to_json_binary, Addr, CosmosMsg, StdResult, WasmMsg}; use crate::msg::ExecuteMsg; @@ -16,7 +16,7 @@ impl ServiceRegistry { } pub fn call>(&self, msg: T) -> StdResult { - let msg = to_binary(&msg.into())?; + let msg = to_json_binary(&msg.into())?; Ok(WasmMsg::Execute { contract_addr: self.addr().into(), msg, diff --git a/contracts/service-registry/src/msg.rs b/contracts/service-registry/src/msg.rs index 70d555523..c95a1f2e0 100644 --- a/contracts/service-registry/src/msg.rs +++ b/contracts/service-registry/src/msg.rs @@ -59,7 +59,7 @@ pub enum ExecuteMsg { #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - #[returns(Vec)] + #[returns(Vec)] GetActiveWorkers { service_name: String, chain_name: ChainName, diff --git a/contracts/service-registry/src/state.rs b/contracts/service-registry/src/state.rs index bda65eee3..abf101796 100644 --- a/contracts/service-registry/src/state.rs +++ b/contracts/service-registry/src/state.rs @@ -3,7 +3,7 @@ use cosmwasm_schema::cw_serde; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{Addr, Timestamp, Uint128, Uint256}; +use cosmwasm_std::{Addr, Timestamp, Uint128}; use cw_storage_plus::{Item, Map}; #[cw_serde] @@ -13,7 +13,7 @@ pub struct Config { pub const CONFIG: Item = Item::new("config"); -use axelar_wasm_std::snapshot::Participant; +use axelar_wasm_std::{nonempty, snapshot::Participant}; use crate::ContractError; @@ -37,22 +37,20 @@ pub struct Worker { pub service_name: String, } -impl TryFrom for Participant { - type Error = ContractError; +#[cw_serde] +pub struct WeightedWorker { + pub worker_info: Worker, + pub weight: nonempty::Uint256, +} + +/// For now, all workers have equal weight, regardless of amount bonded +pub const WORKER_WEIGHT: nonempty::Uint256 = nonempty::Uint256::one(); - fn try_from(worker: Worker) -> Result { - match worker.bonding_state { - BondingState::Bonded { amount: _ } => Ok(Self { - address: worker.address, - // Weight is set to one to ensure all workers have same weight. In the future, it should be derived from amount bonded - // If the weight is changed to a non-constant value, the signing session completed event from multisig and the signature - // optimization during proof construction may require re-evaluation, so that relayers could take advantage of late - // signatures to get a more optimized version of the proof. - weight: Uint256::one() - .try_into() - .expect("violated invariant: weight must not be zero"), - }), - _ => Err(ContractError::InvalidBondingState(worker.bonding_state)), +impl From for Participant { + fn from(worker: WeightedWorker) -> Participant { + Self { + weight: worker.weight, + address: worker.worker_info.address, } } } @@ -80,7 +78,9 @@ impl BondingState { | BondingState::Unbonding { amount, unbonded_at: _, - } => amount + to_add, + } => amount + .checked_add(to_add) + .map_err(ContractError::Overflow)?, BondingState::Unbonded => to_add, }; if amount.is_zero() { diff --git a/contracts/service-registry/tests/tests.rs b/contracts/service-registry/tests/tests.rs index 42e8f1933..a3e29d157 100644 --- a/contracts/service-registry/tests/tests.rs +++ b/contracts/service-registry/tests/tests.rs @@ -3,10 +3,11 @@ mod test_utils; use std::{str::FromStr, vec}; use connection_router_api::ChainName; -use cosmwasm_std::{coins, Addr, BlockInfo, Uint128}; +use cosmwasm_std::{coins, Addr, BlockInfo, StdResult, Uint128}; use cw_multi_test::App; use integration_tests::contract::Contract; use service_registry::msg::QueryMsg; +use service_registry::state::{WeightedWorker, WORKER_WEIGHT}; use service_registry::{ msg::ExecuteMsg, state::{AuthorizationState, BondingState, Worker}, @@ -218,32 +219,39 @@ fn register_chain_support() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!( workers, - vec![Worker { - address: worker, - bonding_state: BondingState::Bonded { - amount: min_worker_bond + vec![WeightedWorker { + worker_info: Worker { + address: worker, + bonding_state: BondingState::Bonded { + amount: min_worker_bond + }, + authorization_state: AuthorizationState::Authorized, + service_name: service_name.into() }, - authorization_state: AuthorizationState::Authorized, - service_name: service_name.into() + weight: WORKER_WEIGHT }] ); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name: ChainName::from_str("random chain").unwrap(), - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name: ChainName::from_str("random chain").unwrap(), + }, + ) + .unwrap(); assert_eq!(workers, vec![]); } @@ -322,13 +330,15 @@ fn register_and_deregister_support_for_single_chain() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!(workers, vec![]); } @@ -411,13 +421,15 @@ fn register_and_deregister_support_for_multiple_chains() { assert!(res.is_ok()); for chain in chains { - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name: chain, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name: chain, + }, + ) + .unwrap(); assert_eq!(workers, vec![]); } } @@ -504,33 +516,40 @@ fn register_for_multiple_chains_deregister_for_first_one() { // Verify that worker is not associated with the deregistered chain let deregistered_chain = chains[0].clone(); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name: deregistered_chain, - }, - ); - assert_eq!(workers, vec![]); - - // Verify that worker is still associated with other chains - for chain in chains.iter().skip(1) { - let workers: Vec = service_registry.query( + let workers: Vec = service_registry + .query( &app, &QueryMsg::GetActiveWorkers { service_name: service_name.into(), - chain_name: chain.clone(), + chain_name: deregistered_chain, }, - ); + ) + .unwrap(); + assert_eq!(workers, vec![]); + + // Verify that worker is still associated with other chains + for chain in chains.iter().skip(1) { + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name: chain.clone(), + }, + ) + .unwrap(); assert_eq!( workers, - vec![Worker { - address: worker.clone(), - bonding_state: BondingState::Bonded { - amount: min_worker_bond + vec![WeightedWorker { + worker_info: Worker { + address: worker.clone(), + bonding_state: BondingState::Bonded { + amount: min_worker_bond + }, + authorization_state: AuthorizationState::Authorized, + service_name: service_name.into() }, - authorization_state: AuthorizationState::Authorized, - service_name: service_name.into() + weight: WORKER_WEIGHT }] ); } @@ -612,22 +631,27 @@ fn register_support_for_a_chain_deregister_support_for_another_chain() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!( workers, - vec![Worker { - address: worker, - bonding_state: BondingState::Bonded { - amount: min_worker_bond + vec![WeightedWorker { + worker_info: Worker { + address: worker, + bonding_state: BondingState::Bonded { + amount: min_worker_bond + }, + authorization_state: AuthorizationState::Authorized, + service_name: service_name.into() }, - authorization_state: AuthorizationState::Authorized, - service_name: service_name.into() + weight: WORKER_WEIGHT }] ); } @@ -717,22 +741,27 @@ fn register_deregister_register_support_for_single_chain() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!( workers, - vec![Worker { - address: worker, - bonding_state: BondingState::Bonded { - amount: min_worker_bond + vec![WeightedWorker { + worker_info: Worker { + address: worker, + bonding_state: BondingState::Bonded { + amount: min_worker_bond + }, + authorization_state: AuthorizationState::Authorized, + service_name: service_name.into() }, - authorization_state: AuthorizationState::Authorized, - service_name: service_name.into() + weight: WORKER_WEIGHT }] ); } @@ -801,13 +830,15 @@ fn deregister_previously_unsupported_single_chain() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!(workers, vec![]) } @@ -875,13 +906,15 @@ fn register_and_deregister_support_for_single_chain_unbonded() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!(workers, vec![]); } @@ -932,13 +965,15 @@ fn deregister_from_unregistered_worker_single_chain() { test_utils::are_contract_err_strings_equal(err, ContractError::WorkerNotFound); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!(workers, vec![]); } @@ -1044,13 +1079,15 @@ fn unbond_worker() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!(workers, vec![]) } @@ -1151,13 +1188,15 @@ fn bond_but_not_authorized() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!(workers, vec![]) } @@ -1223,13 +1262,15 @@ fn bond_but_not_enough() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!(workers, vec![]) } @@ -1295,22 +1336,27 @@ fn bond_before_authorize() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!( workers, - vec![Worker { - address: worker, - bonding_state: BondingState::Bonded { - amount: min_worker_bond + vec![WeightedWorker { + worker_info: Worker { + address: worker, + bonding_state: BondingState::Bonded { + amount: min_worker_bond + }, + authorization_state: AuthorizationState::Authorized, + service_name: service_name.into() }, - authorization_state: AuthorizationState::Authorized, - service_name: service_name.into() + weight: WORKER_WEIGHT }] ); } @@ -1395,22 +1441,27 @@ fn unbond_then_rebond() { ); assert!(res.is_ok()); - let workers: Vec = service_registry.query( - &app, - &QueryMsg::GetActiveWorkers { - service_name: service_name.into(), - chain_name, - }, - ); + let workers: Vec = service_registry + .query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name, + }, + ) + .unwrap(); assert_eq!( workers, - vec![Worker { - address: worker, - bonding_state: BondingState::Bonded { - amount: min_worker_bond + vec![WeightedWorker { + worker_info: Worker { + address: worker, + bonding_state: BondingState::Bonded { + amount: min_worker_bond + }, + authorization_state: AuthorizationState::Authorized, + service_name: service_name.into() }, - authorization_state: AuthorizationState::Authorized, - service_name: service_name.into() + weight: WORKER_WEIGHT }] ); } @@ -1442,7 +1493,7 @@ fn unbonding_period() { max_num_workers: Some(100), min_worker_bond, bond_denom: AXL_DENOMINATION.into(), - unbonding_period_days: unbonding_period_days.clone(), + unbonding_period_days, description: "Some service".into(), }, ); @@ -1503,7 +1554,7 @@ fn unbonding_period() { service_name: service_name.into(), }, ); - assert!(!res.is_ok()); + assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), axelar_wasm_std::ContractError::from(ContractError::InvalidBondingState( @@ -1551,3 +1602,118 @@ fn unbonding_period() { initial_bal ); } + +#[test] +fn get_active_workers_should_not_return_less_than_min() { + let workers = vec![Addr::unchecked("worker1"), Addr::unchecked("worker2")]; + let min_num_workers = workers.len() as u16; + + let mut app = App::new(|router, _, storage| { + for worker in &workers { + router + .bank + .init_balance(storage, worker, coins(100000, AXL_DENOMINATION)) + .unwrap() + } + }); + + let governance = Addr::unchecked("gov"); + let service_registry = + test_utils::ServiceRegistryContract::instantiate_contract(&mut app, governance.clone()); + + let service_name = "validators"; + let min_worker_bond = Uint128::new(100); + let _ = service_registry + .execute( + &mut app, + governance.clone(), + &ExecuteMsg::RegisterService { + service_name: service_name.into(), + service_contract: Addr::unchecked("nowhere"), + min_num_workers, + max_num_workers: Some(100), + min_worker_bond, + bond_denom: AXL_DENOMINATION.into(), + unbonding_period_days: 10, + description: "Some service".into(), + }, + ) + .unwrap(); + + let _ = service_registry + .execute( + &mut app, + governance, + &ExecuteMsg::AuthorizeWorkers { + workers: workers.iter().map(|w| w.into()).collect(), + service_name: service_name.into(), + }, + ) + .unwrap(); + + let chain_name = ChainName::from_str("ethereum").unwrap(); + + for worker in &workers { + // should return err until all workers are registered + let res: StdResult> = service_registry.query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name: chain_name.clone(), + }, + ); + assert!(res.is_err()); + + let _ = service_registry + .execute_with_funds( + &mut app, + worker.clone(), + &ExecuteMsg::BondWorker { + service_name: service_name.into(), + }, + &coins(min_worker_bond.u128(), AXL_DENOMINATION), + ) + .unwrap(); + + let _ = service_registry + .execute( + &mut app, + worker.clone(), + &ExecuteMsg::RegisterChainSupport { + service_name: service_name.into(), + chains: vec![chain_name.clone()], + }, + ) + .unwrap(); + } + + // all workers registered, should not return err now + let res: StdResult> = service_registry.query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name: chain_name.clone(), + }, + ); + assert!(res.is_ok()); + + // remove one, should return err again + let _ = service_registry + .execute( + &mut app, + workers[0].clone(), + &ExecuteMsg::DeregisterChainSupport { + service_name: service_name.into(), + chains: vec![chain_name.clone()], + }, + ) + .unwrap(); + let res: StdResult> = service_registry.query( + &app, + &QueryMsg::GetActiveWorkers { + service_name: service_name.into(), + chain_name: chain_name.clone(), + }, + ); + assert!(res.is_err()); +} diff --git a/contracts/voting-verifier/Cargo.toml b/contracts/voting-verifier/Cargo.toml index a5e35774d..bd936fe17 100644 --- a/contracts/voting-verifier/Cargo.toml +++ b/contracts/voting-verifier/Cargo.toml @@ -54,3 +54,6 @@ thiserror = { workspace = true } [dev-dependencies] cw-multi-test = "0.15.1" integration-tests = { workspace = true } + +[lints] +workspace = true diff --git a/contracts/voting-verifier/src/contract.rs b/contracts/voting-verifier/src/contract.rs index ed7567aa9..0a1a72498 100644 --- a/contracts/voting-verifier/src/contract.rs +++ b/contracts/voting-verifier/src/contract.rs @@ -1,7 +1,7 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Attribute, Binary, Deps, DepsMut, Env, Event, MessageInfo, Response, StdResult, + to_json_binary, Attribute, Binary, Deps, DepsMut, Env, Event, MessageInfo, Response, StdResult, }; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; @@ -58,10 +58,10 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { } QueryMsg::GetMessagesStatus { messages } => { - to_binary(&query::messages_status(deps, &messages)?) + to_json_binary(&query::messages_status(deps, &messages)?) } QueryMsg::GetWorkerSetStatus { new_operators } => { - to_binary(&query::worker_set_status(deps, &new_operators)?) + to_json_binary(&query::worker_set_status(deps, &new_operators)?) } } } @@ -69,7 +69,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { #[cfg(test)] mod test { use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, Addr, Empty, OwnedDeps, Uint128, Uint64, WasmQuery, }; @@ -77,10 +77,16 @@ mod test { use axelar_wasm_std::{ nonempty, operators::Operators, voting::Vote, Threshold, VerificationStatus, }; - use connection_router_api::{ChainName, CrossChainId, Message, ID_SEPARATOR}; - use service_registry::state::{AuthorizationState, BondingState, Worker}; + use connection_router_api::{ChainName, CrossChainId, Message}; + use service_registry::state::{ + AuthorizationState, BondingState, WeightedWorker, Worker, WORKER_WEIGHT, + }; - use crate::{error::ContractError, events::TxEventConfirmation, msg::VerifyMessagesResponse}; + use crate::{ + error::ContractError, + events::{TxEventConfirmation, TX_HASH_EVENT_INDEX_SEPARATOR}, + msg::VerifyMessagesResponse, + }; use super::*; @@ -91,7 +97,7 @@ mod test { const POLL_BLOCK_EXPIRY: u64 = 100; fn source_chain() -> ChainName { - "source_chain".parse().unwrap() + "source-chain".parse().unwrap() } fn assert_contract_err_strings_equal( @@ -142,7 +148,17 @@ mod test { deps.querier.update_wasm(|wq| match wq { WasmQuery::Smart { contract_addr, .. } if contract_addr == SERVICE_REGISTRY_ADDRESS => { - Ok(to_binary(&workers()).into()).into() + Ok(to_json_binary( + &workers() + .into_iter() + .map(|w| WeightedWorker { + worker_info: w, + weight: WORKER_WEIGHT, + }) + .collect::>(), + ) + .into()) + .into() } _ => panic!("no mock for this query"), }); @@ -151,7 +167,7 @@ mod test { } fn message_id(id: &str, index: u64) -> nonempty::String { - format!("{}{}{}", id, ID_SEPARATOR, index) + format!("{}{}{}", id, TX_HASH_EVENT_INDEX_SEPARATOR, index) .try_into() .unwrap() } @@ -161,16 +177,17 @@ mod test { .map(|i| Message { cc_id: CrossChainId { chain: source_chain(), - id: format!("id:{i}").parse().unwrap(), + id: message_id("id", i), }, source_address: format!("source_address{i}").parse().unwrap(), - destination_chain: format!("destination_chain{i}").parse().unwrap(), + destination_chain: format!("destination-chain{i}").parse().unwrap(), destination_address: format!("destination_address{i}").parse().unwrap(), payload_hash: [0; 32], }) .collect() } + #[allow(clippy::arithmetic_side_effects)] fn mock_env_expired() -> Env { let mut env = mock_env(); env.block.height += POLL_BLOCK_EXPIRY; @@ -186,20 +203,20 @@ mod test { Message { cc_id: CrossChainId { chain: source_chain(), - id: "id:1".parse().unwrap(), + id: message_id("id", 1), }, source_address: "source_address1".parse().unwrap(), - destination_chain: "destination_chain1".parse().unwrap(), + destination_chain: "destination-chain1".parse().unwrap(), destination_address: "destination_address1".parse().unwrap(), payload_hash: [0; 32], }, Message { cc_id: CrossChainId { - chain: "other_chain".parse().unwrap(), - id: "id:2".parse().unwrap(), + chain: "other-chain".parse().unwrap(), + id: message_id("id", 2), }, source_address: "source_address2".parse().unwrap(), - destination_chain: "destination_chain2".parse().unwrap(), + destination_chain: "destination-chain2".parse().unwrap(), destination_address: "destination_address2".parse().unwrap(), payload_hash: [0; 32], }, @@ -218,21 +235,21 @@ mod test { }; let res = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg).unwrap(); - let reply: VerifyMessagesResponse = from_binary(&res.data.unwrap()).unwrap(); + let reply: VerifyMessagesResponse = from_json(res.data.unwrap()).unwrap(); assert_eq!(reply.verification_statuses.len(), 2); assert_eq!( reply.verification_statuses, vec![ ( CrossChainId { - id: "id:0".parse().unwrap(), + id: message_id("id", 0), chain: source_chain() }, VerificationStatus::None ), ( CrossChainId { - id: "id:1".parse().unwrap(), + id: message_id("id", 1), chain: source_chain() }, VerificationStatus::None @@ -398,8 +415,8 @@ mod test { ); assert!(res.is_ok()); - let res: Vec<(CrossChainId, VerificationStatus)> = from_binary( - &query( + let res: Vec<(CrossChainId, VerificationStatus)> = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::GetMessagesStatus { @@ -436,8 +453,8 @@ mod test { ); assert!(res.is_ok()); - let res: Vec<(CrossChainId, VerificationStatus)> = from_binary( - &query( + let res: Vec<(CrossChainId, VerificationStatus)> = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::GetMessagesStatus { @@ -472,7 +489,7 @@ mod test { let res = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg).unwrap(); - let reply: VerifyMessagesResponse = from_binary(&res.data.unwrap()).unwrap(); + let reply: VerifyMessagesResponse = from_json(res.data.unwrap()).unwrap(); assert_eq!(reply.verification_statuses.len(), messages.len()); assert_eq!( @@ -483,8 +500,8 @@ mod test { .collect::>() ); - let statuses: Vec<(CrossChainId, VerificationStatus)> = from_binary( - &query( + let statuses: Vec<(CrossChainId, VerificationStatus)> = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::GetMessagesStatus { @@ -537,8 +554,8 @@ mod test { ) .unwrap(); - let statuses: Vec<(CrossChainId, VerificationStatus)> = from_binary( - &query( + let statuses: Vec<(CrossChainId, VerificationStatus)> = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::GetMessagesStatus { @@ -578,8 +595,8 @@ mod test { let res = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg); assert!(res.is_ok()); - let res: VerificationStatus = from_binary( - &query( + let res: VerificationStatus = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::GetWorkerSetStatus { @@ -628,8 +645,8 @@ mod test { ); assert!(res.is_ok()); - let res: VerificationStatus = from_binary( - &query( + let res: VerificationStatus = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::GetWorkerSetStatus { @@ -681,8 +698,8 @@ mod test { ); assert!(res.is_ok()); - let res: VerificationStatus = from_binary( - &query( + let res: VerificationStatus = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::GetWorkerSetStatus { @@ -736,8 +753,8 @@ mod test { ); assert!(res.is_ok()); - let res: VerificationStatus = from_binary( - &query( + let res: VerificationStatus = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::GetWorkerSetStatus { @@ -783,8 +800,8 @@ mod test { ); assert!(res.is_ok()); - let res: VerificationStatus = from_binary( - &query( + let res: VerificationStatus = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::GetWorkerSetStatus { diff --git a/contracts/voting-verifier/src/error.rs b/contracts/voting-verifier/src/error.rs index 2ad1ec300..42d5c3a00 100644 --- a/contracts/voting-verifier/src/error.rs +++ b/contracts/voting-verifier/src/error.rs @@ -1,7 +1,7 @@ use axelar_wasm_std::{nonempty, voting}; use axelar_wasm_std_derive::IntoContractError; use connection_router_api::ChainName; -use cosmwasm_std::StdError; +use cosmwasm_std::{OverflowError, StdError}; use service_registry; use thiserror::Error; @@ -10,6 +10,9 @@ pub enum ContractError { #[error(transparent)] Std(#[from] StdError), + #[error(transparent)] + Overflow(#[from] OverflowError), + #[error(transparent)] RouterError(#[from] connection_router_api::error::Error), diff --git a/contracts/voting-verifier/src/events.rs b/contracts/voting-verifier/src/events.rs index 44119d3bb..510da63da 100644 --- a/contracts/voting-verifier/src/events.rs +++ b/contracts/voting-verifier/src/events.rs @@ -1,12 +1,15 @@ use std::vec::Vec; +use axelar_wasm_std::hash::Hash; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Attribute, Event}; +use cosmwasm_std::{Addr, Attribute, Event, HexBinary}; use axelar_wasm_std::nonempty; use axelar_wasm_std::operators::Operators; use axelar_wasm_std::voting::{PollId, Vote}; -use connection_router_api::{Address, ChainName, Message, ID_SEPARATOR}; +use connection_router_api::{Address, ChainName, Message}; + +pub const TX_HASH_EVENT_INDEX_SEPARATOR: char = '-'; use crate::error::ContractError; use crate::state::Config; @@ -112,13 +115,14 @@ impl From for Event { #[cw_serde] pub struct WorkerSetConfirmation { pub tx_id: nonempty::String, - pub event_index: u64, + pub event_index: u32, pub operators: Operators, } impl WorkerSetConfirmation { pub fn new(message_id: nonempty::String, operators: Operators) -> Result { let (tx_id, event_index) = parse_message_id(&message_id)?; + Ok(Self { tx_id, event_index, @@ -130,7 +134,7 @@ impl WorkerSetConfirmation { #[cw_serde] pub struct TxEventConfirmation { pub tx_id: nonempty::String, - pub event_index: u64, + pub event_index: u32, pub destination_address: Address, pub destination_chain: ChainName, pub source_address: Address, @@ -160,22 +164,38 @@ impl TryFrom for TxEventConfirmation { fn parse_message_id( message_id: &nonempty::String, -) -> Result<(nonempty::String, u64), ContractError> { +) -> Result<(nonempty::String, u32), ContractError> { // expected format: : - let components = message_id.split(ID_SEPARATOR).collect::>(); + let components = message_id + .split(TX_HASH_EVENT_INDEX_SEPARATOR) + .collect::>(); if components.len() != 2 { return Err(ContractError::InvalidMessageID(message_id.to_string())); } + let event_index = components[1]; + if event_index != "0" && event_index.starts_with('0') { + return Err(ContractError::InvalidMessageID(message_id.to_string())); + } + Ok(( components[0].try_into()?, - components[1] - .parse::() + event_index + .parse() .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?, )) } +pub fn construct_message_id(tx_hash: Hash, event_index: u32) -> String { + format!( + "{}{}{}", + HexBinary::from(tx_hash), + TX_HASH_EVENT_INDEX_SEPARATOR, + event_index + ) +} + pub struct Voted { pub poll_id: PollId, pub voter: Addr, @@ -210,3 +230,75 @@ impl From for Event { ) } } + +#[cfg(test)] +mod test { + use axelar_wasm_std::nonempty; + use cosmwasm_std::HexBinary; + + use crate::events::{construct_message_id, parse_message_id, TX_HASH_EVENT_INDEX_SEPARATOR}; + + #[test] + fn should_be_able_to_parse_and_then_reconstruct_msg_id() { + let message_id: nonempty::String = format!( + "{}{}{}", + "a83c04cc4b86ae3095f2d0db4180db2c4065f1506955b244eda65d3a1ce733af", + TX_HASH_EVENT_INDEX_SEPARATOR, + 1 + ) + .try_into() + .unwrap(); + let (hash, event_index) = parse_message_id(&message_id).unwrap(); + let reconstructed = construct_message_id( + HexBinary::from_hex(&hash) + .unwrap() + .as_slice() + .try_into() + .unwrap(), + event_index, + ); + assert_eq!(message_id.to_string(), reconstructed); + } + + #[test] + fn should_not_parse_msg_id_without_event_index() { + let message_id: nonempty::String = + "a83c04cc4b86ae3095f2d0db4180db2c4065f1506955b244eda65d3a1ce733af" + .try_into() + .unwrap(); + let res = parse_message_id(&message_id); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_wrong_seperator() { + let message_id: nonempty::String = format!( + "{}{}{}", + "a83c04cc4b86ae3095f2d0db4180db2c4065f1506955b244eda65d3a1ce733af", "+", 1 + ) + .try_into() + .unwrap(); + let res = parse_message_id(&message_id); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_non_integer_event_index() { + let message_id: nonempty::String = + "a83c04cc4b86ae3095f2d0db4180db2c4065f1506955b244eda65d3a1ce733af-foobar" + .try_into() + .unwrap(); + let res = parse_message_id(&message_id); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_event_index_with_leading_zeroes() { + let message_id: nonempty::String = + "a83c04cc4b86ae3095f2d0db4180db2c4065f1506955b244eda65d3a1ce733af-01" + .try_into() + .unwrap(); + let res = parse_message_id(&message_id); + assert!(res.is_err()); + } +} diff --git a/contracts/voting-verifier/src/execute.rs b/contracts/voting-verifier/src/execute.rs index 28351eb5a..ee07eb564 100644 --- a/contracts/voting-verifier/src/execute.rs +++ b/contracts/voting-verifier/src/execute.rs @@ -1,5 +1,6 @@ use cosmwasm_std::{ - to_binary, Deps, DepsMut, Env, MessageInfo, QueryRequest, Response, Storage, WasmMsg, WasmQuery, + to_json_binary, Deps, DepsMut, Env, MessageInfo, OverflowError, OverflowOperation, + QueryRequest, Response, Storage, WasmMsg, WasmQuery, }; use axelar_wasm_std::{ @@ -11,8 +12,7 @@ use axelar_wasm_std::{ }; use connection_router_api::{ChainName, Message}; -use service_registry::msg::QueryMsg; -use service_registry::state::Worker; +use service_registry::{msg::QueryMsg, state::WeightedWorker}; use crate::events::{ PollEnded, PollMetadata, PollStarted, TxEventConfirmation, Voted, WorkerSetConfirmation, @@ -37,13 +37,9 @@ pub fn verify_worker_set( let config = CONFIG.load(deps.storage)?; let snapshot = take_snapshot(deps.as_ref(), &config.source_chain)?; let participants = snapshot.get_participants(); + let expires_at = calculate_expiration(env.block.height, config.block_expiry)?; - let poll_id = create_worker_set_poll( - deps.storage, - env.block.height, - config.block_expiry, - snapshot, - )?; + let poll_id = create_worker_set_poll(deps.storage, expires_at, snapshot)?; POLL_WORKER_SETS.save( deps.storage, @@ -59,7 +55,7 @@ pub fn verify_worker_set( source_chain: config.source_chain, source_gateway_address: config.source_gateway_address, confirmation_height: config.confirmation_height, - expires_at: env.block.height + config.block_expiry, + expires_at, participants, }, } @@ -92,7 +88,7 @@ pub fn verify_messages( .map(|message| message_status(deps.as_ref(), &message).map(|status| (status, message))) .collect::, _>>()?; - let response = Response::new().set_data(to_binary(&VerifyMessagesResponse { + let response = Response::new().set_data(to_json_binary(&VerifyMessagesResponse { verification_statuses: messages .iter() .map(|(status, message)| (message.cc_id.to_owned(), status.to_owned())) @@ -117,13 +113,9 @@ pub fn verify_messages( let snapshot = take_snapshot(deps.as_ref(), &msgs_to_verify[0].cc_id.chain)?; let participants = snapshot.get_participants(); - let id = create_messages_poll( - deps.storage, - env.block.height, - config.block_expiry, - snapshot, - msgs_to_verify.len(), - )?; + let expires_at = calculate_expiration(env.block.height, config.block_expiry)?; + + let id = create_messages_poll(deps.storage, expires_at, snapshot, msgs_to_verify.len())?; for (idx, message) in msgs_to_verify.iter().enumerate() { POLL_MESSAGES.save( @@ -146,7 +138,7 @@ pub fn verify_messages( source_chain: config.source_chain, source_gateway_address: config.source_gateway_address, confirmation_height: config.confirmation_height, - expires_at: env.block.height + config.block_expiry, + expires_at, participants, }, } @@ -200,7 +192,7 @@ pub fn end_poll(deps: DepsMut, env: Env, poll_id: PollId) -> Result Result Result { @@ -234,15 +226,16 @@ fn take_snapshot(deps: Deps, chain: &ChainName) -> Result = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: config.service_registry_contract.to_string(), - msg: to_binary(&active_workers_query)?, - }))?; + let workers: Vec = + deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: config.service_registry_contract.to_string(), + msg: to_json_binary(&active_workers_query)?, + }))?; let participants = workers .into_iter() - .map(service_registry::state::Worker::try_into) - .collect::, _>>()?; + .map(service_registry::state::WeightedWorker::into) + .collect::>(); Ok(snapshot::Snapshot::new( config.voting_threshold, @@ -252,13 +245,12 @@ fn take_snapshot(deps: Deps, chain: &ChainName) -> Result Result { let id = POLL_ID.incr(store)?; - let poll = WeightedPoll::new(id, snapshot, block_height + expiry, 1); + let poll = WeightedPoll::new(id, snapshot, expires_at, 1); POLLS.save(store, id, &state::Poll::ConfirmWorkerSet(poll))?; Ok(id) @@ -266,15 +258,21 @@ fn create_worker_set_poll( fn create_messages_poll( store: &mut dyn Storage, - block_height: u64, - expiry: u64, + expires_at: u64, snapshot: snapshot::Snapshot, poll_size: usize, ) -> Result { let id = POLL_ID.incr(store)?; - let poll = WeightedPoll::new(id, snapshot, block_height + expiry, poll_size); + let poll = WeightedPoll::new(id, snapshot, expires_at, poll_size); POLLS.save(store, id, &state::Poll::Messages(poll))?; Ok(id) } + +fn calculate_expiration(block_height: u64, block_expiry: u64) -> Result { + block_height + .checked_add(block_expiry) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, block_height, block_expiry)) + .map_err(ContractError::from) +} diff --git a/contracts/voting-verifier/src/query.rs b/contracts/voting-verifier/src/query.rs index ecc5a221e..ca61ccde2 100644 --- a/contracts/voting-verifier/src/query.rs +++ b/contracts/voting-verifier/src/query.rs @@ -87,6 +87,7 @@ mod tests { }; use cosmwasm_std::{testing::mock_dependencies, Addr, Uint256, Uint64}; + use crate::events::TX_HASH_EVENT_INDEX_SEPARATOR; use crate::state::PollContent; use super::*; @@ -197,11 +198,13 @@ mod tests { fn message(id: u64) -> Message { Message { cc_id: CrossChainId { - chain: "source_chain".parse().unwrap(), - id: format!("id:{id}").parse().unwrap(), + chain: "source-chain".parse().unwrap(), + id: format!("id{TX_HASH_EVENT_INDEX_SEPARATOR}{id}") + .parse() + .unwrap(), }, source_address: format!("source_address{id}").parse().unwrap(), - destination_chain: format!("destination_chain{id}").parse().unwrap(), + destination_chain: format!("destination-chain{id}").parse().unwrap(), destination_address: format!("destination_address{id}").parse().unwrap(), payload_hash: [0; 32], } diff --git a/doc/src/contracts/connection_router.md b/doc/src/contracts/connection_router.md index 319756c57..f2e46720b 100644 --- a/doc/src/contracts/connection_router.md +++ b/doc/src/contracts/connection_router.md @@ -77,10 +77,12 @@ pub struct GatewayUnfrozen { pub struct ChainFrozen { pub name: ChainName, + pub direction: GatewayDirection, } pub struct ChainUnfrozen { pub name: ChainName, + pub direction: GatewayDirection, } pub struct MessageRouted { diff --git a/doc/src/contracts/multisig.md b/doc/src/contracts/multisig.md index 3f27445e8..89fe9e638 100644 --- a/doc/src/contracts/multisig.md +++ b/doc/src/contracts/multisig.md @@ -94,6 +94,11 @@ SignatureVerifier->>Multisig: returns true/false deactivate Multisig ``` +## Authorization +Prior to calling `StartSigningSession`, the prover contract must first be *authorized*. +For a contract to become authorized, the governance account needs to call `AuthorizeCaller`, and specify the contract address to authorize. +Similarly, the governance account can revoke authorization of a particular contract by calling `UnauthorizeCaller`. + ## Interface ```Rust @@ -108,9 +113,14 @@ pub enum ExecuteMsg { session_id: Uint64, signature: HexBinary, }, + RegisterWorkerSet { + worker_set: WorkerSet, + }, + // callable only by governance AuthorizeCaller { contract_address: Addr, }, + // callable only by governance UnauthorizeCaller { contract_address: Addr, }, @@ -161,11 +171,11 @@ pub enum Event { worker: Addr, public_key: PublicKey, }, - // Emitted when a StartSigningSession caller is authorized + // Emitted when a contract is authorized by governance to create signing sessions CallerAuthorized { contract_address: Addr, }, - // Emitted when a StartSigningSession caller is unauthorized + // Emitted when a contract is unauthorized by governance to create signing sessions CallerUnauthorized { contract_address: Addr, }, diff --git a/doc/src/overview.md b/doc/src/overview.md index a7c7b7ef5..7b67f3b8f 100644 --- a/doc/src/overview.md +++ b/doc/src/overview.md @@ -187,12 +187,12 @@ messages from the gateway, and interacts with the multisig contract to conduct t ### Multisig Contract [`multisig`](contracts/multisig.md) is responsible for signing arbitrary blobs of data. Contracts register with the -multisig contract to generate a key id, and then use that key id to initiate signing sessions. Off chain workers +multisig contract to generate a key id, and then use that key id to initiate signing sessions. Off chain verifiers associated with the key id sign messages when new signing sessions are created. ### Service Registry -[`service-registry`](contracts/service_registry.md) is responsible for tracking workers associated with specific -services. Two example services are voting and signing. Workers must be authorized to join a service via governance -vote. Once authorized, workers must also bond a sufficient amount of stake before becoming active in the service. +[`service-registry`](contracts/service_registry.md) is responsible for tracking verifiers associated with specific +services. Two example services are voting and signing. Verifiers must be authorized to join a service via governance +vote. Once authorized, verifiers must also bond a sufficient amount of stake before becoming active in the service. Services query the service registry to create weighted snapshots of the active worker set. diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 0cf916950..4b251f229 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -57,3 +57,6 @@ sha3 = { workspace = true } thiserror = { workspace = true } tofn = { git = "https://github.com/axelarnetwork/tofn.git", branch = "update-deps" } voting-verifier = { workspace = true } + +[lints] +workspace = true diff --git a/integration-tests/src/connection_router_contract.rs b/integration-tests/src/connection_router_contract.rs new file mode 100644 index 000000000..996ceaf28 --- /dev/null +++ b/integration-tests/src/connection_router_contract.rs @@ -0,0 +1,45 @@ +use crate::contract::Contract; +use cosmwasm_std::Addr; +use cw_multi_test::{App, ContractWrapper, Executor}; + +#[derive(Clone)] +pub struct ConnectionRouterContract { + pub contract_addr: Addr, +} + +impl ConnectionRouterContract { + pub fn instantiate_contract(app: &mut App, admin: Addr, governance: Addr, nexus: Addr) -> Self { + let code = ContractWrapper::new( + connection_router::contract::execute, + connection_router::contract::instantiate, + connection_router::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + let contract_addr = app + .instantiate_contract( + code_id, + Addr::unchecked("router"), + &connection_router::msg::InstantiateMsg { + admin_address: admin.to_string(), + governance_address: governance.to_string(), + nexus_gateway: nexus.to_string(), + }, + &[], + "connection_router", + None, + ) + .unwrap(); + + ConnectionRouterContract { contract_addr } + } +} + +impl Contract for ConnectionRouterContract { + type QMsg = connection_router_api::msg::QueryMsg; + type ExMsg = connection_router_api::msg::ExecuteMsg; + + fn contract_address(&self) -> Addr { + self.contract_addr.clone() + } +} diff --git a/integration-tests/src/contract.rs b/integration-tests/src/contract.rs index 190aa24fb..4d2cea980 100644 --- a/integration-tests/src/contract.rs +++ b/integration-tests/src/contract.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Addr, Coin, StdError}; +use cosmwasm_std::{Addr, Coin, StdError, StdResult}; use cw_multi_test::{App, AppResponse, Executor}; use error_stack::{report, Result}; use serde::de::DeserializeOwned; @@ -9,13 +9,12 @@ pub trait Contract { type ExMsg; fn contract_address(&self) -> Addr; - fn query(&self, app: &App, query_message: &Self::QMsg) -> T + fn query(&self, app: &App, query_message: &Self::QMsg) -> StdResult where Self::QMsg: Serialize, { app.wrap() .query_wasm_smart(self.contract_address(), query_message) - .unwrap() } fn execute( diff --git a/integration-tests/src/gateway_contract.rs b/integration-tests/src/gateway_contract.rs new file mode 100644 index 000000000..7ed4b8585 --- /dev/null +++ b/integration-tests/src/gateway_contract.rs @@ -0,0 +1,48 @@ +use crate::contract::Contract; +use cosmwasm_std::Addr; +use cw_multi_test::{App, ContractWrapper, Executor}; + +#[derive(Clone)] +pub struct GatewayContract { + pub contract_addr: Addr, +} + +impl GatewayContract { + pub fn instantiate_contract( + app: &mut App, + router_address: Addr, + verifier_address: Addr, + ) -> Self { + let code = ContractWrapper::new( + gateway::contract::execute, + gateway::contract::instantiate, + gateway::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + let contract_addr = app + .instantiate_contract( + code_id, + Addr::unchecked("anyone"), + &gateway::msg::InstantiateMsg { + router_address: router_address.to_string(), + verifier_address: verifier_address.to_string(), + }, + &[], + "gateway", + None, + ) + .unwrap(); + + GatewayContract { contract_addr } + } +} + +impl Contract for GatewayContract { + type QMsg = gateway_api::msg::QueryMsg; + type ExMsg = gateway_api::msg::ExecuteMsg; + + fn contract_address(&self) -> Addr { + self.contract_addr.clone() + } +} diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index 2943dbb50..f9192b121 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -1 +1,9 @@ +pub mod connection_router_contract; pub mod contract; +pub mod gateway_contract; +pub mod multisig_contract; +pub mod multisig_prover_contract; +pub mod protocol; +pub mod rewards_contract; +pub mod service_registry_contract; +pub mod voting_verifier_contract; diff --git a/integration-tests/src/multisig_contract.rs b/integration-tests/src/multisig_contract.rs new file mode 100644 index 000000000..63d35b0d0 --- /dev/null +++ b/integration-tests/src/multisig_contract.rs @@ -0,0 +1,50 @@ +use crate::contract::Contract; +use cosmwasm_std::Addr; +use cw_multi_test::{App, ContractWrapper, Executor}; + +#[derive(Clone)] +pub struct MultisigContract { + pub contract_addr: Addr, +} + +impl MultisigContract { + pub fn instantiate_contract( + app: &mut App, + governance: Addr, + rewards_address: Addr, + block_expiry: u64, + ) -> Self { + let code = ContractWrapper::new( + multisig::contract::execute, + multisig::contract::instantiate, + multisig::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + let contract_addr = app + .instantiate_contract( + code_id, + Addr::unchecked("anyone"), + &multisig::msg::InstantiateMsg { + rewards_address: rewards_address.to_string(), + governance_address: governance.to_string(), + block_expiry, + }, + &[], + "multisig", + None, + ) + .unwrap(); + + MultisigContract { contract_addr } + } +} + +impl Contract for MultisigContract { + type QMsg = multisig::msg::QueryMsg; + type ExMsg = multisig::msg::ExecuteMsg; + + fn contract_address(&self) -> Addr { + self.contract_addr.clone() + } +} diff --git a/integration-tests/src/multisig_prover_contract.rs b/integration-tests/src/multisig_prover_contract.rs new file mode 100644 index 000000000..c59bcc1a9 --- /dev/null +++ b/integration-tests/src/multisig_prover_contract.rs @@ -0,0 +1,70 @@ +use crate::{contract::Contract, protocol::Protocol}; +use axelar_wasm_std::Threshold; +use cosmwasm_std::{Addr, Uint256}; +use cw_multi_test::{ContractWrapper, Executor}; +use multisig::key::KeyType; +use multisig_prover::encoding::Encoder; + +#[derive(Clone)] +pub struct MultisigProverContract { + pub contract_addr: Addr, + pub admin_addr: Addr, +} + +impl MultisigProverContract { + pub fn instantiate_contract( + protocol: &mut Protocol, + admin_address: Addr, + gateway_address: Addr, + voting_verifier_address: Addr, + chain_name: String, + ) -> Self { + let code = ContractWrapper::new( + multisig_prover::contract::execute, + multisig_prover::contract::instantiate, + multisig_prover::contract::query, + ) + .with_reply(multisig_prover::contract::reply); + let app = &mut protocol.app; + let code_id = app.store_code(Box::new(code)); + + let contract_addr = app + .instantiate_contract( + code_id, + Addr::unchecked("anyone"), + &multisig_prover::msg::InstantiateMsg { + admin_address: admin_address.to_string(), + governance_address: protocol.governance_address.to_string(), + gateway_address: gateway_address.to_string(), + multisig_address: protocol.multisig.contract_addr.to_string(), + service_registry_address: protocol.service_registry.contract_addr.to_string(), + voting_verifier_address: voting_verifier_address.to_string(), + destination_chain_id: Uint256::zero(), + signing_threshold: Threshold::try_from((2, 3)).unwrap().try_into().unwrap(), + service_name: protocol.service_name.to_string(), + chain_name: chain_name.to_string(), + worker_set_diff_threshold: 0, + encoder: Encoder::Abi, + key_type: KeyType::Ecdsa, + }, + &[], + "multisig_prover", + None, + ) + .unwrap(); + + MultisigProverContract { + contract_addr, + admin_addr: admin_address, + } + } +} + +impl Contract for MultisigProverContract { + type QMsg = multisig_prover::msg::QueryMsg; + type ExMsg = multisig_prover::msg::ExecuteMsg; + + fn contract_address(&self) -> Addr { + self.contract_addr.clone() + } +} diff --git a/integration-tests/src/protocol.rs b/integration-tests/src/protocol.rs new file mode 100644 index 000000000..c3a79a493 --- /dev/null +++ b/integration-tests/src/protocol.rs @@ -0,0 +1,21 @@ +use axelar_wasm_std::nonempty; +use cosmwasm_std::Addr; +use cw_multi_test::App; + +use crate::{ + connection_router_contract::ConnectionRouterContract, multisig_contract::MultisigContract, + rewards_contract::RewardsContract, service_registry_contract::ServiceRegistryContract, +}; + +pub struct Protocol { + pub genesis_address: Addr, // holds u128::max coins, can use to send coins to other addresses + pub governance_address: Addr, + pub connection_router: ConnectionRouterContract, + pub router_admin_address: Addr, + pub multisig: MultisigContract, + pub service_registry: ServiceRegistryContract, + pub service_name: nonempty::String, + pub rewards: RewardsContract, + pub rewards_params: rewards::msg::Params, + pub app: App, +} diff --git a/integration-tests/src/rewards_contract.rs b/integration-tests/src/rewards_contract.rs new file mode 100644 index 000000000..48f1f02dc --- /dev/null +++ b/integration-tests/src/rewards_contract.rs @@ -0,0 +1,50 @@ +use crate::contract::Contract; +use cosmwasm_std::{Addr, Binary, Deps, Env, StdResult}; +use cw_multi_test::{App, ContractWrapper, Executor}; + +#[derive(Clone)] +pub struct RewardsContract { + pub contract_addr: Addr, +} + +impl RewardsContract { + pub fn instantiate_contract( + app: &mut App, + governance: Addr, + rewards_denom: String, + params: rewards::msg::Params, + ) -> Self { + let code = ContractWrapper::new( + rewards::contract::execute, + rewards::contract::instantiate, + |_: Deps, _: Env, _: rewards::msg::QueryMsg| -> StdResult { todo!() }, + ); + let code_id = app.store_code(Box::new(code)); + + let contract_addr = app + .instantiate_contract( + code_id, + Addr::unchecked("anyone"), + &rewards::msg::InstantiateMsg { + governance_address: governance.to_string(), + rewards_denom, + params, + }, + &[], + "rewards", + None, + ) + .unwrap(); + + RewardsContract { contract_addr } + } +} + +impl Contract for RewardsContract { + type QMsg = rewards::msg::QueryMsg; + type ExMsg = rewards::msg::ExecuteMsg; + + fn contract_address(&self) -> Addr { + self.contract_addr.clone() + } +} diff --git a/integration-tests/src/service_registry_contract.rs b/integration-tests/src/service_registry_contract.rs new file mode 100644 index 000000000..34d79c477 --- /dev/null +++ b/integration-tests/src/service_registry_contract.rs @@ -0,0 +1,40 @@ +use crate::contract::Contract; +use cosmwasm_std::Addr; +use cw_multi_test::{App, ContractWrapper, Executor}; +use service_registry::contract::{execute, instantiate, query}; + +#[derive(Clone)] +pub struct ServiceRegistryContract { + pub contract_addr: Addr, +} + +impl ServiceRegistryContract { + pub fn instantiate_contract(app: &mut App, governance: Addr) -> Self { + let code = ContractWrapper::new(execute, instantiate, query); + let code_id = app.store_code(Box::new(code)); + + let contract_addr = app + .instantiate_contract( + code_id, + Addr::unchecked("anyone"), + &service_registry::msg::InstantiateMsg { + governance_account: governance.clone().into(), + }, + &[], + "service_registry", + None, + ) + .unwrap(); + + ServiceRegistryContract { contract_addr } + } +} + +impl Contract for ServiceRegistryContract { + type QMsg = service_registry::msg::QueryMsg; + type ExMsg = service_registry::msg::ExecuteMsg; + + fn contract_address(&self) -> Addr { + self.contract_addr.clone() + } +} diff --git a/integration-tests/src/voting_verifier_contract.rs b/integration-tests/src/voting_verifier_contract.rs new file mode 100644 index 000000000..9d061a23e --- /dev/null +++ b/integration-tests/src/voting_verifier_contract.rs @@ -0,0 +1,61 @@ +use crate::contract::Contract; +use axelar_wasm_std::nonempty; +use axelar_wasm_std::MajorityThreshold; +use connection_router_api::ChainName; +use cosmwasm_std::Addr; +use cw_multi_test::{App, ContractWrapper, Executor}; + +#[derive(Clone)] +pub struct VotingVerifierContract { + pub contract_addr: Addr, +} + +impl VotingVerifierContract { + pub fn instantiate_contract( + app: &mut App, + service_registry_address: nonempty::String, + service_name: nonempty::String, + source_gateway_address: nonempty::String, + voting_threshold: MajorityThreshold, + source_chain: ChainName, + rewards_address: Addr, + ) -> Self { + let code = ContractWrapper::new( + voting_verifier::contract::execute, + voting_verifier::contract::instantiate, + voting_verifier::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + let contract_addr = app + .instantiate_contract( + code_id, + Addr::unchecked("anyone"), + &voting_verifier::msg::InstantiateMsg { + service_registry_address, + service_name, + source_gateway_address, + voting_threshold, + block_expiry: 10, + confirmation_height: 5, + source_chain, + rewards_address: rewards_address.to_string(), + }, + &[], + "voting_verifier", + None, + ) + .unwrap(); + + VotingVerifierContract { contract_addr } + } +} + +impl Contract for VotingVerifierContract { + type QMsg = voting_verifier::msg::QueryMsg; + type ExMsg = voting_verifier::msg::ExecuteMsg; + + fn contract_address(&self) -> Addr { + self.contract_addr.clone() + } +} diff --git a/integration-tests/tests/message_routing.rs b/integration-tests/tests/message_routing.rs index f85a994da..e61425c36 100644 --- a/integration-tests/tests/message_routing.rs +++ b/integration-tests/tests/message_routing.rs @@ -1,9 +1,10 @@ -use connection_router_api::{CrossChainId, Message}; use cosmwasm_std::{HexBinary, Uint128}; +use connection_router_api::{CrossChainId, Message}; + use crate::test_utils::AXL_DENOMINATION; -mod test_utils; +pub mod test_utils; /// Tests that a single message can be routed fully through the protocol. Submits a message to the /// gateway, votes on the poll, routes the message to the outgoing gateway, triggers signing at the prover /// and signs via multisig. Also tests that rewards are distributed as expected for voting and signing. @@ -14,7 +15,7 @@ fn single_message_can_be_verified_and_routed_and_proven_and_rewards_are_distribu let msgs = vec![Message { cc_id: CrossChainId { chain: chain1.chain_name.clone(), - id: "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924:3" + id: "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924-3" .to_string() .try_into() .unwrap(), @@ -39,13 +40,12 @@ fn single_message_can_be_verified_and_routed_and_proven_and_rewards_are_distribu let msg_ids: Vec = msgs.iter().map(|msg| msg.cc_id.clone()).collect(); // start the flow by submitting the message to the gateway - let (poll_id, expiry) = - test_utils::verify_messages(&mut protocol.app, &chain1.gateway_address, &msgs); + let (poll_id, expiry) = test_utils::verify_messages(&mut protocol.app, &chain1.gateway, &msgs); // do voting test_utils::vote_success_for_all_messages( &mut protocol.app, - &chain1.voting_verifier_address, + &chain1.voting_verifier, &msgs, &workers, poll_id, @@ -53,30 +53,25 @@ fn single_message_can_be_verified_and_routed_and_proven_and_rewards_are_distribu test_utils::advance_at_least_to_height(&mut protocol.app, expiry); - test_utils::end_poll(&mut protocol.app, &chain1.voting_verifier_address, poll_id); + test_utils::end_poll(&mut protocol.app, &chain1.voting_verifier, poll_id); // should be verified, now route - test_utils::route_messages(&mut protocol.app, &chain1.gateway_address, &msgs); + test_utils::route_messages(&mut protocol.app, &chain1.gateway, &msgs); // check that the message can be found at the outgoing gateway let found_msgs = - test_utils::get_messages_from_gateway(&mut protocol.app, &chain2.gateway_address, &msg_ids); + test_utils::get_messages_from_gateway(&mut protocol.app, &chain2.gateway, &msg_ids); assert_eq!(found_msgs, msgs); // trigger signing and submit all necessary signatures let session_id = test_utils::construct_proof_and_sign( - &mut protocol.app, - &chain2.multisig_prover_address, - &protocol.multisig_address, + &mut protocol, + &chain2.multisig_prover, &msgs, &workers, ); - let proof = test_utils::get_proof( - &mut protocol.app, - &chain2.multisig_prover_address, - &session_id, - ); + let proof = test_utils::get_proof(&mut protocol.app, &chain2.multisig_prover, &session_id); // proof should be complete by now assert!(matches!( @@ -92,18 +87,14 @@ fn single_message_can_be_verified_and_routed_and_proven_and_rewards_are_distribu ); test_utils::distribute_rewards( - &mut protocol.app, - &protocol.rewards_address, + &mut protocol, &chain1.chain_name, - &chain1.voting_verifier_address, - ); - test_utils::distribute_rewards( - &mut protocol.app, - &protocol.rewards_address, - &chain2.chain_name, - &protocol.multisig_address, + chain1.voting_verifier.contract_addr.clone(), ); + let protocol_multisig_address = protocol.multisig.contract_addr.clone(); + test_utils::distribute_rewards(&mut protocol, &chain2.chain_name, protocol_multisig_address); + // rewards split evenly amongst all workers, but there are two contracts that rewards should have been distributed for let expected_rewards = Uint128::from(protocol.rewards_params.rewards_per_epoch) / Uint128::from(workers.len() as u64) diff --git a/integration-tests/tests/test_utils/mod.rs b/integration-tests/tests/test_utils/mod.rs index 7176f9ef6..0282ff4e9 100644 --- a/integration-tests/tests/test_utils/mod.rs +++ b/integration-tests/tests/test_utils/mod.rs @@ -5,10 +5,19 @@ use axelar_wasm_std::{ }; use connection_router_api::{ChainName, CrossChainId, Message}; use cosmwasm_std::{ - coins, Addr, Attribute, Binary, BlockInfo, Deps, Env, Event, HexBinary, StdResult, Uint128, - Uint256, Uint64, + coins, Addr, Attribute, BlockInfo, Event, HexBinary, StdError, Uint128, Uint256, Uint64, }; -use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; +use cw_multi_test::{App, AppResponse, Executor}; + +use integration_tests::contract::Contract; +use integration_tests::gateway_contract::GatewayContract; +use integration_tests::multisig_contract::MultisigContract; +use integration_tests::multisig_prover_contract::MultisigProverContract; +use integration_tests::rewards_contract::RewardsContract; +use integration_tests::service_registry_contract::ServiceRegistryContract; +use integration_tests::voting_verifier_contract::VotingVerifierContract; +use integration_tests::{connection_router_contract::ConnectionRouterContract, protocol::Protocol}; + use k256::ecdsa; use sha3::{Digest, Keccak256}; @@ -18,6 +27,7 @@ use multisig::{ }; use multisig_prover::encoding::{make_operators, Encoder}; use rewards::state::PoolId; +use service_registry::msg::ExecuteMsg; use tofn::ecdsa::KeyPair; pub const AXL_DENOMINATION: &str = "uaxl"; @@ -41,16 +51,16 @@ type PollExpiryBlock = u64; pub fn verify_messages( app: &mut App, - gateway_address: &Addr, + gateway: &GatewayContract, msgs: &[Message], ) -> (PollId, PollExpiryBlock) { - let response = app.execute_contract( + let response = gateway.execute( + app, Addr::unchecked("relayer"), - gateway_address.clone(), &gateway_api::msg::ExecuteMsg::VerifyMessages(msgs.to_vec()), - &[], ); assert!(response.is_ok()); + let response = response.unwrap(); let poll_id = get_event_attribute(&response.events, "wasm-messages_poll_started", "poll_id") @@ -62,33 +72,30 @@ pub fn verify_messages( (poll_id, expiry) } -pub fn route_messages(app: &mut App, gateway_address: &Addr, msgs: &[Message]) { - let response = app.execute_contract( +pub fn route_messages(app: &mut App, gateway: &GatewayContract, msgs: &[Message]) { + let response = gateway.execute( + app, Addr::unchecked("relayer"), - gateway_address.clone(), &gateway_api::msg::ExecuteMsg::RouteMessages(msgs.to_vec()), - &[], ); - assert!(response.is_ok()); } pub fn vote_success_for_all_messages( app: &mut App, - voting_verifier_address: &Addr, - messages: &Vec, - workers: &Vec, + voting_verifier: &VotingVerifierContract, + messages: &[Message], + workers: &[Worker], poll_id: PollId, ) { for worker in workers { - let response = app.execute_contract( + let response = voting_verifier.execute( + app, worker.addr.clone(), - voting_verifier_address.clone(), &voting_verifier::msg::ExecuteMsg::Vote { poll_id, votes: vec![Vote::SucceededOnChain; messages.len()], }, - &[], ); assert!(response.is_ok()); } @@ -96,62 +103,52 @@ pub fn vote_success_for_all_messages( pub fn vote_true_for_worker_set( app: &mut App, - voting_verifier_address: &Addr, + voting_verifier: &VotingVerifierContract, workers: &Vec, poll_id: PollId, ) { for worker in workers { - let response = app.execute_contract( + let response = voting_verifier.execute( + app, worker.addr.clone(), - voting_verifier_address.clone(), &voting_verifier::msg::ExecuteMsg::Vote { poll_id, votes: vec![Vote::SucceededOnChain; 1], }, - &[], ); - assert!(response.is_ok()); + assert!(response.is_ok()) } } /// Ends the poll. Be sure the current block height has advanced at least to the poll expiration, else this will fail -pub fn end_poll(app: &mut App, voting_verifier_address: &Addr, poll_id: PollId) { - let response = app.execute_contract( +pub fn end_poll(app: &mut App, voting_verifier: &VotingVerifierContract, poll_id: PollId) { + let response = voting_verifier.execute( + app, Addr::unchecked("relayer"), - voting_verifier_address.clone(), &voting_verifier::msg::ExecuteMsg::EndPoll { poll_id }, - &[], ); - assert!(response.is_ok()); } pub fn construct_proof_and_sign( - app: &mut App, - multisig_prover_address: &Addr, - multisig_address: &Addr, + protocol: &mut Protocol, + multisig_prover: &MultisigProverContract, messages: &[Message], workers: &Vec, ) -> Uint64 { - let response = app.execute_contract( + let response = multisig_prover.execute( + &mut protocol.app, Addr::unchecked("relayer"), - multisig_prover_address.clone(), &multisig_prover::msg::ExecuteMsg::ConstructProof { message_ids: messages.iter().map(|msg| msg.cc_id.clone()).collect(), }, - &[], ); assert!(response.is_ok()); - sign_proof(app, multisig_address, workers, response.unwrap()) + sign_proof(protocol, workers, response.unwrap()) } -pub fn sign_proof( - app: &mut App, - multisig_address: &Addr, - workers: &Vec, - response: AppResponse, -) -> Uint64 { +pub fn sign_proof(protocol: &mut Protocol, workers: &Vec, response: AppResponse) -> Uint64 { let msg_to_sign = get_event_attribute(&response.events, "wasm-signing_started", "msg") .map(|attr| attr.value.clone()) .expect("couldn't find message to sign"); @@ -173,86 +170,83 @@ pub fn sign_proof( let sig = ecdsa::Signature::from_der(&signature).unwrap(); - let response = app.execute_contract( + let response = protocol.multisig.execute( + &mut protocol.app, worker.addr.clone(), - multisig_address.clone(), &multisig::msg::ExecuteMsg::SubmitSignature { session_id, signature: HexBinary::from(sig.to_vec()), }, - &[], ); - assert!(response.is_ok()); } session_id } -pub fn register_service( - app: &mut App, - service_registry: Addr, - governance_addr: Addr, - service_name: nonempty::String, - min_worker_bond: Uint128, -) { - let response = app.execute_contract( - governance_addr, - service_registry, - &service_registry::msg::ExecuteMsg::RegisterService { - service_name: service_name.to_string(), +pub fn register_service(protocol: &mut Protocol, min_worker_bond: Uint128) { + let response = protocol.service_registry.execute( + &mut protocol.app, + protocol.governance_address.clone(), + &ExecuteMsg::RegisterService { + service_name: protocol.service_name.to_string(), service_contract: Addr::unchecked("nowhere"), min_num_workers: 0, max_num_workers: Some(100), - min_worker_bond: min_worker_bond, + min_worker_bond, bond_denom: AXL_DENOMINATION.into(), unbonding_period_days: 10, description: "Some service".into(), }, - &[], ); assert!(response.is_ok()); } pub fn get_messages_from_gateway( app: &mut App, - gateway_address: &Addr, + gateway: &GatewayContract, message_ids: &[CrossChainId], ) -> Vec { - let query_response = app.wrap().query_wasm_smart( - gateway_address, + let query_response: Result, StdError> = gateway.query( + app, &gateway_api::msg::QueryMsg::GetOutgoingMessages { message_ids: message_ids.to_owned(), }, ); assert!(query_response.is_ok()); + query_response.unwrap() } pub fn get_proof( app: &mut App, - multisig_prover_address: &Addr, + multisig_prover: &MultisigProverContract, multisig_session_id: &Uint64, ) -> multisig_prover::msg::GetProofResponse { - let query_response = app.wrap().query_wasm_smart( - multisig_prover_address, - &multisig_prover::msg::QueryMsg::GetProof { - multisig_session_id: *multisig_session_id, - }, - ); + let query_response: Result = multisig_prover + .query( + app, + &multisig_prover::msg::QueryMsg::GetProof { + multisig_session_id: *multisig_session_id, + }, + ); assert!(query_response.is_ok()); + query_response.unwrap() } -pub fn get_worker_set(app: &mut App, multisig_prover_address: &Addr) -> WorkerSet { - let query_response = app.wrap().query_wasm_smart( - multisig_prover_address, - &multisig_prover::msg::QueryMsg::GetWorkerSet, - ); +pub fn get_worker_set( + app: &mut App, + multisig_prover_contract: &MultisigProverContract, +) -> WorkerSet { + let query_response: Result = + multisig_prover_contract.query(app, &multisig_prover::msg::QueryMsg::GetWorkerSet); assert!(query_response.is_ok()); + query_response.unwrap() } +#[allow(clippy::arithmetic_side_effects)] pub fn advance_height(app: &mut App, increment: u64) { let cur_block = app.block_info(); app.set_block(BlockInfo { @@ -271,40 +265,21 @@ pub fn advance_at_least_to_height(app: &mut App, desired_height: u64) { } } -pub fn distribute_rewards( - app: &mut App, - rewards_address: &Addr, - chain_name: &ChainName, - contract_address: &Addr, -) { - let response = app.execute_contract( +pub fn distribute_rewards(protocol: &mut Protocol, chain_name: &ChainName, contract_address: Addr) { + let response = protocol.rewards.execute( + &mut protocol.app, Addr::unchecked("relayer"), - rewards_address.clone(), &rewards::msg::ExecuteMsg::DistributeRewards { pool_id: PoolId { chain_name: chain_name.clone(), - contract: contract_address.clone(), + contract: contract_address, }, epoch_count: None, }, - &[], ); assert!(response.is_ok()); } -pub struct Protocol { - pub genesis_address: Addr, // holds u128::max coins, can use to send coins to other addresses - pub governance_address: Addr, - pub router_address: Addr, - pub router_admin_address: Addr, - pub multisig_address: Addr, - pub service_registry_address: Addr, - pub service_name: nonempty::String, - pub rewards_address: Addr, - pub rewards_params: rewards::msg::Params, - pub app: App, -} - pub fn setup_protocol(service_name: nonempty::String) -> Protocol { let genesis = Addr::unchecked("genesis"); let mut app = App::new(|router, _, storage| { @@ -317,13 +292,11 @@ pub fn setup_protocol(service_name: nonempty::String) -> Protocol { let governance_address = Addr::unchecked("governance"); let nexus_gateway = Addr::unchecked("nexus_gateway"); - let router_address = instantiate_connection_router( + let connection_router = ConnectionRouterContract::instantiate_contract( &mut app, - connection_router::msg::InstantiateMsg { - admin_address: router_admin_address.to_string(), - governance_address: governance_address.to_string(), - nexus_gateway: nexus_gateway.to_string(), - }, + router_admin_address.clone(), + governance_address.clone(), + nexus_gateway.clone(), ); let rewards_params = rewards::msg::Params { @@ -331,38 +304,32 @@ pub fn setup_protocol(service_name: nonempty::String) -> Protocol { rewards_per_epoch: Uint128::from(100u128).try_into().unwrap(), participation_threshold: (1, 2).try_into().unwrap(), }; - let rewards_address = instantiate_rewards( - &mut app, - rewards::msg::InstantiateMsg { - governance_address: governance_address.to_string(), - rewards_denom: AXL_DENOMINATION.to_string(), - params: rewards_params.clone(), - }, - ); - let multisig_address = instantiate_multisig( + let rewards = RewardsContract::instantiate_contract( &mut app, - multisig::msg::InstantiateMsg { - rewards_address: rewards_address.to_string(), - governance_address: governance_address.to_string(), - block_expiry: SIGNATURE_BLOCK_EXPIRY, - }, + governance_address.clone(), + AXL_DENOMINATION.to_string(), + rewards_params.clone(), ); - let service_registry_address = instantiate_service_registry( + + let multisig = MultisigContract::instantiate_contract( &mut app, - service_registry::msg::InstantiateMsg { - governance_account: governance_address.to_string(), - }, + governance_address.clone(), + rewards.contract_addr.clone(), + SIGNATURE_BLOCK_EXPIRY, ); + let service_registry = + ServiceRegistryContract::instantiate_contract(&mut app, governance_address.clone()); + Protocol { genesis_address: genesis, governance_address, - router_address, + connection_router, router_admin_address, - multisig_address, - service_registry_address, + multisig, + service_registry, service_name, - rewards_address, + rewards, rewards_params, app, } @@ -383,59 +350,49 @@ pub struct Worker { pub key_pair: KeyPair, } -pub fn register_workers( - app: &mut App, - service_registry: Addr, - multisig: Addr, - governance_addr: Addr, - genesis: Addr, - workers: &Vec, - service_name: nonempty::String, - min_worker_bond: Uint128, -) { - let response = app.execute_contract( - governance_addr, - service_registry.clone(), - &service_registry::msg::ExecuteMsg::AuthorizeWorkers { +pub fn register_workers(protocol: &mut Protocol, workers: &Vec, min_worker_bond: Uint128) { + let response = protocol.service_registry.execute( + &mut protocol.app, + protocol.governance_address.clone(), + &ExecuteMsg::AuthorizeWorkers { workers: workers .iter() .map(|worker| worker.addr.to_string()) .collect(), - service_name: service_name.to_string(), + service_name: protocol.service_name.to_string(), }, - &[], ); assert!(response.is_ok()); for worker in workers { - let response = app.send_tokens( - genesis.clone(), + let response = protocol.app.send_tokens( + protocol.genesis_address.clone(), worker.addr.clone(), &coins(min_worker_bond.u128(), AXL_DENOMINATION), ); assert!(response.is_ok()); - let response = app.execute_contract( + + let response = protocol.service_registry.execute_with_funds( + &mut protocol.app, worker.addr.clone(), - service_registry.clone(), - &service_registry::msg::ExecuteMsg::BondWorker { - service_name: service_name.to_string(), + &ExecuteMsg::BondWorker { + service_name: protocol.service_name.to_string(), }, &coins(min_worker_bond.u128(), AXL_DENOMINATION), ); assert!(response.is_ok()); - let response = app.execute_contract( + let response = protocol.service_registry.execute( + &mut protocol.app, worker.addr.clone(), - service_registry.clone(), - &service_registry::msg::ExecuteMsg::RegisterChainSupport { - service_name: service_name.to_string(), + &ExecuteMsg::RegisterChainSupport { + service_name: protocol.service_name.to_string(), chains: worker.supported_chains.clone(), }, - &[], ); assert!(response.is_ok()); - let address_hash = Keccak256::digest(&worker.addr.as_bytes()); + let address_hash = Keccak256::digest(worker.addr.as_bytes()); let sig = tofn::ecdsa::sign( worker.key_pair.signing_key(), @@ -444,74 +401,55 @@ pub fn register_workers( .unwrap(); let sig = ecdsa::Signature::from_der(&sig).unwrap(); - let response = app.execute_contract( + let response = protocol.multisig.execute( + &mut protocol.app, worker.addr.clone(), - multisig.clone(), &multisig::msg::ExecuteMsg::RegisterPublicKey { public_key: PublicKey::Ecdsa(HexBinary::from( worker.key_pair.encoded_verifying_key(), )), signed_sender_address: HexBinary::from(sig.to_vec()), }, - &[], ); assert!(response.is_ok()); } } -pub fn deregister_workers( - app: &mut App, - service_registry: Addr, - governance_addr: Addr, - workers: &Vec, - service_name: nonempty::String, -) { - let response = app.execute_contract( - governance_addr, - service_registry.clone(), - &service_registry::msg::ExecuteMsg::UnauthorizeWorkers { +pub fn deregister_workers(protocol: &mut Protocol, workers: &Vec) { + let response = protocol.service_registry.execute( + &mut protocol.app, + protocol.governance_address.clone(), + &ExecuteMsg::UnauthorizeWorkers { workers: workers .iter() .map(|worker| worker.addr.to_string()) .collect(), - service_name: service_name.to_string(), + service_name: protocol.service_name.to_string(), }, - &[], ); assert!(response.is_ok()); for worker in workers { - let response = app.execute_contract( + let response = protocol.service_registry.execute( + &mut protocol.app, worker.addr.clone(), - service_registry.clone(), - &service_registry::msg::ExecuteMsg::UnbondWorker { - service_name: service_name.to_string(), + &ExecuteMsg::UnbondWorker { + service_name: protocol.service_name.to_string(), }, - &[], ); assert!(response.is_ok()); } } -pub fn update_worker_set(app: &mut App, relayer_addr: Addr, multisig_prover: Addr) -> AppResponse { - let response = app.execute_contract( - relayer_addr.clone(), - multisig_prover.clone(), - &multisig_prover::msg::ExecuteMsg::UpdateWorkerSet, - &[], - ); - - assert!(response.is_ok()); - - response.unwrap() -} - -pub fn confirm_worker_set(app: &mut App, relayer_addr: Addr, multisig_prover: Addr) { - let response = app.execute_contract( +pub fn confirm_worker_set( + app: &mut App, + relayer_addr: Addr, + multisig_prover: &MultisigProverContract, +) { + let response = multisig_prover.execute( + app, relayer_addr.clone(), - multisig_prover.clone(), &multisig_prover::msg::ExecuteMsg::ConfirmWorkerSet, - &[], ); assert!(response.is_ok()); } @@ -533,17 +471,18 @@ fn get_worker_set_poll_id_and_expiry(response: AppResponse) -> (PollId, PollExpi pub fn create_worker_set_poll( app: &mut App, relayer_addr: Addr, - voting_verifier: Addr, + voting_verifier: &VotingVerifierContract, worker_set: WorkerSet, ) -> (PollId, PollExpiryBlock) { - let response = app.execute_contract( + let response = voting_verifier.execute( + app, relayer_addr.clone(), - voting_verifier.clone(), &voting_verifier::msg::ExecuteMsg::VerifyWorkerSet { - message_id: "ethereum:00".parse().unwrap(), + message_id: "7477095de32cfca1522076e3581501ddc249c5796622d1194f0b7ef891769bdb-0" + .parse() + .unwrap(), new_operators: make_operators(worker_set.clone(), Encoder::Abi), }, - &[], ); assert!(response.is_ok()); @@ -575,7 +514,7 @@ pub fn workers_to_worker_set(protocol: &mut Protocol, workers: &Vec) -> WorkerSet::new( pubkeys_by_participant, - total_weight.mul_ceil((2u64, 3u64)).into(), + total_weight.mul_ceil((2u64, 3u64)), protocol.app.block_info().height, ) } @@ -594,49 +533,33 @@ pub fn create_new_workers_vec( .collect() } -pub fn update_registry_and_construct_proof( +pub fn update_registry_and_construct_worker_set_update_proof( protocol: &mut Protocol, new_workers: &Vec, workers_to_remove: &Vec, current_workers: &Vec, - chain_multisig_prover_address: &Addr, + chain_multisig_prover: &MultisigProverContract, min_worker_bond: Uint128, ) -> Uint64 { // Register new workers - register_workers( - &mut protocol.app, - protocol.service_registry_address.clone(), - protocol.multisig_address.clone(), - protocol.governance_address.clone(), - protocol.genesis_address.clone(), - new_workers, - protocol.service_name.clone(), - min_worker_bond, - ); + register_workers(protocol, new_workers, min_worker_bond); // Deregister old workers - deregister_workers( + deregister_workers(protocol, workers_to_remove); + + let response = chain_multisig_prover.execute( &mut protocol.app, - protocol.service_registry_address.clone(), - protocol.governance_address.clone(), - workers_to_remove, - protocol.service_name.clone(), + Addr::unchecked("relayer"), + &multisig_prover::msg::ExecuteMsg::UpdateWorkerSet, ); - // Construct proof and sign - construct_proof_and_sign( - &mut protocol.app, - &chain_multisig_prover_address, - &protocol.multisig_address, - &Vec::::new(), - ¤t_workers, - ) + sign_proof(protocol, current_workers, response.unwrap()) } pub fn execute_worker_set_poll( protocol: &mut Protocol, relayer_addr: &Addr, - verifier_address: &Addr, + voting_verifier: &VotingVerifierContract, new_workers: &Vec, ) { // Create worker set @@ -646,118 +569,105 @@ pub fn execute_worker_set_poll( let (poll_id, expiry) = create_worker_set_poll( &mut protocol.app, relayer_addr.clone(), - verifier_address.clone(), + voting_verifier, new_worker_set.clone(), ); // Vote for the worker set - vote_true_for_worker_set(&mut protocol.app, verifier_address, new_workers, poll_id); + vote_true_for_worker_set(&mut protocol.app, voting_verifier, new_workers, poll_id); // Advance to expiration height advance_at_least_to_height(&mut protocol.app, expiry); // End the poll - end_poll(&mut protocol.app, verifier_address, poll_id); + end_poll(&mut protocol.app, voting_verifier, poll_id); } #[derive(Clone)] pub struct Chain { - pub gateway_address: Addr, - pub voting_verifier_address: Addr, - pub multisig_prover_address: Addr, + pub gateway: GatewayContract, + pub voting_verifier: VotingVerifierContract, + pub multisig_prover: MultisigProverContract, pub chain_name: ChainName, } pub fn setup_chain(protocol: &mut Protocol, chain_name: ChainName) -> Chain { - let voting_verifier_address = instantiate_voting_verifier( + let voting_verifier = VotingVerifierContract::instantiate_contract( &mut protocol.app, - voting_verifier::msg::InstantiateMsg { - service_registry_address: protocol - .service_registry_address - .to_string() - .try_into() - .unwrap(), - service_name: protocol.service_name.clone(), - source_gateway_address: "doesn't matter".to_string().try_into().unwrap(), - voting_threshold: Threshold::try_from((9, 10)).unwrap().try_into().unwrap(), - block_expiry: 10, - confirmation_height: 5, - source_chain: chain_name.clone(), - rewards_address: protocol.rewards_address.to_string(), - }, + protocol + .service_registry + .contract_addr + .to_string() + .try_into() + .unwrap(), + protocol.service_name.clone(), + "doesn't matter".to_string().try_into().unwrap(), + Threshold::try_from((9, 10)).unwrap().try_into().unwrap(), + chain_name.clone(), + protocol.rewards.contract_addr.clone(), ); - let gateway_address = instantiate_gateway( + + let gateway = GatewayContract::instantiate_contract( &mut protocol.app, - gateway::msg::InstantiateMsg { - router_address: protocol.router_address.to_string(), - verifier_address: voting_verifier_address.to_string(), - }, + protocol.connection_router.contract_address().clone(), + voting_verifier.contract_addr.clone(), ); - let multisig_prover_address = instantiate_multisig_prover( - &mut protocol.app, - multisig_prover::msg::InstantiateMsg { - admin_address: Addr::unchecked("doesn't matter").to_string(), - gateway_address: gateway_address.to_string(), - multisig_address: protocol.multisig_address.to_string(), - service_registry_address: protocol.service_registry_address.to_string(), - voting_verifier_address: voting_verifier_address.to_string(), - destination_chain_id: Uint256::zero(), - signing_threshold: Threshold::try_from((2, 3)).unwrap().try_into().unwrap(), - service_name: protocol.service_name.to_string(), - chain_name: chain_name.to_string(), - worker_set_diff_threshold: 0, - encoder: Encoder::Abi, - key_type: KeyType::Ecdsa, - }, + + let multisig_prover_admin = Addr::unchecked(chain_name.to_string() + "prover_admin"); + let multisig_prover = MultisigProverContract::instantiate_contract( + protocol, + multisig_prover_admin.clone(), + gateway.contract_addr.clone(), + voting_verifier.contract_addr.clone(), + chain_name.to_string(), ); - let response = protocol.app.execute_contract( - Addr::unchecked("doesn't matter"), - multisig_prover_address.clone(), + + let response = multisig_prover.execute( + &mut protocol.app, + multisig_prover_admin, &multisig_prover::msg::ExecuteMsg::UpdateWorkerSet, - &[], ); assert!(response.is_ok()); - let response = protocol.app.execute_contract( + + let response = protocol.multisig.execute( + &mut protocol.app, protocol.governance_address.clone(), - protocol.multisig_address.clone(), &multisig::msg::ExecuteMsg::AuthorizeCaller { - contract_address: multisig_prover_address.clone(), + contract_address: multisig_prover.contract_addr.clone(), }, - &[], ); assert!(response.is_ok()); - let response = protocol.app.execute_contract( + let response = protocol.connection_router.execute( + &mut protocol.app, protocol.governance_address.clone(), - protocol.router_address.clone(), &connection_router_api::msg::ExecuteMsg::RegisterChain { chain: chain_name.clone(), - gateway_address: gateway_address.to_string().try_into().unwrap(), + gateway_address: gateway.contract_addr.to_string().try_into().unwrap(), }, - &[], ); assert!(response.is_ok()); - let response = protocol.app.execute_contract( + let response = protocol.rewards.execute_with_funds( + &mut protocol.app, protocol.genesis_address.clone(), - protocol.rewards_address.clone(), &rewards::msg::ExecuteMsg::AddRewards { pool_id: PoolId { chain_name: chain_name.clone(), - contract: voting_verifier_address.clone(), + contract: voting_verifier.contract_addr.clone(), }, }, &coins(1000, AXL_DENOMINATION), ); assert!(response.is_ok()); - let response = protocol.app.execute_contract( + let response = protocol.rewards.execute_with_funds( + &mut protocol.app, protocol.genesis_address.clone(), - protocol.rewards_address.clone(), &rewards::msg::ExecuteMsg::AddRewards { pool_id: PoolId { chain_name: chain_name.clone(), - contract: protocol.multisig_address.clone(), + contract: protocol.multisig.contract_addr.clone(), }, }, &coins(1000, AXL_DENOMINATION), @@ -765,173 +675,13 @@ pub fn setup_chain(protocol: &mut Protocol, chain_name: ChainName) -> Chain { assert!(response.is_ok()); Chain { - gateway_address, - voting_verifier_address, - multisig_prover_address, + gateway, + voting_verifier, + multisig_prover, chain_name, } } -pub fn instantiate_connection_router( - app: &mut App, - instantiate_msg: connection_router::msg::InstantiateMsg, -) -> Addr { - let code = ContractWrapper::new( - connection_router::contract::execute, - connection_router::contract::instantiate, - connection_router::contract::query, - ); - let code_id = app.store_code(Box::new(code)); - - let contract_addr = app.instantiate_contract( - code_id, - Addr::unchecked("anyone"), - &instantiate_msg, - &[], - "connection_router", - None, - ); - - assert!(contract_addr.is_ok()); - contract_addr.unwrap() -} - -pub fn instantiate_multisig(app: &mut App, instantiate_msg: multisig::msg::InstantiateMsg) -> Addr { - let code = ContractWrapper::new( - multisig::contract::execute, - multisig::contract::instantiate, - multisig::contract::query, - ); - let code_id = app.store_code(Box::new(code)); - - let contract_addr = app.instantiate_contract( - code_id, - Addr::unchecked("anyone"), - &instantiate_msg, - &[], - "multisig", - None, - ); - - assert!(contract_addr.is_ok()); - contract_addr.unwrap() -} - -pub fn instantiate_rewards(app: &mut App, instantiate_msg: rewards::msg::InstantiateMsg) -> Addr { - let code = ContractWrapper::new( - rewards::contract::execute, - rewards::contract::instantiate, - |_: Deps, _: Env, _: rewards::msg::QueryMsg| -> StdResult { todo!() }, - ); - let code_id = app.store_code(Box::new(code)); - - let contract_addr = app.instantiate_contract( - code_id, - Addr::unchecked("anyone"), - &instantiate_msg, - &[], - "rewards", - None, - ); - - assert!(contract_addr.is_ok()); - contract_addr.unwrap() -} - -pub fn instantiate_voting_verifier( - app: &mut App, - instantiate_msg: voting_verifier::msg::InstantiateMsg, -) -> Addr { - let code = ContractWrapper::new( - voting_verifier::contract::execute, - voting_verifier::contract::instantiate, - voting_verifier::contract::query, - ); - let code_id = app.store_code(Box::new(code)); - - let contract_addr = app.instantiate_contract( - code_id, - Addr::unchecked("anyone"), - &instantiate_msg, - &[], - "voting_verifier", - None, - ); - - assert!(contract_addr.is_ok()); - contract_addr.unwrap() -} - -pub fn instantiate_gateway(app: &mut App, instantiate_msg: gateway::msg::InstantiateMsg) -> Addr { - let code = ContractWrapper::new( - gateway::contract::execute, - gateway::contract::instantiate, - gateway::contract::query, - ); - let code_id = app.store_code(Box::new(code)); - - let contract_addr = app.instantiate_contract( - code_id, - Addr::unchecked("anyone"), - &instantiate_msg, - &[], - "gateway", - None, - ); - - assert!(contract_addr.is_ok()); - contract_addr.unwrap() -} - -pub fn instantiate_service_registry( - app: &mut App, - instantiate_msg: service_registry::msg::InstantiateMsg, -) -> Addr { - let code = ContractWrapper::new( - service_registry::contract::execute, - service_registry::contract::instantiate, - service_registry::contract::query, - ); - let code_id = app.store_code(Box::new(code)); - - let contract_addr = app.instantiate_contract( - code_id, - Addr::unchecked("anyone"), - &instantiate_msg, - &[], - "service_registry", - None, - ); - - assert!(contract_addr.is_ok()); - contract_addr.unwrap() -} - -pub fn instantiate_multisig_prover( - app: &mut App, - instantiate_msg: multisig_prover::msg::InstantiateMsg, -) -> Addr { - let code = ContractWrapper::new( - multisig_prover::contract::execute, - multisig_prover::contract::instantiate, - multisig_prover::contract::query, - ) - .with_reply(multisig_prover::contract::reply); - let code_id = app.store_code(Box::new(code)); - - let contract_addr = app.instantiate_contract( - code_id, - Addr::unchecked("anyone"), - &instantiate_msg, - &[], - "multisig_prover", - None, - ); - - assert!(contract_addr.is_ok()); - contract_addr.unwrap() -} - // Creates an instance of Axelar Amplifier with an initial worker set registered, and returns the instance, the chains, the workers, and the minimum worker bond. pub fn setup_test_case() -> (Protocol, Chain, Chain, Vec, Uint128) { let mut protocol = setup_protocol("validators".to_string().try_into().unwrap()); @@ -952,25 +702,10 @@ pub fn setup_test_case() -> (Protocol, Chain, Chain, Vec, Uint128) { }, ]; let min_worker_bond = Uint128::new(100); - register_service( - &mut protocol.app, - protocol.service_registry_address.clone(), - protocol.governance_address.clone(), - protocol.service_name.clone(), - min_worker_bond.clone(), - ); + register_service(&mut protocol, min_worker_bond); - register_workers( - &mut protocol.app, - protocol.service_registry_address.clone(), - protocol.multisig_address.clone(), - protocol.governance_address.clone(), - protocol.genesis_address.clone(), - &workers, - protocol.service_name.clone(), - min_worker_bond, - ); - let chain1 = setup_chain(&mut protocol, chains.get(0).unwrap().clone()); + register_workers(&mut protocol, &workers, min_worker_bond); + let chain1 = setup_chain(&mut protocol, chains.first().unwrap().clone()); let chain2 = setup_chain(&mut protocol, chains.get(1).unwrap().clone()); (protocol, chain1, chain2, workers, min_worker_bond) } diff --git a/integration-tests/tests/update_worker_set.rs b/integration-tests/tests/update_worker_set.rs index ef739d973..c63199b2f 100644 --- a/integration-tests/tests/update_worker_set.rs +++ b/integration-tests/tests/update_worker_set.rs @@ -1,9 +1,10 @@ -use connection_router_api::Message; use cosmwasm_std::Addr; use cw_multi_test::Executor; + +use integration_tests::contract::Contract; use test_utils::Worker; -mod test_utils; +pub mod test_utils; #[test] fn worker_set_can_be_initialized_and_then_manually_updated() { @@ -16,8 +17,7 @@ fn worker_set_can_be_initialized_and_then_manually_updated() { let simulated_worker_set = test_utils::workers_to_worker_set(&mut protocol, &initial_workers); - let worker_set = - test_utils::get_worker_set(&mut protocol.app, ðereum.multisig_prover_address); + let worker_set = test_utils::get_worker_set(&mut protocol.app, ðereum.multisig_prover); assert_eq!(worker_set, simulated_worker_set); @@ -38,45 +38,25 @@ fn worker_set_can_be_initialized_and_then_manually_updated() { let expected_new_worker_set = test_utils::workers_to_worker_set(&mut protocol, &new_workers); - test_utils::register_workers( - &mut protocol.app, - protocol.service_registry_address.clone(), - protocol.multisig_address.clone(), - protocol.governance_address.clone(), - protocol.genesis_address.clone(), - &new_workers, - protocol.service_name.clone(), - min_worker_bond, - ); + test_utils::register_workers(&mut protocol, &new_workers, min_worker_bond); // remove old workers - test_utils::deregister_workers( - &mut protocol.app, - protocol.service_registry_address.clone(), - protocol.governance_address.clone(), - &initial_workers, - protocol.service_name.clone(), - ); - - let response = test_utils::update_worker_set( - &mut protocol.app, - Addr::unchecked("relayer"), - ethereum.multisig_prover_address.clone(), - ); + test_utils::deregister_workers(&mut protocol, &initial_workers); + + let response = protocol + .app + .execute_contract( + ethereum.multisig_prover.admin_addr.clone(), + ethereum.multisig_prover.contract_addr.clone(), + &multisig_prover::msg::ExecuteMsg::UpdateWorkerSet, + &[], + ) + .unwrap(); // sign with old workers - let session_id = test_utils::sign_proof( - &mut protocol.app, - &protocol.multisig_address, - &initial_workers, - response, - ); + let session_id = test_utils::sign_proof(&mut protocol, &initial_workers, response); - let proof = test_utils::get_proof( - &mut protocol.app, - ðereum.multisig_prover_address, - &session_id, - ); + let proof = test_utils::get_proof(&mut protocol.app, ðereum.multisig_prover, &session_id); assert!(matches!( proof.status, multisig_prover::msg::ProofStatus::Completed { .. } @@ -87,142 +67,29 @@ fn worker_set_can_be_initialized_and_then_manually_updated() { let (poll_id, expiry) = test_utils::create_worker_set_poll( &mut protocol.app, Addr::unchecked("relayer"), - ethereum.voting_verifier_address.clone(), + ðereum.voting_verifier, expected_new_worker_set.clone(), ); // do voting test_utils::vote_true_for_worker_set( &mut protocol.app, - ðereum.voting_verifier_address, + ðereum.voting_verifier, &new_workers, poll_id, ); test_utils::advance_at_least_to_height(&mut protocol.app, expiry); - test_utils::end_poll( - &mut protocol.app, - ðereum.voting_verifier_address, - poll_id, - ); + test_utils::end_poll(&mut protocol.app, ðereum.voting_verifier, poll_id); test_utils::confirm_worker_set( &mut protocol.app, Addr::unchecked("relayer"), - ethereum.multisig_prover_address.clone(), + ðereum.multisig_prover, ); - let new_worker_set = - test_utils::get_worker_set(&mut protocol.app, ðereum.multisig_prover_address); - - assert_eq!(new_worker_set, expected_new_worker_set); -} - -#[test] -fn worker_set_can_be_initialized_and_then_automatically_updated_during_proof_construction() { - let chains = vec![ - "Ethereum".to_string().try_into().unwrap(), - "Polygon".to_string().try_into().unwrap(), - ]; - let (mut protocol, ethereum, _, initial_workers, min_worker_bond) = - test_utils::setup_test_case(); - - let simulated_worker_set = test_utils::workers_to_worker_set(&mut protocol, &initial_workers); - - let worker_set = - test_utils::get_worker_set(&mut protocol.app, ðereum.multisig_prover_address); - - assert_eq!(worker_set, simulated_worker_set); - - // add third and fourth worker - let mut new_workers = Vec::new(); - let new_worker = Worker { - addr: Addr::unchecked("worker3"), - supported_chains: chains.clone(), - key_pair: test_utils::generate_key(2), - }; - new_workers.push(new_worker); - let new_worker = Worker { - addr: Addr::unchecked("worker4"), - supported_chains: chains.clone(), - key_pair: test_utils::generate_key(3), - }; - new_workers.push(new_worker); - - let expected_new_worker_set = test_utils::workers_to_worker_set(&mut protocol, &new_workers); - - test_utils::register_workers( - &mut protocol.app, - protocol.service_registry_address.clone(), - protocol.multisig_address.clone(), - protocol.governance_address.clone(), - protocol.genesis_address.clone(), - &new_workers, - protocol.service_name.clone(), - min_worker_bond, - ); - - // remove old workers - test_utils::deregister_workers( - &mut protocol.app, - protocol.service_registry_address.clone(), - protocol.governance_address.clone(), - &initial_workers, - protocol.service_name.clone(), - ); - - let session_id = test_utils::construct_proof_and_sign( - &mut protocol.app, - ðereum.multisig_prover_address, - &protocol.multisig_address, - &Vec::::new(), - &initial_workers, - ); - - let proof = test_utils::get_proof( - &mut protocol.app, - ðereum.multisig_prover_address, - &session_id, - ); - assert!(matches!( - proof.status, - multisig_prover::msg::ProofStatus::Completed { .. } - )); - - assert_eq!(proof.message_ids.len(), 0); - - let (poll_id, expiry) = test_utils::create_worker_set_poll( - &mut protocol.app, - Addr::unchecked("relayer"), - ethereum.voting_verifier_address.clone(), - expected_new_worker_set.clone(), - ); - - // do voting - test_utils::vote_true_for_worker_set( - &mut protocol.app, - ðereum.voting_verifier_address, - &new_workers, - poll_id, - ); - - test_utils::advance_at_least_to_height(&mut protocol.app, expiry); - - test_utils::end_poll( - &mut protocol.app, - ðereum.voting_verifier_address, - poll_id, - ); - - test_utils::confirm_worker_set( - &mut protocol.app, - Addr::unchecked("relayer"), - ethereum.multisig_prover_address.clone(), - ); - - let new_worker_set = - test_utils::get_worker_set(&mut protocol.app, ðereum.multisig_prover_address); + let new_worker_set = test_utils::get_worker_set(&mut protocol.app, ðereum.multisig_prover); assert_eq!(new_worker_set, expected_new_worker_set); } @@ -238,8 +105,7 @@ fn worker_set_cannot_be_updated_again_while_pending_worker_is_not_yet_confirmed( let simulated_worker_set = test_utils::workers_to_worker_set(&mut protocol, &initial_workers); - let worker_set = - test_utils::get_worker_set(&mut protocol.app, ðereum.multisig_prover_address); + let worker_set = test_utils::get_worker_set(&mut protocol.app, ðereum.multisig_prover); assert_eq!(worker_set, simulated_worker_set); @@ -249,24 +115,24 @@ fn worker_set_cannot_be_updated_again_while_pending_worker_is_not_yet_confirmed( vec![("worker3".to_string(), 2), ("worker4".to_string(), 3)], ); - let first_wave_worker_set = - test_utils::workers_to_worker_set(&mut protocol, &first_wave_of_new_workers); + test_utils::register_workers(&mut protocol, &first_wave_of_new_workers, min_worker_bond); - // register the new workers (3 and 4), deregister all old workers, then create proof and get id - let session_id = test_utils::update_registry_and_construct_proof( - &mut protocol, - &first_wave_of_new_workers, - &initial_workers, - &initial_workers, - ðereum.multisig_prover_address, - min_worker_bond, - ); + // Deregister old workers + test_utils::deregister_workers(&mut protocol, &initial_workers); - let proof = test_utils::get_proof( - &mut protocol.app, - ðereum.multisig_prover_address, - &session_id, - ); + let response = protocol + .app + .execute_contract( + ethereum.multisig_prover.admin_addr.clone(), + ethereum.multisig_prover.contract_addr.clone(), + &multisig_prover::msg::ExecuteMsg::UpdateWorkerSet, + &[], + ) + .unwrap(); + + let session_id = test_utils::sign_proof(&mut protocol, &initial_workers, response); + + let proof = test_utils::get_proof(&mut protocol.app, ðereum.multisig_prover, &session_id); // proof must be completed assert!(matches!( @@ -279,7 +145,7 @@ fn worker_set_cannot_be_updated_again_while_pending_worker_is_not_yet_confirmed( test_utils::execute_worker_set_poll( &mut protocol, &Addr::unchecked("relayer"), - ðereum.voting_verifier_address, + ðereum.voting_verifier, &first_wave_of_new_workers, ); @@ -287,41 +153,15 @@ fn worker_set_cannot_be_updated_again_while_pending_worker_is_not_yet_confirmed( let second_wave_of_new_workers = test_utils::create_new_workers_vec(chains.clone(), vec![("worker5".to_string(), 5)]); - let _second_wave_session_id = test_utils::update_registry_and_construct_proof( - &mut protocol, - &second_wave_of_new_workers, - &first_wave_of_new_workers, - &initial_workers, - ðereum.multisig_prover_address, - min_worker_bond, - ); - - // confirm the first rotation's set of workers - test_utils::confirm_worker_set( - &mut protocol.app, - Addr::unchecked("relayer"), - ethereum.multisig_prover_address.clone(), - ); + test_utils::register_workers(&mut protocol, &second_wave_of_new_workers, min_worker_bond); - // get the latest worker set, it should be equal to the first wave worker set - let latest_worker_set = - test_utils::get_worker_set(&mut protocol.app, ðereum.multisig_prover_address); - assert_eq!(latest_worker_set, first_wave_worker_set); + // Deregister old workers + test_utils::deregister_workers(&mut protocol, &first_wave_of_new_workers); - // attempt to confirm the second rotation - test_utils::execute_worker_set_poll( - &mut protocol, - &Addr::unchecked("relayer"), - ðereum.voting_verifier_address, - &second_wave_of_new_workers, - ); - - let response = protocol.app.execute_contract( + let response = ethereum.multisig_prover.execute( + &mut protocol.app, Addr::unchecked("relayer"), - ethereum.multisig_prover_address.clone(), - &multisig_prover::msg::ExecuteMsg::ConfirmWorkerSet, - &[], + &multisig_prover::msg::ExecuteMsg::UpdateWorkerSet, ); - assert!(response.is_err()); } diff --git a/packages/axelar-wasm-std-derive/Cargo.toml b/packages/axelar-wasm-std-derive/Cargo.toml index 3083a7044..1639c75e7 100644 --- a/packages/axelar-wasm-std-derive/Cargo.toml +++ b/packages/axelar-wasm-std-derive/Cargo.toml @@ -15,3 +15,6 @@ quote = "1.0.33" report = { workspace = true } syn = "2.0.29" thiserror = { workspace = true } + +[lints] +workspace = true diff --git a/packages/axelar-wasm-std/Cargo.toml b/packages/axelar-wasm-std/Cargo.toml index c0a128cfb..ccad58344 100644 --- a/packages/axelar-wasm-std/Cargo.toml +++ b/packages/axelar-wasm-std/Cargo.toml @@ -35,7 +35,7 @@ cosmwasm-storage = { workspace = true } cw-storage-plus = { workspace = true } error-stack = { workspace = true } flagset = { version = "0.4.3", features = ["serde"] } -num-traits = { version = "0.2.14", default-features = false } +num-traits = { workspace = true } report = { workspace = true } schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } @@ -49,3 +49,6 @@ valuable = { version = "0.1.0", features = ["derive"] } cw-multi-test = "0.15.1" hex = { version = "0.4.3", default-features = false } rand = "0.8.5" + +[lints] +workspace = true diff --git a/packages/axelar-wasm-std/src/counter.rs b/packages/axelar-wasm-std/src/counter.rs index fb232bb93..05a709663 100644 --- a/packages/axelar-wasm-std/src/counter.rs +++ b/packages/axelar-wasm-std/src/counter.rs @@ -1,16 +1,16 @@ -use std::ops::AddAssign; - -use cosmwasm_std::{StdResult, Storage}; +use cosmwasm_std::{OverflowError, OverflowOperation, StdError, StdResult, Storage}; use cw_storage_plus::Item; -use num_traits::One; +use num_traits::{CheckedAdd, One}; use serde::de::DeserializeOwned; use serde::Serialize; -pub struct Counter<'a, T: AddAssign + Copy + Default> { +pub struct Counter<'a, T: Copy + Default> { item: Item<'a, T>, } -impl<'a, T: AddAssign + Copy + Default + One + Serialize + DeserializeOwned> Counter<'a, T> { +impl<'a, T: Copy + Default + One + CheckedAdd + Serialize + ToString + DeserializeOwned> + Counter<'a, T> +{ pub const fn new(name: &'a str) -> Self { Counter { item: Item::new(name), @@ -23,7 +23,9 @@ impl<'a, T: AddAssign + Copy + Default + One + Serialize + DeserializeOwned> Cou pub fn incr(&self, store: &mut dyn Storage) -> StdResult { let mut value = self.cur(store); - value += T::one(); + value = value.checked_add(&T::one()).ok_or_else(|| { + StdError::overflow(OverflowError::new(OverflowOperation::Add, value, 1)) + })?; self.item.save(store, &value)?; Ok(value) } @@ -43,6 +45,6 @@ mod tests { assert_eq!(counter.cur(&store), 0); assert_eq!(counter.incr(&mut store).unwrap(), 1); assert_eq!(counter.incr(&mut store).unwrap(), 2); - assert_eq!(counter.cur(&mut store), 2); + assert_eq!(counter.cur(&store), 2); } } diff --git a/packages/axelar-wasm-std/src/nonempty/uint.rs b/packages/axelar-wasm-std/src/nonempty/uint.rs index 862c782e1..afad3a074 100644 --- a/packages/axelar-wasm-std/src/nonempty/uint.rs +++ b/packages/axelar-wasm-std/src/nonempty/uint.rs @@ -52,6 +52,12 @@ impl fmt::Display for Uint64 { #[derive(Copy, PartialOrd, Eq)] pub struct Uint256(cosmwasm_std::Uint256); +impl Uint256 { + pub const fn one() -> Self { + Self(cosmwasm_std::Uint256::one()) + } +} + impl TryFrom for Uint256 { type Error = Error; diff --git a/packages/axelar-wasm-std/src/operators.rs b/packages/axelar-wasm-std/src/operators.rs index ec58163c7..37c0a87e7 100644 --- a/packages/axelar-wasm-std/src/operators.rs +++ b/packages/axelar-wasm-std/src/operators.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{to_binary, HexBinary, Uint256}; +use cosmwasm_std::{to_json_binary, HexBinary, Uint256}; use sha3::{Digest, Keccak256}; @@ -24,7 +24,8 @@ impl Operators { pub fn hash(&self) -> Hash { let mut hasher = Keccak256::new(); hasher.update( - to_binary(&self.weights_by_addresses).expect("could not serialize serializable object"), + to_json_binary(&self.weights_by_addresses) + .expect("could not serialize serializable object"), ); hasher.update(self.threshold.to_be_bytes()); hasher.finalize().into() diff --git a/packages/axelar-wasm-std/src/snapshot.rs b/packages/axelar-wasm-std/src/snapshot.rs index fdf7f40c7..56e67f4fa 100644 --- a/packages/axelar-wasm-std/src/snapshot.rs +++ b/packages/axelar-wasm-std/src/snapshot.rs @@ -28,8 +28,7 @@ impl Snapshot { let participants: HashMap = participants .into_iter() .map(|participant| { - let weight: &Uint256 = participant.weight.as_ref(); - total_weight += weight; + total_weight = total_weight.saturating_add(participant.weight.into()); (participant.address.to_string(), participant) }) @@ -66,7 +65,7 @@ impl Snapshot { #[cfg(test)] mod tests { - use cosmwasm_std::{from_binary, to_binary, Uint64}; + use cosmwasm_std::{from_json, to_json_binary, Uint64}; use crate::Threshold; @@ -134,8 +133,8 @@ mod tests { default_participants(), ); - let serialized = to_binary(&snapshot).unwrap(); - let deserialized: Snapshot = from_binary(&serialized).unwrap(); + let serialized = to_json_binary(&snapshot).unwrap(); + let deserialized: Snapshot = from_json(serialized).unwrap(); assert_eq!(snapshot, deserialized); } diff --git a/packages/axelar-wasm-std/src/threshold.rs b/packages/axelar-wasm-std/src/threshold.rs index 198c6e8a0..cc626ea1c 100644 --- a/packages/axelar-wasm-std/src/threshold.rs +++ b/packages/axelar-wasm-std/src/threshold.rs @@ -127,7 +127,12 @@ impl TryFrom for MajorityThreshold { type Error = Error; fn try_from(value: Threshold) -> Result { - if value.numerator() <= value.denominator() / Uint64::from(2u64) { + if value.numerator() + <= value + .denominator() + .checked_div(Uint64::from(2u64)) + .expect("division by zero") + { Err(Error::NoMajority) } else { Ok(MajorityThreshold { diff --git a/packages/axelar-wasm-std/src/voting.rs b/packages/axelar-wasm-std/src/voting.rs index b7b49fb29..0af10affa 100644 --- a/packages/axelar-wasm-std/src/voting.rs +++ b/packages/axelar-wasm-std/src/voting.rs @@ -16,13 +16,14 @@ use std::array::TryFromSliceError; use std::collections::BTreeMap; use std::fmt; -use std::ops::AddAssign; +use std::ops::Add; use std::ops::Mul; use std::str::FromStr; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, StdError, StdResult, Uint256, Uint64}; use cw_storage_plus::{IntKey, Key, KeyDeserialize, PrimaryKey}; +use num_traits::CheckedAdd; use num_traits::One; use strum::EnumIter; use strum::EnumString; @@ -86,23 +87,35 @@ impl FromStr for PollId { } } +// trait `Mul` is required by `One` trait impl Mul for PollId { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { - Self(self.0 * rhs.0) + Self(self.0.mul(rhs.0)) } } +// trait `One` is required by `counter::Counter` impl One for PollId { fn one() -> Self { PollId(Uint64::one()) } } -impl AddAssign for PollId { - fn add_assign(&mut self, other: Self) { - self.0 += other.0; +// trait `Add` is required by `CheckedAdd` trait +impl Add for PollId { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + Self(self.0.add(other.0)) + } +} + +// trait `CheckedAdd` is required by `counter::Counter` +impl CheckedAdd for PollId { + fn checked_add(&self, other: &Self) -> Option { + Some(Self(self.0.checked_add(other.0).ok()?)) } } @@ -179,10 +192,15 @@ impl Tallies { } pub fn tally(&mut self, vote: &Vote, weight: &Uint256) { - *self + let key = vote.to_string(); + + let tally = self .0 - .get_mut(&vote.to_string()) - .unwrap_or(&mut Uint256::zero()) += weight; + .get(&key) + .unwrap_or(&Uint256::zero()) + .saturating_add(*weight); + + self.0.insert(key, tally); } } diff --git a/packages/connection-router-api/Cargo.toml b/packages/connection-router-api/Cargo.toml index a57eb794f..470270374 100644 --- a/packages/connection-router-api/Cargo.toml +++ b/packages/connection-router-api/Cargo.toml @@ -25,3 +25,6 @@ valuable = "0.1.0" [dev-dependencies] hex = "0.4.3" rand = "0.8.5" + +[lints] +workspace = true diff --git a/packages/connection-router-api/src/client.rs b/packages/connection-router-api/src/client.rs index a0a8cad01..e756a318d 100644 --- a/packages/connection-router-api/src/client.rs +++ b/packages/connection-router-api/src/client.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{to_binary, Addr, WasmMsg}; +use cosmwasm_std::{to_json_binary, Addr, WasmMsg}; use crate::msg::ExecuteMsg; use crate::Message; @@ -11,7 +11,7 @@ impl Router { fn execute(&self, msg: &ExecuteMsg) -> WasmMsg { WasmMsg::Execute { contract_addr: self.address.to_string(), - msg: to_binary(&msg).expect("msg should always be serializable"), + msg: to_json_binary(&msg).expect("msg should always be serializable"), funds: vec![], } } diff --git a/packages/connection-router-api/src/primitives.rs b/packages/connection-router-api/src/primitives.rs index e501d2653..0b4791ae1 100644 --- a/packages/connection-router-api/src/primitives.rs +++ b/packages/connection-router-api/src/primitives.rs @@ -16,7 +16,7 @@ use valuable::Valuable; use crate::error::*; -pub const ID_SEPARATOR: char = ':'; +pub const CHAIN_NAME_DELIMITER: char = '_'; #[cw_serde] pub struct Message { @@ -103,7 +103,7 @@ impl FromStr for CrossChainId { type Err = Error; fn from_str(s: &str) -> Result { - let parts = s.split_once(ID_SEPARATOR); + let parts = s.split_once(CHAIN_NAME_DELIMITER); let (chain, id) = parts .map(|(chain, id)| { ( @@ -122,7 +122,7 @@ impl FromStr for CrossChainId { impl fmt::Display for CrossChainId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}{}{}", self.chain, ID_SEPARATOR, *self.id) + write!(f, "{}{}{}", self.chain, CHAIN_NAME_DELIMITER, *self.id) } } @@ -162,7 +162,7 @@ impl FromStr for ChainName { type Err = Error; fn from_str(s: &str) -> Result { - if s.contains(ID_SEPARATOR) || s.is_empty() { + if s.contains(CHAIN_NAME_DELIMITER) || s.is_empty() { return Err(Error::InvalidChainName); } @@ -277,7 +277,7 @@ impl ChainEndpoint { mod tests { use super::*; - use cosmwasm_std::to_vec; + use cosmwasm_std::to_json_vec; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use sha3::{Digest, Sha3_256}; @@ -287,12 +287,12 @@ mod tests { // will cause this test to fail, indicating that a migration is needed. fn test_message_struct_unchanged() { let expected_message_hash = - "9f9b9c55ccf5ce5a82f66385cae9e84e402a272fece5a2e22a199dbefc91d8bf"; + "e8052da3a89c90468cc6e4e242a827f8579fb0ea8e298b1650d73a0f7e81abc3"; let msg = dummy_message(); assert_eq!( - hex::encode(Sha3_256::digest(&to_vec(&msg).unwrap())), + hex::encode(Sha3_256::digest(to_json_vec(&msg).unwrap())), expected_message_hash ); } @@ -301,7 +301,7 @@ mod tests { #[test] fn hash_id_unchanged() { let expected_message_hash = - "0135c407f6a58fdcfb879f8d9eae19f870a89f8619537dcde265b4599361a7b6"; + "d30a374a795454706b43259998aafa741267ecbc8b6d5771be8d7b8c9a9db263"; let msg = dummy_message(); @@ -318,7 +318,7 @@ mod tests { // name contains id separator assert_eq!( - format!("chain {ID_SEPARATOR}") + format!("chain {CHAIN_NAME_DELIMITER}") .parse::() .unwrap_err(), Error::InvalidChainName @@ -349,14 +349,14 @@ mod tests { fn should_not_deserialize_invalid_chain_name() { assert_eq!( "chain name is invalid", - serde_json::from_str::(format!("\"\"").as_str()) + serde_json::from_str::("\"\"") .unwrap_err() .to_string() ); assert_eq!( "chain name is invalid", - serde_json::from_str::(format!("\"chain{ID_SEPARATOR}\"").as_str()) + serde_json::from_str::(format!("\"chain{CHAIN_NAME_DELIMITER}\"").as_str()) .unwrap_err() .to_string() ); @@ -377,13 +377,13 @@ mod tests { fn dummy_message() -> Message { Message { cc_id: CrossChainId { - id: "hash:index".parse().unwrap(), + id: "hash-index".parse().unwrap(), chain: "chain".parse().unwrap(), }, source_address: "source_address".parse().unwrap(), - destination_chain: "destination_chain".parse().unwrap(), + destination_chain: "destination-chain".parse().unwrap(), destination_address: "destination_address".parse().unwrap(), - payload_hash: [1; 32].into(), + payload_hash: [1; 32], } } } diff --git a/packages/events-derive/Cargo.toml b/packages/events-derive/Cargo.toml index 7854b1d03..630f8b3d1 100644 --- a/packages/events-derive/Cargo.toml +++ b/packages/events-derive/Cargo.toml @@ -15,3 +15,6 @@ quote = "1.0.33" serde = "1.0.183" serde_json = "1.0.105" syn = "2.0.29" + +[lints] +workspace = true diff --git a/packages/events/Cargo.toml b/packages/events/Cargo.toml index bb7d77d3a..91ca72d8f 100644 --- a/packages/events/Cargo.toml +++ b/packages/events/Cargo.toml @@ -16,3 +16,6 @@ serde_json = "1.0.105" # The fix for the issue is at https://github.com/axelarnetwork/tendermint-rs/commit/e97033e20e660a7e707ea86db174ec047bbba50d. tendermint = { git = "https://github.com/axelarnetwork/tendermint-rs.git", branch = "v0.33.x" } thiserror = { workspace = true } + +[lints] +workspace = true diff --git a/packages/gateway-api/Cargo.toml b/packages/gateway-api/Cargo.toml index 2aeda2239..f2527fbe9 100644 --- a/packages/gateway-api/Cargo.toml +++ b/packages/gateway-api/Cargo.toml @@ -8,3 +8,6 @@ edition = "2021" [dependencies] connection-router-api ={ workspace = true } cosmwasm-schema = { workspace = true } + +[lints] +workspace = true diff --git a/packages/report/Cargo.toml b/packages/report/Cargo.toml index 51cb3e496..65f56e40b 100644 --- a/packages/report/Cargo.toml +++ b/packages/report/Cargo.toml @@ -11,3 +11,6 @@ eyre = "0.6.8" itertools = { workspace = true } thiserror = { workspace = true } valuable = { version = "0.1.0", features = ["derive"] } + +[lints] +workspace = true diff --git a/packages/signature-verifier-api/Cargo.toml b/packages/signature-verifier-api/Cargo.toml index 8dd99c411..032f49ee2 100644 --- a/packages/signature-verifier-api/Cargo.toml +++ b/packages/signature-verifier-api/Cargo.toml @@ -10,3 +10,6 @@ cosmwasm-std = { workspace = true } cosmwasm-storage = { workspace = true } error-stack = { workspace = true } thiserror = { workspace = true } + +[lints] +workspace = true diff --git a/packages/signature-verifier-api/src/client.rs b/packages/signature-verifier-api/src/client.rs index 3b549ee89..14851c740 100644 --- a/packages/signature-verifier-api/src/client.rs +++ b/packages/signature-verifier-api/src/client.rs @@ -1,5 +1,7 @@ use cosmwasm_schema::serde::de::DeserializeOwned; -use cosmwasm_std::{to_binary, Addr, HexBinary, QuerierWrapper, QueryRequest, Uint64, WasmQuery}; +use cosmwasm_std::{ + to_json_binary, Addr, HexBinary, QuerierWrapper, QueryRequest, Uint64, WasmQuery, +}; use error_stack::{Result, ResultExt}; use crate::msg::QueryMsg; @@ -14,7 +16,7 @@ impl SignatureVerifier<'_> { self.querier .query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: self.address.to_string(), - msg: to_binary(msg).expect("msg should always be serializable"), + msg: to_json_binary(msg).expect("msg should always be serializable"), })) .change_context(Error::QuerySignatureVerifier) }