From 4e37dc4bb88624765384d1662549c00e991acc4a Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Wed, 10 Apr 2024 19:35:39 -0700 Subject: [PATCH] fix: review sov-ibc-transfer implementation and apply fixes (#133) * fix: remove now redundant escrowed_token state + move away from base64-encoding memo * chore: update basecoin rev * imp: move away from memo + introduce minted_token_id_to_name state * fix: enforce context to use ibc_transfer address as sender * fix: apply review comments * feat: add transfer_mintedTokenName RPC method * chore: add comment for token_id map check in unescrow_coins_validate * imp: add comments for create_token * chore: clean-ups + comments * nit --- .cargo/config.toml | 1 - Cargo.lock | 221 +++++++--------- Cargo.toml | 4 +- clients/sov-celestia/types/Cargo.toml | 1 - mocks/Cargo.toml | 1 - mocks/src/configs.rs | 9 +- mocks/src/relayer/msgs/cosmos.rs | 17 +- mocks/src/relayer/msgs/rollup.rs | 15 +- mocks/src/sovereign/rollup.rs | 25 +- mocks/src/sovereign/runner.rs | 24 +- mocks/src/tests/transfer.rs | 130 +++++---- .../sov-consensus-state-tracker/Cargo.toml | 1 - modules/sov-ibc-transfer/Cargo.toml | 22 +- modules/sov-ibc-transfer/src/context.rs | 248 ++++++++++-------- modules/sov-ibc-transfer/src/lib.rs | 25 +- modules/sov-ibc-transfer/src/rpc.rs | 67 ++--- modules/sov-ibc/src/rpc/helpers.rs | 11 +- modules/sov-ibc/src/rpc/methods.rs | 3 +- 18 files changed, 407 insertions(+), 418 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 9bae44bf..2665c1ce 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,4 +1,3 @@ - [patch.crates-io] sov-ibc = { path = "modules/sov-ibc" } diff --git a/Cargo.lock b/Cargo.lock index 7f54c263..4b45dda7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,9 +127,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "argh" @@ -150,7 +150,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -436,7 +436,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -447,7 +447,7 @@ checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -458,7 +458,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -469,9 +469,9 @@ checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "autotools" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8da1805e028a172334c3b680f93e71126f2327622faef2ec3d893c0a4ad77" +checksum = "ef941527c41b0fc0dd48511a8154cd5fc7e29200a0ff8b7203c5d777dbc795cf" dependencies = [ "cc", ] @@ -707,7 +707,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -879,9 +879,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-slice-cast" @@ -928,7 +928,7 @@ checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -959,9 +959,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.90" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" dependencies = [ "jobserver", "libc", @@ -1088,7 +1088,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -1535,7 +1535,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -1571,7 +1571,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -1582,7 +1582,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -1626,9 +1626,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -1714,14 +1714,14 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] name = "downcast-rs" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "downloader" @@ -1870,9 +1870,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -1899,14 +1899,14 @@ dependencies = [ [[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", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -1927,7 +1927,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -2156,7 +2156,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -2221,9 +2221,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -2338,9 +2338,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -2425,15 +2425,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "http" version = "0.2.12" @@ -2996,7 +2987,7 @@ checksum = "3f5010acf3b7fec09c24d05b946424a9f7884f9647ed837c1a1676d3eabac154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -3006,7 +2997,7 @@ source = "git+https://github.com/cosmos/ibc-rs.git?rev=c579628c67#c579628c672bdb dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -3482,7 +3473,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -3639,9 +3630,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" @@ -3752,9 +3743,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.8.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "ndarray" @@ -3851,7 +3842,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -4020,9 +4011,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ "memchr", "thiserror", @@ -4056,14 +4047,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[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" @@ -4106,7 +4097,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -4230,9 +4221,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", "prost-derive", @@ -4240,13 +4231,13 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.10.5", + "heck 0.5.0", + "itertools 0.12.1", "log", "multimap", "once_cell", @@ -4255,29 +4246,28 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.55", + "syn 2.0.58", "tempfile", - "which", ] [[package]] name = "prost-derive" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] name = "prost-types" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" dependencies = [ "prost", ] @@ -4325,9 +4315,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -4985,9 +4975,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "rusty-fork" @@ -5029,9 +5019,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.1" +version = "2.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788745a868b0e751750388f4e6546eb921ef714a4317fa6954f7cde114eb2eb7" +checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0" dependencies = [ "cfg-if", "derive_more", @@ -5041,9 +5031,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.1" +version = "2.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dc2f4e8bc344b9fc3d5f74f72c2e55bfc38d28dc2ebc69c194a3df424e4d9ac" +checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", @@ -5128,9 +5118,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", @@ -5141,9 +5131,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", @@ -5249,7 +5239,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -5276,13 +5266,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -5569,7 +5559,6 @@ dependencies = [ name = "sov-celestia-client-types" version = "0.1.0" dependencies = [ - "base64 0.21.7", "bytes", "derive_more", "hex", @@ -5609,7 +5598,6 @@ name = "sov-consensus-state-tracker" version = "0.1.0" dependencies = [ "anyhow", - "base64 0.21.7", "borsh", "ibc-app-transfer", "ibc-core", @@ -5684,7 +5672,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "base64 0.21.7", "basecoin", "borsh", "const-rollup-config", @@ -5752,8 +5739,8 @@ name = "sov-ibc-transfer" version = "0.1.0" dependencies = [ "anyhow", - "base64 0.21.7", "borsh", + "derive_more", "ibc-app-transfer", "ibc-core", "jsonrpsee", @@ -5981,9 +5968,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -6019,9 +6006,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.55" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -6290,7 +6277,7 @@ checksum = "c8f546451eaa38373f549093fe9fd05e7d2bade739e2ddf834b9968621d60107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -6310,7 +6297,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -6367,9 +6354,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[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", @@ -6402,7 +6389,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -6546,7 +6533,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -6634,7 +6621,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -6718,7 +6705,7 @@ checksum = "563b3b88238ec95680aef36bdece66896eaa7ce3c0f1b4f39d38fb2435261352" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -6748,7 +6735,7 @@ checksum = "ac73887f47b9312552aa90ef477927ff014d63d1920ca8037c6c1951eab64bb1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -6910,7 +6897,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", "wasm-bindgen-shared", ] @@ -6967,7 +6954,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7000,7 +6987,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -7190,18 +7177,6 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "winapi" version = "0.3.9" @@ -7462,7 +7437,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -7482,7 +7457,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -7499,9 +7474,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 105c8316..7aab98b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,8 +76,8 @@ ibc-query = { version = "0.51.0", default-features = false, features ibc-testkit = { version = "0.51.0", default-features = false } # NOTE: `ibc-proto` is solely required by `sov-ibc-proto`. When needing Protobuf -# Rust types in the project, importing from their respective `ibc` type crates is -# a more efficient approach. +# Rust types from `ibc-proto` in the project, importing from respective `ibc` +# crates is a more efficient approach. ibc-proto = { version = "0.42.2", default-features = false } basecoin = { version = "0.1.0" } diff --git a/clients/sov-celestia/types/Cargo.toml b/clients/sov-celestia/types/Cargo.toml index 03b7dbb7..ab4eca36 100644 --- a/clients/sov-celestia/types/Cargo.toml +++ b/clients/sov-celestia/types/Cargo.toml @@ -19,7 +19,6 @@ workspace = true [dependencies] # external dependencies -base64 = { workspace = true, features = ["alloc"] } bytes = { workspace = true } hex = { version = "0.4.3" } jmt = { workspace = true } diff --git a/mocks/Cargo.toml b/mocks/Cargo.toml index 8a69c260..38a0214a 100644 --- a/mocks/Cargo.toml +++ b/mocks/Cargo.toml @@ -16,7 +16,6 @@ workspace = true # external dependencies anyhow = { workspace = true } async-trait = { version = "0.1.74", default-features = false } -base64 = { workspace = true } borsh = { workspace = true } jmt = { workspace = true } prost = { workspace = true } diff --git a/mocks/src/configs.rs b/mocks/src/configs.rs index 728c0805..63d6a489 100644 --- a/mocks/src/configs.rs +++ b/mocks/src/configs.rs @@ -5,7 +5,7 @@ use std::path::Path; use ibc_core::host::types::identifiers::ChainId; use ibc_testkit::fixtures::core::signer::dummy_bech32_account; use serde::de::DeserializeOwned; -use sov_bank::{get_token_id, GasTokenConfig, TokenId}; +use sov_bank::{get_token_id, TokenConfig, TokenId, GAS_TOKEN_ID}; #[cfg(feature = "celestia-da")] use sov_consensus_state_tracker::CelestiaService; #[cfg(feature = "mock-da")] @@ -45,11 +45,12 @@ pub struct TestSetupConfig { impl TestSetupConfig { /// Returns list of tokens in the bank configuration - pub fn gas_token_config(&self) -> GasTokenConfig { + pub fn gas_token_config(&self) -> TokenConfig { self.rollup_genesis_config .bank_config .gas_token_config .clone() + .into() } /// Returns the address of the relayer. We use the last address in the list @@ -104,10 +105,8 @@ pub async fn default_config_with_celestia_da() -> TestSetupConfig, /// An arbitrary user address on the rollup. pub sov_address: Address, /// The token name on the Cosmos chain. diff --git a/mocks/src/relayer/msgs/cosmos.rs b/mocks/src/relayer/msgs/cosmos.rs index 5df5e105..23e7386e 100644 --- a/mocks/src/relayer/msgs/cosmos.rs +++ b/mocks/src/relayer/msgs/cosmos.rs @@ -2,12 +2,10 @@ use std::str::FromStr; -use base64::engine::general_purpose; -use base64::Engine; use basecoin::modules::ibc::AnyClientState; use ibc_app_transfer::types::msgs::transfer::MsgTransfer; use ibc_app_transfer::types::packet::PacketData; -use ibc_app_transfer::types::{Coin, Memo, PrefixedDenom}; +use ibc_app_transfer::types::{Coin, PrefixedDenom}; use ibc_core::channel::types::msgs::MsgRecvPacket; use ibc_core::channel::types::packet::Packet; use ibc_core::channel::types::timeout::TimeoutHeight; @@ -100,17 +98,6 @@ where /// Builds a Cosmos chain token transfer message; serialized to Any pub fn build_msg_transfer_for_cos(&self, config: &TransferTestConfig) -> MsgTransfer { - let memo = match config.sov_token_id { - Some(token_id) => { - let mut token_id_buf = String::new(); - - general_purpose::STANDARD_NO_PAD.encode_string(token_id, &mut token_id_buf); - - token_id_buf.into() - } - None => Memo::from_str("").unwrap(), - }; - let packet_data = PacketData { token: Coin { denom: PrefixedDenom::from_str(&config.cos_denom).unwrap(), @@ -118,7 +105,7 @@ where }, sender: Signer::from(config.cos_address.clone()), receiver: Signer::from(config.sov_address.to_string()), - memo, + memo: "".into(), }; MsgTransfer { diff --git a/mocks/src/relayer/msgs/rollup.rs b/mocks/src/relayer/msgs/rollup.rs index 949487b6..13213f78 100644 --- a/mocks/src/relayer/msgs/rollup.rs +++ b/mocks/src/relayer/msgs/rollup.rs @@ -1,8 +1,6 @@ //! Contains rollup specific message builders for the relayer. use std::str::FromStr; -use base64::engine::general_purpose; -use base64::Engine; use ibc_app_transfer::types::msgs::transfer::MsgTransfer; use ibc_app_transfer::types::packet::PacketData; use ibc_app_transfer::types::{Coin, PrefixedDenom}; @@ -101,14 +99,11 @@ where CallMessage::Core(msg_update_client.to_any()) } - /// Builds a sdk token transfer message wrapped in a `CallMessage` with the given amount - /// Note: keep the amount value lower than the initial balance of the sender address + /// Builds a `MsgTransfer` with the given configuration + /// + /// Note: keep the amount value lower than the initial balance of the sender + /// address pub fn build_msg_transfer_for_sov(&self, config: &TransferTestConfig) -> MsgTransfer { - let mut token_id_buf = String::new(); - - general_purpose::STANDARD_NO_PAD - .encode_string(config.sov_token_id.unwrap(), &mut token_id_buf); - let packet_data = PacketData { token: Coin { denom: PrefixedDenom::from_str(&config.sov_denom).unwrap(), @@ -116,7 +111,7 @@ where }, sender: Signer::from(config.sov_address.to_string()), receiver: Signer::from(config.cos_address.clone()), - memo: token_id_buf.into(), + memo: "".into(), }; MsgTransfer { diff --git a/mocks/src/sovereign/rollup.rs b/mocks/src/sovereign/rollup.rs index d119f09c..bf202612 100644 --- a/mocks/src/sovereign/rollup.rs +++ b/mocks/src/sovereign/rollup.rs @@ -7,7 +7,7 @@ use std::sync::{Arc, Mutex}; use ibc_client_tendermint::types::Header; use ibc_core::client::types::Height; use ibc_core::host::types::identifiers::ChainId; -use sov_bank::{CallMessage as BankCallMessage, TokenId}; +use sov_bank::{CallMessage as BankCallMessage, TokenConfig, TokenId}; use sov_celestia_client::types::client_message::test_util::dummy_sov_header; use sov_celestia_client::types::client_message::SovTmHeader; use sov_consensus_state_tracker::{ConsensusStateTracker, HasConsensusState}; @@ -18,6 +18,7 @@ use sov_modules_api::{Context, Spec, WorkingSet}; use sov_rollup_interface::services::da::DaService; use sov_state::{MerkleProofSpec, ProverStorage, Storage}; +use super::DEFAULT_SALT; use crate::cosmos::MockTendermint; use crate::sovereign::runtime::RuntimeCall; use crate::sovereign::Runtime; @@ -153,25 +154,25 @@ where .unwrap() } - /// Returns token ID of an IBC denom - pub fn get_minted_token_id(&self, token_denom: String) -> Option { - let mut working_set = WorkingSet::new(self.prover_storage()); - + pub fn get_token_id(&self, token_config: TokenConfig) -> Option { self.runtime() - .ibc_transfer - .minted_token(token_denom, &mut working_set) - .map(|token| token.token_id) + .bank + .token_id( + token_config.token_name, + token_config.address_and_balances[0].0.clone(), + DEFAULT_SALT, + ) .ok() } - /// Searches the transfer module to retrieve the ID of the token ID in - /// escrow based on its name(denom). - pub fn get_escrowed_token_id(&self, token_denom: String) -> Option { + /// Searches the transfer module by given token denom and returns the token + /// ID if the token has been minted. + pub fn get_minted_token_id(&self, token_denom: String) -> Option { let mut working_set = WorkingSet::new(self.prover_storage()); self.runtime() .ibc_transfer - .escrowed_token(token_denom, &mut working_set) + .minted_token_id(token_denom, &mut working_set) .map(|token| token.token_id) .ok() } diff --git a/mocks/src/sovereign/runner.rs b/mocks/src/sovereign/runner.rs index b346e451..d26cde6a 100644 --- a/mocks/src/sovereign/runner.rs +++ b/mocks/src/sovereign/runner.rs @@ -5,7 +5,7 @@ use sov_consensus_state_tracker::HasConsensusState; use sov_kernels::basic::BasicKernelGenesisConfig; use sov_modules_api::runtime::capabilities::{Kernel, KernelSlotHooks}; use sov_modules_api::{ - DispatchCall, Gas, GasMeter, Genesis, KernelWorkingSet, ModuleInfo, SlotData, Spec, + CallResponse, DispatchCall, Gas, GasMeter, Genesis, KernelWorkingSet, SlotData, Spec, StateCheckpoint, }; use sov_rollup_interface::da::BlockHeaderTrait; @@ -15,7 +15,7 @@ use sov_state::{MerkleProofSpec, ProverStorage, Storage}; use tokio::task::JoinHandle; use tracing::{debug, info}; -use super::{GenesisConfig, MockRollup, RuntimeCall}; +use super::{GenesisConfig, MockRollup}; use crate::utils::{wait_for_block, MutexUtil}; impl MockRollup @@ -105,20 +105,18 @@ where let rollup_ctx = self.rollup_ctx(); - for m in self.mempool() { - // Sets the sender address to the address of the 'sov-ibc' - // module, ensuring that the module's address is used for the - // token creation. - if let RuntimeCall::ibc(_) = m { - self.resolve_ctx(self.runtime().ibc.address().clone(), visible_slot); - } + // Resets the sender address to the address of the relayer + self.resolve_ctx(rollup_ctx.sender().clone(), visible_slot); + for m in self.mempool() { + // NOTE: on failures, we silently ignore the message and continue as + // it is in the real-case scenarios self.runtime() .dispatch_call(m.clone(), &mut working_set, &self.rollup_ctx()) - .unwrap(); - - // Resets the sender address to the address of the relayer - self.resolve_ctx(rollup_ctx.sender().clone(), visible_slot); + .unwrap_or_else(|e| { + info!("rollup: error executing message: {e:?}"); + CallResponse::default() + }); } *self.mempool.acquire_mutex() = vec![]; diff --git a/mocks/src/tests/transfer.rs b/mocks/src/tests/transfer.rs index 686a7c1a..925b7fbd 100644 --- a/mocks/src/tests/transfer.rs +++ b/mocks/src/tests/transfer.rs @@ -4,7 +4,7 @@ use ibc_app_transfer::types::{PrefixedDenom, TracePrefix}; use ibc_core::client::context::client_state::ClientStateCommon; use ibc_core::host::types::identifiers::{ChannelId, PortId}; use ibc_core::primitives::ToProto; -use sov_bank::{TokenConfig, GAS_TOKEN_ID}; +use sov_bank::TokenConfig; use sov_ibc::call::CallMessage; use sov_ibc::clients::AnyClientState; use test_log::test; @@ -24,62 +24,67 @@ async fn test_escrow_unescrow_on_sov() { // set transfer parameters let gas_token = relayer_builder.setup_cfg().gas_token_config(); - let gas_token_id = GAS_TOKEN_ID; - let mut cfg = TransferTestConfig::builder() - .sov_denom(gas_token.token_name.clone()) - .sov_token_id(Some(GAS_TOKEN_ID)) .sov_address(gas_token.address_and_balances[0].0) .build(); let expected_sender_balance = gas_token.address_and_balances[0].1 - cfg.amount * 2; // ----------------------------------------------------------------------- - // Send a `MsgTransfer` to the rollup + // Send a `MsgTransfer` to the rollup (twice) // ----------------------------------------------------------------------- let msg_transfer_on_sov = rly.build_msg_transfer_for_sov(&cfg); rly.src_chain_ctx() .submit_msgs(vec![ - CallMessage::Transfer(msg_transfer_on_sov.clone()).into() + CallMessage::Transfer(msg_transfer_on_sov.clone()).into(), + CallMessage::Transfer(msg_transfer_on_sov.clone()).into(), ]) .await; // ----------------------------------------------------------------------- - // Check that the token has been escrowed + // Check the sender balance has been updated correctly // ----------------------------------------------------------------------- - let escrowed_token = rly + let sender_balance = rly .src_chain_ctx() .service() - .get_escrowed_token_id(gas_token_id.to_string()) - .unwrap(); + .get_balance_of(cfg.sov_address, gas_token.token_id); - assert_eq!(escrowed_token, gas_token_id); + assert_eq!(sender_balance, expected_sender_balance); // ----------------------------------------------------------------------- - // Transfer the same token once again + // Send a `MsgRecvPacket` paired with a `MsgUpdateClient` to the Cosmos chain // ----------------------------------------------------------------------- - rly.src_chain_ctx() - .submit_msgs(vec![ - CallMessage::Transfer(msg_transfer_on_sov.clone()).into() - ]) + let target_height = match rly.src_chain_ctx().query(QueryReq::HostHeight).await { + QueryResp::HostHeight(height) => height, + _ => panic!("unexpected response"), + }; + + let msg_update_client = rly.build_msg_update_client_for_cos(target_height).await; + + let msg_recv_packet = rly + .build_msg_recv_packet_for_cos(target_height, msg_transfer_on_sov) .await; - // ----------------------------------------------------------------------- - // Check the sender balance has been updated correctly - // ----------------------------------------------------------------------- - let sender_balance = rly - .src_chain_ctx() + rly.dst_chain_ctx() + .submit_msgs(vec![msg_update_client, msg_recv_packet.to_any()]) + .await; + + let expected_denom_on_cos = + "transfer/channel-0/token_1rwrh8gn2py0dl4vv65twgctmlwck6esm2as9dftumcw89kqqn3nqrduss6"; + + let receiver_balance = rly + .dst_chain_ctx() .service() - .get_balance_of(cfg.sov_address, gas_token_id); + .get_balance_of(expected_denom_on_cos, cfg.cos_address.clone()); - assert_eq!(sender_balance, expected_sender_balance); + assert_eq!(receiver_balance, Some(cfg.amount)); // ----------------------------------------------------------------------- // Transfer another token but with the same name as the previous one // ----------------------------------------------------------------------- - let fake_token_message = rly.build_msg_create_token(&gas_token.clone().into()); + let fake_token_message = rly.build_msg_create_token(&gas_token.clone()); rly.src_chain_ctx() .submit_msgs(vec![fake_token_message.clone().into()]) @@ -89,8 +94,8 @@ async fn test_escrow_unescrow_on_sov() { .setup_cfg() .get_token_id_for_relayer(&gas_token.token_name); - cfg.sov_token_id = Some(fake_token_id); cfg.amount = 50; + cfg.sov_denom = fake_token_id.to_bech32().to_string(); let fake_token_sender_initial_balance = rly .src_chain_ctx() @@ -108,20 +113,12 @@ async fn test_escrow_unescrow_on_sov() { // ----------------------------------------------------------------------- // Check that the token has been escrowed as a distinct asset // ----------------------------------------------------------------------- - let escrowed_token = rly - .src_chain_ctx() - .service() - .get_escrowed_token_id(fake_token_id.to_string()) - .unwrap(); - - assert_eq!(escrowed_token, fake_token_id); - - let sender_genuine_token_balance = rly + let gas_token_sender_balance = rly .src_chain_ctx() .service() - .get_balance_of(cfg.sov_address, gas_token_id); + .get_balance_of(cfg.sov_address, gas_token.token_id); - assert_eq!(sender_genuine_token_balance, expected_sender_balance); + assert_eq!(gas_token_sender_balance, expected_sender_balance); let fake_token_sender_balance = rly .src_chain_ctx() @@ -145,21 +142,23 @@ async fn test_mint_burn_on_sov() { // set transfer parameters let gas_token = relayer_builder.setup_cfg().gas_token_config(); let mut cfg = TransferTestConfig::builder() - .sov_denom(gas_token.token_name.clone()) .sov_address(gas_token.address_and_balances[0].0) .build(); + // Fake token with the same parameters as the gas token but a different name let fake_token = TokenConfig { token_name: "transfer/channel-0/basecoin".to_string(), - ..gas_token.into() + ..gas_token }; - let fake_token_message = rly.build_msg_create_token(&fake_token); + let create_fake_token_message = rly.build_msg_create_token(&fake_token); rly.src_chain_ctx() - .submit_msgs(vec![fake_token_message.clone().into()]) + .submit_msgs(vec![create_fake_token_message.clone().into()]) .await; + let fake_token_id = rly.src_chain_ctx().service().get_token_id(fake_token); + // Store the current balance of the sender to check it later after the transfers let initial_sender_balance = rly .dst_chain_ctx() @@ -211,22 +210,22 @@ async fn test_mint_burn_on_sov() { assert_eq!(client_state.latest_height(), target_height); // ----------------------------------------------------------------------- - // Check uniqueness of the created token ID + // Check uniqueness of the token minted on the rollup // ----------------------------------------------------------------------- let denom_path_prefix = TracePrefix::new(PortId::transfer(), ChannelId::zero()); let mut prefixed_denom = PrefixedDenom::from_str(&cfg.cos_denom).unwrap(); prefixed_denom.add_trace_prefix(denom_path_prefix); - let token_id_on_sov = rly + let minted_token_id = rly .src_chain_ctx() .service() .get_minted_token_id(prefixed_denom.to_string()) .unwrap(); - assert_ne!(token_id_on_sov, fake_token.token_id); + assert_ne!(Some(minted_token_id), fake_token_id); // ----------------------------------------------------------------------- - // Transfer the same token once again to the Cosmos chain + // Submit another same `MsgTransfer` to the Cosmos chain // ----------------------------------------------------------------------- rly.dst_chain_ctx() .submit_msgs(vec![msg_transfer_on_cos.clone().to_any()]) @@ -237,6 +236,10 @@ async fn test_mint_burn_on_sov() { _ => panic!("unexpected response"), }; + // ----------------------------------------------------------------------- + // Send a `MsgRecvPacket` paired with a `MsgUpdateClient` to the rollup + // ----------------------------------------------------------------------- + let msg_update_client = rly.build_msg_update_client_for_sov(target_height).await; let msg_recv_packet = rly @@ -253,7 +256,7 @@ async fn test_mint_burn_on_sov() { let receiver_balance = rly .src_chain_ctx() .service() - .get_balance_of(cfg.sov_address, token_id_on_sov); + .get_balance_of(cfg.sov_address, minted_token_id); let mut expected_receiver_balance = cfg.amount * 2; @@ -274,9 +277,8 @@ async fn test_mint_burn_on_sov() { // ----------------------------------------------------------------------- cfg.sov_denom = "transfer/channel-0/basecoin".to_string(); - cfg.sov_token_id = Some(token_id_on_sov); - let msg_transfer_on_sov = rly.build_msg_transfer_for_sov(&cfg); + let msg_transfer_on_sov = rly.build_msg_transfer_for_sov(&cfg.clone()); rly.src_chain_ctx() .submit_msgs(vec![ @@ -302,22 +304,42 @@ async fn test_mint_burn_on_sov() { // ----------------------------------------------------------------------- // Check the token has been burned on rollup and unescrowed on Cosmos chain // ----------------------------------------------------------------------- - let sender_balance = rly + let receiver_balance = rly .src_chain_ctx() .service() - .get_balance_of(cfg.sov_address, token_id_on_sov); + .get_balance_of(cfg.sov_address, minted_token_id); expected_receiver_balance -= cfg.amount; - assert_eq!(sender_balance, expected_receiver_balance); + assert_eq!(receiver_balance, expected_receiver_balance); - let receiver_balance = rly + let sender_balance = rly .dst_chain_ctx() .service() - .get_balance_of(&cfg.cos_denom, cfg.cos_address) + .get_balance_of(&cfg.cos_denom, cfg.cos_address.clone()) .unwrap(); expected_sender_balance += cfg.amount; - assert_eq!(receiver_balance, expected_sender_balance); + assert_eq!(sender_balance, expected_sender_balance); + + // ----------------------------------------------------------------------- + // Check if sending back a token with the same ID as IBC-minted token fails + // ----------------------------------------------------------------------- + cfg.sov_denom = minted_token_id.to_bech32().to_string(); + + let msg_transfer_on_sov = rly.build_msg_transfer_for_sov(&cfg); + + rly.src_chain_ctx() + .submit_msgs(vec![ + CallMessage::Transfer(msg_transfer_on_sov.clone()).into() + ]) + .await; + + let receiver_balance = rly + .src_chain_ctx() + .service() + .get_balance_of(cfg.sov_address, minted_token_id); + + assert_eq!(receiver_balance, expected_receiver_balance); } diff --git a/modules/sov-consensus-state-tracker/Cargo.toml b/modules/sov-consensus-state-tracker/Cargo.toml index 59a9016d..e296baa5 100644 --- a/modules/sov-consensus-state-tracker/Cargo.toml +++ b/modules/sov-consensus-state-tracker/Cargo.toml @@ -15,7 +15,6 @@ workspace = true [dependencies] # external dependencies anyhow = { workspace = true } -base64 = { workspace = true } borsh = { workspace = true } jsonrpsee = { workspace = true, optional = true } prost = { workspace = true } diff --git a/modules/sov-ibc-transfer/Cargo.toml b/modules/sov-ibc-transfer/Cargo.toml index affedb79..a2810a7b 100644 --- a/modules/sov-ibc-transfer/Cargo.toml +++ b/modules/sov-ibc-transfer/Cargo.toml @@ -14,16 +14,16 @@ workspace = true [dependencies] # external dependencies -anyhow = { workspace = true } -base64 = { workspace = true } -borsh = { workspace = true } -jsonrpsee = { workspace = true, optional = true } -prost = { workspace = true } -schemars = { workspace = true, optional = true } -serde = { workspace = true } -serde_json = { workspace = true, optional = true } -thiserror = { workspace = true } -uint = "0.9" +anyhow = { workspace = true } +borsh = { workspace = true } +derive_more = { workspace = true } +jsonrpsee = { workspace = true, optional = true } +prost = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } +uint = "0.9" # ibc dependencies ibc-app-transfer = { workspace = true, features = ["borsh", "schema"] } @@ -36,9 +36,7 @@ sov-rollup-interface = { workspace = true } [features] default = [] -serde = ["serde_json"] native = [ - "serde", "sov-bank/native", "sov-modules-api/native", "sov-rollup-interface/native", diff --git a/modules/sov-ibc-transfer/src/context.rs b/modules/sov-ibc-transfer/src/context.rs index ac42f9a1..bd94c504 100644 --- a/modules/sov-ibc-transfer/src/context.rs +++ b/modules/sov-ibc-transfer/src/context.rs @@ -1,9 +1,7 @@ use std::cell::RefCell; use std::rc::Rc; +use std::str::FromStr; -use base64::engine::general_purpose; -use base64::Engine; -use borsh::BorshDeserialize; use ibc_app_transfer::context::{TokenTransferExecutionContext, TokenTransferValidationContext}; use ibc_app_transfer::module::{ on_acknowledgement_packet_validate, on_chan_open_ack_validate, on_chan_open_confirm_validate, @@ -29,6 +27,15 @@ use uint::FromDecStrErr; use super::IbcTransfer; use crate::utils::compute_escrow_address; +/// Using a different salt will result in a different token address. Since +/// ICS-20 tokens coming from other chains are guaranteed to have unique names, +/// we don't need to use different salt values, thus we just use 0. +const SALT: u64 = 0u64; + +/// Maximum memo length allowed for ICS-20 transfers. This bound corresponds to +/// the `MaximumMemoLength` in the `ibc-go` +const MAXIMUM_MEMO_LENGTH: usize = 32768; + /// We need to create a wrapper around the `Transfer` module and `WorkingSet`, /// because we only get the `WorkingSet` at call-time from the Sovereign SDK, /// which must be passed to `TokenTransferValidationContext` methods through @@ -52,6 +59,22 @@ impl<'ws, S: Spec> IbcTransferContext<'ws, S> { } } + /// Stores mapping from "denom to token ID" and vice versa for an + /// IBC-created token. + fn record_minted_token(&self, token_id: TokenId, token_name: String) { + self.ibc_transfer.minted_token_id_to_name.set( + &token_id, + &token_name, + *self.working_set.borrow_mut(), + ); + + self.ibc_transfer.minted_token_name_to_id.set( + &token_name, + &token_id, + *self.working_set.borrow_mut(), + ); + } + /// Obtains the escrow address for a given port and channel pair by looking /// up the cache. If the cache does not contain the address, it is computed /// and stored in the cache. @@ -72,6 +95,44 @@ impl<'ws, S: Spec> IbcTransferContext<'ws, S> { }) } + /// Creates a new token with the specified `token_name` and mints an initial + /// balance to the `minter_address`. + /// + /// Note: The mint authority must be held by the `IbcTransfer` module, so + /// the `authorized_minters` is set to the `IbcTransfer` address. Also, + /// remember that the `token_name` is a denom prefixed with IBC and + /// originates from the counterparty chain. + fn create_token( + &self, + token_name: String, + minter_address: S::Address, + ) -> Result { + // Make sure to use `ibc_transfer` address as the sender + let context = Context::new( + self.ibc_transfer.address.clone(), + self.sdk_context.sequencer().clone(), + self.sdk_context.visible_slot_number(), + ); + + let new_token_id = self + .ibc_transfer + .bank + .create_token( + token_name.clone(), + SALT, + 0, + minter_address, + vec![self.ibc_transfer.address.clone()], + &context, + &mut self.working_set.borrow_mut(), + ) + .map_err(|err| TokenTransferError::Other(err.to_string()))?; + + self.record_minted_token(new_token_id, token_name); + + Ok(new_token_id) + } + /// Transfers `amount` tokens from `from_account` to `to_account` fn transfer( &self, @@ -148,39 +209,32 @@ where coin: &PrefixedCoin, memo: &Memo, ) -> Result<(), TokenTransferError> { - let token_id_buf = general_purpose::STANDARD_NO_PAD - .decode(memo.as_ref()) - .map_err(|e| TokenTransferError::Other(format!("Failed to decode memo: {e}")))?; - - let token_id = BorshDeserialize::deserialize(&mut token_id_buf.as_slice()) - .map_err(|e| TokenTransferError::Other(format!("Failed to decode memo: {e}")))?; + // Disallowing large memos to prevent potential overloads on the system. + if memo.as_ref().len() > MAXIMUM_MEMO_LENGTH { + return Err(TokenTransferError::Other(format!( + "Memo size exceeds maximum allowed length: {MAXIMUM_MEMO_LENGTH}" + ))); + } - let expected_token_id = { - self.ibc_transfer - .minted_tokens - .get(&coin.denom.to_string(), *self.working_set.borrow_mut()) - .ok_or(TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })? - }; + let token_name = coin.denom.to_string(); - if token_id != expected_token_id { - return Err(TokenTransferError::InvalidCoin { + let minted_token_id = self + .ibc_transfer + .minted_token_name_to_id + .get(&token_name, *self.working_set.borrow_mut()) + .ok_or(TokenTransferError::InvalidCoin { coin: coin.to_string(), - }); - } + })?; let sender_balance: u64 = self .ibc_transfer .bank .get_balance_of( account.address.clone(), - token_id, + minted_token_id, *self.working_set.borrow_mut(), ) - .ok_or(TokenTransferError::InvalidCoin { - coin: coin.denom.to_string(), - })?; + .ok_or(TokenTransferError::InvalidCoin { coin: token_name })?; let sender_balance: Amount = sender_balance.into(); @@ -203,25 +257,28 @@ where coin: &PrefixedCoin, memo: &Memo, ) -> Result<(), TokenTransferError> { - let token_id_buf = general_purpose::STANDARD_NO_PAD - .decode(memo.as_ref()) - .map_err(|e| TokenTransferError::Other(format!("Failed to decode memo: {e}")))?; + // Disallowing large memos to prevent potential overloads on the system. + if memo.as_ref().len() > MAXIMUM_MEMO_LENGTH { + return Err(TokenTransferError::Other(format!( + "Memo size exceeds maximum allowed length: {MAXIMUM_MEMO_LENGTH}" + ))); + } - let token_id = BorshDeserialize::deserialize(&mut token_id_buf.as_slice()) - .map_err(|e| TokenTransferError::Other(format!("Failed to decode memo: {e}")))?; + let token_id = TokenId::from_str(&coin.denom.to_string()).map_err(|e| { + TokenTransferError::InvalidCoin { + coin: coin.to_string(), + } + })?; - let token_name = self + if let Some(token_name) = self .ibc_transfer - .bank - .get_token_name(&token_id, &mut self.working_set.borrow_mut()) - .ok_or(TokenTransferError::Other(format!( - "No token with address {token_id}", - )))?; - - if token_name != coin.denom.to_string() { - return Err(TokenTransferError::InvalidCoin { - coin: coin.to_string(), - }); + .minted_token_id_to_name + .get(&token_id, *self.working_set.borrow_mut()) + { + return Err(TokenTransferError::Other(format!( + "Token with ID '{token_id}' is an IBC-created token and cannot be escrowed. \ + Use '{token_name}' as denom for sending back an IBC token to the source chain" + ))); } let sender_balance: u64 = self @@ -269,16 +326,28 @@ where channel_id: &ChannelId, coin: &PrefixedCoin, ) -> Result<(), TokenTransferError> { + let token_id = TokenId::from_str(&coin.denom.to_string()).map_err(|e| { + TokenTransferError::InvalidCoin { + coin: coin.to_string(), + } + })?; + + // NOTE: There is no way to make this fail from an honest counterparty + // chain, as this method only fails when the counterparty chain + // produces a malicious IBC transfer `send_packet()` + if let Some(token_name) = self + .ibc_transfer + .minted_token_id_to_name + .get(&token_id, *self.working_set.borrow_mut()) + { + return Err(TokenTransferError::Other(format!( + "Token with ID '{token_id}' is an IBC-created token and cannot be unescrowed.\ + Use '{token_name}' as denom for receiving an IBC token from the source chain" + ))); + } + // ensure that escrow account has enough balance let escrow_balance: Amount = { - let token_id = { - self.ibc_transfer - .escrowed_tokens - .get(&coin.denom.to_string(), *self.working_set.borrow_mut()) - .ok_or(TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })? - }; let escrow_address = self.obtain_escrow_address(port_id, channel_id); let escrow_balance = self @@ -311,54 +380,19 @@ impl<'ws, S: Spec> TokenTransferExecutionContext for IbcTransferContext<'ws, S> account: &Self::AccountId, coin: &PrefixedCoin, ) -> Result<(), TokenTransferError> { - let denom = coin.denom.to_string(); + let token_name = coin.denom.to_string(); // 1. if token ID doesn't exist in `minted_tokens`, then create a new // token and store in `minted_tokens` let token_id = { let maybe_token_id = self .ibc_transfer - .minted_tokens - .get(&denom, *self.working_set.borrow_mut()); + .minted_token_name_to_id + .get(&token_name, *self.working_set.borrow_mut()); match maybe_token_id { Some(token_id) => token_id, - // Create a new token - None => { - let token_name = coin.denom.to_string(); - // Using a different salt will result in a different token - // address. Since ICS-20 tokens coming from other chains are - // guaranteed to have unique names, we don't need to use - // different salt values. - let salt = 0u64; - let initial_balance = 0; - // Note: unused since initial_balance = 0 - let minter_address = account.address.clone(); - // Only the transfer module is allowed to mint - let authorized_minters = vec![self.ibc_transfer.address.clone()]; - let new_token_id = self - .ibc_transfer - .bank - .create_token( - token_name, - salt, - initial_balance, - minter_address, - authorized_minters, - &self.sdk_context, - &mut self.working_set.borrow_mut(), - ) - .map_err(|err| TokenTransferError::Other(err.to_string()))?; - - // Store the new address in `minted_tokens` - self.ibc_transfer.minted_tokens.set( - &denom, - &new_token_id, - *self.working_set.borrow_mut(), - ); - - new_token_id - } + None => self.create_token(token_name, account.address.clone())?, } }; @@ -397,7 +431,7 @@ impl<'ws, S: Spec> TokenTransferExecutionContext for IbcTransferContext<'ws, S> let token_id = { self.ibc_transfer - .minted_tokens + .minted_token_name_to_id .get(&denom, *self.working_set.borrow_mut()) .ok_or(TokenTransferError::InvalidCoin { coin: coin.to_string(), @@ -428,29 +462,20 @@ impl<'ws, S: Spec> TokenTransferExecutionContext for IbcTransferContext<'ws, S> port_id: &PortId, channel_id: &ChannelId, coin: &PrefixedCoin, - memo: &Memo, + _memo: &Memo, ) -> Result<(), TokenTransferError> { - let token_id_buf = general_purpose::STANDARD_NO_PAD - .decode(memo.as_ref()) - .map_err(|e| TokenTransferError::Other(format!("Failed to decode memo: {e}")))?; - - let token_id: TokenId = BorshDeserialize::deserialize(&mut token_id_buf.as_slice()) - .map_err(|e| TokenTransferError::Other(format!("Failed to decode memo: {e}")))?; - // The token name on the Sovereign SDK chains is not guaranteed to be // unique, and hence we must use the token ID (which is guaranteed to be // unique) as the ICS-20 denom to ensure uniqueness. - let denom = token_id.to_string(); - - // 1. ensure that token exists in `self.escrowed_tokens` map, which is - // necessary information when unescrowing tokens - self.ibc_transfer - .escrowed_tokens - .set(&denom, &token_id, *self.working_set.borrow_mut()); + let token_id = TokenId::from_str(&coin.denom.to_string()).map_err(|e| { + TokenTransferError::InvalidCoin { + coin: coin.to_string(), + } + })?; - // 2. transfer coins to escrow account let escrow_account = self.obtain_escrow_address(port_id, channel_id); + // transfer coins to escrow account self.transfer( token_id, &from_account.address, @@ -471,18 +496,15 @@ impl<'ws, S: Spec> TokenTransferExecutionContext for IbcTransferContext<'ws, S> channel_id: &ChannelId, coin: &PrefixedCoin, ) -> Result<(), TokenTransferError> { - let token_id = self - .ibc_transfer - .escrowed_tokens - .get(&coin.denom.to_string(), *self.working_set.borrow_mut()) - .ok_or(TokenTransferError::InvalidCoin { + let token_id = TokenId::from_str(&coin.denom.to_string()).map_err(|e| { + TokenTransferError::InvalidCoin { coin: coin.to_string(), - })?; - - // transfer coins out of escrow account to `to_account` + } + })?; let escrow_account = self.obtain_escrow_address(port_id, channel_id); + // transfer coins out of escrow account to `to_account` self.transfer(token_id, &escrow_account, &to_account.address, &coin.amount)?; Ok(()) diff --git a/modules/sov-ibc-transfer/src/lib.rs b/modules/sov-ibc-transfer/src/lib.rs index f14b2325..c910188f 100644 --- a/modules/sov-ibc-transfer/src/lib.rs +++ b/modules/sov-ibc-transfer/src/lib.rs @@ -1,6 +1,6 @@ pub mod context; mod genesis; -mod utils; +pub mod utils; use anyhow::anyhow; use ibc_core::handler::types::events::IbcEvent; @@ -28,22 +28,19 @@ pub struct IbcTransfer { #[module] bank: sov_bank::Bank, - /// Keeps track of the address of each token we minted by token denom. + /// Maps the token name to its corresponding token ID for tokens minted + /// through IBC. This mapping is used during mint/burn processes to validate + /// if the token exists and gives out needed ID for the minting/burning + /// process. #[state] - minted_tokens: StateMap, + minted_token_name_to_id: StateMap, - /// Keeps track of the address of each token we escrowed as a function of - /// the token denom. We need this map because we have the token ID - /// information when escrowing the tokens (i.e. when someone calls a - /// `send_transfer()`), but not when unescrowing tokens (i.e in a - /// `recv_packet`), in which case the only information we have is the ICS 20 - /// denom, and amount. Given that every token that is unescrowed has been - /// previously escrowed, our strategy to get the token ID associated - /// with a denom is - /// 1. when tokens are escrowed, save the mapping `denom -> token ID` - /// 2. when tokens are unescrowed, lookup the token ID by `denom` + /// Maps the token ID to its corresponding token name for tokens minted + /// through IBC. This mapping is used during escrows/un-escrows to verify if + /// the `TokenId` extracted from the `denom` is not an IBC-minted token, + /// indicating it is a native token for escrow/un-escrows purposes. #[state] - escrowed_tokens: StateMap, + minted_token_id_to_name: StateMap, /// Keeps track of escrow addresses associated with a specific port and /// channel pair, offering an efficient means to access these addresses diff --git a/modules/sov-ibc-transfer/src/rpc.rs b/modules/sov-ibc-transfer/src/rpc.rs index 8badcdd2..81eeec9d 100644 --- a/modules/sov-ibc-transfer/src/rpc.rs +++ b/modules/sov-ibc-transfer/src/rpc.rs @@ -7,13 +7,9 @@ use sov_modules_api::{Spec, WorkingSet}; use super::IbcTransfer; -#[derive(Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Clone)] -pub struct EscrowedTokenResponse { - pub token_id: TokenId, -} - #[derive(Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Clone)] pub struct MintedTokenResponse { + pub token_name: String, pub token_id: TokenId, } @@ -22,39 +18,50 @@ impl IbcTransfer where S: Spec, { - #[rpc_method(name = "escrowedToken")] - pub fn escrowed_token( + #[rpc_method(name = "mintedTokenName")] + pub fn minted_token_name( &self, - token_denom: String, + token_id: TokenId, working_set: &mut WorkingSet, - ) -> RpcResult { - let token_id = - self.escrowed_tokens - .get(&token_denom, working_set) - .ok_or(ErrorObjectOwned::owned( - jsonrpsee::types::error::UNKNOWN_ERROR_CODE, - format!("No escrowed token found for denom {token_denom}"), - None::, - ))?; + ) -> RpcResult { + let token_name = self + .minted_token_id_to_name + .get(&token_id, working_set) + .ok_or(to_jsonrpsee_error(format!( + "No IBC-created token found for ID: '{token_id}'" + )))?; - Ok(EscrowedTokenResponse { token_id }) + Ok(MintedTokenResponse { + token_name, + token_id, + }) } - #[rpc_method(name = "mintedToken")] - pub fn minted_token( + #[rpc_method(name = "mintedTokenId")] + pub fn minted_token_id( &self, - token_denom: String, + token_name: String, working_set: &mut WorkingSet, ) -> RpcResult { - let token_id = - self.minted_tokens - .get(&token_denom, working_set) - .ok_or(ErrorObjectOwned::owned( - jsonrpsee::types::error::UNKNOWN_ERROR_CODE, - format!("No minted token found for denom {token_denom}"), - None::, - ))?; + let token_id = self + .minted_token_name_to_id + .get(&token_name, working_set) + .ok_or(to_jsonrpsee_error(format!( + "No IBC-created token found for denom: '{token_name}'" + )))?; - Ok(MintedTokenResponse { token_id }) + Ok(MintedTokenResponse { + token_name, + token_id, + }) } } + +/// Creates an jsonrpsee error object +pub fn to_jsonrpsee_error(err: impl ToString) -> ErrorObjectOwned { + ErrorObjectOwned::owned( + jsonrpsee::types::error::UNKNOWN_ERROR_CODE, + err.to_string(), + None::, + ) +} diff --git a/modules/sov-ibc/src/rpc/helpers.rs b/modules/sov-ibc/src/rpc/helpers.rs index 1158f0c7..ef5e9e40 100644 --- a/modules/sov-ibc/src/rpc/helpers.rs +++ b/modules/sov-ibc/src/rpc/helpers.rs @@ -5,7 +5,7 @@ use borsh::BorshSerialize; use ibc_core::client::types::Height; use ibc_core::host::ValidationContext; use jsonrpsee::core::RpcResult; -use jsonrpsee::types::ErrorObjectOwned; +use sov_ibc_transfer::to_jsonrpsee_error; use sov_modules_api::{Spec, StateMap, WorkingSet}; use sov_state::storage::{StateCodec, StateItemCodec}; @@ -34,15 +34,6 @@ impl Ibc { } } -/// Creates an jsonrpsee error object -pub fn to_jsonrpsee_error(err: impl ToString) -> ErrorObjectOwned { - ErrorObjectOwned::owned( - jsonrpsee::types::error::UNKNOWN_ERROR_CODE, - err.to_string(), - None::, - ) -} - /// Trait for proof agnostic storage value retrieval. /// /// This trait is introduced to avoid duplication in the query methods diff --git a/modules/sov-ibc/src/rpc/methods.rs b/modules/sov-ibc/src/rpc/methods.rs index cadcd524..5fe6ea11 100644 --- a/modules/sov-ibc/src/rpc/methods.rs +++ b/modules/sov-ibc/src/rpc/methods.rs @@ -38,11 +38,12 @@ use ibc_query::core::connection::{ QueryConnectionsRequest, QueryConnectionsResponse, }; use jsonrpsee::core::RpcResult; +use sov_ibc_transfer::to_jsonrpsee_error; use sov_modules_api::macros::rpc_gen; use sov_modules_api::{Spec, WorkingSet}; use crate::context::IbcContext; -use crate::helpers::{to_jsonrpsee_error, WithProof, WithoutProof}; +use crate::helpers::{WithProof, WithoutProof}; use crate::Ibc; /// Structure returned by the `client_state` rpc method.