diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3e4fdd698c3..0f588feb0d5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -149,10 +149,3 @@ vercel.json @iotaledger/tooling # Disable code ownership for these auto-generated files /Cargo.lock /pnpm-lock.yaml - -# TODO -# Bridge related stuff will be skipped for now -# /crates/iota-bridge/ -# /crates/iota-bridge-cli/ -# /crates/iota-bridge-indexer/ -# /bridge/ diff --git a/.github/crates-filters.yml b/.github/crates-filters.yml index 5c8457d46c0..b76d447392c 100644 --- a/.github/crates-filters.yml +++ b/.github/crates-filters.yml @@ -14,12 +14,6 @@ iota-aws-orchestrator: - "crates/iota-aws-orchestrator/**" iota-benchmark: - "crates/iota-benchmark/**" -iota-bridge: - - "crates/iota-bridge/**" -iota-bridge-cli: - - "crates/iota-bridge-cli/**" -iota-bridge-indexer: - - "crates/iota-bridge-indexer/**" iota-cluster-test: - "crates/iota-cluster-test/**" iota-common: diff --git a/Cargo.lock b/Cargo.lock index 4f534dbb589..2cdf90704b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,19 +25,13 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -87,13 +81,13 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "const-random", - "getrandom 0.2.15", + "getrandom 0.3.3", "once_cell", "version_check", "zerocopy", @@ -116,9 +110,9 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "aligned-vec" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e0966165eaf052580bd70eb1b32cb3d6245774c0104d1b2793e9650bf83b52a" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" dependencies = [ "equator", ] @@ -140,9 +134,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -171,7 +165,7 @@ dependencies = [ "ed25519", "futures", "hex", - "http 1.1.0", + "http 1.3.1", "matchit 0.5.0", "pin-project-lite", "pkcs8 0.10.2", @@ -179,16 +173,16 @@ dependencies = [ "quinn-proto", "rand 0.8.5", "rcgen", - "ring 0.17.13", - "rustls 0.23.18", - "rustls-webpki 0.103.1", + "ring", + "rustls 0.23.27", + "rustls-webpki 0.103.3", "serde", "serde_json", "socket2", "tap", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tower 0.4.13", "tracing", "x509-parser", @@ -202,7 +196,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -236,7 +230,7 @@ dependencies = [ "tokio", "tower 0.4.13", "tracing", - "uuid 1.10.0", + "uuid", ] [[package]] @@ -247,9 +241,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -262,43 +256,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" dependencies = [ "backtrace", ] @@ -314,9 +309,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] @@ -356,7 +351,7 @@ dependencies = [ "blake2", "derivative", "digest 0.10.7", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -528,9 +523,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "arrow" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ba0d7248932f4e2a12fb37f0a2e3ec82b3bdedbac2a1dce186e036843b8f8c" +checksum = "d3a3ec4fe573f9d1f59d99c085197ef669b00b088ba1d7bb75224732d9357a74" dependencies = [ "arrow-arith", "arrow-array", @@ -549,9 +544,9 @@ dependencies = [ [[package]] name = "arrow-arith" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d60afcdc004841a5c8d8da4f4fa22d64eb19c0c01ef4bcedd77f175a7cf6e38f" +checksum = "6dcf19f07792d8c7f91086c67b574a79301e367029b17fcf63fb854332246a10" dependencies = [ "arrow-array", "arrow-buffer", @@ -564,9 +559,9 @@ dependencies = [ [[package]] name = "arrow-array" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f16835e8599dbbb1659fd869d865254c4cf32c6c2bb60b6942ac9fc36bfa5da" +checksum = "7845c32b41f7053e37a075b3c2f29c6f5ea1b3ca6e5df7a2d325ee6e1b4a63cf" dependencies = [ "ahash", "arrow-buffer", @@ -574,15 +569,15 @@ dependencies = [ "arrow-schema", "chrono", "half", - "hashbrown 0.14.5", + "hashbrown 0.15.3", "num", ] [[package]] name = "arrow-buffer" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a1f34f0faae77da6b142db61deba2cb6d60167592b178be317b341440acba80" +checksum = "5b5c681a99606f3316f2a99d9c8b6fa3aad0b1d34d8f6d7a1b471893940219d8" dependencies = [ "bytes", "half", @@ -591,9 +586,9 @@ dependencies = [ [[package]] name = "arrow-cast" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "450e4abb5775bca0740bec0bcf1b1a5ae07eff43bd625661c4436d8e8e4540c4" +checksum = "6365f8527d4f87b133eeb862f9b8093c009d41a210b8f101f91aa2392f61daac" dependencies = [ "arrow-array", "arrow-buffer", @@ -611,9 +606,9 @@ dependencies = [ [[package]] name = "arrow-csv" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a4e4d63830a341713e35d9a42452fbc6241d5f42fa5cf6a4681b8ad91370c4" +checksum = "30dac4d23ac769300349197b845e0fd18c7f9f15d260d4659ae6b5a9ca06f586" dependencies = [ "arrow-array", "arrow-buffer", @@ -630,9 +625,9 @@ dependencies = [ [[package]] name = "arrow-data" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b1e618bbf714c7a9e8d97203c806734f012ff71ae3adc8ad1b075689f540634" +checksum = "cd962fc3bf7f60705b25bcaa8eb3318b2545aa1d528656525ebdd6a17a6cd6fb" dependencies = [ "arrow-buffer", "arrow-schema", @@ -642,9 +637,9 @@ dependencies = [ [[package]] name = "arrow-ipc" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98e983549259a2b97049af7edfb8f28b8911682040e99a94e4ceb1196bd65c2" +checksum = "c3527365b24372f9c948f16e53738eb098720eea2093ae73c7af04ac5e30a39b" dependencies = [ "arrow-array", "arrow-buffer", @@ -656,9 +651,9 @@ dependencies = [ [[package]] name = "arrow-json" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b198b9c6fcf086501730efbbcb483317b39330a116125af7bb06467d04b352a3" +checksum = "acdec0024749fc0d95e025c0b0266d78613727b3b3a5d4cf8ea47eb6d38afdd1" dependencies = [ "arrow-array", "arrow-buffer", @@ -667,7 +662,7 @@ dependencies = [ "arrow-schema", "chrono", "half", - "indexmap 2.5.0", + "indexmap 2.9.0", "lexical-core", "num", "serde", @@ -676,9 +671,9 @@ dependencies = [ [[package]] name = "arrow-ord" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2427f37b4459a4b9e533045abe87a5183a5e0995a3fc2c2fd45027ae2cc4ef3f" +checksum = "79af2db0e62a508d34ddf4f76bfd6109b6ecc845257c9cba6f939653668f89ac" dependencies = [ "arrow-array", "arrow-buffer", @@ -691,9 +686,9 @@ dependencies = [ [[package]] name = "arrow-row" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15959657d92e2261a7a323517640af87f5afd9fd8a6492e424ebee2203c567f6" +checksum = "da30e9d10e9c52f09ea0cf15086d6d785c11ae8dcc3ea5f16d402221b6ac7735" dependencies = [ "ahash", "arrow-array", @@ -705,15 +700,15 @@ dependencies = [ [[package]] name = "arrow-schema" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf0388a18fd7f7f3fe3de01852d30f54ed5182f9004db700fbe3ba843ed2794" +checksum = "35b0f9c0c3582dd55db0f136d3b44bfa0189df07adcf7dc7f2f2e74db0f52eb8" [[package]] name = "arrow-select" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b83e5723d307a38bf00ecd2972cd078d1339c7fd3eb044f609958a9a24463f3a" +checksum = "92fc337f01635218493c23da81a364daf38c694b05fc20569c3193c11c561984" dependencies = [ "ahash", "arrow-array", @@ -725,9 +720,9 @@ dependencies = [ [[package]] name = "arrow-string" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab3db7c09dd826e74079661d84ed01ed06547cf75d52c2818ef776d0d852305" +checksum = "d596a9fc25dae556672d5069b090331aca8acb93cae426d8b7dcdf1c558fa0ce" dependencies = [ "arrow-array", "arrow-buffer", @@ -737,16 +732,7 @@ dependencies = [ "memchr", "num", "regex", - "regex-syntax 0.8.4", -] - -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", + "regex-syntax 0.8.5", ] [[package]] @@ -779,8 +765,8 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", - "synstructure 0.13.1", + "syn 2.0.101", + "synstructure 0.13.2", ] [[package]] @@ -791,20 +777,20 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "assert_cmd" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66" dependencies = [ "anstyle", "bstr", "doc-comment", "libc", - "predicates 3.1.2", + "predicates 3.1.3", "predicates-core", "predicates-tree", "wait-timeout", @@ -812,18 +798,18 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.12" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" +checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" dependencies = [ - "brotli", + "brotli 8.0.1", "flate2", "futures-core", "memchr", "pin-project-lite", "tokio", - "zstd 0.13.2", - "zstd-safe 7.2.1", + "zstd", + "zstd-safe", ] [[package]] @@ -846,8 +832,8 @@ dependencies = [ "futures-timer", "futures-util", "handlebars", - "http 1.1.0", - "indexmap 2.5.0", + "http 1.3.1", + "indexmap 2.9.0", "lru", "mime", "multer", @@ -861,7 +847,7 @@ dependencies = [ "serde_urlencoded", "static_assertions_next", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "tracing", "tracing-futures", ] @@ -880,7 +866,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tower-service", ] @@ -892,13 +878,13 @@ checksum = "a94c2d176893486bd37cd1b6defadd999f7357bf5804e92f510c08bcf16c538f" dependencies = [ "Inflector", "async-graphql-parser", - "darling 0.20.10", - "proc-macro-crate 3.2.0", + "darling 0.20.11", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", "strum 0.26.3", - "syn 2.0.100", - "thiserror 1.0.64", + "syn 2.0.101", + "thiserror 1.0.69", ] [[package]] @@ -920,7 +906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef5ec94176a12a8cbe985cd73f2e54dc9c702c88c766bdef12f1f3a67cedbee1" dependencies = [ "bytes", - "indexmap 2.5.0", + "indexmap 2.9.0", "serde", "serde_json", ] @@ -933,14 +919,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -949,13 +935,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -966,24 +952,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", -] - -[[package]] -name = "async_io_stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" -dependencies = [ - "futures", - "pharos", - "rustc_version", + "syn 2.0.101", ] [[package]] @@ -1013,17 +988,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" -[[package]] -name = "auto_impl" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "auto_ops" version = "0.3.0" @@ -1032,15 +996,15 @@ checksum = "7460f7dd8e100147b82a63afca1a20eb6c231ee36b90ba7272e14951cb58af59" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.6" +version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848d7b9b605720989929279fa644ce8f244d0ce3146fcca5b70e4eb7b3c020fc" +checksum = "02a18fd934af6ae7ca52410d4548b98eb895aab0f1ea417d168d85db1434a141" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1057,8 +1021,8 @@ dependencies = [ "bytes", "fastrand", "hex", - "http 0.2.12", - "ring 0.17.13", + "http 1.3.1", + "ring", "time", "tokio", "tracing", @@ -1068,9 +1032,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +checksum = "687bc16bc431a8533fe0097c7f0182874767f920989d7260950172ae8e3c4465" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1080,36 +1044,32 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.10.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd82dba44d209fddb11c190e0a94b78651f95299598e472215667417a03ff1d" +checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" dependencies = [ "aws-lc-sys", - "mirai-annotations", - "paste", "zeroize", ] [[package]] name = "aws-lc-sys" -version = "0.22.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7a4168111d7eb622a31b214057b8509c0a7e1794f44c546d742330dc793972" +checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" dependencies = [ "bindgen 0.69.5", "cc", "cmake", "dunce", "fs_extra", - "libc", - "paste", ] [[package]] name = "aws-runtime" -version = "1.4.3" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" +checksum = "6c4063282c69991e57faab9e5cb21ae557e59f5b0fb285c196335243df8dc25c" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1123,18 +1083,17 @@ dependencies = [ "fastrand", "http 0.2.12", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "tracing", - "uuid 1.10.0", + "uuid", ] [[package]] name = "aws-sdk-dynamodb" -version = "1.47.0" +version = "1.75.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8abc61e7374a01ecebcc3fd465655a2e1b83455f15b26716bfac05c35d25fa1b" +checksum = "f8b430cce50bf8a12bd77873581e9b05aaa351df3b435b8dd28319ecc71db494" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1148,16 +1107,15 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-ec2" -version = "1.74.1" +version = "1.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab6f74dd8b24475ed56ffaeabbbbc3748ec639d16e78ff73f3eefa95b90bfebe" +checksum = "a10d4df0634b58ee0f4e3ef7b0c94f03695e3187ed0e2ce4dfec5c9990a2efd7" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1172,16 +1130,15 @@ dependencies = [ "aws-types", "fastrand", "http 0.2.12", - "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-kms" -version = "1.44.0" +version = "1.69.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6550445e0913c9383375f4a5a2f550817567a19a178107fce1e1afd767f802a" +checksum = "c64c93b24f98760979113386e444886fc812632d4d84910b802d69a2bdbb5349" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1193,17 +1150,17 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", + "fastrand", "http 0.2.12", - "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sso" -version = "1.43.0" +version = "1.69.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a9d27ed1c12b1140c47daf1bc541606c43fdafd918c4797d520db0043ceef2" +checksum = "6e9c2e5f57f4a5b7f7e7a1c54427d679ab2337a29e578efbd005e244fd90c4ea" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1215,17 +1172,17 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", + "fastrand", "http 0.2.12", - "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-ssooidc" -version = "1.44.0" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44514a6ca967686cde1e2a1b81df6ef1883d0e3e570da8d8bc5c491dcb6fc29b" +checksum = "0acaed9a89e82afa50045f68d27bc927600bdc0cc7d617dec61dfb9e6952d22f" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1237,17 +1194,17 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", + "fastrand", "http 0.2.12", - "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sts" -version = "1.43.0" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7a4d279762a35b9df97209f6808b95d4fe78547fe2316b4d200a0283960c5a" +checksum = "015851dba63fbf22c70c278d8e60c85f008d03640c5b40908730802e1267f651" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1260,17 +1217,17 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", + "fastrand", "http 0.2.12", - "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sigv4" -version = "1.2.4" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8db6904450bafe7473c6ca9123f88cc11089e41a025408f992db4e22d3be68" +checksum = "3734aecf9ff79aa401a6ca099d076535ab465ff76b46440cf567c8e70b65dc13" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -1281,19 +1238,18 @@ dependencies = [ "hex", "hmac", "http 0.2.12", - "http 1.1.0", - "once_cell", + "http 1.3.1", "percent-encoding", - "sha2 0.10.8", + "sha2 0.10.9", "time", "tracing", ] [[package]] name = "aws-smithy-async" -version = "1.2.1" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" dependencies = [ "futures-util", "pin-project-lite", @@ -1302,9 +1258,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.11" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6" +checksum = "99335bec6cdc50a346fda1437f9fefe33abf8c99060739a546a16457f2862ca9" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -1312,23 +1268,60 @@ dependencies = [ "bytes-utils", "futures-core", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", "tracing", ] +[[package]] +name = "aws-smithy-http-client" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e44697a9bded898dcd0b1cb997430d949b87f4f8940d91023ae9062bf218250" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.4.10", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.6.0", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.5", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.27", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tower 0.5.2", + "tracing", +] + [[package]] name = "aws-smithy-json" -version = "0.60.7" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +checksum = "92144e45819cae7dc62af23eac5a038a58aa544432d2102609654376a900bd07" dependencies = [ "aws-smithy-types", ] +[[package]] +name = "aws-smithy-observability" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9364d5989ac4dd918e5cc4c4bdcc61c9be17dcd2586ea7f69e348fc7c6cab393" +dependencies = [ + "aws-smithy-runtime-api", +] + [[package]] name = "aws-smithy-query" version = "0.60.7" @@ -1341,42 +1334,39 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.1" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" +checksum = "14302f06d1d5b7d333fd819943075b13d27c7700b414f574c3c35859bfb55d5e" dependencies = [ "aws-smithy-async", "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "fastrand", - "h2 0.3.26", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", - "httparse", - "hyper 0.14.30", - "hyper-rustls 0.24.2", - "once_cell", "pin-project-lite", "pin-utils", - "rustls 0.21.12", "tokio", "tracing", ] [[package]] name = "aws-smithy-runtime-api" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" +checksum = "a1e5d9e3a80a18afa109391fb5ad09c3daf887b516c6fd805a157c6ea7994a57" dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.1.0", + "http 1.3.1", "pin-project-lite", "tokio", "tracing", @@ -1385,16 +1375,16 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.6" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03701449087215b5369c7ea17fef0dd5d24cb93439ec5af0c7615f58c3f22605" +checksum = "40076bd09fadbc12d5e026ae080d0930defa606856186e31d83ccc6a255eeaf3" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.1.0", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -1406,7 +1396,7 @@ dependencies = [ "serde", "time", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", ] [[package]] @@ -1420,9 +1410,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.3" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" +checksum = "8a322fec39e4df22777ed3ad8ea868ac2f94cd15e1a55f6ee8d8d6305057689a" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1434,19 +1424,19 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.6" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", - "base64 0.21.7", + "base64 0.22.1", "bytes", "futures-util", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "hyper-util", "itoa", "matchit 0.7.3", @@ -1460,9 +1450,9 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", - "tokio-tungstenite 0.23.1", + "tokio-tungstenite", "tower 0.5.2", "tower-layer", "tower-service", @@ -1471,20 +1461,20 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tower-layer", "tower-service", "tracing", @@ -1492,25 +1482,26 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.4" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" +checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" dependencies = [ "axum", "axum-core", "bytes", + "fastrand", "futures-util", "headers", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", + "multer", "pin-project-lite", "serde", "tower 0.5.2", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -1521,17 +1512,17 @@ dependencies = [ "arc-swap", "bytes", "futures-util", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "hyper-util", "pin-project-lite", - "rustls 0.23.18", - "rustls-pemfile 2.1.3", + "rustls 0.23.27", + "rustls-pemfile 2.2.0", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.2", "tower 0.4.13", "tower-service", ] @@ -1543,7 +1534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom 0.2.15", + "getrandom 0.2.16", "instant", "pin-project-lite", "rand 0.8.5", @@ -1552,17 +1543,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.4", + "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -1625,9 +1616,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bcrypt-pbkdf" @@ -1637,7 +1628,7 @@ checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" dependencies = [ "blowfish", "pbkdf2 0.12.2", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -1647,7 +1638,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b6598a2f5d564fb7855dc6b06fd1c38cff5a72bd8b863a4d021938497b440a" dependencies = [ "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -1683,7 +1674,7 @@ dependencies = [ "byteorder", "ff", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -1708,9 +1699,9 @@ dependencies = [ [[package]] name = "bigdecimal" -version = "0.4.5" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee" +checksum = "1a22f228ab7a1b23027ccc6c350b72868017af7ea8356fbdf19f8d991c690013" dependencies = [ "autocfg", "libm", @@ -1760,7 +1751,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1769,7 +1760,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools 0.12.1", @@ -1782,15 +1773,15 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.100", + "syn 2.0.101", "which", ] [[package]] name = "bip32" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa13fae8b6255872fd86f7faf4b41168661d7d78609f7bfe6771b85c6739a15b" +checksum = "db40d3dfbeab4e031d78c844642fa0caa0b0db11ce1607ac9d2986dff1405c69" dependencies = [ "bs58 0.5.1", "hmac", @@ -1799,25 +1790,26 @@ dependencies = [ "pbkdf2 0.12.2", "rand_core 0.6.4", "ripemd", - "sha2 0.10.8", + "secp256k1", + "sha2 0.10.9", "subtle", "zeroize", ] [[package]] name = "bit-set" -version = "0.5.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitcoin-private" @@ -1842,9 +1834,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" dependencies = [ "serde", ] @@ -1893,9 +1885,9 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99" dependencies = [ "arrayref", "arrayvec", @@ -1904,9 +1896,9 @@ dependencies = [ [[package]] name = "blake2s_simd" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" +checksum = "e90f7deecfac93095eb874a40febd69427776e24e1bd7f87f33ac62d6f0174df" dependencies = [ "arrayref", "arrayvec", @@ -1959,9 +1951,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" +checksum = "47c79a94619fade3c0b887670333513a67ac28a6a7e653eb260bf0d4103db38d" dependencies = [ "cc", "glob", @@ -1993,20 +1985,41 @@ checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "brotli" -version = "6.0.0" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 4.0.3", +] + +[[package]] +name = "brotli" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 5.0.0", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor", ] [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -2024,32 +2037,32 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2 0.10.8", + "sha2 0.10.9", "tinyvec", ] [[package]] name = "bstr" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.9", "serde", ] [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byte-slice-cast" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytecount" @@ -2059,9 +2072,9 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" [[package]] name = "byteorder" @@ -2071,9 +2084,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] @@ -2097,24 +2110,13 @@ dependencies = [ "bytes", ] -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] @@ -2133,7 +2135,7 @@ dependencies = [ "instant", "lazy_static", "once_cell", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", ] @@ -2173,9 +2175,9 @@ checksum = "6b5271031022835ee8c7582fe67403bd6cb3d962095787af7921027234bab5bf" [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -2191,21 +2193,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 1.0.64", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -2240,9 +2228,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.16" +version = "1.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" dependencies = [ "jobserver", "libc", @@ -2270,6 +2258,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chacha20" version = "0.9.1" @@ -2360,9 +2354,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -2370,36 +2364,36 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim 0.11.1", - "terminal_size 0.4.1", + "terminal_size", ] [[package]] name = "clap_complete" -version = "4.5.40" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9" +checksum = "c91d3baa3bcd889d60e6ef28874126a0b384fd225ab83aa6d8a801c519194ce1" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2410,9 +2404,9 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cmake" -version = "0.1.51" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] @@ -2438,58 +2432,6 @@ dependencies = [ "unicode-width 0.1.14", ] -[[package]] -name = "coins-bip32" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" -dependencies = [ - "bs58 0.5.1", - "coins-core", - "digest 0.10.7", - "hmac", - "k256", - "serde", - "sha2 0.10.8", - "thiserror 1.0.64", -] - -[[package]] -name = "coins-bip39" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" -dependencies = [ - "bitvec 1.0.1", - "coins-bip32", - "hmac", - "once_cell", - "pbkdf2 0.12.2", - "rand 0.8.5", - "sha2 0.10.8", - "thiserror 1.0.64", -] - -[[package]] -name = "coins-core" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" -dependencies = [ - "base64 0.21.7", - "bech32", - "bs58 0.5.1", - "digest 0.10.7", - "generic-array", - "hex", - "ripemd", - "serde", - "serde_derive", - "sha2 0.10.8", - "sha3 0.10.8", - "thiserror 1.0.64", -] - [[package]] name = "collectable" version = "0.0.2" @@ -2498,45 +2440,45 @@ checksum = "08abddbaad209601e53c7dd4308d8c04c06f17bb7df006434e586a22b83be45a" [[package]] name = "color-eyre" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +checksum = "e6e1761c0e16f8883bbbb8ce5990867f4f06bf11a0253da6495a04ce4b6ef0ec" dependencies = [ "backtrace", "color-spantrace", "eyre", "indenter", "once_cell", - "owo-colors 3.5.0", + "owo-colors", "tracing-error", ] [[package]] name = "color-spantrace" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +checksum = "2ddd8d5bfda1e11a501d0a7303f3bfed9aa632ebdb859be40d0fd70478ed70d5" dependencies = [ "once_cell", - "owo-colors 3.5.0", + "owo-colors", "tracing-core", "tracing-error", ] [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -2603,7 +2545,7 @@ dependencies = [ "enum_dispatch", "fastcrypto", "futures", - "http 1.1.0", + "http 1.3.1", "iota-common", "iota-http", "iota-macros", @@ -2619,18 +2561,18 @@ dependencies = [ "quinn-proto", "rand 0.8.5", "rstest", - "rustls 0.23.18", + "rustls 0.23.27", "serde", "shared-crypto", "strum_macros 0.26.4", "tap", "telemetry-subscribers", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.2", "tokio-stream", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tonic", "tonic-build", "tonic-rustls", @@ -2661,29 +2603,29 @@ dependencies = [ "tempfile", "tokio", "tokio-stream", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tracing", "typed-store", ] [[package]] name = "console" -version = "0.15.8" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ - "encode_unicode 0.3.6", - "lazy_static", + "encode_unicode", "libc", - "unicode-width 0.1.14", - "windows-sys 0.52.0", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", ] [[package]] name = "console-api" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ed14aa9c9f927213c6e4f3ef75faaad3406134efe84ba2cb7983431d5f0931" +checksum = "8030735ecb0d128428b64cd379809817e620a40e5001c54465b99ec5feec2857" dependencies = [ "futures-core", "prost", @@ -2694,9 +2636,9 @@ dependencies = [ [[package]] name = "console-subscriber" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e3a111a37f3333946ebf9da370ba5c5577b18eb342ec683eb488dd21980302" +checksum = "6539aa9c6a4cd31f4b1c040f860a1eac9aa80e7df6b05d506a6e7179936d6a01" dependencies = [ "console-api", "crossbeam-channel", @@ -2718,19 +2660,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "const-hex" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" -dependencies = [ - "cfg-if", - "cpufeatures", - "hex", - "proptest", - "serde", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -2752,7 +2681,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -2763,6 +2692,26 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3618cccc083bb987a415d85c02ca6c9994ea5b44731ec28b9ecf09658655fba9" +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -2791,6 +2740,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -2818,9 +2777,9 @@ dependencies = [ [[package]] name = "count-min-sketch" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca319fe30d7b68949da20d78b612215708af87157d49665a4545dadcc20fecc7" +checksum = "3fef0a447ef2e9e6bd57e379f88702c58c4a4253ba82fb175bd7db012192311a" dependencies = [ "rand 0.8.5", "siphasher", @@ -2846,9 +2805,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -2924,9 +2883,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -2943,18 +2902,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" @@ -2994,11 +2953,11 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "crossterm_winapi", - "mio 1.0.2", + "mio 1.0.3", "parking_lot 0.12.3", - "rustix", + "rustix 0.38.44", "signal-hook", "signal-hook-mio", "winapi", @@ -3015,9 +2974,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-bigint" @@ -3056,9 +3015,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] @@ -3096,7 +3055,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3120,11 +3079,11 @@ checksum = "478c02b53607e3f21c374f024c2cfc2154e554905bba478e8e09409f10ce3726" dependencies = [ "cynic-proc-macros", "ref-cast", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "serde_json", "static_assertions", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -3135,14 +3094,14 @@ checksum = "7c0ec86f960a00ce087e96ff6f073f6ff28b6876d69ce8caa06c03fb4143981c" dependencies = [ "counter", "cynic-parser", - "darling 0.20.10", + "darling 0.20.11", "once_cell", - "ouroboros 0.18.4", + "ouroboros 0.18.5", "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.100", - "thiserror 1.0.64", + "syn 2.0.101", + "thiserror 1.0.69", ] [[package]] @@ -3151,7 +3110,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "718f6cd8c54ae5249fd42b0c86639df0100b8a86eea2e5f1b915cde2e1481453" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.9.0", "lalrpop-util", "logos", ] @@ -3163,9 +3122,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a69ecdf4aa110fed1c0c8de290bc8ccb2835388733cf2f418f0abdf6ff3899" dependencies = [ "cynic-codegen", - "darling 0.20.10", + "darling 0.20.11", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3180,12 +3139,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", + "darling_core 0.20.11", + "darling_macro 0.20.11", ] [[package]] @@ -3204,16 +3163,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3229,20 +3188,20 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core 0.20.10", + "darling_core 0.20.11", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "dary_heap" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" +checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728" [[package]] name = "dashmap" @@ -3259,15 +3218,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "data-encoding-macro" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" +checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -3275,12 +3234,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" +checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.101", ] [[package]] @@ -3299,7 +3258,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ - "uuid 1.10.0", + "uuid", ] [[package]] @@ -3315,9 +3274,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "pem-rfc7468 0.7.0", @@ -3340,9 +3299,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -3372,26 +3331,26 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3411,17 +3370,17 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "unicode-xid", ] [[package]] name = "diesel" -version = "2.2.4" +version = "2.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e" +checksum = "ff3e1edb1f37b4953dd5176916347289ed43d7119cc2e6c7c3f7849ff44ea506" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "byteorder", "chrono", "diesel_derives", @@ -3433,15 +3392,15 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.2.3" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4" +checksum = "68d4216021b3ea446fd2047f5c8f8fe6e98af34508a254a01e4d6bc1e844f84d" dependencies = [ "diesel_table_macro_syntax", "dsl_auto_type", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3461,7 +3420,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3524,6 +3483,15 @@ dependencies = [ "dirs-sys 0.4.1", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -3541,7 +3509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -3553,10 +3521,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -3564,7 +3544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -3576,14 +3556,14 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "dissimilar" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" +checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921" [[package]] name = "doc-comment" @@ -3605,16 +3585,16 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dsl_auto_type" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +checksum = "139ae9aca7527f85f26dd76483eb38533fd84bd571065da1739656ef71c5ff5b" dependencies = [ - "darling 0.20.10", + "darling 0.20.11", "either", "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3633,15 +3613,15 @@ dependencies = [ "nom", "rust_decimal", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", "time", ] [[package]] name = "dyn-clone" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "ecdsa" @@ -3649,7 +3629,7 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.9", + "der 0.7.10", "digest 0.10.7", "elliptic-curve", "rfc6979", @@ -3681,7 +3661,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "sha2 0.9.9", - "thiserror 1.0.64", + "thiserror 1.0.69", "zeroize", ] @@ -3695,7 +3675,7 @@ dependencies = [ "ed25519", "rand_core 0.6.4", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "subtle", "zeroize", ] @@ -3712,15 +3692,15 @@ dependencies = [ "hex", "rand_core 0.6.4", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "zeroize", ] [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" @@ -3746,21 +3726,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ena" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" -dependencies = [ - "log", -] - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "encode_unicode" version = "1.0.0" @@ -3769,31 +3734,13 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] -[[package]] -name = "enr" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3d8dc56e02f954cac8eb489772c552c473346fc34f67412bb6244fd647f7e4" -dependencies = [ - "base64 0.21.7", - "bytes", - "hex", - "k256", - "log", - "rand 0.8.5", - "rlp", - "serde", - "sha3 0.10.8", - "zeroize", -] - [[package]] name = "enum-as-inner" version = "0.6.1" @@ -3803,7 +3750,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3822,7 +3769,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3850,35 +3797,35 @@ dependencies = [ [[package]] name = "equator" -version = "0.2.2" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35da53b5a021d2484a7cc49b2ac7f2d840f8236a286f84202369bd338d761ea" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" dependencies = [ "equator-macro", ] [[package]] name = "equator-macro" -version = "0.2.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf679796c0322556351f287a51b49e48f7c4986e727b5dd78c972d30e2e16cc" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erasable" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f11890ce181d47a64e5d1eb4b6caba0e7bae911a356723740d058a5d0340b7d" +checksum = "437cfb75878119ed8265685c41a115724eae43fb7cc5a0bf0e4ecc3b803af1c4" dependencies = [ "autocfg", "scopeguard", @@ -3886,465 +3833,158 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] -name = "eth-keystore" -version = "0.5.0" +name = "ethnum" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" -dependencies = [ - "aes", - "ctr", - "digest 0.10.7", - "hex", - "hmac", - "pbkdf2 0.11.0", - "rand 0.8.5", - "scrypt 0.10.0", - "serde", - "serde_json", - "sha2 0.10.8", - "sha3 0.10.8", - "thiserror 1.0.64", - "uuid 0.8.2", -] +checksum = "0939f82868b77ef93ce3c3c3daf2b3c526b456741da5a1a4559e590965b6026b" [[package]] -name = "ethabi" -version = "18.0.0" +name = "expect-test" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +checksum = "63af43ff4431e848fb47472a920f14fa71c24de13255a5692e93d4e90302acb0" dependencies = [ - "ethereum-types", - "hex", + "dissimilar", "once_cell", - "regex", - "serde", - "serde_json", - "sha3 0.10.8", - "thiserror 1.0.64", - "uint", ] [[package]] -name = "ethbloom" -version = "0.13.0" +name = "eyre" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ - "crunchy", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", - "impl-rlp", - "impl-serde 0.4.0", - "scale-info", - "tiny-keccak", + "indenter", + "once_cell", ] [[package]] -name = "ethereum-types" -version = "0.14.1" +name = "fail" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +checksum = "3be3c61c59fdc91f5dbc3ea31ee8623122ce80057058be560654c5d410d181a6" dependencies = [ - "ethbloom", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", - "impl-rlp", - "impl-serde 0.4.0", - "primitive-types 0.12.2", - "scale-info", - "uint", + "lazy_static", + "log", + "rand 0.7.3", ] [[package]] -name = "ethers" -version = "2.0.14" +name = "fast_chemail" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816841ea989f0c69e459af1cf23a6b0033b19a55424a1ea3a30099becdb8dec0" +checksum = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4" dependencies = [ - "ethers-addressbook", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-middleware", - "ethers-providers", - "ethers-signers", - "ethers-solc", + "ascii_utils", ] [[package]] -name = "ethers-addressbook" -version = "2.0.14" +name = "fastbloom" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5495afd16b4faa556c3bba1f21b98b4983e53c1755022377051a975c3b021759" +checksum = "27cea6e7f512d43b098939ff4d5a5d6fe3db07971e1d05176fe26c642d33f5b8" dependencies = [ - "ethers-core", - "once_cell", - "serde", - "serde_json", + "getrandom 0.3.3", + "rand 0.9.1", + "siphasher", + "wide", ] [[package]] -name = "ethers-contract" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fceafa3578c836eeb874af87abacfb041f92b4da0a78a5edd042564b8ecdaaa" +name = "fastcrypto" +version = "0.1.8" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=2f502fd8570fe4e9cff36eea5bbd6fef22002898#2f502fd8570fe4e9cff36eea5bbd6fef22002898" dependencies = [ - "const-hex", - "ethers-contract-abigen", - "ethers-contract-derive", - "ethers-core", - "ethers-providers", - "futures-util", + "aes", + "aes-gcm", + "ark-ec", + "ark-ff", + "ark-secp256r1", + "ark-serialize", + "auto_ops", + "base64ct", + "bech32", + "bincode", + "blake2", + "blst", + "bs58 0.4.0", + "cbc", + "ctr", + "curve25519-dalek-ng", + "derive_more 0.99.20", + "digest 0.10.7", + "ecdsa", + "ed25519-consensus", + "elliptic-curve", + "fastcrypto-derive", + "generic-array", + "hex", + "hex-literal", + "hkdf", + "lazy_static", + "num-bigint 0.4.6", "once_cell", - "pin-project", - "serde", - "serde_json", - "thiserror 1.0.64", -] - -[[package]] -name = "ethers-contract-abigen" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04ba01fbc2331a38c429eb95d4a570166781f14290ef9fdb144278a90b5a739b" -dependencies = [ - "Inflector", - "const-hex", - "dunce", - "ethers-core", - "ethers-etherscan", - "eyre", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "reqwest 0.11.27", + "p256", + "rand 0.8.5", + "readonly", + "rfc6979", + "rsa 0.8.2", + "schemars", + "secp256k1", "serde", "serde_json", - "syn 2.0.100", - "toml 0.8.19", - "walkdir", + "serde_with", + "sha2 0.10.9", + "sha3 0.10.8", + "signature 2.2.0", + "static_assertions", + "thiserror 1.0.69", + "tokio", + "typenum", + "zeroize", ] [[package]] -name = "ethers-contract-derive" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87689dcabc0051cde10caaade298f9e9093d65f6125c14575db3fd8c669a168f" +name = "fastcrypto-derive" +version = "0.1.3" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=2f502fd8570fe4e9cff36eea5bbd6fef22002898#2f502fd8570fe4e9cff36eea5bbd6fef22002898" dependencies = [ - "Inflector", - "const-hex", - "ethers-contract-abigen", - "ethers-core", - "proc-macro2", "quote", - "serde_json", - "syn 2.0.100", + "syn 1.0.109", ] [[package]] -name = "ethers-core" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" +name = "fastcrypto-tbls" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=2f502fd8570fe4e9cff36eea5bbd6fef22002898#2f502fd8570fe4e9cff36eea5bbd6fef22002898" dependencies = [ - "arrayvec", - "bytes", - "cargo_metadata 0.18.1", - "chrono", - "const-hex", - "elliptic-curve", - "ethabi", - "generic-array", - "k256", - "num_enum", - "once_cell", - "open-fastrlp", + "bcs", + "digest 0.10.7", + "fastcrypto", + "hex", + "itertools 0.10.5", "rand 0.8.5", - "rlp", "serde", - "serde_json", - "strum 0.26.3", - "syn 2.0.100", - "tempfile", - "thiserror 1.0.64", - "tiny-keccak", - "unicode-xid", + "sha3 0.10.8", + "tap", + "tracing", + "typenum", + "zeroize", ] [[package]] -name = "ethers-etherscan" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79e5973c26d4baf0ce55520bd732314328cabe53193286671b47144145b9649" -dependencies = [ - "chrono", - "ethers-core", - "reqwest 0.11.27", - "semver", - "serde", - "serde_json", - "thiserror 1.0.64", - "tracing", -] - -[[package]] -name = "ethers-middleware" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f9fdf09aec667c099909d91908d5eaf9be1bd0e2500ba4172c1d28bfaa43de" -dependencies = [ - "async-trait", - "auto_impl", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-providers", - "ethers-signers", - "futures-channel", - "futures-locks", - "futures-util", - "instant", - "reqwest 0.11.27", - "serde", - "serde_json", - "thiserror 1.0.64", - "tokio", - "tracing", - "tracing-futures", - "url", -] - -[[package]] -name = "ethers-providers" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6434c9a33891f1effc9c75472e12666db2fa5a0fec4b29af6221680a6fe83ab2" -dependencies = [ - "async-trait", - "auto_impl", - "base64 0.21.7", - "bytes", - "const-hex", - "enr", - "ethers-core", - "futures-channel", - "futures-core", - "futures-timer", - "futures-util", - "hashers", - "http 0.2.12", - "instant", - "jsonwebtoken 8.3.0", - "once_cell", - "pin-project", - "reqwest 0.11.27", - "serde", - "serde_json", - "thiserror 1.0.64", - "tokio", - "tokio-tungstenite 0.20.1", - "tracing", - "tracing-futures", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "ws_stream_wasm", -] - -[[package]] -name = "ethers-signers" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228875491c782ad851773b652dd8ecac62cda8571d3bc32a5853644dd26766c2" -dependencies = [ - "async-trait", - "coins-bip32", - "coins-bip39", - "const-hex", - "elliptic-curve", - "eth-keystore", - "ethers-core", - "rand 0.8.5", - "sha2 0.10.8", - "thiserror 1.0.64", - "tracing", -] - -[[package]] -name = "ethers-solc" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66244a771d9163282646dbeffe0e6eca4dda4146b6498644e678ac6089b11edd" -dependencies = [ - "cfg-if", - "const-hex", - "dirs 5.0.1", - "dunce", - "ethers-core", - "glob", - "home", - "md-5", - "num_cpus", - "once_cell", - "path-slash", - "rayon", - "regex", - "semver", - "serde", - "serde_json", - "solang-parser", - "svm-rs", - "thiserror 1.0.64", - "tiny-keccak", - "tokio", - "tracing", - "walkdir", - "yansi 0.5.1", -] - -[[package]] -name = "ethnum" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" - -[[package]] -name = "expect-test" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" -dependencies = [ - "dissimilar", - "once_cell", -] - -[[package]] -name = "eyre" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fail" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3c61c59fdc91f5dbc3ea31ee8623122ce80057058be560654c5d410d181a6" -dependencies = [ - "lazy_static", - "log", - "rand 0.7.3", -] - -[[package]] -name = "fast_chemail" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4" -dependencies = [ - "ascii_utils", -] - -[[package]] -name = "fastcrypto" -version = "0.1.8" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=2f502fd8570fe4e9cff36eea5bbd6fef22002898#2f502fd8570fe4e9cff36eea5bbd6fef22002898" -dependencies = [ - "aes", - "aes-gcm", - "ark-ec", - "ark-ff", - "ark-secp256r1", - "ark-serialize", - "auto_ops", - "base64ct", - "bech32", - "bincode", - "blake2", - "blst", - "bs58 0.4.0", - "cbc", - "ctr", - "curve25519-dalek-ng", - "derive_more 0.99.18", - "digest 0.10.7", - "ecdsa", - "ed25519-consensus", - "elliptic-curve", - "fastcrypto-derive", - "generic-array", - "hex", - "hex-literal 0.4.1", - "hkdf", - "lazy_static", - "num-bigint 0.4.6", - "once_cell", - "p256", - "rand 0.8.5", - "readonly", - "rfc6979", - "rsa 0.8.2", - "schemars", - "secp256k1", - "serde", - "serde_json", - "serde_with", - "sha2 0.10.8", - "sha3 0.10.8", - "signature 2.2.0", - "static_assertions", - "thiserror 1.0.64", - "tokio", - "typenum", - "zeroize", -] - -[[package]] -name = "fastcrypto-derive" -version = "0.1.3" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=2f502fd8570fe4e9cff36eea5bbd6fef22002898#2f502fd8570fe4e9cff36eea5bbd6fef22002898" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "fastcrypto-tbls" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=2f502fd8570fe4e9cff36eea5bbd6fef22002898#2f502fd8570fe4e9cff36eea5bbd6fef22002898" -dependencies = [ - "bcs", - "digest 0.10.7", - "fastcrypto", - "hex", - "itertools 0.10.5", - "rand 0.8.5", - "serde", - "sha3 0.10.8", - "tap", - "tracing", - "typenum", - "zeroize", -] - -[[package]] -name = "fastcrypto-vdf" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=2f502fd8570fe4e9cff36eea5bbd6fef22002898#2f502fd8570fe4e9cff36eea5bbd6fef22002898" +name = "fastcrypto-vdf" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=2f502fd8570fe4e9cff36eea5bbd6fef22002898#2f502fd8570fe4e9cff36eea5bbd6fef22002898" dependencies = [ "bcs", "fastcrypto", @@ -4372,7 +4012,7 @@ dependencies = [ "ark-snark", "bcs", "byte-slice-cast", - "derive_more 0.99.18", + "derive_more 0.99.20", "fastcrypto", "ff", "im", @@ -4382,7 +4022,7 @@ dependencies = [ "num-bigint 0.4.6", "once_cell", "regex", - "reqwest 0.12.7", + "reqwest 0.12.15", "schemars", "serde", "serde_json", @@ -4391,9 +4031,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdlimit" @@ -4406,9 +4046,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "bitvec 1.0.1", "byteorder", @@ -4419,12 +4059,11 @@ dependencies = [ [[package]] name = "ff_derive" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f54704be45ed286151c5e11531316eaef5b8f5af7d597b806fdb8af108d84a" +checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" dependencies = [ "addchain", - "cfg-if", "num-bigint 0.3.3", "num-integer", "num-traits", @@ -4499,11 +4138,17 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flatbuffers" -version = "24.3.25" +version = "24.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8add37afff2d4ffa83bc748a70b4b1370984f6980768554182424ef71447c35f" +checksum = "4f1baf0dbf96932ec9a3038d57900329c015b0bfb7b63d904f3bc27e2b02a096" dependencies = [ "bitflags 1.3.2", "rustc_version", @@ -4511,12 +4156,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.33" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -4534,6 +4179,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -4545,19 +4196,9 @@ dependencies = [ [[package]] name = "fragile" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" [[package]] name = "fs_extra" @@ -4588,9 +4229,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -4619,9 +4260,9 @@ checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -4635,16 +4276,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-locks" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" -dependencies = [ - "futures-channel", - "futures-task", -] - [[package]] name = "futures-macro" version = "0.3.31" @@ -4653,7 +4284,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4675,7 +4306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" dependencies = [ "gloo-timers 0.2.6", - "send_wrapper 0.4.0", + "send_wrapper", ] [[package]] @@ -4696,15 +4327,6 @@ dependencies = [ "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "gcp-bigquery-client" version = "0.18.0" @@ -4714,13 +4336,13 @@ dependencies = [ "async-stream", "async-trait", "dyn-clone", - "hyper 0.14.30", + "hyper 0.14.32", "hyper-rustls 0.24.2", "log", "reqwest 0.11.27", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "time", "tokio", "tokio-stream", @@ -4728,6 +4350,20 @@ dependencies = [ "yup-oauth2", ] +[[package]] +name = "generator" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows 0.61.1", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -4753,9 +4389,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -4764,16 +4400,30 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", +] + [[package]] name = "getset" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f636605b743120a8d32ed92fc27b6cde1a769f8f936c065151eb66f88ded513c" +checksum = "f3586f256131df87204eb733da72e3d3eb4f343c639f4b7be279ac7c48baeafe" dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4788,9 +4438,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "git-version" @@ -4809,14 +4459,14 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gloo-net" @@ -4828,12 +4478,12 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils", - "http 1.1.0", + "http 1.3.1", "js-sys", "pin-project", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -4921,37 +4571,37 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.5.0", + "indexmap 2.9.0", "slab", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tracing", ] [[package]] name = "h2" -version = "0.4.6" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.5.0", + "http 1.3.1", + "indexmap 2.9.0", "slab", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tracing", ] [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -4969,7 +4619,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -4999,12 +4649,14 @@ dependencies = [ ] [[package]] -name = "hashers" -version = "1.0.1" +name = "hashbrown" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ - "fxhash", + "allocator-api2", + "equivalent", + "foldhash", ] [[package]] @@ -5030,7 +4682,7 @@ dependencies = [ "base64 0.21.7", "bytes", "headers-core", - "http 1.1.0", + "http 1.3.1", "httpdate", "mime", "sha1", @@ -5042,7 +4694,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http 1.1.0", + "http 1.3.1", ] [[package]] @@ -5065,9 +4717,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" [[package]] name = "hex" @@ -5078,12 +4730,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - [[package]] name = "hex-literal" version = "0.4.1" @@ -5116,11 +4762,11 @@ checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5136,9 +4782,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -5163,33 +4809,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.1.0", + "futures-core", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "http-range-header" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" [[package]] name = "httparse" -version = "1.9.4" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -5199,15 +4845,15 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -5229,15 +4875,15 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", - "http 1.1.0", + "h2 0.4.10", + "http 1.3.1", "http-body 1.0.1", "httparse", "httpdate", @@ -5256,7 +4902,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.32", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", @@ -5266,31 +4912,31 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.4.1", + "http 1.3.1", + "hyper 1.6.0", "hyper-util", "log", - "rustls 0.23.18", - "rustls-native-certs 0.8.0", + "rustls 0.23.27", + "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.2", "tower-service", - "webpki-roots 0.26.6", + "webpki-roots 0.26.11", ] [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.4.1", + "hyper 1.6.0", "hyper-util", "pin-project-lite", "tokio", @@ -5299,16 +4945,17 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", - "hyper 1.4.1", + "hyper 1.6.0", + "libc", "pin-project-lite", "socket2", "tokio", @@ -5318,16 +4965,17 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core 0.61.2", ] [[package]] @@ -5341,21 +4989,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -5364,31 +5013,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -5396,67 +5025,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -5476,9 +5092,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -5519,16 +5135,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec 3.6.12", -] - -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", + "parity-scale-codec 3.7.5", ] [[package]] @@ -5551,13 +5158,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.101", ] [[package]] @@ -5579,33 +5186,33 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.3", "serde", ] [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width 0.1.14", + "unicode-width 0.2.0", + "web-time", ] [[package]] name = "indoc" -version = "2.0.5" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" [[package]] name = "inline_colorization" @@ -5635,9 +5242,9 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "block-padding 0.3.3", "generic-array", @@ -5654,23 +5261,21 @@ dependencies = [ "dyn-clone", "lazy_static", "newline-converter", - "thiserror 1.0.64", + "thiserror 1.0.69", "unicode-segmentation", "unicode-width 0.1.14", ] [[package]] name = "insta" -version = "1.42.2" +version = "1.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084" +checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371" dependencies = [ "console", - "linked-hash-map", "once_cell", "pest", "pest_derive", - "pin-project", "serde", "similar", ] @@ -5692,11 +5297,11 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" dependencies = [ - "darling 0.20.10", + "darling 0.20.11", "indoc", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5746,12 +5351,11 @@ dependencies = [ "fastcrypto-zkp", "fs_extra", "futures", - "http 1.1.0", + "http 1.3.1", "im", "inquire", "insta", "insta-cmd", - "iota-bridge", "iota-config", "iota-execution", "iota-faucet", @@ -5797,7 +5401,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "regex", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "serde_json", "serde_yaml", @@ -5810,13 +5414,13 @@ dependencies = [ "telemetry-subscribers", "tempfile", "test-cluster", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tower 0.4.13", "tower-http", "tracing", "url", - "uuid 1.10.0", + "uuid", ] [[package]] @@ -5898,7 +5502,7 @@ dependencies = [ "tap", "telemetry-subscribers", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -5978,13 +5582,13 @@ dependencies = [ "iota-types", "prettytable-rs", "prometheus-parse", - "reqwest 0.12.7", + "reqwest 0.12.15", "russh", "russh-keys", "serde", "serde_json", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", ] @@ -6033,114 +5637,9 @@ dependencies = [ "telemetry-subscribers", "test-cluster", "tokio", - "tokio-util 0.7.12", - "tracing", - "typed-store", -] - -[[package]] -name = "iota-bridge" -version = "1.2.0-alpha" -dependencies = [ - "anyhow", - "arc-swap", - "async-trait", - "axum", - "backoff", - "bcs", - "bin-version", - "clap", - "enum_dispatch", - "ethers", - "eyre", - "fastcrypto", - "futures", - "hex-literal 0.3.4", - "iota-authority-aggregation", - "iota-config", - "iota-json-rpc-api", - "iota-json-rpc-types", - "iota-keys", - "iota-metrics", - "iota-sdk 1.2.0-alpha", - "iota-test-transaction-builder", - "iota-types", - "lru", - "maplit", - "move-core-types", - "num_enum", - "once_cell", - "prometheus", - "rand 0.8.5", - "reqwest 0.12.7", - "serde", - "serde_json", - "serde_with", - "shared-crypto", - "tap", - "telemetry-subscribers", - "tempfile", - "test-cluster", - "tokio", + "tokio-util 0.7.15", "tracing", "typed-store", - "url", -] - -[[package]] -name = "iota-bridge-cli" -version = "1.2.0-alpha" -dependencies = [ - "anyhow", - "clap", - "ethers", - "fastcrypto", - "futures", - "iota-bridge", - "iota-config", - "iota-json-rpc-types", - "iota-keys", - "iota-sdk 1.2.0-alpha", - "iota-types", - "move-core-types", - "reqwest 0.12.7", - "serde", - "serde_json", - "serde_with", - "shared-crypto", - "telemetry-subscribers", - "tokio", - "tracing", -] - -[[package]] -name = "iota-bridge-indexer" -version = "1.2.0-alpha" -dependencies = [ - "anyhow", - "async-trait", - "backoff", - "bcs", - "clap", - "diesel", - "ethers", - "futures", - "iota-bridge", - "iota-config", - "iota-data-ingestion-core", - "iota-indexer-builder", - "iota-json-rpc-types", - "iota-metrics", - "iota-sdk 1.2.0-alpha", - "iota-test-transaction-builder", - "iota-types", - "prometheus", - "serde", - "serde_yaml", - "tap", - "telemetry-subscribers", - "tokio", - "tracing", ] [[package]] @@ -6172,7 +5671,7 @@ dependencies = [ "move-core-types", "prometheus", "regex", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde_json", "shared-crypto", "telemetry-subscribers", @@ -6180,7 +5679,7 @@ dependencies = [ "test-cluster", "tokio", "tracing", - "uuid 1.10.0", + "uuid", ] [[package]] @@ -6215,7 +5714,7 @@ dependencies = [ "once_cell", "prometheus", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "serde_yaml", "tracing", @@ -6250,7 +5749,7 @@ dependencies = [ "fs_extra", "futures", "im", - "indexmap 2.5.0", + "indexmap 2.9.0", "iota-archival", "iota-authority-aggregation", "iota-common", @@ -6295,7 +5794,7 @@ dependencies = [ "quinn-proto", "rand 0.8.5", "rayon", - "reqwest 0.12.7", + "reqwest 0.12.15", "roaring", "rstest", "scopeguard", @@ -6312,7 +5811,7 @@ dependencies = [ "tempfile", "test-cluster", "test-fuzz", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-stream", "tower 0.4.13", @@ -6361,7 +5860,7 @@ dependencies = [ "digest 0.10.7", "ed25519-zebra", "generic-array", - "getrandom 0.2.15", + "getrandom 0.2.16", "hkdf", "hmac", "iterator-sorted 0.1.0", @@ -6369,9 +5868,9 @@ dependencies = [ "num-traits", "pbkdf2 0.12.2", "rand 0.8.5", - "scrypt 0.11.0", + "scrypt", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "tiny-keccak", "unicode-normalization", "x25519-dalek", @@ -6410,7 +5909,7 @@ dependencies = [ "telemetry-subscribers", "tempfile", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tracing", "url", ] @@ -6441,10 +5940,10 @@ dependencies = [ "serde_json", "tap", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-stream", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tracing", "url", ] @@ -6462,10 +5961,9 @@ dependencies = [ "fastcrypto", "fastcrypto-zkp", "futures", - "indexmap 2.5.0", + "indexmap 2.9.0", "insta", "iota", - "iota-bridge", "iota-config", "iota-core", "iota-framework", @@ -6521,7 +6019,7 @@ dependencies = [ name = "iota-execution" version = "0.1.0" dependencies = [ - "cargo_metadata 0.15.4", + "cargo_metadata", "iota-adapter-latest", "iota-move-natives-latest", "iota-protocol-config", @@ -6544,9 +6042,9 @@ dependencies = [ "clap", "expect-test", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "toml 0.7.8", - "toml_edit 0.22.22", + "toml_edit 0.22.26", ] [[package]] @@ -6561,7 +6059,7 @@ dependencies = [ "clap", "eyre", "futures", - "http 1.1.0", + "http 1.3.1", "iota-config", "iota-json-rpc-types", "iota-keys", @@ -6579,7 +6077,7 @@ dependencies = [ "telemetry-subscribers", "tempfile", "test-cluster", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tonic", "tower 0.4.13", @@ -6587,7 +6085,7 @@ dependencies = [ "tracing", "ttl_cache", "typed-store", - "uuid 1.10.0", + "uuid", ] [[package]] @@ -6687,7 +6185,7 @@ dependencies = [ "rand_pcg", "rand_seeder", "regex", - "reqwest 0.12.7", + "reqwest 0.12.15", "schemars", "serde", "serde_json", @@ -6695,7 +6193,7 @@ dependencies = [ "serde_yaml", "shared-crypto", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tracing", "tracing-subscriber", @@ -6756,9 +6254,9 @@ dependencies = [ "fastcrypto-zkp", "futures", "hex", - "http 1.1.0", + "http 1.3.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "im", "insta", "iota-framework", @@ -6789,7 +6287,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "regex", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "serde_json", "serde_with", @@ -6802,14 +6300,14 @@ dependencies = [ "telemetry-subscribers", "tempfile", "test-cluster", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "toml 0.7.8", "tower 0.4.13", "tower-http", "tracing", - "uuid 1.10.0", + "uuid", ] [[package]] @@ -6819,11 +6317,11 @@ dependencies = [ "anyhow", "async-graphql", "axum", - "hyper 1.4.1", + "hyper 1.6.0", "iota-graphql-rpc-headers", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", ] @@ -6840,17 +6338,17 @@ version = "1.2.0-alpha" dependencies = [ "axum", "bytes", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "hyper-util", "pin-project-lite", - "reqwest 0.12.7", + "reqwest 0.12.15", "socket2", "tokio", - "tokio-rustls 0.26.0", - "tokio-util 0.7.12", + "tokio-rustls 0.26.2", + "tokio-util 0.7.15", "tower 0.4.13", "tracing", ] @@ -6910,9 +6408,9 @@ dependencies = [ "telemetry-subscribers", "tempfile", "test-cluster", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tracing", "url", ] @@ -6929,7 +6427,7 @@ dependencies = [ "prometheus", "telemetry-subscribers", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tracing", ] @@ -6969,8 +6467,8 @@ dependencies = [ "fastcrypto", "futures", "http-body 1.0.1", - "hyper 1.4.1", - "indexmap 2.5.0", + "hyper 1.6.0", + "indexmap 2.9.0", "iota-config", "iota-core", "iota-json", @@ -7001,9 +6499,9 @@ dependencies = [ "statrs", "tap", "telemetry-subscribers", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tower 0.4.13", "tower-http", "tracing", @@ -7036,7 +6534,7 @@ dependencies = [ "anyhow", "async-trait", "bcs", - "hyper 1.4.1", + "hyper 1.6.0", "iota-config", "iota-core", "iota-json", @@ -7060,7 +6558,7 @@ dependencies = [ "move-package", "prometheus", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest 0.12.15", "telemetry-subscribers", "test-cluster", "tokio", @@ -7136,7 +6634,7 @@ dependencies = [ "log", "move-core-types", "object_store 0.10.2", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "serde_json", "serde_yaml", @@ -7178,7 +6676,7 @@ dependencies = [ "humantime", "once_cell", "prometheus-http-query", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "serde_yaml", "strum 0.26.3", @@ -7206,10 +6704,10 @@ dependencies = [ "simple-server-timing-header", "sysinfo", "tap", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tracing", - "uuid 1.10.0", + "uuid", ] [[package]] @@ -7296,7 +6794,7 @@ dependencies = [ "fastcrypto", "fastcrypto-vdf", "fastcrypto-zkp", - "indexmap 2.5.0", + "indexmap 2.9.0", "iota-protocol-config", "iota-types", "move-binary-format", @@ -7318,7 +6816,7 @@ dependencies = [ "iota-types", "move-core-types", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -7368,7 +6866,7 @@ dependencies = [ "bytes", "eyre", "futures", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", "multiaddr", "pin-project-lite", @@ -7422,7 +6920,7 @@ dependencies = [ "iota-types", "move-vm-profiler", "prometheus", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "snap", "tap", @@ -7481,7 +6979,7 @@ dependencies = [ "fastcrypto", "iota-types", "move-core-types", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "serde_json", "tracing", @@ -7500,7 +6998,7 @@ dependencies = [ "move-core-types", "move-package", "move-symbol-pool", - "thiserror 1.0.64", + "thiserror 1.0.69", "tracing", ] @@ -7511,7 +7009,7 @@ dependencies = [ "async-trait", "bcs", "eyre", - "hyper 1.4.1", + "hyper 1.6.0", "insta", "iota-move-build", "iota-types", @@ -7522,7 +7020,7 @@ dependencies = [ "move-core-types", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tower 0.4.13", ] @@ -7534,7 +7032,7 @@ dependencies = [ "msim-macros", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -7577,7 +7075,7 @@ dependencies = [ "futures", "git-version", "hex", - "hyper 1.4.1", + "hyper 1.6.0", "iota-metrics", "iota-sdk 1.2.0-alpha", "iota-tls", @@ -7591,9 +7089,9 @@ dependencies = [ "prost-build", "protobuf", "rand 0.8.5", - "reqwest 0.12.7", - "rustls 0.23.18", - "rustls-pemfile 2.1.3", + "reqwest 0.12.15", + "rustls 0.23.27", + "rustls-pemfile 2.2.0", "serde", "serde_json", "serde_with", @@ -7617,7 +7115,7 @@ dependencies = [ "bcs", "clap", "futures", - "http 1.1.0", + "http 1.3.1", "iota-config", "iota-core", "iota-execution", @@ -7648,9 +7146,9 @@ dependencies = [ "similar", "tabled", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tracing", ] @@ -7673,14 +7171,14 @@ dependencies = [ "openapiv3", "prometheus", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest 0.12.15", "schemars", "serde", "serde_json", "serde_with", "serde_yaml", "tap", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "url", ] @@ -7704,9 +7202,9 @@ dependencies = [ "object_store 0.10.2", "serde", "serde_yaml", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tracing", "tracing-subscriber", ] @@ -7724,7 +7222,7 @@ dependencies = [ "eyre", "fastcrypto", "futures", - "hyper 1.4.1", + "hyper 1.6.0", "iota-config", "iota-json-rpc-types", "iota-keys", @@ -7737,7 +7235,7 @@ dependencies = [ "move-core-types", "once_cell", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "serde_json", "shared-crypto", @@ -7747,7 +7245,7 @@ dependencies = [ "telemetry-subscribers", "tempfile", "test-cluster", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tracing", "typed-store", @@ -7795,7 +7293,7 @@ dependencies = [ "serde_derive", "serde_json", "serde_with", - "winnow 0.6.20", + "winnow 0.6.26", ] [[package]] @@ -7806,9 +7304,9 @@ checksum = "c1dfb0cae5c5bad8186576ca00b8be675257431eda028dcea01cb3b9f276037e" dependencies = [ "async-trait", "bech32", - "bitflags 2.6.0", + "bitflags 2.9.1", "bytemuck", - "derive_more 0.99.18", + "derive_more 0.99.20", "futures", "getset", "gloo-timers 0.3.0", @@ -7832,7 +7330,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "url", "zeroize", @@ -7864,14 +7362,14 @@ dependencies = [ "jsonrpsee", "move-core-types", "rand 0.8.5", - "reqwest 0.12.7", - "rustls 0.23.18", + "reqwest 0.12.15", + "rustls 0.23.27", "serde", "serde_json", "serde_with", "shared-crypto", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -7986,7 +7484,7 @@ dependencies = [ "tar", "tempfile", "test-cluster", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tracing", "ureq", @@ -8004,7 +7502,7 @@ dependencies = [ "expect-test", "fs_extra", "git-version", - "hyper 1.4.1", + "hyper 1.6.0", "iota", "iota-json-rpc-types", "iota-metrics", @@ -8018,7 +7516,7 @@ dependencies = [ "move-package", "move-symbol-pool", "prometheus", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "telemetry-subscribers", "tempfile", @@ -8048,8 +7546,8 @@ dependencies = [ "eyre", "fastcrypto", "futures", - "hyper 1.4.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "indicatif", "integer-encoding", "iota-config", @@ -8071,8 +7569,8 @@ dependencies = [ "parking_lot 0.12.3", "percent-encoding", "prometheus", - "reqwest 0.12.7", - "rustls 0.23.18", + "reqwest 0.12.15", + "rustls 0.23.27", "serde", "serde_json", "strum 0.26.3", @@ -8083,7 +7581,7 @@ dependencies = [ "tracing", "typed-store", "url", - "zstd 0.13.2", + "zstd", ] [[package]] @@ -8094,7 +7592,7 @@ dependencies = [ "bcs", "clap", "futures", - "indexmap 2.5.0", + "indexmap 2.9.0", "iota-core", "iota-json-rpc-types", "iota-macros", @@ -8193,11 +7691,11 @@ dependencies = [ "pkcs8 0.10.2", "rand 0.8.5", "rcgen", - "reqwest 0.12.7", - "rustls 0.23.18", - "rustls-webpki 0.103.1", + "reqwest 0.12.15", + "rustls 0.23.27", + "rustls-webpki 0.103.3", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.2", "tower-layer", "x509-parser", ] @@ -8291,7 +7789,7 @@ dependencies = [ "eyre", "fastcrypto", "futures", - "http 1.1.0", + "http 1.3.1", "iota-config", "iota-core", "iota-framework", @@ -8354,7 +7852,7 @@ dependencies = [ "fastcrypto-zkp", "hex", "im", - "indexmap 2.5.0", + "indexmap 2.9.0", "iota-enum-compat-util", "iota-macros", "iota-metrics", @@ -8402,7 +7900,7 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", "tap", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tonic", "tracing", @@ -8461,21 +7959,21 @@ dependencies = [ "stronghold-derive", "stronghold-utils", "stronghold_engine", - "thiserror 1.0.64", + "thiserror 1.0.69", "zeroize", ] [[package]] name = "ipnet" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.5" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c25163201be6ded9e686703e85532f8f852ea1f92ba625cb3c51f7fe6d07a4a" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" dependencies = [ "memchr", "serde", @@ -8483,13 +7981,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.4.0", + "hermit-abi 0.5.1", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8527,36 +8025,36 @@ dependencies = [ [[package]] name = "itertools" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jemalloc-ctl" @@ -8581,9 +8079,9 @@ dependencies = [ [[package]] name = "jiff" -version = "0.2.5" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -8594,27 +8092,29 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.5" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "jni" -version = "0.19.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", + "cfg-if", "combine", "jni-sys", "log", - "thiserror 1.0.64", + "thiserror 1.0.69", "walkdir", + "windows-sys 0.45.0", ] [[package]] @@ -8625,19 +8125,21 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -8652,9 +8154,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "834af00800e962dee8f7bfc0f60601de215e73e78e5497d733a2919da837d3c8" +checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -8670,50 +8172,50 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def0fd41e2f53118bd1620478d12305b2c75feef57ea1f93ef70568c98081b7e" +checksum = "bacb85abf4117092455e1573625e21b8f8ef4dec8aff13361140b2dc266cdff2" dependencies = [ "base64 0.22.1", "futures-channel", "futures-util", "gloo-net", - "http 1.1.0", + "http 1.3.1", "jsonrpsee-core", "pin-project", - "rustls 0.23.18", + "rustls 0.23.27", "rustls-pki-types", "rustls-platform-verifier", "soketto", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", - "tokio-rustls 0.26.0", - "tokio-util 0.7.12", + "tokio-rustls 0.26.2", + "tokio-util 0.7.15", "tracing", "url", ] [[package]] name = "jsonrpsee-core" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76637f6294b04e747d68e69336ef839a3493ca62b35bf488ead525f7da75c5bb" +checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" dependencies = [ "async-trait", "bytes", "futures-timer", "futures-util", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "jsonrpsee-types", "parking_lot 0.12.3", "pin-project", "rand 0.8.5", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -8722,23 +8224,23 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c24e981ad17798bbca852b0738bfb7b94816ed687bd0d5da60bfa35fa0fdc3" +checksum = "c872b6c9961a4ccc543e321bb5b89f6b2d2c7fe8b61906918273a3333c95400c" dependencies = [ "async-trait", "base64 0.22.1", "http-body 1.0.1", - "hyper 1.4.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", - "rustls 0.23.18", + "rustls 0.23.27", "rustls-platform-verifier", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tower 0.4.13", "tracing", @@ -8747,28 +8249,28 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcae0c6c159e11541080f1f829873d8f374f81eda0abc67695a13fc8dc1a580" +checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" dependencies = [ "heck 0.5.0", - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "jsonrpsee-server" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b7a3df90a1a60c3ed68e7ca63916b53e9afa928e33531e87f61a9c8e9ae87b" +checksum = "55e363146da18e50ad2b51a0a7925fc423137a0b1371af8235b1c231a0647328" dependencies = [ "futures-util", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -8777,31 +8279,31 @@ dependencies = [ "serde", "serde_json", "soketto", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-stream", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tower 0.4.13", "tracing", ] [[package]] name = "jsonrpsee-types" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddb81adb1a5ae9182df379e374a79e24e992334e7346af4d065ae5b2acb8d4c6" +checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" dependencies = [ - "http 1.1.0", + "http 1.3.1", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] name = "jsonrpsee-wasm-client" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e41af42ca39657313748174d02766e5287d3a57356f16756dbd8065b933977" +checksum = "e6558a9586cad43019dafd0b6311d0938f46efc116b34b28c74778bc11a2edf6" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -8810,11 +8312,11 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4f3642a292f5b76d8a16af5c88c16a0860f2ccc778104e5c848b28183d9538" +checksum = "01b3323d890aa384f12148e8d2a1fd18eb66e9e7e825f9de4fa53bcc19b93eef" dependencies = [ - "http 1.1.0", + "http 1.3.1", "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", @@ -8823,28 +8325,14 @@ dependencies = [ [[package]] name = "jsonwebtoken" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" -dependencies = [ - "base64 0.21.7", - "pem 1.1.1", - "ring 0.16.20", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "jsonwebtoken" -version = "9.3.0" +version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "js-sys", - "pem 3.0.4", - "ring 0.17.13", + "pem", + "ring", "serde", "serde_json", "simple_asn1", @@ -8861,8 +8349,7 @@ dependencies = [ "elliptic-curve", "once_cell", "serdect", - "sha2 0.10.8", - "signature 2.2.0", + "sha2 0.10.9", ] [[package]] @@ -8876,9 +8363,9 @@ dependencies = [ [[package]] name = "kqueue" -version = "1.0.8" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" dependencies = [ "kqueue-sys", "libc", @@ -8894,34 +8381,13 @@ dependencies = [ "libc", ] -[[package]] -name = "lalrpop" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" -dependencies = [ - "ascii-canvas", - "bit-set", - "ena", - "itertools 0.11.0", - "lalrpop-util", - "petgraph 0.6.5", - "regex", - "regex-syntax 0.8.4", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", - "walkdir", -] - [[package]] name = "lalrpop-util" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.7", + "regex-automata 0.4.9", ] [[package]] @@ -8930,7 +8396,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.9.8", + "spin", ] [[package]] @@ -8947,9 +8413,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "lexical-core" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431c65b318a590c1de6b8fd6e72798c92291d27762d94c9e6c37ed7a73d8458" +checksum = "b765c31809609075565a70b4b71402281283aeda7ecaf4818ac14a7b2ade8958" dependencies = [ "lexical-parse-float", "lexical-parse-integer", @@ -8960,9 +8426,9 @@ dependencies = [ [[package]] name = "lexical-parse-float" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb17a4bdb9b418051aa59d41d65b1c9be5affab314a872e5ad7f06231fb3b4e0" +checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" dependencies = [ "lexical-parse-integer", "lexical-util", @@ -8971,9 +8437,9 @@ dependencies = [ [[package]] name = "lexical-parse-integer" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df98f4a4ab53bf8b175b363a34c7af608fe31f93cc1fb1bf07130622ca4ef61" +checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" dependencies = [ "lexical-util", "static_assertions", @@ -8981,18 +8447,18 @@ dependencies = [ [[package]] name = "lexical-util" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85314db53332e5c192b6bca611fb10c114a80d1b831ddac0af1e9be1b9232ca0" +checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" dependencies = [ "static_assertions", ] [[package]] name = "lexical-write-float" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e7c3ad4e37db81c1cbe7cf34610340adc09c322871972f74877a712abc6c809" +checksum = "c5afc668a27f460fb45a81a757b6bf2f43c2d7e30cb5a2dcd3abf294c78d62bd" dependencies = [ "lexical-util", "lexical-write-integer", @@ -9001,9 +8467,9 @@ dependencies = [ [[package]] name = "lexical-write-integer" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb89e9f6958b83258afa3deed90b5de9ef68eef090ad5086c791cd2345610162" +checksum = "629ddff1a914a836fb245616a7888b62903aae58fa771e1d83943035efa0f978" dependencies = [ "lexical-util", "static_assertions", @@ -9011,9 +8477,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libflate" @@ -9041,19 +8507,19 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "libm" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -9061,9 +8527,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "libc", - "redox_syscall 0.5.6", + "redox_syscall 0.5.12", ] [[package]] @@ -9084,9 +8550,9 @@ dependencies = [ [[package]] name = "libsodium-sys-stable" -version = "1.21.2" +version = "1.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42631d334de875c636a1aae7adb515653ac2e771e5a2ce74b1053f5a4412df3a" +checksum = "b023d38f2afdfe36f81e15a9d7232097701d7b107e3a93ba903083985e235239" dependencies = [ "cc", "libc", @@ -9096,7 +8562,7 @@ dependencies = [ "tar", "ureq", "vcpkg", - "zip 2.2.0", + "zip", ] [[package]] @@ -9112,9 +8578,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.20" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "pkg-config", @@ -9129,15 +8595,21 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" @@ -9149,72 +8621,86 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "lockfree-object-pool" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" - [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" dependencies = [ "serde", ] [[package]] name = "logos" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6b6e02facda28ca5fb8dbe4b152496ba3b1bd5a4b40bb2b1b2d8ad74e0f39b" +checksum = "7251356ef8cb7aec833ddf598c6cb24d17b689d20b993f9d11a3d764e34e6458" dependencies = [ "logos-derive", ] [[package]] name = "logos-codegen" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32eb6b5f26efacd015b000bfc562186472cd9b34bdba3f6b264e2a052676d10" +checksum = "59f80069600c0d66734f5ff52cc42f2dabd6b29d205f333d61fd7832e9e9963f" dependencies = [ "beef", "fnv", "lazy_static", "proc-macro2", "quote", - "regex-syntax 0.8.4", - "syn 2.0.100", + "regex-syntax 0.8.5", + "syn 2.0.101", ] [[package]] name = "logos-derive" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5d0c5463c911ef55624739fc353238b4e310f0144be1f875dc42fec6bfd5ec" +checksum = "24fb722b06a9dc12adb0963ed585f19fc61dc5413e6a9be9422ef92c091e731d" dependencies = [ "logos-codegen", ] +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "lru" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.3", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "lsp-server" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "550446e84739dcaf6d48a4a093973850669e13e8a34d8f8d64851041be267cd9" +checksum = "9462c4dc73e17f971ec1f171d44bfffb72e65a130117233388a0ebc7ec5656f9" dependencies = [ "crossbeam-channel", "log", "serde", + "serde_derive", "serde_json", ] @@ -9233,9 +8719,9 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.11.0" +version = "1.11.1+lz4-1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb44a01837a858d47e5a630d2ccf304c8efcc4b83b8f9f75b7a9ee4fcc6e57d" +checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" dependencies = [ "cc", "libc", @@ -9250,12 +8736,6 @@ dependencies = [ "twox-hash", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "matchers" version = "0.1.0" @@ -9279,9 +8759,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "matrixmultiply" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" dependencies = [ "autocfg", "rawpointer", @@ -9329,33 +8809,32 @@ dependencies = [ [[package]] name = "miette" -version = "7.2.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "backtrace", "backtrace-ext", "cfg-if", "miette-derive", - "owo-colors 4.1.0", + "owo-colors", "supports-color", "supports-hyperlinks", "supports-unicode", - "terminal_size 0.3.0", + "terminal_size", "textwrap", - "thiserror 1.0.64", "unicode-width 0.1.14", ] [[package]] name = "miette-derive" -version = "7.2.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -9365,7 +8844,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" dependencies = [ "serde", - "toml 0.8.19", + "toml 0.8.22", ] [[package]] @@ -9403,24 +8882,15 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "minisign-verify" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a05b5d0594e0cb1ad8cee3373018d2b84e25905dc75b2468114cc9a8e86cfc20" - -[[package]] -name = "miniz_oxide" -version = "0.7.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] +checksum = "6367d84fb54d4242af283086402907277715b8fe46976963af5ebf173f8efba3" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -9439,23 +8909,16 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] -[[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - [[package]] name = "mockall" version = "0.11.4" @@ -9485,22 +8948,21 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.8" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "once_cell", + "loom", "parking_lot 0.12.3", - "quanta", + "portable-atomic", "rustc_version", "smallvec", "tagptr", - "thiserror 1.0.64", - "triomphe", - "uuid 1.10.0", + "thiserror 1.0.69", + "uuid", ] [[package]] @@ -9586,7 +9048,7 @@ name = "move-bytecode-utils" version = "0.1.0" dependencies = [ "anyhow", - "indexmap 2.5.0", + "indexmap 2.9.0", "move-binary-format", "move-core-types", "petgraph 0.5.1", @@ -9742,7 +9204,7 @@ dependencies = [ "ref-cast", "serde", "serde_bytes", - "thiserror 1.0.64", + "thiserror 1.0.69", "uint", ] @@ -9755,7 +9217,7 @@ dependencies = [ "clap", "codespan", "colored", - "indexmap 2.5.0", + "indexmap 2.9.0", "move-abstract-interpreter", "move-binary-format", "move-bytecode-source-map", @@ -9924,7 +9386,7 @@ name = "move-proc-macros" version = "0.1.0" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -10121,7 +9583,7 @@ dependencies = [ "socket2", "tap", "tokio-util 0.7.13", - "toml 0.8.19", + "toml 0.8.22", "tracing", "tracing-subscriber", ] @@ -10146,11 +9608,11 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.1.0", + "http 1.3.1", "httparse", "memchr", "mime", - "spin 0.9.8", + "spin", "version_check", ] @@ -10211,9 +9673,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "naive-timer" @@ -10247,7 +9709,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -10259,7 +9721,7 @@ dependencies = [ "libc", "once_cell", "parking_lot 0.12.3", - "thiserror 1.0.64", + "thiserror 1.0.69", "widestring", "winapi", ] @@ -10283,12 +9745,6 @@ dependencies = [ "trait-set", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - [[package]] name = "newline-converter" version = "0.2.2" @@ -10361,7 +9817,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "crossbeam-channel", "filetime", "fsevent-sys", @@ -10555,10 +10011,10 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -10578,9 +10034,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -10597,16 +10053,16 @@ dependencies = [ "chrono", "futures", "humantime", - "hyper 1.4.1", + "hyper 1.6.0", "itertools 0.13.0", "md-5", "parking_lot 0.12.3", "percent-encoding", - "quick-xml", + "quick-xml 0.36.2", "rand 0.8.5", - "reqwest 0.12.7", - "ring 0.17.13", - "rustls-pemfile 2.1.3", + "reqwest 0.12.15", + "ring", + "rustls-pemfile 2.2.0", "serde", "serde_json", "snafu 0.7.5", @@ -10618,9 +10074,9 @@ dependencies = [ [[package]] name = "object_store" -version = "0.11.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a0c4b3a0e31f8b66f71ad8064521efa773910196e2cde791436f13409f3b45" +checksum = "3cfccb68961a56facde1163f9319e0d15743352344e7808a11795fb99698dcaf" dependencies = [ "async-trait", "base64 0.22.1", @@ -10628,15 +10084,15 @@ dependencies = [ "chrono", "futures", "humantime", - "hyper 1.4.1", + "hyper 1.6.0", "itertools 0.13.0", "md-5", "parking_lot 0.12.3", "percent-encoding", - "quick-xml", + "quick-xml 0.37.5", "rand 0.8.5", - "reqwest 0.12.7", - "ring 0.17.13", + "reqwest 0.12.15", + "ring", "serde", "serde_json", "snafu 0.8.5", @@ -10663,9 +10119,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" -version = "11.1.4" +version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "opaque-debug" @@ -10673,37 +10129,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "open-fastrlp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", - "ethereum-types", - "open-fastrlp-derive", -] - -[[package]] -name = "open-fastrlp-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" -dependencies = [ - "bytes", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "openapiv3" version = "2.0.0" source = "git+https://github.com/bmwill/openapiv3.git?rev=ca4b4845b7c159a39f5c68ad8f7f76cb6f4d6963#ca4b4845b7c159a39f5c68ad8f7f76cb6f4d6963" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.9.0", "schemars", "serde", "serde_json", @@ -10711,9 +10142,9 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "opentelemetry" @@ -10726,7 +10157,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -10739,7 +10170,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 1.0.64", + "thiserror 1.0.69", "tracing", ] @@ -10751,12 +10182,12 @@ checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" dependencies = [ "async-trait", "futures-core", - "http 1.1.0", + "http 1.3.1", "opentelemetry 0.27.1", "opentelemetry-proto", "opentelemetry_sdk", "prost", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tonic", "tracing", @@ -10791,7 +10222,7 @@ dependencies = [ "percent-encoding", "rand 0.8.5", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -10825,12 +10256,12 @@ dependencies = [ [[package]] name = "ouroboros" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" dependencies = [ "aliasable", - "ouroboros_macro 0.18.4", + "ouroboros_macro 0.18.5", "static_assertions", ] @@ -10844,28 +10275,27 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "ouroboros_macro" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" dependencies = [ "heck 0.4.1", - "itertools 0.12.1", "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "outref" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" [[package]] name = "overload" @@ -10875,15 +10305,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owo-colors" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" - -[[package]] -name = "owo-colors" -version = "4.1.0" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" +checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec" [[package]] name = "p256" @@ -10894,19 +10318,19 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] name = "p384" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -10920,7 +10344,7 @@ dependencies = [ "elliptic-curve", "primeorder", "rand_core 0.6.4", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -10984,15 +10408,17 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.12" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ "arrayvec", "bitvec 1.0.1", "byte-slice-cast", + "const_format", "impl-trait-for-tuples", - "parity-scale-codec-derive 3.6.12", + "parity-scale-codec-derive 3.7.5", + "rustversion", "serde", ] @@ -11010,14 +10436,14 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.12" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.101", ] [[package]] @@ -11063,16 +10489,16 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.6", + "redox_syscall 0.5.12", "smallvec", "windows-targets 0.52.6", ] [[package]] name = "parquet" -version = "53.1.0" +version = "53.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310c46a70a3ba90d98fec39fa2da6d9d731e544191da6fb56c9d199484d0dd3e" +checksum = "2f8cf58b29782a7add991f655ff42929e31a7859f5319e53db9e39a714cb113c" dependencies = [ "ahash", "arrow-array", @@ -11083,12 +10509,12 @@ dependencies = [ "arrow-schema", "arrow-select", "base64 0.22.1", - "brotli", + "brotli 7.0.0", "bytes", "chrono", "flate2", "half", - "hashbrown 0.14.5", + "hashbrown 0.15.3", "lz4_flex", "num", "num-bigint 0.4.6", @@ -11097,7 +10523,7 @@ dependencies = [ "snap", "thrift", "twox-hash", - "zstd 0.13.2", + "zstd", "zstd-sys", ] @@ -11139,17 +10565,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77144664f6aac5f629d7efa815f5098a054beeeca6ccafee5ec453fd2b0c53f9" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "ciborium", "coset", "data-encoding", - "getrandom 0.2.15", + "getrandom 0.2.16", "hmac", - "indexmap 2.5.0", + "indexmap 2.9.0", "rand 0.8.5", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "strum 0.25.0", "typeshare", "zeroize", @@ -11189,12 +10615,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "path-slash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" - [[package]] name = "pbkdf2" version = "0.11.0" @@ -11204,7 +10624,7 @@ dependencies = [ "digest 0.10.7", "hmac", "password-hash", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -11225,18 +10645,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - -[[package]] -name = "pem" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64 0.22.1", "serde", @@ -11268,20 +10679,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.13" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", - "thiserror 1.0.64", + "thiserror 2.0.12", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.13" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ "pest", "pest_generator", @@ -11289,26 +10700,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.13" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "pest_meta" -version = "2.7.13" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" dependencies = [ "once_cell", "pest", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -11328,88 +10739,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.5.0", + "indexmap 2.9.0", ] [[package]] -name = "pharos" -version = "0.5.3" +name = "petgraph" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ - "futures", - "rustc_version", + "fixedbitset 0.5.7", + "indexmap 2.9.0", ] [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", - "phf_shared 0.11.2", + "phf_shared", ] [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.2", + "phf_shared", "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", - "phf_shared 0.11.2", + "phf_shared", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ "siphasher", ] [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11442,7 +10844,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der 0.7.9", + "der 0.7.10", "pkcs8 0.10.2", "spki 0.7.3", ] @@ -11455,10 +10857,10 @@ checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" dependencies = [ "aes", "cbc", - "der 0.7.9", + "der 0.7.10", "pbkdf2 0.12.2", - "scrypt 0.11.0", - "sha2 0.10.8", + "scrypt", + "sha2 0.10.9", "spki 0.7.3", ] @@ -11478,7 +10880,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.9", + "der 0.7.10", "pkcs5", "rand_core 0.6.4", "spki 0.7.3", @@ -11486,9 +10888,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plotters" @@ -11556,6 +10958,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -11580,33 +10991,28 @@ dependencies = [ "smallvec", "symbolic-demangle", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "pq-sys" -version = "0.6.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cc05d7ea95200187117196eee9edd0644424911821aeb28a18ce60ea0b8793" +checksum = "41c852911b98f5981956037b2ca976660612e548986c30af075e753107bc3400" dependencies = [ + "libc", "vcpkg", ] -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "predicates" version = "2.1.5" @@ -11623,9 +11029,9 @@ dependencies = [ [[package]] name = "predicates" -version = "3.1.2" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "difflib", @@ -11634,15 +11040,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", @@ -11666,17 +11072,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", - "yansi 1.0.1", + "yansi", ] [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11686,7 +11092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" dependencies = [ "csv", - "encode_unicode 1.0.0", + "encode_unicode", "is-terminal", "lazy_static", "term", @@ -11722,9 +11128,7 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash 0.8.0", "impl-codec 0.6.0", - "impl-rlp", "impl-serde 0.4.0", - "scale-info", "uint", ] @@ -11740,11 +11144,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.22", + "toml_edit 0.22.26", ] [[package]] @@ -11790,14 +11194,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -11810,9 +11214,9 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "version_check", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -11847,7 +11251,7 @@ checksum = "0fcebfa99f03ae51220778316b37d24981e36322c82c24848f48c5bd0f64cbdb" dependencies = [ "enum-as-inner", "mime", - "reqwest 0.12.7", + "reqwest 0.12.15", "serde", "time", "url", @@ -11867,19 +11271,19 @@ dependencies = [ [[package]] name = "proptest" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.6.0", + "bitflags 2.9.1", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -11893,14 +11297,14 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "prost" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -11908,43 +11312,42 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ - "bytes", "heck 0.5.0", - "itertools 0.13.0", + "itertools 0.14.0", "log", "multimap", "once_cell", - "petgraph 0.6.5", + "petgraph 0.7.1", "prettyplease", "prost", "prost-types", "regex", - "syn 2.0.100", + "syn 2.0.101", "tempfile", ] [[package]] name = "prost-derive" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "prost-types" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" dependencies = [ "prost", ] @@ -11958,7 +11361,7 @@ dependencies = [ "bytes", "once_cell", "protobuf-support", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -11967,29 +11370,29 @@ version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" dependencies = [ - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] name = "psm" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" dependencies = [ "cc", ] [[package]] name = "public-suffix" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a59553bc595dc1514e7d713e6167cf1c4d68ef6fcc2d10ad834a97a1ca9bc4" +checksum = "51315bca45305dd8aa64b831b33e71abac528ca8058c0651346a39b8d3009498" [[package]] name = "quanta" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" dependencies = [ "crossbeam-utils", "libc", @@ -12016,48 +11419,66 @@ dependencies = [ "serde", ] +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", + "cfg_aliases", "futures-io", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", - "rustls 0.23.18", + "rustc-hash 2.1.1", + "rustls 0.23.27", "socket2", - "thiserror 1.0.64", + "thiserror 2.0.12", "tokio", "tracing", + "web-time", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ "bytes", - "rand 0.8.5", - "ring 0.17.13", - "rustc-hash 2.0.0", - "rustls 0.23.18", + "fastbloom", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.1", + "ring", + "rustc-hash 2.1.1", + "rustls 0.23.27", + "rustls-pki-types", "slab", - "thiserror 1.0.64", + "thiserror 2.0.12", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.5" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" dependencies = [ + "cfg_aliases", "libc", "once_cell", "socket2", @@ -12074,6 +11495,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "r2d2" version = "0.8.10" @@ -12121,6 +11548,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -12141,6 +11578,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -12156,7 +11603,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", ] [[package]] @@ -12220,7 +11676,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cassowary", "compact_str", "crossterm 0.28.1", @@ -12237,11 +11693,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.1.0" +version = "11.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", ] [[package]] @@ -12272,12 +11728,12 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54077e1872c46788540de1ea3d7f4ccb1983d12f9aa909b234468676c1a36779" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" dependencies = [ - "pem 3.0.4", - "ring 0.17.13", + "pem", + "ring", "rustls-pki-types", "time", "yasna", @@ -12285,13 +11741,13 @@ dependencies = [ [[package]] name = "readonly" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25d631e41bfb5fdcde1d4e2215f62f7f0afa3ff11e26563765bd6ea1d229aeb" +checksum = "f2a62d85ed81ca5305dc544bd42c8804c5060b78ffa5ad3c64b0fb6a8c13d062" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -12302,7 +11758,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", + "mio 1.0.3", "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", @@ -12322,11 +11778,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.6" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", ] [[package]] @@ -12335,41 +11791,52 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.16", "libredox", - "thiserror 1.0.64", + "thiserror 2.0.12", ] [[package]] name = "ref-cast" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -12383,13 +11850,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -12406,9 +11873,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -12424,7 +11891,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.30", + "hyper 0.14.32", "hyper-rustls 0.24.2", "ipnet", "js-sys", @@ -12453,9 +11920,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "async-compression", "base64 0.22.1", @@ -12463,12 +11930,12 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.6", - "http 1.1.0", + "h2 0.4.10", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "ipnet", "js-sys", @@ -12478,24 +11945,25 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.18", - "rustls-native-certs 0.7.3", - "rustls-pemfile 2.1.3", + "rustls 0.23.27", + "rustls-native-certs 0.8.1", + "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", - "tokio-rustls 0.26.0", - "tokio-util 0.7.12", + "tokio-rustls 0.26.2", + "tokio-util 0.7.15", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.6", + "webpki-roots 0.26.11", "windows-registry", ] @@ -12507,10 +11975,10 @@ checksum = "562ceb5a604d3f7c885a792d42c199fd8af239d0a51b2fa6a78aafa092452b04" dependencies = [ "anyhow", "async-trait", - "http 1.1.0", - "reqwest 0.12.7", + "http 1.3.1", + "reqwest 0.12.15", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", "tower-service", ] @@ -12523,11 +11991,11 @@ dependencies = [ "anyhow", "async-trait", "futures", - "getrandom 0.2.15", - "http 1.1.0", - "hyper 1.4.1", + "getrandom 0.2.16", + "http 1.3.1", + "hyper 1.6.0", "parking_lot 0.11.2", - "reqwest 0.12.7", + "reqwest 0.12.15", "reqwest-middleware", "retry-policies", "tokio", @@ -12556,30 +12024,15 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - -[[package]] -name = "ring" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", - "untrusted 0.9.0", + "untrusted", "windows-sys 0.52.0", ] @@ -12598,33 +12051,11 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rlp-derive", - "rustc-hex", -] - -[[package]] -name = "rlp-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "roaring" -version = "0.10.10" +version = "0.10.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652edd001c53df0b3f96a36a8dc93fce6866988efc16808235653c6bcac8bf2" +checksum = "19e8d2cfa184d94d0726d650a9f4a1be7f9b76ac9fdb954219878dc00c1c1e7b" dependencies = [ "bytemuck", "byteorder", @@ -12647,7 +12078,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.9.1", "serde", "serde_derive", ] @@ -12673,7 +12104,7 @@ dependencies = [ "pkcs1 0.4.1", "pkcs8 0.9.0", "rand_core 0.6.4", - "sha2 0.10.8", + "sha2 0.10.9", "signature 2.2.0", "subtle", "zeroize", @@ -12681,9 +12112,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" dependencies = [ "const-oid", "digest 0.10.7", @@ -12693,7 +12124,7 @@ dependencies = [ "pkcs1 0.7.5", "pkcs8 0.10.2", "rand_core 0.6.4", - "sha2 0.10.8", + "sha2 0.10.9", "signature 2.2.0", "spki 0.7.3", "subtle", @@ -12735,7 +12166,7 @@ dependencies = [ "aes", "aes-gcm", "async-trait", - "bitflags 2.6.0", + "bitflags 2.9.1", "byteorder", "cbc", "chacha20", @@ -12746,7 +12177,7 @@ dependencies = [ "flate2", "futures", "generic-array", - "hex-literal 0.4.1", + "hex-literal", "hmac", "log", "num-bigint 0.4.6", @@ -12760,11 +12191,11 @@ dependencies = [ "russh-cryptovec", "russh-keys", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "ssh-encoding", "ssh-key", "subtle", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", ] @@ -12792,7 +12223,7 @@ dependencies = [ "cbc", "ctr", "data-encoding", - "der 0.7.9", + "der 0.7.10", "digest 0.10.7", "dirs 5.0.1", "ecdsa", @@ -12813,16 +12244,16 @@ dependencies = [ "pkcs8 0.10.2", "rand 0.8.5", "rand_core 0.6.4", - "rsa 0.9.6", + "rsa 0.9.8", "russh-cryptovec", "sec1", "serde", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "spki 0.7.3", "ssh-encoding", "ssh-key", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-stream", "typenum", @@ -12843,9 +12274,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.36.0" +version = "1.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" dependencies = [ "arrayvec", "num-traits", @@ -12865,9 +12296,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc-hex" @@ -12895,15 +12326,28 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", ] [[package]] @@ -12913,7 +12357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.13", + "ring", "rustls-webpki 0.101.7", "sct", ] @@ -12925,7 +12369,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.13", + "ring", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", @@ -12934,16 +12378,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.18" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "aws-lc-rs", "log", "once_cell", - "ring 0.17.13", + "ring", "rustls-pki-types", - "rustls-webpki 0.102.8", + "rustls-webpki 0.103.3", "subtle", "zeroize", ] @@ -12957,33 +12401,19 @@ dependencies = [ "openssl-probe", "rustls-pemfile 1.0.4", "schannel", - "security-framework", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile 2.1.3", - "rustls-pki-types", - "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] name = "rustls-native-certs" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.2.0", ] [[package]] @@ -12997,39 +12427,42 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "web-time", + "zeroize", +] [[package]] name = "rustls-platform-verifier" -version = "0.3.4" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbb878bdfdf63a336a5e63561b1835e7a8c91524f51621db870169eac84b490" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ - "core-foundation", + "core-foundation 0.10.0", "core-foundation-sys", "jni", "log", "once_cell", - "rustls 0.23.18", - "rustls-native-certs 0.7.3", + "rustls 0.23.27", + "rustls-native-certs 0.8.1", "rustls-platform-verifier-android", - "rustls-webpki 0.102.8", - "security-framework", + "rustls-webpki 0.103.3", + "security-framework 3.2.0", "security-framework-sys", - "webpki-roots 0.26.6", - "winapi", + "webpki-root-certs 0.26.11", + "windows-sys 0.59.0", ] [[package]] @@ -13044,8 +12477,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.13", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -13054,28 +12487,28 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "aws-lc-rs", - "ring 0.17.13", + "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ - "ring 0.17.13", + "aws-lc-rs", + "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "rusty-fork" @@ -13091,15 +12524,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe_arch" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" dependencies = [ "bytemuck", ] @@ -13122,35 +12555,11 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scale-info" -version = "2.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" -dependencies = [ - "cfg-if", - "derive_more 0.99.18", - "parity-scale-codec 3.6.12", - "scale-info-derive", -] - -[[package]] -name = "scale-info-derive" -version = "2.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" -dependencies = [ - "proc-macro-crate 3.2.0", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -13166,9 +12575,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", "either", @@ -13179,33 +12588,27 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "scoped-tls" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] -name = "scrypt" -version = "0.10.0" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" -dependencies = [ - "hmac", - "pbkdf2 0.11.0", - "salsa20", - "sha2 0.10.8", -] +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scrypt" @@ -13215,7 +12618,7 @@ checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" dependencies = [ "pbkdf2 0.12.2", "salsa20", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -13224,8 +12627,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.13", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -13241,7 +12644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", - "der 0.7.9", + "der 0.7.10", "generic-array", "pkcs8 0.10.2", "serdect", @@ -13284,19 +12687,31 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", - "core-foundation", + "bitflags 2.9.1", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.9.1", + "core-foundation 0.10.0", "core-foundation-sys", "libc", - "num-bigint 0.4.6", "security-framework-sys", ] [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -13304,9 +12719,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] @@ -13317,23 +12732,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" - [[package]] name = "seq-macro" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -13355,7 +12764,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b5b14ebbcc4e4f2b3642fa99c388649da58d1dc3308c7d109f39f565d1710f0" dependencies = [ "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -13366,27 +12775,27 @@ checksum = "5b6798a64289ff550d8d79847467789a5fd30b42c9c406a4d6dc0bc9b567e55c" dependencies = [ "once_cell", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] name = "serde_bytes" -version = "0.11.15" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -13397,16 +12806,16 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.9.0", "itoa", "memchr", "ryu", @@ -13415,9 +12824,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ "itoa", "serde", @@ -13425,13 +12834,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -13457,15 +12866,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.9.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.5.0", + "indexmap 2.9.0", "serde", "serde_derive", "serde_json", @@ -13475,14 +12884,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.9.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ - "darling 0.20.10", + "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -13529,7 +12938,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -13569,9 +12978,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -13622,11 +13031,11 @@ dependencies = [ [[package]] name = "shellexpand" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" dependencies = [ - "dirs 5.0.1", + "dirs 6.0.0", ] [[package]] @@ -13637,9 +13046,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "libc", "signal-hook-registry", @@ -13653,15 +13062,15 @@ checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio 0.8.11", - "mio 1.0.2", + "mio 1.0.3", "signal-hook", ] [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -13706,9 +13115,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "similar" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "simple-server-timing-header" @@ -13718,13 +13127,13 @@ checksum = "16e78919e05c9b8e123d435a4ad104b488ad1585631830e413830985c214086e" [[package]] name = "simple_asn1" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint 0.4.6", "num-traits", - "thiserror 1.0.64", + "thiserror 2.0.12", "time", ] @@ -13760,9 +13169,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "sized-chunks" @@ -13794,15 +13203,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "smawk" -version = "0.3.2" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "snafu" @@ -13844,7 +13247,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -13866,18 +13269,18 @@ dependencies = [ "futures", "glob", "log", - "object_store 0.11.0", + "object_store 0.11.2", "regex", - "reqwest 0.12.7", + "reqwest 0.12.15", "reqwest-middleware", "reqwest-retry", "serde", "serde_json", "snowflake-jwt", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "url", - "uuid 1.10.0", + "uuid", ] [[package]] @@ -13887,19 +13290,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f1c0c8818bb7400e821b166075ec771c8e8a012af41a8982b34eefaad4739fd" dependencies = [ "base64 0.22.1", - "jsonwebtoken 9.3.0", - "rsa 0.9.6", + "jsonwebtoken", + "rsa 0.9.8", "serde", - "sha2 0.10.8", - "thiserror 1.0.64", + "sha2 0.10.9", + "thiserror 1.0.69", "time", ] [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -13907,40 +13310,20 @@ dependencies = [ [[package]] name = "soketto" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" dependencies = [ "base64 0.22.1", "bytes", "futures", - "http 1.1.0", + "http 1.3.1", "httparse", "log", "rand 0.8.5", "sha1", ] -[[package]] -name = "solang-parser" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c425ce1c59f4b154717592f0bdf4715c3a1d55058883622d3157e1f0908a5b26" -dependencies = [ - "itertools 0.11.0", - "lalrpop", - "lalrpop-util", - "phf", - "thiserror 1.0.64", - "unicode-xid", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -13973,7 +13356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.9", + "der 0.7.10", ] [[package]] @@ -14001,14 +13384,14 @@ checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" dependencies = [ "base64ct", "pem-rfc7468 0.7.0", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] name = "ssh-key" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9b366a80cf18bb6406f4cf4d10aebfb46140a8c0c33f666a144c5c76ecbafc" +checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" dependencies = [ "bcrypt-pbkdf", "ed25519-dalek", @@ -14017,9 +13400,9 @@ dependencies = [ "p384", "p521", "rand_core 0.6.4", - "rsa 0.9.6", + "rsa 0.9.8", "sec1", - "sha2 0.10.8", + "sha2 0.10.9", "signature 2.2.0", "ssh-cipher", "ssh-encoding", @@ -14035,9 +13418,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stacker" -version = "0.1.17" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" dependencies = [ "cc", "cfg-if", @@ -14070,19 +13453,6 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot 0.12.3", - "phf_shared 0.10.0", - "precomputed-hash", -] - [[package]] name = "stronghold-derive" version = "1.0.0" @@ -14108,7 +13478,7 @@ dependencies = [ "nix 0.24.3", "rand 0.8.5", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", "windows 0.36.1", "zeroize", ] @@ -14137,7 +13507,7 @@ dependencies = [ "paste", "serde", "stronghold-runtime", - "thiserror 1.0.64", + "thiserror 1.0.69", "zeroize", ] @@ -14200,7 +13570,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14213,7 +13583,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14240,18 +13610,18 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "supports-color" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" dependencies = [ "is_ci", ] [[package]] name = "supports-hyperlinks" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" +checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" [[package]] name = "supports-unicode" @@ -14259,43 +13629,23 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" -[[package]] -name = "svm-rs" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11297baafe5fa0c99d5722458eac6a5e25c01eb1b8e5cd137f54079093daa7a4" -dependencies = [ - "dirs 5.0.1", - "fs2", - "hex", - "once_cell", - "reqwest 0.11.27", - "semver", - "serde", - "serde_json", - "sha2 0.10.8", - "thiserror 1.0.64", - "url", - "zip 0.6.6", -] - [[package]] name = "symbolic-common" -version = "12.11.1" +version = "12.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fdf97c441f18a4f92425b896a4ec7a27e03631a0b1047ec4e34e9916a9a167e" +checksum = "6a1150bdda9314f6cfeeea801c23f5593c6e6a6c72e64f67e48d723a12b8efdb" dependencies = [ "debugid", "memmap2", "stable_deref_trait", - "uuid 1.10.0", + "uuid", ] [[package]] name = "symbolic-demangle" -version = "12.11.1" +version = "12.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc8ece6b129e97e53d1fbb3f61d33a6a9e5369b11d01228c068094d6d134eaea" +checksum = "9f66537def48fbc704a92e4fdaab7833bc7cb2255faca8182592fb5fa617eb82" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -14315,9 +13665,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -14332,9 +13682,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -14353,13 +13703,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14383,7 +13733,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -14435,9 +13785,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.42" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -14463,7 +13813,7 @@ dependencies = [ "opentelemetry_sdk", "prometheus", "prost", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tonic", "tracing", @@ -14474,14 +13824,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.3", "once_cell", - "rustix", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -14498,38 +13848,28 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "terminal_size" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" -dependencies = [ - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "terminal_size" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix", + "rustix 1.0.7", "windows-sys 0.59.0", ] [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "test-cluster" @@ -14540,7 +13880,6 @@ dependencies = [ "fastcrypto", "fastcrypto-zkp", "futures", - "iota-bridge", "iota-config", "iota-core", "iota-framework", @@ -14585,7 +13924,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48db3bbc562408b2111f3a0c96ec416ffa3ab66f8a6ab42579b608b9f74744e1" dependencies = [ - "cargo_metadata 0.15.4", + "cargo_metadata", "proc-macro2", "quote", "serde", @@ -14598,14 +13937,14 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17cd05c077c7b9c5d0045c539f9c5e911187bf65cd1b407d28239efc7b54880" dependencies = [ - "darling 0.20.10", + "darling 0.20.11", "if_chain", "itertools 0.10.5", "lazy_static", "proc-macro2", "quote", "subprocess", - "syn 2.0.100", + "syn 2.0.101", "test-fuzz-internal", "toolchain_find", ] @@ -14626,22 +13965,21 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" dependencies = [ - "smawk", "unicode-linebreak", - "unicode-width 0.1.14", + "unicode-width 0.2.0", ] [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.64", + "thiserror-impl 1.0.69", ] [[package]] @@ -14655,13 +13993,13 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14672,7 +14010,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14707,9 +14045,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -14724,15 +14062,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -14750,8 +14088,8 @@ dependencies = [ "pbkdf2 0.11.0", "rand 0.8.5", "rustc-hash 1.1.0", - "sha2 0.10.8", - "thiserror 1.0.64", + "sha2 0.10.9", + "thiserror 1.0.69", "unicode-normalization", "wasm-bindgen", "zeroize", @@ -14768,9 +14106,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -14788,9 +14126,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -14810,7 +14148,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", + "mio 1.0.3", "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", @@ -14828,7 +14166,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14838,7 +14176,7 @@ source = "git+https://github.com/iotaledger/tokio-madsim-fork.git?branch=msim-1. dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14853,59 +14191,42 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.18", - "rustls-pki-types", + "rustls 0.23.27", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util 0.7.12", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" -dependencies = [ - "futures-util", - "log", - "rustls 0.21.12", - "tokio", - "tokio-rustls 0.24.1", - "tungstenite 0.20.1", - "webpki-roots 0.25.4", + "tokio-util 0.7.15", ] [[package]] name = "tokio-tungstenite" -version = "0.23.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.23.0", + "tungstenite", ] [[package]] name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +version = "0.7.13" +source = "git+https://github.com/iotaledger/tokio-madsim-fork.git?branch=msim-1.43.0#9256cf957759096beb965a378bef71d0827d8cb3" dependencies = [ "bytes", "futures-core", @@ -14914,23 +14235,24 @@ dependencies = [ "futures-util", "hashbrown 0.14.5", "pin-project-lite", - "tokio", + "real_tokio", + "slab", ] [[package]] name = "tokio-util" -version = "0.7.13" -source = "git+https://github.com/iotaledger/tokio-madsim-fork.git?branch=msim-1.43.0#9256cf957759096beb965a378bef71d0827d8cb3" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", - "hashbrown 0.14.5", + "hashbrown 0.15.3", "pin-project-lite", - "real_tokio", - "slab", + "tokio", ] [[package]] @@ -14948,7 +14270,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -14957,21 +14279,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.19" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.22", + "toml_edit 0.22.26", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] @@ -14994,7 +14316,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -15003,17 +14325,24 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.20", + "toml_write", + "winnow 0.7.10", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "tonic" version = "0.12.3" @@ -15025,11 +14354,11 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.6", - "http 1.1.0", + "h2 0.4.10", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "hyper-timeout", "hyper-util", "percent-encoding", @@ -15042,27 +14371,28 @@ dependencies = [ "tower-layer", "tower-service", "tracing", - "zstd 0.13.2", + "zstd", ] [[package]] name = "tonic-build" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" dependencies = [ "prettyplease", "proc-macro2", "prost-build", + "prost-types", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "tonic-health" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0a34e6f706bae26b2b490e1da5c3f6a6ff87cae442bcbc7c881bab9631b5a7" +checksum = "1eaf34ddb812120f5c601162d5429933c9b527d901ab0e7f930d3147e33a09b2" dependencies = [ "async-stream", "prost", @@ -15079,17 +14409,17 @@ checksum = "803689f99cfc6de9c3b27aa86bf98553754c72c53b715913f1c14dcd3c030f77" dependencies = [ "async-stream", "bytes", - "h2 0.4.6", - "http 1.1.0", + "h2 0.4.10", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "hyper-timeout", "hyper-util", "pin-project", "socket2", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.2", "tokio-stream", "tonic", "tower 0.5.2", @@ -15126,7 +14456,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tower-layer", "tower-service", "tracing", @@ -15140,12 +14470,12 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "indexmap 2.5.0", + "indexmap 2.9.0", "pin-project-lite", "slab", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tower-layer", "tower-service", "tracing", @@ -15159,11 +14489,11 @@ checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "async-compression", "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.9.1", "bytes", "futures-core", "futures-util", - "http 1.1.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "http-range-header", @@ -15174,12 +14504,12 @@ dependencies = [ "percent-encoding", "pin-project-lite", "tokio", - "tokio-util 0.7.12", + "tokio-util 0.7.15", "tower 0.4.13", "tower-layer", "tower-service", "tracing", - "uuid 1.10.0", + "uuid", ] [[package]] @@ -15196,9 +14526,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -15213,27 +14543,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror 1.0.64", + "thiserror 1.0.69", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -15241,9 +14571,9 @@ dependencies = [ [[package]] name = "tracing-error" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", "tracing-subscriber", @@ -15292,9 +14622,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" dependencies = [ "serde", "tracing-core", @@ -15302,9 +14632,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -15356,12 +14686,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" -[[package]] -name = "triomphe" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" - [[package]] name = "try-lock" version = "0.2.5" @@ -15379,39 +14703,19 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 0.2.12", - "httparse", - "log", - "rand 0.8.5", - "rustls 0.21.12", - "sha1", - "thiserror 1.0.64", - "url", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http 1.3.1", "httparse", "log", "rand 0.8.5", "sha1", - "thiserror 1.0.64", + "thiserror 1.0.69", "utf-8", ] @@ -15441,7 +14745,7 @@ dependencies = [ "itertools 0.13.0", "msim", "once_cell", - "ouroboros 0.18.4", + "ouroboros 0.18.5", "prometheus", "rand 0.8.5", "rocksdb", @@ -15449,7 +14753,7 @@ dependencies = [ "serde", "tap", "tempfile", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tracing", "typed-store-derive", @@ -15472,7 +14776,7 @@ name = "typed-store-error" version = "1.2.0-alpha" dependencies = [ "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -15485,15 +14789,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "typeshare" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f17399b76c2e743d58eac0635d7686e9c00f48cd4776f00695d9882a7d3187" +checksum = "19be0f411120091e76e13e5a0186d8e2bcc3e7e244afdb70152197f1a8486ceb" dependencies = [ "chrono", "serde", @@ -15508,14 +14812,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a615d6c2764852a2e88a4f16e9ce1ea49bb776b5872956309e170d63a042a34f" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" @@ -15543,18 +14847,15 @@ checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-linebreak" @@ -15622,12 +14923,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -15636,18 +14931,18 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.10.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ "base64 0.22.1", "flate2", "log", "once_cell", - "rustls 0.23.18", + "rustls 0.23.27", "rustls-pki-types", "url", - "webpki-roots 0.26.6", + "webpki-roots 0.26.11", ] [[package]] @@ -15674,12 +14969,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -15694,29 +14983,19 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom 0.2.15", - "serde", -] - -[[package]] -name = "uuid" -version = "1.10.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.2.15", - "rand 0.8.5", + "getrandom 0.3.3", + "rand 0.9.1", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "variant_count" @@ -15764,9 +15043,9 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" [[package]] name = "wait-timeout" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] @@ -15802,6 +15081,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasite" version = "0.1.0" @@ -15810,47 +15098,48 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -15858,28 +15147,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -15905,22 +15197,40 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "js-sys", - "wasm-bindgen", + "webpki-root-certs 1.0.0", ] [[package]] -name = "web-time" -version = "1.1.0" +name = "webpki-root-certs" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4" dependencies = [ - "js-sys", - "wasm-bindgen", + "rustls-pki-types", ] [[package]] @@ -15931,9 +15241,18 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] @@ -15947,25 +15266,25 @@ dependencies = [ "either", "home", "once_cell", - "rustix", + "rustix 0.38.44", ] [[package]] name = "whoami" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ - "redox_syscall 0.5.6", + "redox_syscall 0.5.12", "wasite", "web-sys", ] [[package]] name = "wide" -version = "0.7.28" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690" +checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" dependencies = [ "bytemuck", "safe_arch", @@ -16032,12 +15351,25 @@ dependencies = [ ] [[package]] -name = "windows-core" -version = "0.52.0" +name = "windows" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ - "windows-targets 0.52.6", + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", ] [[package]] @@ -16046,12 +15378,36 @@ version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.57.0", + "windows-interface 0.57.0", "windows-result 0.1.2", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -16060,7 +15416,18 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -16071,18 +15438,45 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] -name = "windows-registry" +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-numerics" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-result 0.2.0", - "windows-strings", - "windows-targets 0.52.6", + "windows-core 0.61.2", + "windows-link", +] + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result 0.3.4", + "windows-strings 0.3.1", + "windows-targets 0.53.0", ] [[package]] @@ -16096,21 +15490,38 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -16140,6 +15551,21 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -16164,13 +15590,44 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -16183,12 +15640,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -16201,12 +15670,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -16219,18 +15700,36 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -16243,12 +15742,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -16261,6 +15772,18 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -16273,12 +15796,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -16291,6 +15826,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -16302,9 +15843,18 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -16320,35 +15870,19 @@ dependencies = [ ] [[package]] -name = "write16" -version = "1.0.0" +name = "wit-bindgen-rt" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] [[package]] name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "ws_stream_wasm" -version = "0.7.4" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" -dependencies = [ - "async_io_stream", - "futures", - "js-sys", - "log", - "pharos", - "rustc_version", - "send_wrapper 0.6.0", - "thiserror 1.0.64", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wyz" @@ -16395,13 +15929,12 @@ dependencies = [ [[package]] name = "xattr" -version = "1.3.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", - "linux-raw-sys", - "rustix", + "rustix 1.0.7", ] [[package]] @@ -16419,12 +15952,6 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - [[package]] name = "yansi" version = "1.0.1" @@ -16442,9 +15969,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -16454,14 +15981,14 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", - "synstructure 0.13.1", + "syn 2.0.101", + "synstructure 0.13.2", ] [[package]] @@ -16475,7 +16002,7 @@ dependencies = [ "base64 0.21.7", "futures", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.32", "hyper-rustls 0.24.2", "itertools 0.12.1", "log", @@ -16493,44 +16020,43 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", - "synstructure 0.13.1", + "syn 2.0.101", + "synstructure 0.13.2", ] [[package]] @@ -16551,108 +16077,78 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] -name = "zerovec" -version = "0.10.4" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ + "displaydoc", "yoke", "zerofrom", - "zerovec-derive", ] [[package]] -name = "zerovec-derive" -version = "0.10.3" +name = "zerovec" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "yoke", + "zerofrom", + "zerovec-derive", ] [[package]] -name = "zip" -version = "0.6.6" +name = "zerovec-derive" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq 0.1.5", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2 0.11.0", - "sha1", - "time", - "zstd 0.11.2+zstd.1.5.2", + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] name = "zip" -version = "2.2.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" dependencies = [ "arbitrary", "crc32fast", "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.5.0", + "indexmap 2.9.0", "memchr", - "thiserror 1.0.64", + "thiserror 2.0.12", "zopfli", ] [[package]] name = "zopfli" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" dependencies = [ "bumpalo", "crc32fast", - "lockfree-object-pool", "log", - "once_cell", "simd-adler32", ] [[package]] name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" -dependencies = [ - "zstd-safe 7.2.1", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ - "libc", - "zstd-sys", + "zstd-safe", ] [[package]] @@ -16666,9 +16162,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 7ba08f26ce0..63f08bcfa2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,9 +72,6 @@ members = [ "crates/iota-authority-aggregation", "crates/iota-aws-orchestrator", "crates/iota-benchmark", - "crates/iota-bridge", - "crates/iota-bridge-cli", - "crates/iota-bridge-indexer", "crates/iota-cluster-test", "crates/iota-common", "crates/iota-config", @@ -370,7 +367,6 @@ iota-analytics-indexer-derive = { path = "crates/iota-analytics-indexer-derive" iota-archival = { path = "crates/iota-archival" } iota-authority-aggregation = { path = "crates/iota-authority-aggregation" } iota-benchmark = { path = "crates/iota-benchmark" } -iota-bridge = { path = "crates/iota-bridge" } iota-cluster-test = { path = "crates/iota-cluster-test" } iota-common = { path = "crates/iota-common" } iota-config = { path = "crates/iota-config" } diff --git a/binary-build-list.json b/binary-build-list.json index 44aac5428bd..8315af2fd74 100644 --- a/binary-build-list.json +++ b/binary-build-list.json @@ -5,8 +5,6 @@ "iota-tool", "iota-faucet", "iota-data-ingestion", - "iota-bridge", - "iota-bridge-cli", "iota-graphql-rpc", "iota-indexer", "move-analyzer" diff --git a/bridge/evm/.env.example b/bridge/evm/.env.example deleted file mode 100644 index 395322eae89..00000000000 --- a/bridge/evm/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -PRIVATE_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -INFURA_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -ETHERSCAN_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -SEPOLIA_RPC_URL=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -MAINNET_RPC_URL=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \ No newline at end of file diff --git a/bridge/evm/.gitignore b/bridge/evm/.gitignore deleted file mode 100644 index 1abdde8401a..00000000000 --- a/bridge/evm/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -cache* -.env - -network_config.json -.vscode -cache/ -out*/ -*.txt -!remappings.txt -lcov.info -broadcast/**/31337 - -lib/* \ No newline at end of file diff --git a/bridge/evm/README.md b/bridge/evm/README.md deleted file mode 100644 index 12ac65b3337..00000000000 --- a/bridge/evm/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# 🏄‍♂️ Quick Start - -This project leverages [Foundry](https://github.com/foundry-rs/foundry) to manage dependencies, contract compilation, testing, deployment, and on chain interactions via Solidity scripting. - -#### Environment configuration - -Duplicate rename the `.env.example` file to `.env`. You'll need accounts and api keys for **Infura** and **Etherscan** as well as the necessary RPC URLs. Be sure to add the required values in your newly created `.env` file. - -> **Note** -> The OZ foundry upgrades library uses node to verify upgrade safety. Make sure you have node version 20.0.0 or higher as well as npm version 10.4 or higher installed. - -#### Dependencies - -To install the project dependencies, run: - -```bash -forge install https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable@v5.0.1 https://github.com/foundry-rs/forge-std@v1.3.0 https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades --no-git --no-commit -``` - -#### Compilation - -To compile your contracts, run: - -```bash -forge compile -``` - -#### Testing - -```bash -forge clean -forge test --ffi -``` - -#### Coverage - -```bash -forge coverage -``` - -#### Deployment - -> **Note** -> Make sure the deployment config file for the target chain is created in the `deploy_configs` folder. -> The file should be named `.json` and should have the same fields and in the same order (alphabetical) as the `example.json`. - -```bash -forge clean -forge script script/deploy_bridge.s.sol --rpc-url <> --broadcast --verify --ffi -``` - -**Local deployment** - -```bash -forge clean -forge script script/deploy_bridge.s.sol --fork-url anvil --broadcast --ffi -``` - -All deployments are saved in the `broadcast` directory. - -#### External Resources - -- [Writing OpenZeppelin Upgrades with Foundry](https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades?tab=readme-ov-file) -- [OpenZeppelin Upgrade Requirements](https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core#define-reference-contracts) diff --git a/bridge/evm/contracts/BridgeCommittee.sol b/bridge/evm/contracts/BridgeCommittee.sol deleted file mode 100644 index 1b31e220fb7..00000000000 --- a/bridge/evm/contracts/BridgeCommittee.sol +++ /dev/null @@ -1,165 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "./interfaces/IBridgeCommittee.sol"; -import "./utils/CommitteeUpgradeable.sol"; - -/// @title BridgeCommittee -/// @notice This contract manages the committee members of the IotaBridge. The committee members are -/// responsible for signing messages used to update various bridge state including the committee itself. -/// The contract also provides functions to manage a blocklist of committee members whose signatures are invalid -/// once they are blocklisted. -contract BridgeCommittee is IBridgeCommittee, CommitteeUpgradeable { - /* ========== STATE VARIABLES ========== */ - - mapping(address committeeMember => uint16 stakeAmount) public committeeStake; - mapping(address committeeMember => uint8 index) public committeeIndex; - mapping(address committeeMember => bool isBlocklisted) public blocklist; - IBridgeConfig public config; - - /* ========== INITIALIZERS ========== */ - - /// @notice Initializes the contract with the provided parameters. - /// @dev should be called directly after deployment (see OpenZeppelin upgradeable standards). - /// the provided arrays must have the same length and the total stake provided must be greater than, - /// or equal to the provided minimum stake required. - /// @param committee addresses of the committee members. - /// @param stake amounts of the committee members. - /// @param minStakeRequired minimum stake required for the committee. - function initialize(address[] memory committee, uint16[] memory stake, uint16 minStakeRequired) - external - initializer - { - __CommitteeUpgradeable_init(address(this)); - __UUPSUpgradeable_init(); - - uint256 _committeeLength = committee.length; - - require(_committeeLength < 256, "BridgeCommittee: Committee length must be less than 256"); - - require( - _committeeLength == stake.length, - "BridgeCommittee: Committee and stake arrays must be of the same length" - ); - - uint16 totalStake; - for (uint16 i; i < _committeeLength; i++) { - require( - committeeStake[committee[i]] == 0, "BridgeCommittee: Duplicate committee member" - ); - committeeStake[committee[i]] = stake[i]; - committeeIndex[committee[i]] = uint8(i); - totalStake += stake[i]; - } - - require(totalStake >= minStakeRequired, "BridgeCommittee: total stake is less than minimum"); // 10000 == 100% - } - - /// @notice Initializes the contract with the provided parameters. - /// @dev This function should be called directly after config deployment. The config contract address - /// provided should be verified before bridging any assets. - /// @param _config The address of the BridgeConfig contract. - function initializeConfig(address _config) external { - require(address(config) == address(0), "BridgeCommittee: Config already initialized"); - config = IBridgeConfig(_config); - } - - /* ========== EXTERNAL FUNCTIONS ========== */ - - /// @notice Verifies the provided signatures for the given message by aggregating and validating the - /// stake of each signer against the required stake of the given message type. - /// @dev The function will revert if the total stake of the signers is less than the required stake. - /// @param signatures The array of signatures to be verified. - /// @param message The `BridgeUtils.Message` to be verified. - function verifySignatures(bytes[] memory signatures, BridgeUtils.Message memory message) - external - view - override - { - uint32 requiredStake = BridgeUtils.requiredStake(message); - - uint16 approvalStake; - address signer; - uint256 bitmap; - - // Check validity of each signature and aggregate the approval stake - for (uint16 i; i < signatures.length; i++) { - bytes memory signature = signatures[i]; - // recover the signer from the signature - (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature); - - (signer,,) = ECDSA.tryRecover(BridgeUtils.computeHash(message), v, r, s); - - require(!blocklist[signer], "BridgeCommittee: Signer is blocklisted"); - require(committeeStake[signer] > 0, "BridgeCommittee: Signer has no stake"); - - uint8 index = committeeIndex[signer]; - uint256 mask = 1 << index; - require(bitmap & mask == 0, "BridgeCommittee: Duplicate signature provided"); - bitmap |= mask; - - approvalStake += committeeStake[signer]; - } - - require(approvalStake >= requiredStake, "BridgeCommittee: Insufficient stake amount"); - } - - /// @notice Updates the blocklist status of the provided addresses if provided signatures are valid. - /// @param signatures The array of signatures to validate the message. - /// @param message BridgeUtils containing the update blocklist payload. - function updateBlocklistWithSignatures( - bytes[] memory signatures, - BridgeUtils.Message memory message - ) - external - nonReentrant - verifyMessageAndSignatures(message, signatures, BridgeUtils.BLOCKLIST) - { - // decode the blocklist payload - (bool isBlocklisted, address[] memory _blocklist) = - BridgeUtils.decodeBlocklistPayload(message.payload); - - // update the blocklist - _updateBlocklist(_blocklist, isBlocklisted); - } - - /* ========== INTERNAL FUNCTIONS ========== */ - - /// @notice Updates the blocklist status of the provided addresses. - /// @param _blocklist The addresses to update the blocklist status. - /// @param isBlocklisted new blocklist status. - function _updateBlocklist(address[] memory _blocklist, bool isBlocklisted) private { - // check original blocklist value of each validator - for (uint16 i; i < _blocklist.length; i++) { - blocklist[_blocklist[i]] = isBlocklisted; - } - - emit BlocklistUpdated(_blocklist, isBlocklisted); - } - - /// @notice Splits the provided signature into its r, s, and v components. - /// @param sig The signature to be split. - /// @return r The r component of the signature. - /// @return s The s component of the signature. - /// @return v The v component of the signature. - function splitSignature(bytes memory sig) - internal - pure - returns (bytes32 r, bytes32 s, uint8 v) - { - require(sig.length == 65, "BridgeCommittee: Invalid signature length"); - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - /// @solidity memory-safe-assembly - assembly { - r := mload(add(sig, 32)) - s := mload(add(sig, 64)) - v := byte(0, mload(add(sig, 96))) - } - - //adjust for ethereum signature verification - if (v < 27) v += 27; - } -} diff --git a/bridge/evm/contracts/BridgeConfig.sol b/bridge/evm/contracts/BridgeConfig.sol deleted file mode 100644 index 4a10ee5e3c5..00000000000 --- a/bridge/evm/contracts/BridgeConfig.sol +++ /dev/null @@ -1,189 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import "./utils/CommitteeUpgradeable.sol"; -import "./interfaces/IBridgeConfig.sol"; - -/// @title BridgeConfig -/// @notice This contract manages a registry of supported tokens and supported chain IDs for the IotaBridge. -/// It also provides functions to convert token amounts to IOTA decimal adjusted amounts and vice versa. -contract BridgeConfig is IBridgeConfig, CommitteeUpgradeable { - /* ========== STATE VARIABLES ========== */ - - uint8 public chainID; - mapping(uint8 tokenID => Token) public supportedTokens; - // price in USD (8 decimal precision) (e.g. 1 ETH = 2000 USD => 2000_00000000) - mapping(uint8 tokenID => uint64 tokenPrice) public tokenPrices; - mapping(uint8 chainId => bool isSupported) public supportedChains; - - /// @notice Constructor function for the BridgeConfig contract. - /// @dev the provided arrays must have the same length. - /// @param _committee The address of the BridgeCommittee contract. - /// @param _chainID The ID of the chain this contract is deployed on. - /// @param _supportedTokens The addresses of the supported tokens. - /// @param _tokenPrices An array of token prices (with 8 decimal precision). - /// @param _supportedChains array of supported chain IDs. - function initialize( - address _committee, - uint8 _chainID, - address[] memory _supportedTokens, - uint64[] memory _tokenPrices, - uint8[] memory _supportedChains - ) external initializer { - __CommitteeUpgradeable_init(_committee); - require(_supportedTokens[0] == address(0), "BridgeConfig: Must reserve first token for IOTA"); - require(_supportedTokens.length == 5, "BridgeConfig: Invalid supported token addresses"); - require( - _supportedTokens.length == _tokenPrices.length, "BridgeConfig: Invalid token prices" - ); - - uint8[] memory _iotaDecimals = new uint8[](5); - _iotaDecimals[0] = 9; // IOTA - _iotaDecimals[1] = 8; // wBTC - _iotaDecimals[2] = 8; // wETH - _iotaDecimals[3] = 6; // USDC - _iotaDecimals[4] = 6; // USDT - - for (uint8 i; i < _supportedTokens.length; i++) { - supportedTokens[i] = Token(_supportedTokens[i], _iotaDecimals[i], true); - } - - for (uint8 i; i < _supportedChains.length; i++) { - require(_supportedChains[i] != _chainID, "BridgeConfig: Cannot support self"); - supportedChains[_supportedChains[i]] = true; - } - - for (uint8 i; i < _tokenPrices.length; i++) { - tokenPrices[i] = _tokenPrices[i]; - } - - chainID = _chainID; - } - - /* ========== VIEW FUNCTIONS ========== */ - - /// @notice Returns the address of the token with the given ID. - /// @param tokenID The ID of the token. - /// @return address of the provided token. - function tokenAddressOf(uint8 tokenID) public view override returns (address) { - return supportedTokens[tokenID].tokenAddress; - } - - /// @notice Returns the iota decimal places of the token with the given ID. - /// @param tokenID The ID of the token. - /// @return amount of iota decimal places of the provided token. - function tokenIotaDecimalOf(uint8 tokenID) public view override returns (uint8) { - return supportedTokens[tokenID].iotaDecimal; - } - - /// @notice Returns the price of the token with the given ID. - /// @param tokenID The ID of the token. - /// @return price of the provided token. - function tokenPriceOf(uint8 tokenID) public view override returns (uint64) { - return tokenPrices[tokenID]; - } - - /// @notice Returns whether a token is supported in IotaBridge with the given ID. - /// @param tokenID The ID of the token. - /// @return true if the token is supported, false otherwise. - function isTokenSupported(uint8 tokenID) public view override returns (bool) { - return supportedTokens[tokenID].tokenAddress != address(0); - } - - /// @notice Returns whether a chain is supported in IotaBridge with the given ID. - /// @param chainId The ID of the chain. - /// @return true if the chain is supported, false otherwise. - function isChainSupported(uint8 chainId) public view override returns (bool) { - return supportedChains[chainId]; - } - - /* ========== MUTATIVE FUNCTIONS ========== */ - - /// @notice Updates the token price with the provided message if the provided signatures are valid. - /// @param signatures array of signatures to validate the message. - /// @param message BridgeMessage containing the update token price payload. - function updateTokenPriceWithSignatures( - bytes[] memory signatures, - BridgeUtils.Message memory message - ) - external - nonReentrant - verifyMessageAndSignatures(message, signatures, BridgeUtils.UPDATE_TOKEN_PRICE) - { - // decode the update token payload - (uint8 tokenID, uint64 price) = BridgeUtils.decodeUpdateTokenPricePayload(message.payload); - - _updateTokenPrice(tokenID, price); - } - - function addTokensWithSignatures(bytes[] memory signatures, BridgeUtils.Message memory message) - external - nonReentrant - verifyMessageAndSignatures(message, signatures, BridgeUtils.ADD_EVM_TOKENS) - { - // decode the update token payload - ( - bool native, - uint8[] memory tokenIDs, - address[] memory tokenAddresses, - uint8[] memory iotaDecimals, - uint64[] memory _tokenPrices - ) = BridgeUtils.decodeAddTokensPayload(message.payload); - - // update the token - for (uint8 i; i < tokenIDs.length; i++) { - _addToken(tokenIDs[i], tokenAddresses[i], iotaDecimals[i], _tokenPrices[i], native); - } - } - - /* ========== PRIVATE FUNCTIONS ========== */ - - /// @notice Updates the price of the token with the provided ID. - /// @param tokenID The ID of the token to update. - /// @param tokenPrice The price of the token. - function _updateTokenPrice(uint8 tokenID, uint64 tokenPrice) private { - require(isTokenSupported(tokenID), "BridgeConfig: Unsupported token"); - require(tokenPrice > 0, "BridgeConfig: Invalid token price"); - - tokenPrices[tokenID] = tokenPrice; - - emit TokenPriceUpdated(tokenID, tokenPrice); - } - - /// @notice Updates the token with the provided ID. - /// @param tokenID The ID of the token to update. - /// @param tokenAddress The address of the token. - /// @param iotaDecimal The decimal places of the token. - /// @param tokenPrice The price of the token. - /// @param native Whether the token is native to the chain. - function _addToken( - uint8 tokenID, - address tokenAddress, - uint8 iotaDecimal, - uint64 tokenPrice, - bool native - ) private { - require(tokenAddress != address(0), "BridgeConfig: Invalid token address"); - require(iotaDecimal > 0, "BridgeConfig: Invalid IOTA decimal"); - require(tokenPrice > 0, "BridgeConfig: Invalid token price"); - - uint8 erc20Decimals = IERC20Metadata(tokenAddress).decimals(); - require(erc20Decimals >= iotaDecimal, "BridgeConfig: Invalid IOTA decimal"); - - supportedTokens[tokenID] = Token(tokenAddress, iotaDecimal, native); - tokenPrices[tokenID] = tokenPrice; - - emit TokenAdded(tokenID, tokenAddress, iotaDecimal, tokenPrice); - } - - /* ========== MODIFIERS ========== */ - - /// @notice Requires the given token to be supported. - /// @param tokenID The ID of the token to check. - modifier tokenSupported(uint8 tokenID) { - require(isTokenSupported(tokenID), "BridgeConfig: Unsupported token"); - _; - } -} diff --git a/bridge/evm/contracts/BridgeLimiter.sol b/bridge/evm/contracts/BridgeLimiter.sol deleted file mode 100644 index 4f55a6104d5..00000000000 --- a/bridge/evm/contracts/BridgeLimiter.sol +++ /dev/null @@ -1,184 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import "./interfaces/IBridgeLimiter.sol"; -import "./interfaces/IBridgeConfig.sol"; -import "./utils/CommitteeUpgradeable.sol"; - -/// @title BridgeLimiter -/// @notice A contract that limits the amount of tokens that can be bridged from a given chain within -/// a rolling 24-hour window. This is accomplished by storing the amount bridged from a given chain in USD -/// within a given hourly timestamp. It also provides functions to update the token prices and the total -/// limit of the given chainID measured in USD with 8 decimal precision. -/// The contract is intended to be used and owned by the IotaBridge contract. -contract BridgeLimiter is IBridgeLimiter, CommitteeUpgradeable, OwnableUpgradeable { - /* ========== STATE VARIABLES ========== */ - - mapping(uint256 chainHourTimestamp => uint256 totalAmountBridged) public - chainHourlyTransferAmount; - // total limit in USD (8 decimal precision) (e.g. 1000_00000000 => 1000 USD) - mapping(uint8 chainID => uint64 totalLimit) public chainLimits; - mapping(uint8 chainID => uint32 oldestHourTimestamp) public oldestChainTimestamp; - - /* ========== INITIALIZER ========== */ - - /// @notice Initializes the BridgeLimiter contract with the provided parameters. - /// @dev this function should be called directly after deployment (see OpenZeppelin upgradeable - /// standards). - /// @param _committee The address of the BridgeCommittee contract. - /// @param chainIDs An array of chain IDs to limit. - /// @param _totalLimits The total limit for the bridge (8 decimal precision). - function initialize(address _committee, uint8[] memory chainIDs, uint64[] memory _totalLimits) - external - initializer - { - require( - chainIDs.length == _totalLimits.length, - "BridgeLimiter: invalid chainIDs and totalLimits length" - ); - __CommitteeUpgradeable_init(_committee); - __Ownable_init(msg.sender); - - for (uint8 i; i < chainIDs.length; i++) { - require( - committee.config().isChainSupported(chainIDs[i]), - "BridgeLimiter: Chain not supported" - ); - chainLimits[chainIDs[i]] = _totalLimits[i]; - oldestChainTimestamp[chainIDs[i]] = currentHour(); - } - } - - /* ========== VIEW FUNCTIONS ========== */ - - /// @notice Returns whether the total amount, including the given token amount, will exceed the totalLimit. - /// @dev The function will calculate the given token amount in USD. - /// @param chainID The ID of the chain to check limit for. - /// @param tokenID The ID of the token. - /// @param amount The amount of the token. - /// @return boolean indicating whether the total amount will exceed the limit. - function willAmountExceedLimit(uint8 chainID, uint8 tokenID, uint256 amount) - external - view - override - returns (bool) - { - uint256 windowAmount = calculateWindowAmount(chainID); - uint256 USDAmount = calculateAmountInUSD(tokenID, amount); - return windowAmount + USDAmount > chainLimits[chainID]; - } - - /// @notice Returns whether the total amount, including the given USD amount, will exceed the totalLimit. - /// @param amount The amount in USD. - /// @return boolean indicating whether the total amount will exceed the limit. - function willUSDAmountExceedLimit(uint8 chainID, uint256 amount) public view returns (bool) { - uint256 windowAmount = calculateWindowAmount(chainID); - return windowAmount + amount > chainLimits[chainID]; - } - - /// @dev Calculates the total transfer amount within the rolling 24-hour window. - /// @return total transfer amount within the window. - function calculateWindowAmount(uint8 chainID) public view returns (uint256 total) { - uint32 _currentHour = currentHour(); - // aggregate the last 24 hours - for (uint32 i; i < 24; i++) { - uint256 key = getChainHourTimestampKey(chainID, _currentHour - i); - total += chainHourlyTransferAmount[key]; - } - return total; - } - - /// @notice Calculates the given token amount in USD (8 decimal precision). - /// @param tokenID The ID of the token. - /// @param amount The amount of tokens. - /// @return amount in USD (8 decimal precision). - function calculateAmountInUSD(uint8 tokenID, uint256 amount) public view returns (uint256) { - // get the token address - address tokenAddress = committee.config().tokenAddressOf(tokenID); - // get the decimals - uint8 decimals = IERC20Metadata(tokenAddress).decimals(); - - // calculate amount in USD - return amount * committee.config().tokenPriceOf(tokenID) / (10 ** decimals); - } - - /// @notice Returns the current hour timestamp. - /// @return current hour timestamp. - function currentHour() public view returns (uint32) { - return uint32(block.timestamp / 1 hours); - } - - /// @notice Returns the key for the chain and hour timestamp. - /// @param chainID The ID of the chain. - /// @param hourTimestamp The hour timestamp. - function getChainHourTimestampKey(uint8 chainID, uint32 hourTimestamp) - public - pure - returns (uint256) - { - return (uint256(chainID) << 32) | uint256(hourTimestamp); - } - - /* ========== EXTERNAL FUNCTIONS ========== */ - - /// @notice Updates the bridge transfers for a specific token ID and amount. Only the contract - /// owner can call this function (intended to be the IotaBridge contract). - /// @dev The amount must be greater than 0 and must not exceed the rolling window limit. - /// @param chainID The ID of the chain to record the transfer for. - /// @param tokenID The ID of the token. - /// @param amount The amount of tokens to be transferred. - function recordBridgeTransfers(uint8 chainID, uint8 tokenID, uint256 amount) - external - override - onlyOwner - { - require(amount > 0, "BridgeLimiter: amount must be greater than 0"); - uint256 usdAmount = calculateAmountInUSD(tokenID, amount); - require( - !willUSDAmountExceedLimit(chainID, usdAmount), - "BridgeLimiter: amount exceeds rolling window limit" - ); - - uint32 _currentHour = currentHour(); - - // garbage collect most recently expired hour if possible - uint256 key = getChainHourTimestampKey(chainID, _currentHour - 25); - if (chainHourlyTransferAmount[key] > 0) { - delete chainHourlyTransferAmount[key]; - } - - // update key to current hour - key = getChainHourTimestampKey(chainID, _currentHour); - // update hourly transfers - chainHourlyTransferAmount[key] += usdAmount; - } - - /// @notice Updates the total limit with the provided message if the provided signatures are valid. - /// @param signatures array of signatures to validate the message. - /// @param message The BridgeUtils containing the update limit payload. - function updateLimitWithSignatures( - bytes[] memory signatures, - BridgeUtils.Message memory message - ) - external - nonReentrant - verifyMessageAndSignatures(message, signatures, BridgeUtils.UPDATE_BRIDGE_LIMIT) - { - // decode the update limit payload - (uint8 sourceChainID, uint64 newLimit) = - BridgeUtils.decodeUpdateLimitPayload(message.payload); - - require( - committee.config().isChainSupported(sourceChainID), - "BridgeLimiter: Source chain not supported" - ); - - // update the chain limit - chainLimits[sourceChainID] = newLimit; - - emit LimitUpdated(sourceChainID, newLimit); - } -} diff --git a/bridge/evm/contracts/BridgeVault.sol b/bridge/evm/contracts/BridgeVault.sol deleted file mode 100644 index 6c6ebfcffbc..00000000000 --- a/bridge/evm/contracts/BridgeVault.sol +++ /dev/null @@ -1,74 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; -import "./interfaces/IBridgeVault.sol"; -import "./interfaces/IWETH9.sol"; - -/// @title BridgeVault -/// @notice A contract that acts as a vault for transferring ERC20 tokens and ETH. It enables the owner -/// (intended to be the IotaBridge contract) to transfer tokens to a target address. It also supports -/// unwrapping WETH (Wrapped Ether) and transferring the unwrapped ETH. -/// @dev The contract is initialized with the deployer as the owner. The ownership is intended to be -/// transferred to the IotaBridge contract after the bridge contract is deployed. -contract BridgeVault is Ownable, IBridgeVault, ReentrancyGuard { - /* ========== STATE VARIABLES ========== */ - - IWETH9 public immutable wETH; - - /* ========== CONSTRUCTOR ========== */ - - /// @notice Constructor function for the BridgeVault contract. - /// @param _wETH The address of the Wrapped Ether (WETH) contract. - constructor(address _wETH) Ownable(msg.sender) ReentrancyGuard() { - // Set the WETH address - wETH = IWETH9(_wETH); - } - - /// @notice Transfers ERC20 tokens from the contract to a target address. Only the owner of - /// the contract can call this function. - /// @dev This function is intended to only be called by the IotaBridge contract. - /// @param tokenAddress The address of the ERC20 token. - /// @param recipientAddress The address to transfer the tokens to. - /// @param amount The amount of tokens to transfer. - function transferERC20(address tokenAddress, address recipientAddress, uint256 amount) - external - override - onlyOwner - nonReentrant - { - // Transfer the tokens from the contract to the target address - SafeERC20.safeTransfer(IERC20(tokenAddress), recipientAddress, amount); - } - - /// @notice Unwraps stored wrapped ETH and transfers the newly withdrawn ETH to the provided target - /// address. Only the owner of the contract can call this function. - /// @dev This function is intended to only be called by the IotaBridge contract. - /// @param recipientAddress The address to transfer the ETH to. - /// @param amount The amount of ETH to transfer. - function transferETH(address payable recipientAddress, uint256 amount) - external - override - onlyOwner - nonReentrant - { - // Unwrap the WETH - wETH.withdraw(amount); - - // Transfer the unwrapped ETH to the target address - (bool success,) = recipientAddress.call{value: amount}(""); - require(success, "ETH transfer failed"); - } - - /// @notice Wraps as eth sent to this contract. - /// @dev skip if sender is wETH contract to avoid infinite loop. - receive() external payable { - if (msg.sender != address(wETH)) { - wETH.deposit{value: msg.value}(); - } - } -} diff --git a/bridge/evm/contracts/IotaBridge.sol b/bridge/evm/contracts/IotaBridge.sol deleted file mode 100644 index 3e50db090eb..00000000000 --- a/bridge/evm/contracts/IotaBridge.sol +++ /dev/null @@ -1,291 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/interfaces/IERC20Metadata.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "./utils/CommitteeUpgradeable.sol"; -import "./interfaces/IIotaBridge.sol"; -import "./interfaces/IBridgeVault.sol"; -import "./interfaces/IBridgeLimiter.sol"; -import "./interfaces/IBridgeConfig.sol"; -import "./interfaces/IWETH9.sol"; - -/// @title IotaBridge -/// @notice This contract implements a token bridge that enables users to deposit and withdraw -/// supported tokens to and from other chains. The bridge supports the transfer of Ethereum and ERC20 -/// tokens. Bridge operations are managed by a committee of IOTA validators that are responsible -/// for verifying and processing bridge messages. The bridge is designed to be upgradeable and -/// can be paused in case of an emergency. The bridge also enforces limits on the amount of -/// assets that can be withdrawn to prevent abuse. -contract IotaBridge is IIotaBridge, CommitteeUpgradeable, PausableUpgradeable { - /* ========== STATE VARIABLES ========== */ - - mapping(uint64 nonce => bool isProcessed) public isTransferProcessed; - IBridgeVault public vault; - IBridgeLimiter public limiter; - - uint8 constant IOTA_ADDRESS_LENGTH = 32; - - /* ========== INITIALIZER ========== */ - - /// @notice Initializes the IotaBridge contract with the provided parameters. - /// @dev this function should be called directly after deployment (see OpenZeppelin upgradeable standards). - /// @param _committee The address of the committee contract. - /// @param _vault The address of the bridge vault contract. - /// @param _limiter The address of the bridge limiter contract. - function initialize(address _committee, address _vault, address _limiter) - external - initializer - { - __CommitteeUpgradeable_init(_committee); - __Pausable_init(); - vault = IBridgeVault(_vault); - limiter = IBridgeLimiter(_limiter); - } - - /* ========== EXTERNAL FUNCTIONS ========== */ - - /// @notice Allows the caller to provide signatures that enable the transfer of tokens to - /// the recipient address indicated within the message payload. - /// @dev `message.chainID` represents the sending chain ID. Receiving chain ID needs to match - /// this bridge's chain ID (this chain). - /// @param signatures The array of signatures. - /// @param message The BridgeUtils containing the transfer details. - function transferBridgedTokensWithSignatures( - bytes[] memory signatures, - BridgeUtils.Message memory message - ) - external - nonReentrant - verifyMessageAndSignatures(message, signatures, BridgeUtils.TOKEN_TRANSFER) - onlySupportedChain(message.chainID) - { - // verify that message has not been processed - require(!isTransferProcessed[message.nonce], "IotaBridge: Message already processed"); - - IBridgeConfig config = committee.config(); - - BridgeUtils.TokenTransferPayload memory tokenTransferPayload = - BridgeUtils.decodeTokenTransferPayload(message.payload); - - // verify target chain ID is this chain ID - require( - tokenTransferPayload.targetChain == config.chainID(), "IotaBridge: Invalid target chain" - ); - - // convert amount to ERC20 token decimals - uint256 erc20AdjustedAmount = BridgeUtils.convertIotaToERC20Decimal( - IERC20Metadata(config.tokenAddressOf(tokenTransferPayload.tokenID)).decimals(), - config.tokenIotaDecimalOf(tokenTransferPayload.tokenID), - tokenTransferPayload.amount - ); - - _transferTokensFromVault( - message.chainID, - tokenTransferPayload.tokenID, - tokenTransferPayload.recipientAddress, - erc20AdjustedAmount - ); - - // mark message as processed - isTransferProcessed[message.nonce] = true; - - emit TokensClaimed( - message.chainID, - message.nonce, - config.chainID(), - tokenTransferPayload.tokenID, - erc20AdjustedAmount, - tokenTransferPayload.senderAddress, - tokenTransferPayload.recipientAddress - ); - } - - /// @notice Executes an emergency operation with the provided signatures and message. - /// @dev If the given operation is to freeze and the bridge is already frozen, the operation - /// will revert. - /// @param signatures The array of signatures to verify. - /// @param message The BridgeUtils containing the details of the operation. - function executeEmergencyOpWithSignatures( - bytes[] memory signatures, - BridgeUtils.Message memory message - ) - external - nonReentrant - verifyMessageAndSignatures(message, signatures, BridgeUtils.EMERGENCY_OP) - { - // decode the emergency op message - bool isFreezing = BridgeUtils.decodeEmergencyOpPayload(message.payload); - - if (isFreezing) _pause(); - else _unpause(); - // pausing event emitted in 'PausableUpgradeable.sol' - } - - /// @notice Enables the caller to deposit supported tokens to be bridged to a given - /// destination chain. - /// @dev The provided tokenID and destinationChainID must be supported. The caller must - /// have approved this contract to transfer the given token. - /// @param tokenID The ID of the token to be bridged. - /// @param amount The amount of tokens to be bridged. - /// @param recipientAddress The address on the IOTA chain where the tokens will be sent. - /// @param destinationChainID The ID of the destination chain. - function bridgeERC20( - uint8 tokenID, - uint256 amount, - bytes memory recipientAddress, - uint8 destinationChainID - ) external whenNotPaused nonReentrant onlySupportedChain(destinationChainID) { - require( - recipientAddress.length == IOTA_ADDRESS_LENGTH, - "IotaBridge: Invalid recipient address length" - ); - - IBridgeConfig config = committee.config(); - - require(config.isTokenSupported(tokenID), "IotaBridge: Unsupported token"); - - address tokenAddress = config.tokenAddressOf(tokenID); - - // check that the bridge contract has allowance to transfer the tokens - require( - IERC20(tokenAddress).allowance(msg.sender, address(this)) >= amount, - "IotaBridge: Insufficient allowance" - ); - - // calculate old vault balance - uint256 oldBalance = IERC20(tokenAddress).balanceOf(address(vault)); - - // Transfer the tokens from the contract to the vault - SafeERC20.safeTransferFrom(IERC20(tokenAddress), msg.sender, address(vault), amount); - - // calculate new vault balance - uint256 newBalance = IERC20(tokenAddress).balanceOf(address(vault)); - - // calculate the amount transferred - uint256 amountTransferred = newBalance - oldBalance; - - // Adjust the amount - uint64 iotaAdjustedAmount = BridgeUtils.convertERC20ToIotaDecimal( - IERC20Metadata(tokenAddress).decimals(), - config.tokenIotaDecimalOf(tokenID), - amountTransferred - ); - - emit TokensDeposited( - config.chainID(), - nonces[BridgeUtils.TOKEN_TRANSFER], - destinationChainID, - tokenID, - iotaAdjustedAmount, - msg.sender, - recipientAddress - ); - - // increment token transfer nonce - nonces[BridgeUtils.TOKEN_TRANSFER]++; - } - - /// @notice Enables the caller to deposit Eth to be bridged to a given destination chain. - /// @dev The provided destinationChainID must be supported. - /// @param recipientAddress The address on the destination chain where Eth will be sent. - /// @param destinationChainID The ID of the destination chain. - function bridgeETH(bytes memory recipientAddress, uint8 destinationChainID) - external - payable - whenNotPaused - nonReentrant - onlySupportedChain(destinationChainID) - { - require( - recipientAddress.length == IOTA_ADDRESS_LENGTH, - "IotaBridge: Invalid recipient address length" - ); - - uint256 amount = msg.value; - - // Transfer the unwrapped ETH to the target address - (bool success,) = payable(address(vault)).call{value: amount}(""); - require(success, "IotaBridge: Failed to transfer ETH to vault"); - - // Adjust the amount to emit. - IBridgeConfig config = committee.config(); - - // Adjust the amount - uint64 iotaAdjustedAmount = BridgeUtils.convertERC20ToIotaDecimal( - IERC20Metadata(config.tokenAddressOf(BridgeUtils.ETH)).decimals(), - config.tokenIotaDecimalOf(BridgeUtils.ETH), - amount - ); - - emit TokensDeposited( - config.chainID(), - nonces[BridgeUtils.TOKEN_TRANSFER], - destinationChainID, - BridgeUtils.ETH, - iotaAdjustedAmount, - msg.sender, - recipientAddress - ); - - // increment token transfer nonce - nonces[BridgeUtils.TOKEN_TRANSFER]++; - } - - /* ========== INTERNAL FUNCTIONS ========== */ - - /// @dev Transfers tokens from the vault to a target address. - /// @param sendingChainID The ID of the chain from which the tokens are being transferred. - /// @param tokenID The ID of the token being transferred. - /// @param recipientAddress The address to which the tokens are being transferred. - /// @param amount The amount of tokens being transferred. - function _transferTokensFromVault( - uint8 sendingChainID, - uint8 tokenID, - address recipientAddress, - uint256 amount - ) private whenNotPaused limitNotExceeded(sendingChainID, tokenID, amount) { - address tokenAddress = committee.config().tokenAddressOf(tokenID); - - // Check that the token address is supported - require(tokenAddress != address(0), "IotaBridge: Unsupported token"); - - // transfer eth if token type is eth - if (tokenID == BridgeUtils.ETH) { - vault.transferETH(payable(recipientAddress), amount); - } else { - // transfer tokens from vault to target address - vault.transferERC20(tokenAddress, recipientAddress, amount); - } - - // update amount bridged - limiter.recordBridgeTransfers(sendingChainID, tokenID, amount); - } - - /* ========== MODIFIERS ========== */ - - /// @dev Requires the amount being transferred does not exceed the bridge limit in - /// the last 24 hours. - /// @param tokenID The ID of the token being transferred. - /// @param amount The amount of tokens being transferred. - modifier limitNotExceeded(uint8 chainID, uint8 tokenID, uint256 amount) { - require( - !limiter.willAmountExceedLimit(chainID, tokenID, amount), - "IotaBridge: Amount exceeds bridge limit" - ); - _; - } - - /// @dev Requires the target chain ID is supported. - /// @param targetChainID The ID of the target chain. - modifier onlySupportedChain(uint8 targetChainID) { - require( - committee.config().isChainSupported(targetChainID), - "IotaBridge: Target chain not supported" - ); - _; - } -} diff --git a/bridge/evm/contracts/interfaces/IBridgeCommittee.sol b/bridge/evm/contracts/interfaces/IBridgeCommittee.sol deleted file mode 100644 index 3ecc502f98a..00000000000 --- a/bridge/evm/contracts/interfaces/IBridgeCommittee.sol +++ /dev/null @@ -1,30 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "../utils/BridgeUtils.sol"; -import "./IBridgeConfig.sol"; - -/// @title IBridgeCommittee -/// @notice Interface for the BridgeCommittee contract. -interface IBridgeCommittee { - /// @notice Verifies the provided signatures for the given message by aggregating and validating the - /// stake of each signer against the required stake of the given message type. - /// @dev The function will revert if the total stake of the signers is less than the required stake. - /// @param signatures The array of signatures to be verified. - /// @param message The `BridgeUtils.Message` to be verified. - function verifySignatures(bytes[] memory signatures, BridgeUtils.Message memory message) - external - view; - - /// @notice Returns the interface of the BridgeConfig contract. - /// @return The interface of the BridgeConfig contract. - function config() external view returns (IBridgeConfig); - - /* ========== EVENTS ========== */ - - /// @notice Emitted when the blocklist is updated. - /// @param updatedMembers The addresses of the updated committee members. - /// @param isBlocklisted A boolean indicating whether the committee members are blocklisted or not. - event BlocklistUpdated(address[] updatedMembers, bool isBlocklisted); -} diff --git a/bridge/evm/contracts/interfaces/IBridgeConfig.sol b/bridge/evm/contracts/interfaces/IBridgeConfig.sol deleted file mode 100644 index 5c12ec8ee54..00000000000 --- a/bridge/evm/contracts/interfaces/IBridgeConfig.sol +++ /dev/null @@ -1,49 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -/// @title IBridgeConfig -/// @dev Interface for the BridgeConfig contract. -interface IBridgeConfig { - /* ========== STRUCTS ========== */ - - /// @notice The data struct for the supported bridge tokens. - struct Token { - address tokenAddress; - uint8 iotaDecimal; - bool native; - } - - /* ========== VIEW FUNCTIONS ========== */ - - /// @notice Returns the address of the token with the given ID. - /// @param tokenID The ID of the token. - /// @return address of the provided token. - function tokenAddressOf(uint8 tokenID) external view returns (address); - - /// @notice Returns the iota decimal places of the token with the given ID. - /// @param tokenID The ID of the token. - /// @return amount of iota decimal places of the provided token. - function tokenIotaDecimalOf(uint8 tokenID) external view returns (uint8); - - /// @notice Returns the price of the token with the given ID. - /// @param tokenID The ID of the token. - /// @return price of the provided token. - function tokenPriceOf(uint8 tokenID) external view returns (uint64); - - /// @notice Returns the supported status of the token with the given ID. - /// @param tokenID The ID of the token. - /// @return true if the token is supported, false otherwise. - function isTokenSupported(uint8 tokenID) external view returns (bool); - - /// @notice Returns whether a chain is supported in IotaBridge with the given ID. - /// @param chainId The ID of the chain. - /// @return true if the chain is supported, false otherwise. - function isChainSupported(uint8 chainId) external view returns (bool); - - /// @notice Returns the chain ID of the bridge. - function chainID() external view returns (uint8); - - event TokenAdded(uint8 tokenID, address tokenAddress, uint8 iotaDecimal, uint64 tokenPrice); - event TokenPriceUpdated(uint8 tokenID, uint64 tokenPrice); -} diff --git a/bridge/evm/contracts/interfaces/IBridgeLimiter.sol b/bridge/evm/contracts/interfaces/IBridgeLimiter.sol deleted file mode 100644 index 733a062f897..00000000000 --- a/bridge/evm/contracts/interfaces/IBridgeLimiter.sol +++ /dev/null @@ -1,37 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -/// @title IBridgeLimiter -/// @notice Interface for the BridgeLimiter contract. -interface IBridgeLimiter { - /// @notice Updates the bridge transfers for a specific token ID and amount. Only the contract - /// owner can call this function (intended to be the IotaBridge contract). - /// @dev The amount must be greater than 0 and must not exceed the rolling window limit. - /// @param chainID The ID of the chain to record the transfer for. - /// @param tokenID The ID of the token. - /// @param amount The amount of tokens to be transferred. - function recordBridgeTransfers(uint8 chainID, uint8 tokenID, uint256 amount) external; - - /// @notice Returns whether the total amount, including the given token amount, will exceed the totalLimit. - /// @dev The function will calculate the given token amount in USD. - /// @param chainID The ID of the chain to check limit for. - /// @param tokenID The ID of the token. - /// @param amount The amount of the token. - /// @return boolean indicating whether the total amount will exceed the limit. - function willAmountExceedLimit(uint8 chainID, uint8 tokenID, uint256 amount) - external - view - returns (bool); - - // We no longer emit this event but keep it here for ABI compatibility. - /// @dev (deprecated, not in use) Emitted when the hourly transfer amount is updated. - /// @param hourUpdated The hour that was updated. - /// @param amount The amount in USD transferred. - event HourlyTransferAmountUpdated(uint32 hourUpdated, uint256 amount); - - /// @dev Emitted when the total limit is updated. - /// @param sourceChainID The ID of the source chain. - /// @param newLimit The new limit in USD with 4 decimal places (e.g. 10000 -> $1) - event LimitUpdated(uint8 sourceChainID, uint64 newLimit); -} diff --git a/bridge/evm/contracts/interfaces/IBridgeVault.sol b/bridge/evm/contracts/interfaces/IBridgeVault.sol deleted file mode 100644 index c93907e9765..00000000000 --- a/bridge/evm/contracts/interfaces/IBridgeVault.sol +++ /dev/null @@ -1,18 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -/// @title IBridgeVault -/// @dev Interface for the BridgeVault contract. -interface IBridgeVault { - /// @notice Transfers ERC20 tokens from the BridgeVault contract to a target address. - /// @param tokenAddress The address of the ERC20 token. - /// @param recipientAddress The address to transfer the tokens to. - /// @param amount The amount of tokens to transfer. - function transferERC20(address tokenAddress, address recipientAddress, uint256 amount) external; - - /// @notice Transfers ETH from the BridgeVault contract to a target address. - /// @param recipientAddress The address to transfer the ETH to. - /// @param amount The amount of ETH to transfer. - function transferETH(address payable recipientAddress, uint256 amount) external; -} diff --git a/bridge/evm/contracts/interfaces/IIotaBridge.sol b/bridge/evm/contracts/interfaces/IIotaBridge.sol deleted file mode 100644 index ad874d85ee7..00000000000 --- a/bridge/evm/contracts/interfaces/IIotaBridge.sol +++ /dev/null @@ -1,44 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -/// @title IIotaBridge -/// @dev Interface for the IOTA Bridge contract. -interface IIotaBridge { - /// @notice Emitted when tokens are deposited to be bridged. - /// @param sourceChainID The ID of the source chain (this chain). - /// @param nonce The nonce of the transaction on source chain. - /// @param destinationChainID The ID of the destination chain. - /// @param tokenID The code of the token. - /// @param iotaAdjustedAmount The amount of tokens to transfer, adjusted for IOTA decimals. - /// @param senderAddress The address of the sender. - /// @param recipientAddress The address of the sender. - event TokensDeposited( - uint8 indexed sourceChainID, - uint64 indexed nonce, - uint8 indexed destinationChainID, - uint8 tokenID, - uint64 iotaAdjustedAmount, - address senderAddress, - bytes recipientAddress - ); - - /// @notice Emitted when bridged tokens are transferred to the recipient address. - /// @param sourceChainID The ID of the source chain. - /// @param nonce The nonce of the transaction on source chain. - /// @param destinationChainID The ID of the destination chain (this chain). - /// @param tokenID The code of the token. - /// @param erc20AdjustedAmount The amount of tokens claimed, adjusted for ERC20 decimals. - /// @param senderAddress The address of the sender. - /// @param recipientAddress The address of the sender. - // event BridgedTokensTransferred( - event TokensClaimed( - uint8 indexed sourceChainID, - uint64 indexed nonce, - uint8 indexed destinationChainID, - uint8 tokenID, - uint256 erc20AdjustedAmount, - bytes senderAddress, - address recipientAddress - ); -} diff --git a/bridge/evm/contracts/interfaces/IWETH9.sol b/bridge/evm/contracts/interfaces/IWETH9.sol deleted file mode 100644 index f6257a3e465..00000000000 --- a/bridge/evm/contracts/interfaces/IWETH9.sol +++ /dev/null @@ -1,19 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -/// @title IWETH9 -/// @notice Interface for the WETH9 contract. -interface IWETH9 is IERC20 { - /// @notice Deposit ETH to get wrapped ETH - /// @dev This function enables users to deposit ETH and receive wrapped ETH tokens in return. - /// @dev The amount of ETH to be deposited should be sent along with the function call. - function deposit() external payable; - - /// @notice Withdraw wrapped ETH to get ETH - /// @dev This function allows users to withdraw a specified amount of wrapped ETH and receive ETH in return. - /// @param wad The amount of wrapped ETH to be withdrawn. - function withdraw(uint256 wad) external; -} diff --git a/bridge/evm/contracts/utils/BridgeUtils.sol b/bridge/evm/contracts/utils/BridgeUtils.sol deleted file mode 100644 index 9db8093cc02..00000000000 --- a/bridge/evm/contracts/utils/BridgeUtils.sol +++ /dev/null @@ -1,452 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -/// @title BridgeUtils -/// @notice This library defines the message format and constants for the IOTA native bridge. It also -/// provides functions to encode and decode bridge messages and their payloads. -/// @dev This library only utilizes internal functions to enable upgradeability via the OpenZeppelin -/// UUPS proxy pattern (external libraries are not supported). -library BridgeUtils { - /* ========== STRUCTS ========== */ - - /// @dev A struct that represents a bridge message - /// @param messageType The type of the message, such as token transfer, blocklist, etc. - /// @param version The version of the message format - /// @param nonce The nonce of the message, used to prevent replay attacks - /// @param chainID The chain ID of the source chain (for token transfer messages this is the source chain) - /// @param payload The payload of the message, which depends on the message type - struct Message { - uint8 messageType; - uint8 version; - uint64 nonce; - uint8 chainID; - bytes payload; - } - - /// @dev A struct that represents a token transfer payload - /// @param senderAddressLength The length of the sender address in bytes - /// @param senderAddress The address of the sender on the source chain - /// @param targetChain The chain ID of the target chain - /// @param recipientAddressLength The length of the target address in bytes - /// @param recipientAddress The address of the recipient on the target chain - /// @param tokenID The ID of the token to be transferred - /// @param amount The amount of the token to be transferred - struct TokenTransferPayload { - uint8 senderAddressLength; - bytes senderAddress; - uint8 targetChain; - uint8 recipientAddressLength; - address recipientAddress; - uint8 tokenID; - uint64 amount; - } - - /* ========== CONSTANTS ========== */ - - // message Ids - uint8 public constant TOKEN_TRANSFER = 0; - uint8 public constant BLOCKLIST = 1; - uint8 public constant EMERGENCY_OP = 2; - uint8 public constant UPDATE_BRIDGE_LIMIT = 3; - uint8 public constant UPDATE_TOKEN_PRICE = 4; - uint8 public constant UPGRADE = 5; - uint8 public constant ADD_EVM_TOKENS = 7; - - // Message type stake requirements - uint32 public constant TRANSFER_STAKE_REQUIRED = 3334; - uint32 public constant FREEZING_STAKE_REQUIRED = 450; - uint32 public constant UNFREEZING_STAKE_REQUIRED = 5001; - uint32 public constant UPGRADE_STAKE_REQUIRED = 5001; - uint16 public constant BLOCKLIST_STAKE_REQUIRED = 5001; - uint32 public constant BRIDGE_LIMIT_STAKE_REQUIRED = 5001; - uint32 public constant UPDATE_TOKEN_PRICE_STAKE_REQUIRED = 5001; - uint32 public constant ADD_EVM_TOKENS_STAKE_REQUIRED = 5001; - - // token Ids - uint8 public constant IOTA = 0; - uint8 public constant BTC = 1; - uint8 public constant ETH = 2; - uint8 public constant USDC = 3; - uint8 public constant USDT = 4; - - string public constant MESSAGE_PREFIX = "IOTA_BRIDGE_MESSAGE"; - - /* ========== INTERNAL FUNCTIONS ========== */ - - /// @notice Encodes a bridge message into bytes, using abi.encodePacked to concatenate the message fields. - /// @param message The bridge message to be encoded. - /// @return The encoded message as bytes. - function encodeMessage(Message memory message) internal pure returns (bytes memory) { - bytes memory prefixTypeAndVersion = - abi.encodePacked(MESSAGE_PREFIX, message.messageType, message.version); - bytes memory nonce = abi.encodePacked(message.nonce); - bytes memory chainID = abi.encodePacked(message.chainID); - return bytes.concat(prefixTypeAndVersion, nonce, chainID, message.payload); - } - - /// @notice Computes the hash of a bridge message using keccak256. - /// @param _message The bridge message to be hashed. - /// @return The hash of the message. - function computeHash(Message memory _message) internal pure returns (bytes32) { - return keccak256(encodeMessage(_message)); - } - - /// @notice returns the required stake for the provided message type. - /// @dev The function will revert if the message type is invalid. - /// @param _message The bridge message to be used to determine the required stake. - /// @return The required stake for the provided message type. - function requiredStake(Message memory _message) internal pure returns (uint32) { - if (_message.messageType == TOKEN_TRANSFER) { - return TRANSFER_STAKE_REQUIRED; - } else if (_message.messageType == BLOCKLIST) { - return BLOCKLIST_STAKE_REQUIRED; - } else if (_message.messageType == EMERGENCY_OP) { - bool isFreezing = decodeEmergencyOpPayload(_message.payload); - if (isFreezing) return FREEZING_STAKE_REQUIRED; - return UNFREEZING_STAKE_REQUIRED; - } else if (_message.messageType == UPDATE_BRIDGE_LIMIT) { - return BRIDGE_LIMIT_STAKE_REQUIRED; - } else if (_message.messageType == UPDATE_TOKEN_PRICE) { - return UPDATE_TOKEN_PRICE_STAKE_REQUIRED; - } else if (_message.messageType == UPGRADE) { - return UPGRADE_STAKE_REQUIRED; - } else if (_message.messageType == ADD_EVM_TOKENS) { - return ADD_EVM_TOKENS_STAKE_REQUIRED; - } else { - revert("BridgeUtils: Invalid message type"); - } - } - - /// @notice Converts the provided token amount to the IOTA decimal adjusted amount. - /// @param erc20Decimal The erc20 decimal value for the token. - /// @param iotaDecimal The iota decimal value for the token. - /// @param amount The ERC20 amount of the tokens to convert to IOTA. - /// @return IOTA converted amount. - function convertERC20ToIotaDecimal(uint8 erc20Decimal, uint8 iotaDecimal, uint256 amount) - internal - pure - returns (uint64) - { - if (erc20Decimal == iotaDecimal) { - // ensure provided amount is greater than 0 - require(amount > 0, "BridgeUtils: Insufficient amount provided"); - // Ensure converted amount fits within uint64 - require(amount <= type(uint64).max, "BridgeUtils: Amount too large for uint64"); - return uint64(amount); - } - - require(erc20Decimal > iotaDecimal, "BridgeUtils: Invalid IOTA decimal"); - - // Difference in decimal places - uint256 factor = 10 ** (erc20Decimal - iotaDecimal); - amount = amount / factor; - - // Ensure the converted amount fits within uint64 - require(amount <= type(uint64).max, "BridgeUtils: Amount too large for uint64"); - - // Ensure the converted amount is greater than 0 - require(amount > 0, "BridgeUtils: Insufficient amount provided"); - - return uint64(amount); - } - - /// @notice Converts the provided IOTA decimal adjusted amount to the ERC20 token amount. - /// @param erc20Decimal The erc20 decimal value for the token. - /// @param iotaDecimal The iota decimal value for the token. - /// @param amount The IOTA amount of the tokens to convert to ERC20. - /// @return ERC20 converted amount. - function convertIotaToERC20Decimal(uint8 erc20Decimal, uint8 iotaDecimal, uint64 amount) - internal - pure - returns (uint256) - { - if (iotaDecimal == erc20Decimal) { - return uint256(amount); - } - - require(erc20Decimal > iotaDecimal, "BridgeUtils: Invalid IOTA decimal"); - - // Difference in decimal places - uint256 factor = 10 ** (erc20Decimal - iotaDecimal); - return uint256(amount * factor); - } - - /// @notice Decodes a token transfer payload from bytes to a TokenTransferPayload struct. - /// @dev The function will revert if the payload length is invalid. - /// TokenTransfer payload is 64 bytes. - /// byte 0 : sender address length - /// bytes 1-32 : sender address (as we only support IOTA now, it has to be 32 bytes long) - /// bytes 33 : target chain id - /// byte 34 : target address length - /// bytes 35-54 : target address - /// byte 55 : token id - /// bytes 56-63 : amount - /// @param _payload The payload to be decoded. - /// @return The decoded token transfer payload as a TokenTransferPayload struct. - function decodeTokenTransferPayload(bytes memory _payload) - internal - pure - returns (TokenTransferPayload memory) - { - require(_payload.length == 64, "BridgeUtils: TokenTransferPayload must be 64 bytes"); - - uint8 senderAddressLength = uint8(_payload[0]); - - require( - senderAddressLength == 32, - "BridgeUtils: Invalid sender address length, IOTA address must be 32 bytes" - ); - - // used to offset already read bytes - uint8 offset = 1; - - // extract sender address from payload bytes 1-32 - bytes memory senderAddress = new bytes(senderAddressLength); - for (uint256 i; i < senderAddressLength; i++) { - senderAddress[i] = _payload[i + offset]; - } - - // move offset past the sender address length - offset += senderAddressLength; - - // target chain is a single byte - uint8 targetChain = uint8(_payload[offset++]); - - // target address length is a single byte - uint8 recipientAddressLength = uint8(_payload[offset++]); - require( - recipientAddressLength == 20, - "BridgeUtils: Invalid target address length, EVM address must be 20 bytes" - ); - - // extract target address from payload (35-54) - address recipientAddress; - - // why `add(recipientAddressLength, offset)`? - // At this point, offset = 35, recipientAddressLength = 20. `mload(add(payload, 55))` - // reads the next 32 bytes from bytes 23 in payload, because the first 32 bytes - // of payload stores its length. So in reality, bytes 23 - 54 is loaded. During - // casting to address (20 bytes), the least significant bytes are retained, namely - // `recipientAddress` is bytes 35-54 - assembly { - recipientAddress := mload(add(_payload, add(recipientAddressLength, offset))) - } - - // move offset past the target address length - offset += recipientAddressLength; - - // token id is a single byte - uint8 tokenID = uint8(_payload[offset++]); - - // extract amount from payload - uint64 amount; - uint8 amountLength = 8; // uint64 = 8 bits - - // Why `add(amountLength, offset)`? - // At this point, offset = 56, amountLength = 8. `mload(add(payload, 64))` - // reads the next 32 bytes from bytes 32 in payload, because the first 32 bytes - // of payload stores its length. So in reality, bytes 32 - 63 is loaded. During - // casting to uint64 (8 bytes), the least significant bytes are retained, namely - // `recipientAddress` is bytes 56-63 - assembly { - amount := mload(add(_payload, add(amountLength, offset))) - } - - return TokenTransferPayload( - senderAddressLength, - senderAddress, - targetChain, - recipientAddressLength, - recipientAddress, - tokenID, - amount - ); - } - - /// @notice Decodes a blocklist payload from bytes to a boolean and an array of addresses. - /// @dev The function will revert if the payload length is invalid. - /// Blocklist payload is 2 + 20 * n bytes. - /// byte 0 : blocklist type (0 = blocklist, 1 = unblocklist) - /// byte 1 : number of addresses in the blocklist - /// bytes 2-n : addresses - /// @param _payload The payload to be decoded. - /// @return blocklisting status and the array of addresses to be blocklisted/unblocklisted. - function decodeBlocklistPayload(bytes memory _payload) - internal - pure - returns (bool, address[] memory) - { - uint8 blocklistType = uint8(_payload[0]); - uint8 membersLength = uint8(_payload[1]); - address[] memory members = new address[](membersLength); - uint8 offset = 2; - require((_payload.length - offset) % 20 == 0, "BridgeUtils: Invalid payload length"); - for (uint8 i; i < membersLength; i++) { - // Calculate the starting index for each address - offset += i * 20; - address member; - // Extract each address - assembly { - member := mload(add(add(_payload, 20), offset)) - } - // Store the extracted address - members[i] = member; - } - // blocklistType: 0 = blocklist, 1 = unblocklist - bool blocklisted = (blocklistType == 0); - return (blocklisted, members); - } - - /// @notice Decodes an emergency operation payload from bytes to a boolean. - /// @dev The function will revert if the payload length is invalid. - /// Emergency operation payload is a single byte. - /// byte 0 : operation type (0 = freezing, 1 = unfreezing) - /// @param _payload The payload to be decoded. - /// @return The emergency operation type. - function decodeEmergencyOpPayload(bytes memory _payload) internal pure returns (bool) { - require(_payload.length == 1, "BridgeUtils: Invalid payload length"); - uint8 emergencyOpCode = uint8(_payload[0]); - require(emergencyOpCode <= 1, "BridgeUtils: Invalid op code"); - return emergencyOpCode == 0; - } - - /// @notice Decodes an update limit payload from bytes to a chain ID and a new limit. - /// @dev The function will revert if the payload length is invalid. - /// Update limit payload is 9 bytes. - /// byte 0 : chain ID - /// bytes 1-8 : new limit - /// @param _payload The payload to be decoded. - /// @return senderChainID the sending chain ID to update the limit of. - /// @return newLimit the new limit of the sending chain ID. - function decodeUpdateLimitPayload(bytes memory _payload) - internal - pure - returns (uint8 senderChainID, uint64 newLimit) - { - require(_payload.length == 9, "BridgeUtils: Invalid payload length"); - senderChainID = uint8(_payload[0]); - - // Extracts the uint64 value by loading 32 bytes starting just after the first byte. - // Position uint64 to the least significant bits by shifting it 192 bits to the right. - assembly { - newLimit := shr(192, mload(add(add(_payload, 0x20), 1))) - } - } - - /// @notice Decodes an upgrade payload from bytes to a proxy address, an implementation address, - /// and call data. - /// @dev The function will revert if the payload length is invalid. The payload is expected to be - /// abi encoded. - /// @param _payload The payload to be decoded. - /// @return proxy the address of the proxy to be upgraded. - /// @return implementation the address of the new implementation contract. - /// @return callData the call data to be used in the upgrade. - function decodeUpgradePayload(bytes memory _payload) - internal - pure - returns (address, address, bytes memory) - { - (address proxy, address implementation, bytes memory callData) = - abi.decode(_payload, (address, address, bytes)); - return (proxy, implementation, callData); - } - - /// @notice Decodes an update token price payload from bytes to a token ID and a new price. - /// @dev The function will revert if the payload length is invalid. - /// Update token price payload is 9 bytes. - /// byte 0 : token ID - /// bytes 1-8 : new price - /// @param _payload The payload to be decoded. - /// @return tokenID the token ID to update the price of. - /// @return tokenPrice the new price of the token. - function decodeUpdateTokenPricePayload(bytes memory _payload) - internal - pure - returns (uint8 tokenID, uint64 tokenPrice) - { - require(_payload.length == 9, "BridgeMessage: Invalid payload length"); - tokenID = uint8(_payload[0]); - - // Extracts the uint64 value by loading 32 bytes starting just after the first byte. - // Position uint64 to the least significant bits by shifting it 192 bits to the right. - assembly { - tokenPrice := shr(192, mload(add(add(_payload, 0x20), 1))) - } - } - - /// @notice Decodes an add token payload from bytes to a token ID, a token address, and a token price. - /// @dev The function will revert if the payload length is invalid. - /// Add token payload is 5 + 2n + 20n + 8n bytes (assuming all arrays are of length n). - /// byte 0 : is native - /// byte 1 : number of token IDs - /// byte 2 -> n : token IDs - /// byte n + 1 : number of addresses - /// bytes n + 2 -> m : addresses - /// byte m + 1 : number of iota decimals - /// bytes m + 2 -> i : iota decimals - /// byte i + 1 : number of prices - /// bytes i + 2 -> j : prices (uint64) - /// @param _payload The payload to be decoded. - /// @return native whether the token is native to the chain. - /// @return tokenIDs the token ID to be added. - /// @return tokenAddresses the address of the token to be added. - /// @return iotaDecimals the IOTA decimal places of the tokens to be added. - /// @return tokenPrices the price of the tokens to be added. - function decodeAddTokensPayload(bytes memory _payload) - internal - pure - returns ( - bool native, - uint8[] memory tokenIDs, - address[] memory tokenAddresses, - uint8[] memory iotaDecimals, - uint64[] memory tokenPrices - ) - { - native = _payload[0] != bytes1(0); - - uint8 tokenCount = uint8(_payload[1]); - - // Calculate the starting index for each token ID - uint8 offset = 2; - tokenIDs = new uint8[](tokenCount); - for (uint8 i; i < tokenCount; i++) { - tokenIDs[i] = uint8(_payload[offset++]); - } - - uint8 addressCount = uint8(_payload[offset++]); - tokenAddresses = new address[](addressCount); - for (uint8 i; i < addressCount; i++) { - // Calculate the starting index for each address - address tokenAddress; - // Extract each address - assembly { - tokenAddress := mload(add(add(_payload, 20), offset)) - } - offset += 20; - // Store the extracted address - tokenAddresses[i] = tokenAddress; - } - - uint8 decimalCount = uint8(_payload[offset++]); - iotaDecimals = new uint8[](decimalCount); - for (uint8 i; i < decimalCount; i++) { - iotaDecimals[i] = uint8(_payload[offset++]); - } - - uint8 priceCount = uint8(_payload[offset++]); - tokenPrices = new uint64[](priceCount); - for (uint8 i; i < priceCount; i++) { - // Calculate the starting index for each price - uint64 tokenPrice; - // Extract each price - assembly { - tokenPrice := shr(192, mload(add(add(_payload, 0x20), offset))) - } - offset += 8; - // Store the extracted price - tokenPrices[i] = tokenPrice; - } - } -} diff --git a/bridge/evm/contracts/utils/CommitteeUpgradeable.sol b/bridge/evm/contracts/utils/CommitteeUpgradeable.sol deleted file mode 100644 index 94cd8d135d3..00000000000 --- a/bridge/evm/contracts/utils/CommitteeUpgradeable.sol +++ /dev/null @@ -1,66 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import "../interfaces/IBridgeCommittee.sol"; -import "./MessageVerifier.sol"; - -/// @title CommitteeUpgradeable -/// @notice This contract enables message signature verification using a BridgeCommittee contract, -/// in addition to providing an interface for upgradeability via signed message verification. -/// @dev The contract is intended to be inherited by contracts that require message verification and -/// upgradeability. -abstract contract CommitteeUpgradeable is - UUPSUpgradeable, - MessageVerifier, - ReentrancyGuardUpgradeable -{ - /* ========== STATE VARIABLES ========== */ - - bool private _upgradeAuthorized; - - /* ========== INITIALIZER ========== */ - - function __CommitteeUpgradeable_init(address _committee) internal onlyInitializing { - __ReentrancyGuard_init(); - __MessageVerifier_init(_committee); - committee = IBridgeCommittee(_committee); - } - - /* ========== EXTERNAL FUNCTIONS ========== */ - - /// @notice Enables the upgrade of the inheriting contract by verifying the provided signatures. - /// @dev The function will revert if the provided signatures or message is invalid. - /// @param signatures The array of signatures to be verified. - /// @param message The BridgeUtils to be verified. - function upgradeWithSignatures(bytes[] memory signatures, BridgeUtils.Message memory message) - external - nonReentrant - verifyMessageAndSignatures(message, signatures, BridgeUtils.UPGRADE) - { - // decode the upgrade payload - (address proxy, address implementation, bytes memory callData) = - BridgeUtils.decodeUpgradePayload(message.payload); - - // verify proxy address - require(proxy == address(this), "CommitteeUpgradeable: Invalid proxy address"); - - // authorize upgrade - _upgradeAuthorized = true; - // upgrade contract - upgradeToAndCall(implementation, callData); // Upgraded event emitted with new implementation address - } - - /* ========== INTERNAL FUNCTIONS ========== */ - - /// @notice Authorizes the upgrade of the inheriting contract. - /// @dev The _upgradeAuthorized state variable can only be set with the upgradeWithSignatures - /// function, meaning that the upgrade can only be authorized by the committee. - function _authorizeUpgrade(address) internal override { - require(_upgradeAuthorized, "CommitteeUpgradeable: Unauthorized upgrade"); - _upgradeAuthorized = false; - } -} diff --git a/bridge/evm/contracts/utils/MessageVerifier.sol b/bridge/evm/contracts/utils/MessageVerifier.sol deleted file mode 100644 index 32d5dc0dbd6..00000000000 --- a/bridge/evm/contracts/utils/MessageVerifier.sol +++ /dev/null @@ -1,54 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import "../interfaces/IBridgeCommittee.sol"; - -/// @title MessageVerifier -/// @notice This contract provides an interface to verify messages and their signatures -/// using a BridgeCommittee contract. This contract is also responsible for maintaining -/// nonces for each message type to prevent replay attacks. -/// @dev The contract is intended to be inherited by contracts that require message and signature -/// verification. -abstract contract MessageVerifier is Initializable { - /* ========== STATE VARIABLES ========== */ - - IBridgeCommittee public committee; - mapping(uint8 messageType => uint64 nonce) public nonces; - - /* ========== INITIALIZER ========== */ - - function __MessageVerifier_init(address _committee) internal onlyInitializing { - committee = IBridgeCommittee(_committee); - } - - /* ========== MODIFIERS ========== */ - - /// @notice Verifies the provided message and signatures using the BridgeCommittee contract. - /// @dev The function will revert if the message type does not match the expected type, - /// if the signatures are invalid, or if the message nonce is invalid. - /// @param message The BridgeUtils to be verified. - /// @param signatures The array of signatures to be verified. - /// @param messageType The expected message type of the provided message. - modifier verifyMessageAndSignatures( - BridgeUtils.Message memory message, - bytes[] memory signatures, - uint8 messageType - ) { - // verify message type - require(message.messageType == messageType, "MessageVerifier: message does not match type"); - // verify signatures - committee.verifySignatures(signatures, message); - // increment message type nonce - if (messageType != BridgeUtils.TOKEN_TRANSFER) { - // verify chain ID - require( - message.chainID == committee.config().chainID(), "MessageVerifier: Invalid chain ID" - ); - require(message.nonce == nonces[message.messageType], "MessageVerifier: Invalid nonce"); - nonces[message.messageType]++; - } - _; - } -} diff --git a/bridge/evm/deploy_configs/11155111.json b/bridge/evm/deploy_configs/11155111.json deleted file mode 100644 index 3a50d7abde1..00000000000 --- a/bridge/evm/deploy_configs/11155111.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "committeeMemberStake": [2500, 2500, 2500, 2500], - "committeeMembers": ["0xf04c72634fc11f7078fc8d1e1260d105a6d9c555", "0x6b1b0fb6bb0a217a0fa8a6a880d886437fbfb9a7", "0x278b75716cfdd84612efb78f8ba99240826dca00", "0xcb5c7457b31509f3451d931dd633115451acc0b0"], - "minCommitteeStakeRequired": 10000, - "sourceChainId": 11, - "supportedChainIDs": [1, 2], - "supportedChainLimitsInDollars": [100000000000, 100000000000], - "supportedTokens": ["0x0000000000000000000000000000000000000000", "0x0112D7B36726B3077b72DDb457A9f9c94D9cd71c", "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14", "0x80bF6fb931C8eB99Ab32aeD543ACCFd168fd2a47", "0x4302b99c1DE4de1ceC3eAedAbf88b8ee6Fceb614"], - "tokenPrices": [162000000, 6200000000000, 430000000000, 100000000, 100000000], - "wETHAddress": "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14" -} diff --git a/bridge/evm/deploy_configs/31337.json b/bridge/evm/deploy_configs/31337.json deleted file mode 100644 index 2dffed7e00f..00000000000 --- a/bridge/evm/deploy_configs/31337.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "committeeMemberStake": [2500, 2500, 2500, 2500], - "committeeMembers": ["0x68b43fd906c0b8f024a18c56e06744f7c6157c65", "0xacaef39832cb995c4e049437a3e2ec6a7bad1ab5", "0x8061f127910e8ef56f16a2c411220bad25d61444", "0x508f3f1ff45f4ca3d8e86cdcc91445f00acc59fc"], - "minCommitteeStakeRequired": 10000, - "sourceChainId": 12, - "supportedChainIDs": [1, 2, 3], - "supportedChainLimitsInDollars": [1000000000000000, 1000000000000000, 1000000000000000], - "supportedTokens": [], - "tokenPrices": [128000000, 4325189000000, 259696000000, 100000000, 100000000] -} diff --git a/bridge/evm/deploy_configs/31338.json b/bridge/evm/deploy_configs/31338.json deleted file mode 100644 index bf8a8b28985..00000000000 --- a/bridge/evm/deploy_configs/31338.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "committeeMemberStake": [2500, 2500, 2500, 2500], - "committeeMembers": ["27e24ef6bc45152cd145a0ec28b5d55f2b5afec9", "36d7f7732f1eb8c2cf678fab2ce6c64b27ade701", "4580b223963320d7a78b7b4ccdd8b0f7a2fdb24f", "04130d2a70517f7eb6d0e0198ef8a0db06878a7e"], - "minCommitteeStakeRequired": 10000, - "sourceChainId": 12, - "supportedChainIDs": [1, 2, 3], - "supportedChainLimitsInDollars": [1000000000000000, 1000000000000000, 1000000000000000], - "supportedTokens": [], - "tokenPrices": [128000000, 4325189000000, 259696000000, 100000000, 100000000] -} diff --git a/bridge/evm/deploy_configs/example.json b/bridge/evm/deploy_configs/example.json deleted file mode 100644 index 88cf3de5a74..00000000000 --- a/bridge/evm/deploy_configs/example.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "committeeMemberStake": [0], - "committeeMembers": ["0x000"], - "minCommitteeStakeRequired": 10000, - "sourceChainId": 0, - "supportedChainIDs": [1], - "supportedChainLimitsInDollars": [1000000], - "supportedTokens": ["0x00", "0x00", "0x00", "0x00"], - "tokenPrices": [128000000, 4325189000000, 259696000000, 100000000], - "wETHAddress": "0x00" -} \ No newline at end of file diff --git a/bridge/evm/deploy_configs/sepolia.json b/bridge/evm/deploy_configs/sepolia.json deleted file mode 100644 index b56687658c8..00000000000 --- a/bridge/evm/deploy_configs/sepolia.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "committeeMemberStake": [ - 144, - 361, - 361, - 45 , - 38 , - 143, - 235, - 361, - 36 , - 36 , - 190, - 36 , - 36 , - 35 , - 44 , - 44 , - 307, - 36 , - 35 , - 346, - 325, - 45 , - 36 , - 190, - 45 , - 85 , - 52 , - 63 , - 145, - 44 , - 45 , - 190, - 36 , - 36 , - 63 , - 143, - 44 , - 45 , - 62 , - 45 , - 306, - 163, - 36 , - 36 , - 45 , - 45 , - 45 , - 36 , - 144, - 361, - 144, - 63 , - 44 , - 63 , - 62 , - 216, - 35 , - 361, - 66 , - 190, - 90 , - 45 , - 53 , - 181, - 45 , - 144 - ], - "committeeMembers": [ - "0x3c4bb9746e34b1de1504e36120566e1e6c08cf99", - "0x11dcb2ab3c7f1eabbb4ad999ed5133b2d9df19e8", - "0xcf8c9cf7112243174853131dce8f056b99436bc9", - "0x0a06793977f88df1fb1fa07b39827d45286ed824", - "0xb3eb9f2a2b97df7ad28eb555f57a95e9c2185dfd", - "0xe83139f8035782f81f7d0bd9e699b4c0d432e54e", - "0xf2291476eeace1a3635dd5af2b3f9c97445010c0", - "0xb13c5fa160ea8ae68b94ce3c7b094bc6f63e4d75", - "0x026c9dd4138fb9a57bb1b09df857c0baa9cf12e2", - "0x76be3e48ef4bf913043abc988f5bf2a957034ab1", - "0x961f54b098707cd5333be4273f2ad008c0651e8a", - "0xe1e0577ee91c15103a07253691ed8b6b1bdc5d43", - "0xf0a1b239fe1007f588b2858345325e458cb7edfa", - "0x7182aefac88f05824be3a3503b4c495772d2a6ee", - "0x68ffa898a4c2d47dbcba5f2ed657a794262afb98", - "0x53eee3aac9b6bac48b5592a65388a8d0ccb91658", - "0x109eaca0787554d2f90b2885237a041a5fa3b2b7", - "0x8d323bf2f88951bc26c56885d5ae8ab234dba1a0", - "0xd169738b050b00e311f831b11f87f34cc00ee9a0", - "0x8f06d35464056c0c1f97a18a20a6b4a2fa162971", - "0x62fa8a8f4635805e37be34499c1f2e8b2b7cb8b8", - "0xf60ad09a6e5c4b857b590826eb6e3092738f98e4", - "0xc210088c0e21c82d88a08315339f359c4896ad40", - "0xad392d73ad5580aa3d59fe1356d6e64b7c15aae5", - "0x61665fbc23b80cbac5b642a99949d78e0da11412", - "0x26414dea5b759979f51e00a5070868b689816873", - "0x54f6c7b3fa3dae227c2155768f9ff9f0c5596272", - "0x23c832dfec137dca7aab36b6340d90bef923176b", - "0xec7892559aa5420c70260ee9bf7a2906d9e2c6cb", - "0x3e07992486fdc6aa74df4cd98bb53bfe57852c9e", - "0x69f2059eb1a16fcb6e21d9e7dc6f723f52273274", - "0x1b71a39f44e120bdf006fa47b513d2be40b71909", - "0xe89f046ed1d862de58a3485eab6ee3da679b7c6f", - "0x418cc69e3328812c495da38451803c17b1970868", - "0x58c3776166d8f5a930500b113173644fb8779b70", - "0x3ad19e39c6c1c937bc208bbaeb5afcb695c4c389", - "0xdcc7eed9abd905f9906656dd79d66d087b7dfbcc", - "0xb3497f1ab77cfcd21230d451aac484662ab62eb7", - "0xc70cc492065eb3fbac51df1788c4ea7966166a69", - "0xae7a979920ca07f9437529a2233d7b305d1fca8e", - "0xbf83d481de21cc3e1cb9df2856b2b38acf22a7a8", - "0xce61edc7447874e995b34d8db874356be5acd01a", - "0x43da051e28e57b3f012fa2acbb976f953bf690da", - "0x8a4647514e084e206c8d0d6ef47e2828ee8cb018", - "0xaca7883582faf987c8fe8993312943b6a6aacd83", - "0x15d55cae8b977bbab14aad7a62d33d468982452e", - "0x125ceb827a33bd600c8632a2bc9187fd1e349a93", - "0xfb74e2e1a06f305216e5b5601e79a674f8bcf670", - "0x6137dd045c68776b43078205ce7ab4ba4591bf90", - "0x89c19574412044df06b574f5d69ebccaa2b7cf42", - "0xc31008536661ba6b3535012b21dc2a1adda8fa66", - "0xd4d8f18e406a322c434bfe5be2d109a9a882085c", - "0x077598d65ede259787efa2eb77715a142bc999f6", - "0xe472b10d254f4a4bc9ecbece9b065318ede9ffd4", - "0x1f88299e0956772f987f3af094dd1a20f9601097", - "0x8e1d7a24b9951d75fe8d857b7e9b1c2363eadbd6", - "0x39a27b962120df88f527a05ae1fb83338e413576", - "0x67a825d9faba1050d7930ffbccadf69b497cb4ed", - "0xc4abd2654784f566c6188df512ad215509d7a45c", - "0xcd85e9de4abada02f91d49ddd870688a005745be", - "0x6daca288dcc9d68c7000ddc15bf892c4ef3b222d", - "0x25aeb2387dbab8f45ef71b2d5239aa3d49537d6b", - "0xe44f1eae1c8ce239c97c1a2e7b0d24e83963e6ef", - "0x25968e14d82e0757924cbae3d4f678a460de68d5", - "0xc3fe6e51c1886d079fffa81b378d200dd464a32c", - "0x29ca318d8d69dc0047a2e0bfe345beecb24965c1" - ], - "minCommitteeStakeRequired": 7600, - "sourceChainId": 11, - "supportedChainIDs": [1], - "supportedChainLimitsInDollars": [18446744073709551615], - "supportedTokens": ["0x0000000000000000000000000000000000000000", "0xBe9566f1bc9a6a18ad1ed5620Ccb76ff639534d5", "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14", "0x8140EBa492e02Dbf137080E2E4eC0Bd3e10784a0", "0x4E9D6D3dbFFc32399D514A5a03268e5860b6769d"], - "tokenPrices": [100000000, 7000000000000, 400000000000, 100000000, 100000000], - "wETHAddress": "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14" -} diff --git a/bridge/evm/foundry.toml b/bridge/evm/foundry.toml deleted file mode 100644 index 8b0205893c6..00000000000 --- a/bridge/evm/foundry.toml +++ /dev/null @@ -1,22 +0,0 @@ -[profile.default] -src = 'contracts' -test = 'test' -no_match_test = "testSkip" -out = 'out' -libs = ['lib'] -solc = "0.8.20" -build_info = true -extra_output = ["storageLayout"] -fs_permissions = [{ access = "read", path = "/" }] -gas_reports = ["IotaBridge"] -[fmt] -line_length = 100 -[fuzz] -runs = 1000 -[rpc_endpoints] -mainnet = "${MAINNET_RPC_URL}" -sepolia = "${SEPOLIA_RPC_URL}" -anvil = "http://localhost:8545" -[etherscan] -sepolia = { key = "${ETHERSCAN_API_KEY}" } -mainnet = { key = "${ETHERSCAN_API_KEY}" } diff --git a/bridge/evm/remappings.txt b/bridge/evm/remappings.txt deleted file mode 100644 index 5279b569511..00000000000 --- a/bridge/evm/remappings.txt +++ /dev/null @@ -1,5 +0,0 @@ -@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ -@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ -@openzeppelin/openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/ -ds-test/=lib/forge-std/lib/ds-test/src/ -forge-std/=lib/openzeppelin-foundry-upgrades/lib/forge-std/src/ \ No newline at end of file diff --git a/bridge/evm/script/deploy_bridge.s.sol b/bridge/evm/script/deploy_bridge.s.sol deleted file mode 100644 index 5397073c3e8..00000000000 --- a/bridge/evm/script/deploy_bridge.s.sol +++ /dev/null @@ -1,196 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/Script.sol"; -// import "openzeppelin-foundry-upgrades/Upgrades.sol"; -import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import "openzeppelin-foundry-upgrades/Options.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; -import "../contracts/BridgeCommittee.sol"; -import "../contracts/BridgeVault.sol"; -import "../contracts/BridgeConfig.sol"; -import "../contracts/BridgeLimiter.sol"; -import "../contracts/IotaBridge.sol"; -import "../test/mocks/MockTokens.sol"; - -contract DeployBridge is Script { - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(deployerPrivateKey); - string memory chainID = Strings.toString(block.chainid); - bytes32 chainIDHash = keccak256(abi.encode(chainID)); - bool isLocal = chainIDHash != keccak256(abi.encode("11155111")) - && chainIDHash != keccak256(abi.encode("1")); - string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/deploy_configs/", chainID, ".json"); - // If this is local deployment, we override the path if OVERRIDE_CONFIG_PATH is set. - // This is useful in integration tests where config path is not fixed. - if (isLocal) { - path = vm.envOr("OVERRIDE_CONFIG_PATH", path); - } - - console.log("config path: ", path); - string memory json = vm.readFile(path); - bytes memory bytesJson = vm.parseJson(json); - DeployConfig memory deployConfig = abi.decode(bytesJson, (DeployConfig)); - - // if deploying to local network, deploy mock tokens - if (isLocal) { - console.log("Deploying mock tokens for local network"); - // deploy WETH - deployConfig.WETH = address(new WETH()); - - // deploy mock tokens - MockWBTC wBTC = new MockWBTC(); - MockUSDC USDC = new MockUSDC(); - MockUSDT USDT = new MockUSDT(); - MockKA KA = new MockKA(); - console.log("[Deployed] KA:", address(KA)); - - // update deployConfig with mock addresses - deployConfig.supportedTokens = new address[](5); - // In BridgeConfig.sol `supportedTokens is shifted by one - // and the first token is IOTA. - deployConfig.supportedTokens[0] = address(0); - deployConfig.supportedTokens[1] = address(wBTC); - deployConfig.supportedTokens[2] = deployConfig.WETH; - deployConfig.supportedTokens[3] = address(USDC); - deployConfig.supportedTokens[4] = address(USDT); - } - - // TODO: validate config values before deploying - - // convert supported chains from uint256 to uint8[] - uint8[] memory supportedChainIDs = new uint8[](deployConfig.supportedChainIDs.length); - for (uint256 i; i < deployConfig.supportedChainIDs.length; i++) { - supportedChainIDs[i] = uint8(deployConfig.supportedChainIDs[i]); - } - - // deploy bridge config - // price of IOTA (id = 0) should not be included in tokenPrices - require( - deployConfig.supportedTokens.length == deployConfig.tokenPrices.length, - "supportedTokens.length + 1 != tokenPrices.length" - ); - - // deploy Bridge Committee =================================================================== - - // convert committeeMembers stake from uint256 to uint16[] - uint16[] memory committeeMemberStake = - new uint16[](deployConfig.committeeMemberStake.length); - for (uint256 i; i < deployConfig.committeeMemberStake.length; i++) { - committeeMemberStake[i] = uint16(deployConfig.committeeMemberStake[i]); - } - - Options memory opts; - opts.unsafeSkipAllChecks = true; - - address bridgeCommittee = Upgrades.deployUUPSProxy( - "BridgeCommittee.sol", - abi.encodeCall( - BridgeCommittee.initialize, - ( - deployConfig.committeeMembers, - committeeMemberStake, - uint16(deployConfig.minCommitteeStakeRequired) - ) - ), - opts - ); - - // deploy bridge config ===================================================================== - - // convert token prices from uint256 to uint64 - uint64[] memory tokenPrices = new uint64[](deployConfig.tokenPrices.length); - for (uint256 i; i < deployConfig.tokenPrices.length; i++) { - tokenPrices[i] = uint64(deployConfig.tokenPrices[i]); - } - - address bridgeConfig = Upgrades.deployUUPSProxy( - "BridgeConfig.sol", - abi.encodeCall( - BridgeConfig.initialize, - ( - address(bridgeCommittee), - uint8(deployConfig.sourceChainId), - deployConfig.supportedTokens, - tokenPrices, - supportedChainIDs - ) - ), - opts - ); - - // initialize config in the bridge committee - BridgeCommittee(bridgeCommittee).initializeConfig(address(bridgeConfig)); - - // deploy vault ============================================================================= - - BridgeVault vault = new BridgeVault(deployConfig.WETH); - - // deploy limiter =========================================================================== - - // convert chain limits from uint256 to uint64[] - uint64[] memory chainLimits = - new uint64[](deployConfig.supportedChainLimitsInDollars.length); - for (uint256 i; i < deployConfig.supportedChainLimitsInDollars.length; i++) { - chainLimits[i] = uint64(deployConfig.supportedChainLimitsInDollars[i]); - } - - address limiter = Upgrades.deployUUPSProxy( - "BridgeLimiter.sol", - abi.encodeCall( - BridgeLimiter.initialize, (bridgeCommittee, supportedChainIDs, chainLimits) - ), - opts - ); - - uint8[] memory _destinationChains = new uint8[](1); - _destinationChains[0] = 1; - - // deploy IOTA Bridge ======================================================================== - - address iotaBridge = Upgrades.deployUUPSProxy( - "IotaBridge.sol", - abi.encodeCall(IotaBridge.initialize, (bridgeCommittee, address(vault), limiter)), - opts - ); - - // transfer vault ownership to bridge - vault.transferOwnership(iotaBridge); - // transfer limiter ownership to bridge - BridgeLimiter instance = BridgeLimiter(limiter); - instance.transferOwnership(iotaBridge); - - // print deployed addresses for post deployment setup - console.log("[Deployed] BridgeConfig:", bridgeConfig); - console.log("[Deployed] IotaBridge:", iotaBridge); - console.log("[Deployed] BridgeLimiter:", limiter); - console.log("[Deployed] BridgeCommittee:", bridgeCommittee); - console.log("[Deployed] BridgeVault:", address(vault)); - console.log("[Deployed] BTC:", BridgeConfig(bridgeConfig).tokenAddressOf(1)); - console.log("[Deployed] ETH:", BridgeConfig(bridgeConfig).tokenAddressOf(2)); - console.log("[Deployed] USDC:", BridgeConfig(bridgeConfig).tokenAddressOf(3)); - console.log("[Deployed] USDT:", BridgeConfig(bridgeConfig).tokenAddressOf(4)); - - vm.stopBroadcast(); - } - - // used to ignore for forge coverage - function testSkip() public {} -} - -/// check the following for guidelines on updating deploy_configs and references: -/// https://book.getfoundry.sh/cheatcodes/parse-json -struct DeployConfig { - uint256[] committeeMemberStake; - address[] committeeMembers; - uint256 minCommitteeStakeRequired; - uint256 sourceChainId; - uint256[] supportedChainIDs; - uint256[] supportedChainLimitsInDollars; - address[] supportedTokens; - uint256[] tokenPrices; - address WETH; -} diff --git a/bridge/evm/test/BridgeBaseTest.t.sol b/bridge/evm/test/BridgeBaseTest.t.sol deleted file mode 100644 index 188a41688d2..00000000000 --- a/bridge/evm/test/BridgeBaseTest.t.sol +++ /dev/null @@ -1,154 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/Test.sol"; -import "../contracts/BridgeCommittee.sol"; -import "../contracts/BridgeVault.sol"; -import "../contracts/BridgeLimiter.sol"; -import "../contracts/IotaBridge.sol"; -import "../contracts/BridgeConfig.sol"; - -contract BridgeBaseTest is Test { - address committeeMemberA; - address committeeMemberB; - address committeeMemberC; - address committeeMemberD; - address committeeMemberE; - - uint256 committeeMemberPkA; - uint256 committeeMemberPkB; - uint256 committeeMemberPkC; - uint256 committeeMemberPkD; - uint256 committeeMemberPkE; - - address bridgerA; - address bridgerB; - address bridgerC; - - address deployer; - - // token addresses on mainnet - address wETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; - address wBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; - - address wBTCWhale = 0x6daB3bCbFb336b29d06B9C793AEF7eaA57888922; - address USDCWhale = 0x51eDF02152EBfb338e03E30d65C15fBf06cc9ECC; - address USDTWhale = 0xa7C0D36c4698981FAb42a7d8c783674c6Fe2592d; - - uint64 USD_VALUE_MULTIPLIER = 100000000; // 8 DP accuracy - - uint64 IOTA_PRICE = 1_28000000; - uint64 BTC_PRICE = 43251_89000000; - uint64 ETH_PRICE = 2596_96000000; - uint64 USDC_PRICE = 1_00000000; - uint64[] tokenPrices; - address[] supportedTokens; - uint8[] supportedChains; - - uint8 public chainID = 1; - uint64 totalLimit = 1_000_000 * USD_VALUE_MULTIPLIER; - uint16 minStakeRequired = 10000; - - BridgeCommittee public committee; - IotaBridge public bridge; - BridgeVault public vault; - BridgeLimiter public limiter; - BridgeConfig public config; - - function setUpBridgeTest() public { - vm.createSelectFork( - string.concat("https://mainnet.infura.io/v3/", vm.envString("INFURA_API_KEY")) - ); - (committeeMemberA, committeeMemberPkA) = makeAddrAndKey("a"); - (committeeMemberB, committeeMemberPkB) = makeAddrAndKey("b"); - (committeeMemberC, committeeMemberPkC) = makeAddrAndKey("c"); - (committeeMemberD, committeeMemberPkD) = makeAddrAndKey("d"); - (committeeMemberE, committeeMemberPkE) = makeAddrAndKey("e"); - bridgerA = makeAddr("bridgerA"); - bridgerB = makeAddr("bridgerB"); - bridgerC = makeAddr("bridgerC"); - vm.deal(committeeMemberA, 1 ether); - vm.deal(committeeMemberB, 1 ether); - vm.deal(committeeMemberC, 1 ether); - vm.deal(committeeMemberD, 1 ether); - vm.deal(committeeMemberE, 1 ether); - vm.deal(bridgerA, 1 ether); - vm.deal(bridgerB, 1 ether); - deployer = address(1); - vm.startPrank(deployer); - - // deploy committee ===================================================================== - committee = new BridgeCommittee(); - address[] memory _committee = new address[](5); - uint16[] memory _stake = new uint16[](5); - _committee[0] = committeeMemberA; - _committee[1] = committeeMemberB; - _committee[2] = committeeMemberC; - _committee[3] = committeeMemberD; - _committee[4] = committeeMemberE; - _stake[0] = 1000; - _stake[1] = 1000; - _stake[2] = 1000; - _stake[3] = 2002; - _stake[4] = 4998; - - committee.initialize(_committee, _stake, minStakeRequired); - - // deploy config ===================================================================== - config = new BridgeConfig(); - supportedTokens = new address[](5); - supportedTokens[0] = address(0); - supportedTokens[1] = wBTC; - supportedTokens[2] = wETH; - supportedTokens[3] = USDC; - supportedTokens[4] = USDT; - supportedChains = new uint8[](1); - supportedChains[0] = 0; - tokenPrices = new uint64[](5); - tokenPrices[0] = IOTA_PRICE; - tokenPrices[1] = BTC_PRICE; - tokenPrices[2] = ETH_PRICE; - tokenPrices[3] = USDC_PRICE; - tokenPrices[4] = USDC_PRICE; - - config.initialize( - address(committee), chainID, supportedTokens, tokenPrices, supportedChains - ); - - // initialize config in the bridge committee - committee.initializeConfig(address(config)); - - // deploy vault ===================================================================== - - vault = new BridgeVault(wETH); - - // deploy limiter ===================================================================== - - limiter = new BridgeLimiter(); - uint64[] memory chainLimits = new uint64[](1); - chainLimits[0] = totalLimit; - limiter.initialize(address(committee), supportedChains, chainLimits); - - // deploy bridge ===================================================================== - - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - vault.transferOwnership(address(bridge)); - limiter.transferOwnership(address(bridge)); - } - - function testSkip() public {} - - // Helper function to get the signature components from an address - function getSignature(bytes32 digest, uint256 privateKey) public pure returns (bytes memory) { - // r and s are the outputs of the ECDSA signature - // r,s and v are packed into the signature. It should be 65 bytes: 32 + 32 + 1 - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - - // pack v, r, s into 65bytes signature - return abi.encodePacked(r, s, v); - } -} diff --git a/bridge/evm/test/BridgeCommitteeTest.t.sol b/bridge/evm/test/BridgeCommitteeTest.t.sol deleted file mode 100644 index d8a012f0521..00000000000 --- a/bridge/evm/test/BridgeCommitteeTest.t.sol +++ /dev/null @@ -1,486 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./BridgeBaseTest.t.sol"; -import "../contracts/utils/BridgeUtils.sol"; - -contract BridgeCommitteeTest is BridgeBaseTest { - // This function is called before each unit test - function setUp() public { - setUpBridgeTest(); - } - - function testBridgeCommitteeInitialization() public { - assertEq(committee.committeeStake(committeeMemberA), 1000); - assertEq(committee.committeeStake(committeeMemberB), 1000); - assertEq(committee.committeeStake(committeeMemberC), 1000); - assertEq(committee.committeeStake(committeeMemberD), 2002); - assertEq(committee.committeeStake(committeeMemberE), 4998); - // Assert that the total stake is 10,000 - assertEq( - committee.committeeStake(committeeMemberA) + committee.committeeStake(committeeMemberB) - + committee.committeeStake(committeeMemberC) - + committee.committeeStake(committeeMemberD) - + committee.committeeStake(committeeMemberE), - 10000 - ); - // Check that the blocklist and nonces are initialized to zero - assertEq(committee.blocklist(address(committeeMemberA)), false); - assertEq(committee.blocklist(address(committeeMemberB)), false); - assertEq(committee.blocklist(address(committeeMemberC)), false); - assertEq(committee.blocklist(address(committeeMemberD)), false); - assertEq(committee.blocklist(address(committeeMemberE)), false); - assertEq(committee.nonces(0), 0); - assertEq(committee.nonces(1), 0); - assertEq(committee.nonces(2), 0); - assertEq(committee.nonces(3), 0); - assertEq(committee.nonces(4), 0); - } - - function testBridgeCommitteeInitializationLength() public { - BridgeCommittee _committee = new BridgeCommittee(); - address[] memory _committeeMembers = new address[](256); - - for (uint160 i = 0; i < 256; i++) { - _committeeMembers[i] = address(i); - } - - vm.expectRevert(bytes("BridgeCommittee: Committee length must be less than 256")); - _committee.initialize(_committeeMembers, new uint16[](256), minStakeRequired); - } - - function testBridgeCommitteeInitializeConfig() public { - vm.expectRevert(bytes("BridgeCommittee: Config already initialized")); - // Initialize the committee with the config contract - committee.initializeConfig(address(101)); - } - - function testBridgeFailInitialization() public { - // Test fail initialize: Committee Duplicate Committee Member - BridgeCommittee _committee = new BridgeCommittee(); - address[] memory _committeeDuplicateCommitteeMember = new address[](5); - _committeeDuplicateCommitteeMember[0] = committeeMemberA; - _committeeDuplicateCommitteeMember[1] = committeeMemberB; - _committeeDuplicateCommitteeMember[2] = committeeMemberC; - _committeeDuplicateCommitteeMember[3] = committeeMemberD; - _committeeDuplicateCommitteeMember[4] = committeeMemberA; - - uint16[] memory _stakeDuplicateCommitteeMember = new uint16[](5); - _stakeDuplicateCommitteeMember[0] = 1000; - _stakeDuplicateCommitteeMember[1] = 1000; - _stakeDuplicateCommitteeMember[2] = 1000; - _stakeDuplicateCommitteeMember[3] = 2002; - _stakeDuplicateCommitteeMember[4] = 1000; - - vm.expectRevert(bytes("BridgeCommittee: Duplicate committee member")); - _committee.initialize( - _committeeDuplicateCommitteeMember, _stakeDuplicateCommitteeMember, minStakeRequired - ); - - address[] memory _committeeNotSameLength = new address[](5); - _committeeNotSameLength[0] = committeeMemberA; - _committeeNotSameLength[1] = committeeMemberB; - _committeeNotSameLength[2] = committeeMemberC; - _committeeNotSameLength[3] = committeeMemberD; - _committeeNotSameLength[4] = committeeMemberE; - - uint16[] memory _stakeNotSameLength = new uint16[](4); - _stakeNotSameLength[0] = 1000; - _stakeNotSameLength[1] = 1000; - _stakeNotSameLength[2] = 1000; - _stakeNotSameLength[3] = 2002; - - vm.expectRevert( - bytes("BridgeCommittee: Committee and stake arrays must be of the same length") - ); - - _committee.initialize(_committeeNotSameLength, _stakeNotSameLength, minStakeRequired); - } - - function testVerifySignaturesWithValidSignatures() public { - // Create a message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 1, - chainID: chainID, - payload: "0x0" - }); - - bytes memory messageBytes = BridgeUtils.encodeMessage(message); - - bytes32 messageHash = keccak256(messageBytes); - - bytes[] memory signatures = new bytes[](4); - - // Create signatures from A - D - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - // Call the verifySignatures function and it would not revert - committee.verifySignatures(signatures, message); - } - - function testVerifySignaturesWithInvalidSignatures() public { - // Create a message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 1, - chainID: chainID, - payload: "0x0" - }); - - bytes memory messageBytes = BridgeUtils.encodeMessage(message); - - bytes32 messageHash = keccak256(messageBytes); - - bytes[] memory signatures = new bytes[](3); - - // Create signatures from A - D - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - - // Call the verifySignatures function and expect it to revert - vm.expectRevert(bytes("BridgeCommittee: Insufficient stake amount")); - committee.verifySignatures(signatures, message); - } - - function testVerifySignaturesDuplicateSignature() public { - // Create a message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 1, - chainID: chainID, - payload: "0x0" - }); - - bytes memory messageBytes = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(messageBytes); - - bytes[] memory signatures = new bytes[](4); - - // Create signatures from A - C - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkA); - signatures[2] = getSignature(messageHash, committeeMemberPkB); - signatures[3] = getSignature(messageHash, committeeMemberPkC); - - // Call the verifySignatures function and expect it to revert - vm.expectRevert(bytes("BridgeCommittee: Duplicate signature provided")); - committee.verifySignatures(signatures, message); - } - - function testFailUpdateBlocklistWithSignaturesInvalidNonce() public { - // create payload - address[] memory _blocklist = new address[](1); - _blocklist[0] = committeeMemberA; - bytes memory payload = abi.encode(uint8(0), _blocklist); - - // Create a message with wrong nonce - BridgeUtils.Message memory messageWrongNonce = BridgeUtils.Message({ - messageType: BridgeUtils.BLOCKLIST, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - bytes memory messageBytes = BridgeUtils.encodeMessage(messageWrongNonce); - bytes32 messageHash = keccak256(messageBytes); - bytes[] memory signatures = new bytes[](4); - - // Create signatures from A - D - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("BridgeCommittee: Invalid nonce")); - committee.updateBlocklistWithSignatures(signatures, messageWrongNonce); - } - - function testUpdateBlocklistWithSignaturesMessageDoesNotMatchType() public { - // create payload - address[] memory _blocklist = new address[](1); - _blocklist[0] = committeeMemberA; - bytes memory payload = abi.encode(uint8(0), _blocklist); - - // Create a message with wrong messageType - BridgeUtils.Message memory messageWrongMessageType = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - bytes memory messageBytes = BridgeUtils.encodeMessage(messageWrongMessageType); - bytes32 messageHash = keccak256(messageBytes); - bytes[] memory signatures = new bytes[](4); - - // Create signatures from A - D - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("MessageVerifier: message does not match type")); - committee.updateBlocklistWithSignatures(signatures, messageWrongMessageType); - } - - function testFailUpdateBlocklistWithSignaturesInvalidSignatures() public { - // create payload - address[] memory _blocklist = new address[](1); - _blocklist[0] = committeeMemberA; - bytes memory payload = abi.encode(uint8(0), _blocklist); - - // Create a message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.BLOCKLIST, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - bytes memory messageBytes = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(messageBytes); - bytes[] memory signatures = new bytes[](4); - - // Create signatures from A - signatures[0] = getSignature(messageHash, committeeMemberPkA); - vm.expectRevert(bytes("BridgeCommittee: Invalid signatures")); - committee.updateBlocklistWithSignatures(signatures, message); - } - - function testAddToBlocklist() public { - // create payload - address[] memory _blocklist = new address[](1); - _blocklist[0] = committeeMemberA; - bytes memory payload = hex"0001"; - payload = abi.encodePacked(payload, committeeMemberA); - - // Create a message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.BLOCKLIST, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - - bytes memory messageBytes = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(messageBytes); - bytes[] memory signatures = new bytes[](4); - - // Create signatures from A - D - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - committee.updateBlocklistWithSignatures(signatures, message); - - assertTrue(committee.blocklist(committeeMemberA)); - - // update message - message.nonce = 1; - // reconstruct signatures - messageBytes = BridgeUtils.encodeMessage(message); - messageHash = keccak256(messageBytes); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - // verify CommitteeMemberA's signature is no longer valid - vm.expectRevert(bytes("BridgeCommittee: Signer is blocklisted")); - // re-verify signatures - committee.verifySignatures(signatures, message); - } - - function testSignerNotCommitteeMember() public { - // create payload - bytes memory payload = abi.encode(committeeMemberA); - - // Create a message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - - bytes memory messageBytes = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(messageBytes); - bytes[] memory signatures = new bytes[](4); - - (, uint256 committeeMemberPkF) = makeAddrAndKey("f"); - - // Create signatures from A - D, and F - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkF); - - vm.expectRevert(bytes("BridgeCommittee: Signer has no stake")); - committee.verifySignatures(signatures, message); - } - - function testRemoveFromBlocklist() public { - testAddToBlocklist(); - - // create payload - address[] memory _blocklist = new address[](1); - _blocklist[0] = committeeMemberA; - bytes memory payload = hex"0101"; - payload = abi.encodePacked(payload, committeeMemberA); - - // Create a message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.BLOCKLIST, - version: 1, - nonce: 1, - chainID: chainID, - payload: payload - }); - - bytes memory messageBytes = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(messageBytes); - bytes[] memory signatures = new bytes[](4); - - // Create signatures from B - E - signatures[0] = getSignature(messageHash, committeeMemberPkB); - signatures[1] = getSignature(messageHash, committeeMemberPkC); - signatures[2] = getSignature(messageHash, committeeMemberPkD); - signatures[3] = getSignature(messageHash, committeeMemberPkE); - - committee.updateBlocklistWithSignatures(signatures, message); - - // verify CommitteeMemberA is no longer blocklisted - assertFalse(committee.blocklist(committeeMemberA)); - } - - // An e2e update committee blocklist regression test covering message ser/de - function testUpdateCommitteeBlocklistRegressionTest() public { - address[] memory _committee = new address[](4); - uint16[] memory _stake = new uint16[](4); - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - - bytes memory payload = - hex"010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5"; - // Create blocklist message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.BLOCKLIST, - version: 1, - nonce: 68, - chainID: 2, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d4553534147450101000000000000004402010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5"; - - assertEq(encodedMessage, expectedEncodedMessage); - } - - // An e2e update committee blocklist regression test covering message ser/de and signature verification - function testUpdateCommitteeBlocklistRegressionTestWithSignatures() public { - address[] memory _committee = new address[](4); - uint16[] memory _stake = new uint16[](4); - uint8 chainID = 11; - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - config = new BridgeConfig(); - config.initialize( - address(committee), chainID, supportedTokens, tokenPrices, supportedChains - ); - - committee.initializeConfig(address(config)); - - assertEq(committee.blocklist(0x68B43fD906C0B8F024a18C56e06744F7c6157c65), false); - - // blocklist 1 member 02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4 ("0x68B43fD906C0B8F024a18C56e06744F7c6157c65") - bytes memory payload = hex"000168b43fd906c0b8f024a18c56e06744f7c6157c65"; - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.BLOCKLIST, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d455353414745010100000000000000000b000168b43fd906c0b8f024a18c56e06744f7c6157c65"; - - assertEq(encodedMessage, expectedEncodedMessage); - - bytes[] memory signatures = new bytes[](3); - - signatures[0] = - hex"add4b78733cc1cbf4e50b7f6b60c60370ed43fd57e016f478d49ed5050960c6b0099fc474e4ac92a5f260bd35e2a5870a2ec515897f2b0222ef601658210823400"; - signatures[1] = - hex"7d16301c6ed6de65d9276f6135511102ff2917a97e5ca9fd2bf5a04fa80b0b4530818c3aec19d8da4331b2d5bac08e502507c0ce4e3e60cf9f993196f2123b7e01"; - signatures[2] = - hex"753ae3fc2c22c7254cc9418461345a0bd9c83528d7b2988f03976b839a01e2532b91eefa5cfeeb209cf520329f89ad490cd752cfc9faad1d15df408093b23cd001"; - - committee.verifySignatures(signatures, message); - - committee.updateBlocklistWithSignatures(signatures, message); - - assertEq(committee.blocklist(0x68B43fD906C0B8F024a18C56e06744F7c6157c65), true); - - // unblocklist 1 member 02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4 ("0x68B43fD906C0B8F024a18C56e06744F7c6157c65") - payload = hex"010168b43fd906c0b8f024a18c56e06744f7c6157c65"; - message = BridgeUtils.Message({ - messageType: BridgeUtils.BLOCKLIST, - version: 1, - nonce: 1, - chainID: chainID, - payload: payload - }); - encodedMessage = BridgeUtils.encodeMessage(message); - expectedEncodedMessage = - hex"5355495f4252494447455f4d455353414745010100000000000000010b010168b43fd906c0b8f024a18c56e06744f7c6157c65"; - - assertEq(encodedMessage, expectedEncodedMessage); - - signatures = new bytes[](3); - - // Note sig[0] is from blocklisetd validator, and it does not count. - signatures[0] = - hex"2b62c1b5e17de7f4baeec0f1e9c01107b799edb3070c2c1f00e41c9c1eb550c82ce168d2d635fd8b6999b6bd8f8ec31bcc86d4b13dc094c713c7f0f111d21dad00"; - signatures[1] = - hex"0fc3cc67cb21dac7b7a5ef93a54b9e7b1057cab45cf62b8bd0f6dd217cf99f001d1ebdcf2751ec95d24829403b87ba6f0e603ebf6d98595048474837f9c40a8c00"; - signatures[2] = - hex"62b36dab0d2c10f74d84b5f9838435c396cca1f3c4939eb4df82d1c72430e7ec2a030a980a9514beaeda6dffdc5e177b7edbd18543979f488d8fd09dba753a5500"; - - vm.expectRevert(bytes("BridgeCommittee: Signer is blocklisted")); - committee.verifySignatures(signatures, message); - - // use sig from a unblocklisted validator - signatures[0] = - hex"5f2bef9593c37589c18519e2b97c735e60e3ef26471d07e804fb259ed75beb7e0ad180d932ef8af6885a544ded4e372d75451667c238d8e7215454f8bdbebd3401"; - committee.verifySignatures(signatures, message); - committee.updateBlocklistWithSignatures(signatures, message); - assertEq(committee.blocklist(0x68B43fD906C0B8F024a18C56e06744F7c6157c65), false); - } -} diff --git a/bridge/evm/test/BridgeConfigTest.t.sol b/bridge/evm/test/BridgeConfigTest.t.sol deleted file mode 100644 index d3006d2143d..00000000000 --- a/bridge/evm/test/BridgeConfigTest.t.sol +++ /dev/null @@ -1,503 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./BridgeBaseTest.t.sol"; -import "./mocks/MockTokens.sol"; - -contract BridgeConfigTest is BridgeBaseTest { - function setUp() public { - setUpBridgeTest(); - } - - function testBridgeConfigInitialization() public { - assertTrue(config.tokenAddressOf(1) == wBTC); - assertTrue(config.tokenAddressOf(2) == wETH); - assertTrue(config.tokenAddressOf(3) == USDC); - assertTrue(config.tokenAddressOf(4) == USDT); - assertEq(config.tokenIotaDecimalOf(0), 9); - assertEq(config.tokenIotaDecimalOf(1), 8); - assertEq(config.tokenIotaDecimalOf(2), 8); - assertEq(config.tokenIotaDecimalOf(3), 6); - assertEq(config.tokenIotaDecimalOf(4), 6); - assertEq(config.tokenPriceOf(0), IOTA_PRICE); - assertEq(config.tokenPriceOf(1), BTC_PRICE); - assertEq(config.tokenPriceOf(2), ETH_PRICE); - assertEq(config.tokenPriceOf(3), USDC_PRICE); - assertEq(config.tokenPriceOf(4), USDC_PRICE); - assertEq(config.chainID(), chainID); - assertTrue(config.supportedChains(0)); - } - - function testGetAddress() public { - assertEq(config.tokenAddressOf(1), wBTC); - } - - function testIsTokenSupported() public { - assertTrue(config.isTokenSupported(1)); - assertTrue(!config.isTokenSupported(0)); - } - - function testTokenIotaDecimalOf() public { - assertEq(config.tokenIotaDecimalOf(1), 8); - } - - function testAddTokensWithSignatures() public { - MockUSDC _newToken = new MockUSDC(); - - // Create update tokens payload - bool _isNative = true; - uint8 _numTokenIDs = 1; - uint8 tokenID1 = 10; - uint8 _numAddresses = 1; - address address1 = address(_newToken); - uint8 _numIotaDecimals = 1; - uint8 iotaDecimal1 = 6; - uint8 _numPrices = 1; - uint64 price1 = 100_000 * USD_VALUE_MULTIPLIER; - - bytes memory payload = abi.encodePacked( - _isNative, - _numTokenIDs, - tokenID1, - _numAddresses, - address1, - _numIotaDecimals, - iotaDecimal1, - _numPrices, - price1 - ); - - // Create transfer message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.ADD_EVM_TOKENS, - version: 1, - nonce: 0, - chainID: 1, - payload: payload - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - // test token ID 10 is not supported - assertFalse(config.isTokenSupported(10)); - config.addTokensWithSignatures(signatures, message); - assertTrue(config.isTokenSupported(10)); - assertEq(config.tokenAddressOf(10), address1); - assertEq(config.tokenIotaDecimalOf(10), 6); - assertEq(config.tokenPriceOf(10), 100_000 * USD_VALUE_MULTIPLIER); - } - - function testAddTokensAddressFailure() public { - MockUSDC _newToken = new MockUSDC(); - - // Create update tokens payload - bool _isNative = true; - uint8 _numTokenIDs = 1; - uint8 tokenID1 = 10; - uint8 _numAddresses = 1; - address address1 = address(0); - uint8 _numIotaDecimals = 1; - uint8 iotaDecimal1 = 6; - uint8 _numPrices = 1; - uint64 price1 = 100_000 * USD_VALUE_MULTIPLIER; - - bytes memory payload = abi.encodePacked( - _isNative, - _numTokenIDs, - tokenID1, - _numAddresses, - address1, - _numIotaDecimals, - iotaDecimal1, - _numPrices, - price1 - ); - - // Create Add evm token message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.ADD_EVM_TOKENS, - version: 1, - nonce: 0, - chainID: 1, - payload: payload - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - // address should fail because the address supplied in the message is 0 - vm.expectRevert(bytes("BridgeConfig: Invalid token address")); - config.addTokensWithSignatures(signatures, message); - } - - function testAddTokensIotaDecimalFailure() public { - MockUSDC _newToken = new MockUSDC(); - - // Create add tokens payload - bool _isNative = true; - uint8 _numTokenIDs = 1; - uint8 tokenID1 = 10; - uint8 _numAddresses = 1; - address address1 = address(_newToken); - uint8 _numIotaDecimals = 1; - uint8 iotaDecimal1 = 10; - uint8 _numPrices = 1; - uint64 price1 = 100_000 * USD_VALUE_MULTIPLIER; - - bytes memory payload = abi.encodePacked( - _isNative, - _numTokenIDs, - tokenID1, - _numAddresses, - address1, - _numIotaDecimals, - iotaDecimal1, - _numPrices, - price1 - ); - - // Create transfer message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.ADD_EVM_TOKENS, - version: 1, - nonce: 0, - chainID: 1, - payload: payload - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - // add token should fail because the iota decimal is greater than the eth decimal - vm.expectRevert(bytes("BridgeConfig: Invalid IOTA decimal")); - config.addTokensWithSignatures(signatures, message); - } - - function testAddTokensPriceFailure() public { - MockUSDC _newToken = new MockUSDC(); - - // Create update tokens payload - bool _isNative = true; - uint8 _numTokenIDs = 1; - uint8 tokenID1 = 10; - uint8 _numAddresses = 1; - address address1 = address(_newToken); - uint8 _numIotaDecimals = 1; - uint8 iotaDecimal1 = 10; - uint8 _numPrices = 1; - uint64 price1 = 0; - - bytes memory payload = abi.encodePacked( - _isNative, - _numTokenIDs, - tokenID1, - _numAddresses, - address1, - _numIotaDecimals, - iotaDecimal1, - _numPrices, - price1 - ); - - // Create transfer message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.ADD_EVM_TOKENS, - version: 1, - nonce: 0, - chainID: 1, - payload: payload - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - vm.expectRevert(bytes("BridgeConfig: Invalid token price")); - config.addTokensWithSignatures(signatures, message); - } - - function testUpdateTokenPriceWithSignatures() public { - // Create update tokens payload - uint8 tokenID = BridgeUtils.ETH; - uint64 price = 100_000 * USD_VALUE_MULTIPLIER; - - bytes memory payload = abi.encodePacked(tokenID, price); - - // Create transfer message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPDATE_TOKEN_PRICE, - version: 1, - nonce: 0, - chainID: 1, - payload: payload - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - // test ETH price - assertEq(config.tokenPriceOf(BridgeUtils.ETH), ETH_PRICE); - config.updateTokenPriceWithSignatures(signatures, message); - assertEq(config.tokenPriceOf(BridgeUtils.ETH), 100_000 * USD_VALUE_MULTIPLIER); - } - - // An e2e update token price regression test covering message ser/de - function testUpdateTokenPricesRegressionTest() public { - address[] memory _committee = new address[](4); - uint16[] memory _stake = new uint16[](4); - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - committee.initializeConfig(address(config)); - vault = new BridgeVault(wETH); - - uint64[] memory totalLimits = new uint64[](1); - totalLimits[0] = 1000000; - uint8[] memory _supportedDestinationChains = new uint8[](1); - _supportedDestinationChains[0] = 0; - skip(2 days); - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), _supportedDestinationChains, totalLimits); - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - vault.transferOwnership(address(bridge)); - limiter.transferOwnership(address(bridge)); - - bytes memory payload = hex"01000000003b9aca00"; - - // Create update token price message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPDATE_TOKEN_PRICE, - version: 1, - nonce: 266, - chainID: 3, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d4553534147450401000000000000010a0301000000003b9aca00"; - - assertEq(encodedMessage, expectedEncodedMessage); - } - - // An e2e update token price regression test covering message ser/de and signature verification - function testUpdateTokenPriceRegressionTestWithSigVerification() public { - address[] memory _committee = new address[](4); - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - uint8 sendingChainID = 1; - uint8[] memory _supportedChains = new uint8[](1); - _supportedChains[0] = sendingChainID; - uint8 chainID = 11; - uint16[] memory _stake = new uint16[](4); - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - uint8[] memory _supportedDestinationChains = new uint8[](1); - _supportedDestinationChains[0] = sendingChainID; - - config = new BridgeConfig(); - config.initialize( - address(committee), chainID, supportedTokens, tokenPrices, _supportedChains - ); - - committee.initializeConfig(address(config)); - - vault = new BridgeVault(wETH); - skip(2 days); - - uint64[] memory totalLimits = new uint64[](1); - totalLimits[0] = 1000000; - - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), _supportedDestinationChains, totalLimits); - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - vault.transferOwnership(address(bridge)); - limiter.transferOwnership(address(bridge)); - - // BTC -> 600_000_000 ($60k) - bytes memory payload = hex"010000000023c34600"; - - // Create update token price message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPDATE_TOKEN_PRICE, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d455353414745040100000000000000000b010000000023c34600"; - - assertEq(encodedMessage, expectedEncodedMessage); - - bytes[] memory signatures = new bytes[](3); - - signatures[0] = - hex"eb81068c2214c01bf5d89e6bd748c0d184ae68f74d365174657053af916dcd335960737eb724560a3481bb77b7df4169d8305a034143e1c749fd9f9bcda6cc1601"; - signatures[1] = - hex"116ad7d7bb705374328f85613020777d636fa092f98aa59a1d58f12f36d96f0e7aacfeb8ff356289da8d0d75278ccad8c19ec878db0b836f96ab544e91de1fed01"; - signatures[2] = - hex"b0229b50b0fe3fd4cdb05b31c7689d99e3181f9f11069cb457d73112985865ff504d9a9959c367d02b18b2d78312a012f194798499198410880351ab0a241a0c00"; - - committee.verifySignatures(signatures, message); - - config.updateTokenPriceWithSignatures(signatures, message); - assertEq(config.tokenPrices(BridgeUtils.BTC), 600_000_000); - } - - function testAddTokensRegressionTest() public { - address[] memory _committee = new address[](4); - uint16[] memory _stake = new uint16[](4); - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - uint8[] memory _supportedDestinationChains = new uint8[](1); - _supportedDestinationChains[0] = 0; - config = new BridgeConfig(); - config.initialize( - address(committee), 12, supportedTokens, tokenPrices, _supportedDestinationChains - ); - committee.initializeConfig(address(config)); - vault = new BridgeVault(wETH); - - uint64[] memory totalLimits = new uint64[](1); - totalLimits[0] = 1000000; - - skip(2 days); - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), _supportedDestinationChains, totalLimits); - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - vault.transferOwnership(address(bridge)); - limiter.transferOwnership(address(bridge)); - - bytes memory payload = - hex"0103636465036b175474e89094c44da98b954eedeac495271d0fae7ab96520de3a18e5e111b5eaab095312d7fe84c18360217d8f7ab5e7c516566761ea12ce7f9d720305060703000000003b9aca00000000007735940000000000b2d05e00"; - - ( - bool native, - uint8[] memory tokenIDs, - address[] memory tokenAddresses, - uint8[] memory iotaDecimals, - uint64[] memory tokenPrices - ) = BridgeUtils.decodeAddTokensPayload(payload); - - assertEq(native, true); - assertEq(tokenIDs.length, 3); - assertEq(tokenIDs[0], 99); - assertEq(tokenIDs[1], 100); - assertEq(tokenIDs[2], 101); - - assertEq(tokenAddresses.length, 3); - assertEq(tokenAddresses[0], 0x6B175474E89094C44Da98b954EedeAC495271d0F); // dai - assertEq(tokenAddresses[1], 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84); // lido - assertEq(tokenAddresses[2], 0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72); // ENS - - assertEq(iotaDecimals.length, 3); - assertEq(iotaDecimals[0], 5); - assertEq(iotaDecimals[1], 6); - assertEq(iotaDecimals[2], 7); - - assertEq(tokenPrices.length, 3); - assertEq(tokenPrices[0], 1_000_000_000); - assertEq(tokenPrices[1], 2_000_000_000); - assertEq(tokenPrices[2], 3_000_000_000); - - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.ADD_EVM_TOKENS, - version: 1, - nonce: 0, - chainID: 12, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d455353414745070100000000000000000c0103636465036b175474e89094c44da98b954eedeac495271d0fae7ab96520de3a18e5e111b5eaab095312d7fe84c18360217d8f7ab5e7c516566761ea12ce7f9d720305060703000000003b9aca00000000007735940000000000b2d05e00"; - - assertEq(encodedMessage, expectedEncodedMessage); - - bytes[] memory signatures = new bytes[](3); - - signatures[0] = - hex"98b064aa172d0a66142f2fc45d9cd3255fb096cb92e0fcc9be4688b425aad6b53251c9044de4475e64e85b38b32cd3c813a8010281b00811d40fce9b3b372f2200"; - signatures[1] = - hex"275037d70185c835b0d1ee70a118d1cc5da90db2468fab1fa24517eeec3055d814f0ca65db7e6274dbda92d33c9df914db7ada4901a283ec1d3e8c126827923600"; - signatures[2] = - hex"ebb6669c8fb4b000fd41dde6e464c44c009ddcb47c05e7e5ea3deba71b21bd28156b23b6e7813a0603c57553ce484771c142ba6c981c4753035655e89006c0ee01"; - - config.addTokensWithSignatures(signatures, message); - - assertEq(config.tokenPriceOf(99), 1_000_000_000); - assertEq(config.tokenPriceOf(100), 2_000_000_000); - assertEq(config.tokenPriceOf(101), 3_000_000_000); - assertEq(config.tokenIotaDecimalOf(99), 5); - assertEq(config.tokenIotaDecimalOf(100), 6); - assertEq(config.tokenIotaDecimalOf(101), 7); - assertEq(config.tokenAddressOf(99), 0x6B175474E89094C44Da98b954EedeAC495271d0F); - assertEq(config.tokenAddressOf(100), 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84); - assertEq(config.tokenAddressOf(101), 0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72); - } -} diff --git a/bridge/evm/test/BridgeGasTest.t.sol b/bridge/evm/test/BridgeGasTest.t.sol deleted file mode 100644 index 6d459458f11..00000000000 --- a/bridge/evm/test/BridgeGasTest.t.sol +++ /dev/null @@ -1,284 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./BridgeBaseTest.t.sol"; - -contract BridgeGasTest is BridgeBaseTest { - // This function is called before each unit test - function setUp() public { - setUpBridgeTest(); - } - - // Uncomment to run these tests (must run tests with --via-ir flag) - - // function testTransferBridgedTokensWith7Signatures() public { - // // define committee with 50 members - // address[] memory _committee = new address[](56); - // uint256[] memory pks = new uint256[](56); - // uint16[] memory _stake = new uint16[](56); - // for (uint256 i = 0; i < 56; i++) { - // string memory name = string(abi.encodePacked("committeeMember", i)); - // (address member, uint256 pk) = makeAddrAndKey(name); - // _committee[i] = member; - // pks[i] = pk; - // // 1 member with 2500 stake - // if (i == 55) { - // _stake[i] = 2500; - // // 50 members with 100 stake (total: 5000) - // } else if (i < 50) { - // _stake[i] = 100; - // // 5 members with 500 stake (total: 2500) - // } else { - // _stake[i] = 500; - // } - // } - // committee = new BridgeCommittee(); - // committee.initialize(_committee, _stake, minStakeRequired); - // committee.initializeConfig(address(config)); - // uint256[] memory tokenPrices = new uint256[](4); - // tokenPrices[0] = 10000; // IOTA PRICE - // tokenPrices[1] = 10000; // BTC PRICE - // tokenPrices[2] = 10000; // ETH PRICE - // tokenPrices[3] = 10000; // USDC PRICE - // uint64[] memory totalLimits = new uint64[](1); - // totalLimits[0] = 1000000; - // skip(2 days); - // IotaBridge _bridge = new IotaBridge(); - // _bridge.initialize(address(committee), address(vault), address(limiter), wETH); - // changePrank(address(bridge)); - // limiter.transferOwnership(address(_bridge)); - // vault.transferOwnership(address(_bridge)); - // bridge = _bridge; - - // // Fill vault with WETH - // changePrank(deployer); - // IWETH9(wETH).deposit{value: 10 ether}(); - // IERC20(wETH).transfer(address(vault), 10 ether); - - // // transfer bridged tokens with 7 signatures - // // Create transfer payload - // uint8 senderAddressLength = 32; - // bytes memory senderAddress = abi.encode(0); - // uint8 targetChain = chainID; - // uint8 recipientAddressLength = 20; - // address recipientAddress = bridgerA; - // uint8 tokenID = BridgeUtils.ETH; - // uint64 amount = 100000000; // 1 ether in iota decimals - // bytes memory payload = abi.encodePacked( - // senderAddressLength, - // senderAddress, - // targetChain, - // recipientAddressLength, - // recipientAddress, - // tokenID, - // amount - // ); - - // // Create transfer message - // BridgeUtils.Message memory message = BridgeUtils.Message({ - // messageType: BridgeUtils.TOKEN_TRANSFER, - // version: 1, - // nonce: 1, - // chainID: 0, - // payload: payload - // }); - - // bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - // bytes32 messageHash = keccak256(encodedMessage); - - // bytes[] memory signatures = new bytes[](7); - - // uint8 index; - // for (uint256 i = 50; i < 55; i++) { - // signatures[index++] = getSignature(messageHash, pks[i]); - // } - // signatures[5] = getSignature(messageHash, pks[55]); - // signatures[6] = getSignature(messageHash, pks[0]); - - // bridge.transferBridgedTokensWithSignatures(signatures, message); - // } - - // function testTransferBridgedTokensWith26Signatures() public { - // // define committee with 50 members - // address[] memory _committee = new address[](56); - // uint256[] memory pks = new uint256[](56); - // uint16[] memory _stake = new uint16[](56); - // for (uint256 i = 0; i < 56; i++) { - // string memory name = string(abi.encodePacked("committeeMember", i)); - // (address member, uint256 pk) = makeAddrAndKey(name); - // _committee[i] = member; - // pks[i] = pk; - // // 1 member with 2500 stake - // if (i == 55) { - // _stake[i] = 2500; - // // 50 members with 100 stake (total: 5000) - // } else if (i < 50) { - // _stake[i] = 100; - // // 5 members with 500 stake (total: 2500) - // } else { - // _stake[i] = 500; - // } - // } - // committee = new BridgeCommittee(); - // committee.initialize(_committee, _stake, minStakeRequired); - // committee.initializeConfig(address(config)); - // uint256[] memory tokenPrices = new uint256[](4); - // tokenPrices[0] = 10000; // IOTA PRICE - // tokenPrices[1] = 10000; // BTC PRICE - // tokenPrices[2] = 10000; // ETH PRICE - // tokenPrices[3] = 10000; // USDC PRICE - // uint64[] memory totalLimits = new uint64[](1); - // totalLimits[0] = 1000000; - // skip(2 days); - // IotaBridge _bridge = new IotaBridge(); - // _bridge.initialize(address(committee), address(vault), address(limiter), wETH); - // changePrank(address(bridge)); - // limiter.transferOwnership(address(_bridge)); - // vault.transferOwnership(address(_bridge)); - // bridge = _bridge; - - // // Fill vault with WETH - // changePrank(deployer); - // IWETH9(wETH).deposit{value: 10 ether}(); - // IERC20(wETH).transfer(address(vault), 10 ether); - - // // transfer bridged tokens with 26 signatures - - // // Create transfer payload - // uint8 senderAddressLength = 32; - // bytes memory senderAddress = abi.encode(0); - // uint8 targetChain = chainID; - // uint8 recipientAddressLength = 20; - // address recipientAddress = bridgerA; - // uint8 tokenID = BridgeUtils.ETH; - // uint64 amount = 100000000; // 1 ether in iota decimals - // bytes memory payload = abi.encodePacked( - // senderAddressLength, - // senderAddress, - // targetChain, - // recipientAddressLength, - // recipientAddress, - // tokenID, - // amount - // ); - - // // Create transfer message - // BridgeUtils.Message memory message = BridgeUtils.Message({ - // messageType: BridgeUtils.TOKEN_TRANSFER, - // version: 1, - // nonce: 2, - // chainID: 0, - // payload: payload - // }); - - // bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - // bytes32 messageHash = keccak256(encodedMessage); - - // bytes[] memory signatures = new bytes[](25); - - // uint256 index = 0; - // // add 5 committee members with 100 stake - // for (uint256 i = 50; i < 55; i++) { - // signatures[index++] = getSignature(messageHash, pks[i]); - // } - // // add last committee member with 2500 stake - // signatures[5] = getSignature(messageHash, pks[55]); - - // // add 20 committee members with 100 stake - // for (uint256 i = 0; i < 20; i++) { - // signatures[index++] = getSignature(messageHash, pks[i]); - // } - - // bridge.transferBridgedTokensWithSignatures(signatures, message); - // } - - // function testTransferBridgedTokensWith56Signatures() public { - // // define committee with 50 members - // address[] memory _committee = new address[](56); - // uint256[] memory pks = new uint256[](56); - // uint16[] memory _stake = new uint16[](56); - // for (uint256 i = 0; i < 56; i++) { - // string memory name = string(abi.encodePacked("committeeMember", i)); - // (address member, uint256 pk) = makeAddrAndKey(name); - // _committee[i] = member; - // pks[i] = pk; - // // 1 member with 2500 stake - // if (i == 55) { - // _stake[i] = 2500; - // // 50 members with 100 stake (total: 5000) - // } else if (i < 50) { - // _stake[i] = 100; - // // 5 members with 500 stake (total: 2500) - // } else { - // _stake[i] = 500; - // } - // } - // committee = new BridgeCommittee(); - // committee.initialize(_committee, _stake, minStakeRequired); - // committee.initializeConfig(address(config)); - // uint256[] memory tokenPrices = new uint256[](4); - // tokenPrices[0] = 10000; // IOTA PRICE - // tokenPrices[1] = 10000; // BTC PRICE - // tokenPrices[2] = 10000; // ETH PRICE - // tokenPrices[3] = 10000; // USDC PRICE - // uint64[] memory totalLimits = new uint64[](1); - // totalLimits[0] = 1000000; - // skip(2 days); - // IotaBridge _bridge = new IotaBridge(); - // _bridge.initialize(address(committee), address(vault), address(limiter), wETH); - // changePrank(address(bridge)); - // limiter.transferOwnership(address(_bridge)); - // vault.transferOwnership(address(_bridge)); - // bridge = _bridge; - - // // Fill vault with WETH - // changePrank(deployer); - // IWETH9(wETH).deposit{value: 10 ether}(); - // IERC20(wETH).transfer(address(vault), 10 ether); - - // // transfer bridged tokens with 56 signatures - - // // Create transfer payload - // uint8 senderAddressLength = 32; - // bytes memory senderAddress = abi.encode(0); - // uint8 targetChain = chainID; - // uint8 recipientAddressLength = 20; - // address recipientAddress = bridgerA; - // uint8 tokenID = BridgeUtils.ETH; - // uint64 amount = 100000000; // 1 ether in iota decimals - // bytes memory payload = abi.encodePacked( - // senderAddressLength, - // senderAddress, - // targetChain, - // recipientAddressLength, - // recipientAddress, - // tokenID, - // amount - // ); - - // // Create transfer message - // BridgeUtils.Message memory message = BridgeUtils.Message({ - // messageType: BridgeUtils.TOKEN_TRANSFER, - // version: 1, - // nonce: 3, - // chainID: 0, - // payload: payload - // }); - - // bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - // bytes32 messageHash = keccak256(encodedMessage); - - // bytes[] memory signatures = new bytes[](56); - - // // get all signatures - // for (uint256 i = 0; i < 56; i++) { - // signatures[i] = getSignature(messageHash, pks[i]); - // } - - // bridge.transferBridgedTokensWithSignatures(signatures, message); - // } -} diff --git a/bridge/evm/test/BridgeLimiterTest.t.sol b/bridge/evm/test/BridgeLimiterTest.t.sol deleted file mode 100644 index 106f248e9b1..00000000000 --- a/bridge/evm/test/BridgeLimiterTest.t.sol +++ /dev/null @@ -1,367 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "./BridgeBaseTest.t.sol"; - -contract BridgeLimiterTest is BridgeBaseTest { - uint8 public supportedChainID; - - function setUp() public { - setUpBridgeTest(); - // warp to next nearest hour start - vm.warp(block.timestamp - (block.timestamp % 1 hours)); - supportedChainID = 0; - } - - function testBridgeLimiterInitialization() public { - assertEq(limiter.oldestChainTimestamp(supportedChainID), uint32(block.timestamp / 1 hours)); - assertEq(limiter.chainLimits(supportedChainID), totalLimit); - } - - function testCalculateAmountInUSD() public { - uint8 tokenID = 1; // wBTC - uint256 wBTCAmount = 1_00000000; // wBTC has 8 decimals - uint256 actual = limiter.calculateAmountInUSD(tokenID, wBTCAmount); - assertEq(actual, BTC_PRICE); - tokenID = 2; - uint256 ethAmount = 1 ether; - actual = limiter.calculateAmountInUSD(tokenID, ethAmount); - assertEq(actual, ETH_PRICE); - tokenID = 3; - uint256 usdcAmount = 1_000000; // USDC has 6 decimals - actual = limiter.calculateAmountInUSD(tokenID, usdcAmount); - assertEq(actual, USDC_PRICE); - } - - function testCalculateWindowLimit() public { - changePrank(address(bridge)); - uint8 tokenID = 3; - uint256 amount = 1_000000; // USDC has 6 decimals - limiter.recordBridgeTransfers(supportedChainID, tokenID, amount); - skip(1 hours); - limiter.recordBridgeTransfers(supportedChainID, tokenID, 2 * amount); - skip(1 hours); - uint256 actual = limiter.calculateWindowAmount(supportedChainID); - assertEq(actual, 3 * USD_VALUE_MULTIPLIER); - skip(22 hours); - actual = limiter.calculateWindowAmount(supportedChainID); - assertEq(actual, 2 * USD_VALUE_MULTIPLIER); - skip(59 minutes); - actual = limiter.calculateWindowAmount(supportedChainID); - assertEq(actual, 2 * USD_VALUE_MULTIPLIER); - skip(1 minutes); - actual = limiter.calculateWindowAmount(supportedChainID); - assertEq(actual, 0); - } - - function testAmountWillExceedLimit() public { - changePrank(address(bridge)); - uint8 tokenID = 3; - uint256 amount = 999999 * 1000000; // USDC has 6 decimals - assertFalse(limiter.willAmountExceedLimit(supportedChainID, tokenID, amount)); - limiter.recordBridgeTransfers(supportedChainID, tokenID, amount); - assertTrue(limiter.willAmountExceedLimit(supportedChainID, tokenID, 2000000)); - assertFalse(limiter.willAmountExceedLimit(supportedChainID, tokenID, 1000000)); - } - - function testRecordBridgeTransfer() public { - changePrank(address(bridge)); - uint8 tokenID = 1; - uint256 amount = 1_00000000; // wBTC has 8 decimals - limiter.recordBridgeTransfers(supportedChainID, tokenID, amount); - tokenID = 2; - amount = 1 ether; - limiter.recordBridgeTransfers(supportedChainID, tokenID, amount); - tokenID = 3; - amount = 1000000; // USDC has 6 decimals - limiter.recordBridgeTransfers(supportedChainID, tokenID, amount); - uint256 key = - limiter.getChainHourTimestampKey(supportedChainID, uint32(block.timestamp / 1 hours)); - assertEq(limiter.chainHourlyTransferAmount(key), BTC_PRICE + ETH_PRICE + USDC_PRICE); - } - - function testrecordBridgeTransfersGarbageCollection() public { - changePrank(address(bridge)); - uint8 tokenID = 1; - uint256 amount = 1_00000000; // wBTC has 8 decimals - uint32 hourToDelete = uint32(block.timestamp / 1 hours); - limiter.recordBridgeTransfers(supportedChainID, tokenID, amount); - uint256 keyToDelete = limiter.getChainHourTimestampKey(supportedChainID, hourToDelete); - uint256 deleteAmount = limiter.chainHourlyTransferAmount(keyToDelete); - assertEq(deleteAmount, BTC_PRICE); - skip(25 hours); - limiter.recordBridgeTransfers(supportedChainID, tokenID, amount); - deleteAmount = limiter.chainHourlyTransferAmount(keyToDelete); - assertEq(deleteAmount, 0); - } - - function testUpdateLimitWithSignatures() public { - changePrank(address(bridge)); - uint8 sourceChainID = 0; - uint64 newLimit = 10 * USD_VALUE_MULTIPLIER; - bytes memory payload = abi.encodePacked(sourceChainID, newLimit); - // Create a sample BridgeUtils - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPDATE_BRIDGE_LIMIT, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - - bytes memory messageBytes = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(messageBytes); - - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - assertEq(limiter.chainLimits(supportedChainID), totalLimit); - - // Call the updateLimitWithSignatures function - limiter.updateLimitWithSignatures(signatures, message); - - assertEq(limiter.chainLimits(supportedChainID), 10 * USD_VALUE_MULTIPLIER); - } - - function testMultipleChainLimits() public { - // deploy new committee - address[] memory _committee = new address[](5); - uint16[] memory _stake = new uint16[](5); - _committee[0] = committeeMemberA; - _committee[1] = committeeMemberB; - _committee[2] = committeeMemberC; - _committee[3] = committeeMemberD; - _committee[4] = committeeMemberE; - _stake[0] = 1000; - _stake[1] = 1000; - _stake[2] = 1000; - _stake[3] = 2002; - _stake[4] = 4998; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - // deploy new config contract with 2 supported chains - address[] memory _supportedTokens = new address[](5); - _supportedTokens[0] = address(0); // IOTA - _supportedTokens[1] = wBTC; - _supportedTokens[2] = wETH; - _supportedTokens[3] = USDC; - _supportedTokens[4] = USDT; - uint8[] memory supportedChains = new uint8[](2); - supportedChains[0] = 11; - supportedChains[1] = 12; - config = new BridgeConfig(); - config.initialize( - address(committee), chainID, _supportedTokens, tokenPrices, supportedChains - ); - committee.initializeConfig(address(config)); - // deploy new limiter with 2 supported chains - uint64[] memory totalLimits = new uint64[](2); - totalLimits[0] = 1_000_000 * USD_VALUE_MULTIPLIER; - totalLimits[1] = 2_000_000 * USD_VALUE_MULTIPLIER; - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), supportedChains, totalLimits); - // check if the limits are set correctly - assertEq(limiter.chainLimits(11), 1_000_000 * USD_VALUE_MULTIPLIER); - assertEq(limiter.chainLimits(12), 2_000_000 * USD_VALUE_MULTIPLIER); - // check if the oldestChainTimestamp is set correctly - assertEq(limiter.oldestChainTimestamp(11), uint32(block.timestamp / 1 hours)); - assertEq(limiter.oldestChainTimestamp(12), uint32(block.timestamp / 1 hours)); - - // check that limits are checked correctly - uint8 tokenID = 3; - uint256 amount = 999_999 * 1000000; // USDC has 6 decimals - assertFalse( - limiter.willAmountExceedLimit(11, tokenID, amount), "limit should not be exceeded" - ); - limiter.recordBridgeTransfers(11, tokenID, amount); - - assertTrue(limiter.willAmountExceedLimit(11, tokenID, 2000000), "limit should be exceeded"); - assertFalse( - limiter.willAmountExceedLimit(11, tokenID, 1000000), "limit should not be exceeded" - ); - assertEq( - limiter.calculateWindowAmount(11), - 999999 * USD_VALUE_MULTIPLIER, - "window amount should be correct" - ); - assertEq(limiter.calculateWindowAmount(12), 0, "window amount should be correct"); - // check that transfers are recorded correctly - amount = 1100000 * 1000000; // USDC has 6 decimals - limiter.recordBridgeTransfers(12, tokenID, amount); - assertEq( - limiter.chainHourlyTransferAmount( - limiter.getChainHourTimestampKey(12, uint32(block.timestamp / 1 hours)) - ), - 1_100_000 * USD_VALUE_MULTIPLIER, - "transfer amount should be correct" - ); - assertEq( - limiter.calculateWindowAmount(11), - 999999 * USD_VALUE_MULTIPLIER, - "window amount should be correct" - ); - assertEq( - limiter.calculateWindowAmount(12), - 1100000 * USD_VALUE_MULTIPLIER, - "window amount should be correct" - ); - } - - // An e2e update limit regression test covering message ser/de - function testUpdateLimitRegressionTest() public { - address[] memory _committee = new address[](4); - uint16[] memory _stake = new uint16[](4); - uint8 chainID = 11; - uint8[] memory _supportedChains = new uint8[](1); - uint8 sendingChainID = 1; - _supportedChains[0] = sendingChainID; - uint8[] memory _supportedDestinationChains = new uint8[](1); - _supportedDestinationChains[0] = sendingChainID; - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - - // deploy config - tokenPrices = new uint64[](5); - tokenPrices[0] = 10000; // IOTA PRICE - tokenPrices[1] = 10000; // BTC PRICE - tokenPrices[2] = 10000; // ETH PRICE - tokenPrices[3] = 10000; // USDC PRICE - tokenPrices[4] = 10000; // USDT PRICE - config = new BridgeConfig(); - config.initialize( - address(committee), chainID, supportedTokens, tokenPrices, _supportedChains - ); - - // initialize config in the bridge committee - committee.initializeConfig(address(config)); - - vault = new BridgeVault(wETH); - - skip(2 days); - uint64[] memory totalLimits = new uint64[](1); - totalLimits[0] = 1000000; - - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), _supportedDestinationChains, totalLimits); - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - vault.transferOwnership(address(bridge)); - limiter.transferOwnership(address(bridge)); - - bytes memory payload = hex"0c00000002540be400"; - - // Create update bridge limit message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPDATE_BRIDGE_LIMIT, - version: 1, - nonce: 15, - chainID: 2, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d4553534147450301000000000000000f020c00000002540be400"; - - assertEq(encodedMessage, expectedEncodedMessage); - } - - // An e2e update limit regression test covering message ser/de and signature verification - function testUpdateLimitRegressionTestWithSigVerification() public { - address[] memory _committee = new address[](4); - uint16[] memory _stake = new uint16[](4); - uint8 chainID = 11; - uint8[] memory _supportedChains = new uint8[](1); - uint8 sendingChainID = 1; - _supportedChains[0] = sendingChainID; - uint8[] memory _supportedDestinationChains = new uint8[](1); - _supportedDestinationChains[0] = sendingChainID; - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - - // deploy config - tokenPrices = new uint64[](5); - tokenPrices[0] = 10000; // IOTA PRICE - tokenPrices[1] = 10000; // BTC PRICE - tokenPrices[2] = 10000; // ETH PRICE - tokenPrices[3] = 10000; // USDC PRICE - tokenPrices[4] = 10000; // USDT PRICE - config = new BridgeConfig(); - config.initialize( - address(committee), chainID, supportedTokens, tokenPrices, _supportedChains - ); - - // initialize config in the bridge committee - committee.initializeConfig(address(config)); - - vault = new BridgeVault(wETH); - - skip(2 days); - - uint64[] memory totalLimits = new uint64[](1); - totalLimits[0] = 100 * USD_VALUE_MULTIPLIER; - - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), _supportedDestinationChains, totalLimits); - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - - vault.transferOwnership(address(bridge)); - limiter.transferOwnership(address(bridge)); - - // Fill vault with WETH - changePrank(deployer); - IWETH9(wETH).deposit{value: 10 ether}(); - IERC20(wETH).transfer(address(vault), 10 ether); - // sending chain: 01 (sendingChainID), new limit: 99_900 * USD_VALUE_MULTIPLIER - bytes memory payload = hex"0100000915fa66bc00"; - - // Create update bridge limit message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPDATE_BRIDGE_LIMIT, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d455353414745030100000000000000000b0100000915fa66bc00"; - - assertEq(encodedMessage, expectedEncodedMessage); - - bytes[] memory signatures = new bytes[](3); - - signatures[0] = - hex"d19f71162a73150af2b786befbf248914bd421ac42a7345c47b2ef48f98d24a45eba60676ea17c6aa25ca0f548d3ef97e1498b7232576f08b91b09e1a8daeec001"; - signatures[1] = - hex"5f9de5595ea57405c8b4e2728864f6fd33399f2bb22c5b9e24ee36f9a357d61223512a20cce8eb536c10e99c21c35f357ae26a5cb2083c495d8f280b31d89ec300"; - signatures[2] = - hex"33deda897325e500e84ab97eac33cc3d5bdc3ec46361ab8df1068da71bd8bf077f0f1265d80b23c590c55eeee2612d4cfae03a21f7c67e268c4f09dc2f1a0d9401"; - - committee.verifySignatures(signatures, message); - - limiter.updateLimitWithSignatures(signatures, message); - assertEq(limiter.chainLimits(sendingChainID), 99_900 * USD_VALUE_MULTIPLIER); - } -} diff --git a/bridge/evm/test/BridgeUtilsTest.t.sol b/bridge/evm/test/BridgeUtilsTest.t.sol deleted file mode 100644 index 8a16fd71d46..00000000000 --- a/bridge/evm/test/BridgeUtilsTest.t.sol +++ /dev/null @@ -1,334 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./BridgeBaseTest.t.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; - -contract BridgeUtilsTest is BridgeBaseTest { - // This function is called before each unit test - function setUp() public { - setUpBridgeTest(); - } - - function testConvertERC20ToIotaDecimalAmountTooLargeForUint64() public { - vm.expectRevert(bytes("BridgeUtils: Amount too large for uint64")); - BridgeUtils.convertERC20ToIotaDecimal(18, 8, type(uint256).max); - } - - function testConvertERC20ToIotaDecimalInvalidIotaDecimal() public { - vm.expectRevert(bytes("BridgeUtils: Invalid IOTA decimal")); - BridgeUtils.convertERC20ToIotaDecimal(10, 11, 100); - } - - function testconvertIotaToERC20DecimalInvalidIotaDecimal() public { - vm.expectRevert(bytes("BridgeUtils: Invalid IOTA decimal")); - BridgeUtils.convertIotaToERC20Decimal(10, 11, 100); - } - - function testConvertERC20ToIotaDecimal() public { - // ETH - assertEq(IERC20Metadata(wETH).decimals(), 18); - uint256 ethAmount = 10 ether; - uint64 iotaAmount = BridgeUtils.convertERC20ToIotaDecimal( - IERC20Metadata(config.tokenAddressOf(BridgeUtils.ETH)).decimals(), - config.tokenIotaDecimalOf(BridgeUtils.ETH), - ethAmount - ); - assertEq(iotaAmount, 10_000_000_00); // 10 * 10 ^ 8 - - // USDC - assertEq(IERC20Metadata(USDC).decimals(), 6); - ethAmount = 50_000_000; // 50 USDC - iotaAmount = BridgeUtils.convertERC20ToIotaDecimal( - IERC20Metadata(config.tokenAddressOf(BridgeUtils.USDC)).decimals(), - config.tokenIotaDecimalOf(BridgeUtils.USDC), - ethAmount - ); - assertEq(iotaAmount, ethAmount); - - // USDT - assertEq(IERC20Metadata(USDT).decimals(), 6); - ethAmount = 60_000_000; // 60 USDT - iotaAmount = BridgeUtils.convertERC20ToIotaDecimal( - IERC20Metadata(config.tokenAddressOf(BridgeUtils.USDT)).decimals(), - config.tokenIotaDecimalOf(BridgeUtils.USDT), - ethAmount - ); - assertEq(iotaAmount, ethAmount); - - // BTC - assertEq(IERC20Metadata(wBTC).decimals(), 8); - ethAmount = 2_00_000_000; // 2 BTC - iotaAmount = BridgeUtils.convertERC20ToIotaDecimal( - IERC20Metadata(config.tokenAddressOf(BridgeUtils.BTC)).decimals(), - config.tokenIotaDecimalOf(BridgeUtils.BTC), - ethAmount - ); - assertEq(iotaAmount, ethAmount); - } - - function testconvertIotaToERC20Decimal() public { - // ETH - assertEq(IERC20Metadata(wETH).decimals(), 18); - uint64 iotaAmount = 11_000_000_00; // 11 eth - uint256 ethAmount = BridgeUtils.convertIotaToERC20Decimal( - IERC20Metadata(config.tokenAddressOf(BridgeUtils.ETH)).decimals(), - config.tokenIotaDecimalOf(BridgeUtils.ETH), - iotaAmount - ); - assertEq(ethAmount, 11 ether); - - // USDC - assertEq(IERC20Metadata(USDC).decimals(), 6); - iotaAmount = 50_000_000; // 50 USDC - ethAmount = BridgeUtils.convertIotaToERC20Decimal( - IERC20Metadata(config.tokenAddressOf(BridgeUtils.USDC)).decimals(), - config.tokenIotaDecimalOf(BridgeUtils.USDC), - iotaAmount - ); - assertEq(iotaAmount, ethAmount); - - // USDT - assertEq(IERC20Metadata(USDT).decimals(), 6); - iotaAmount = 50_000_000; // 50 USDT - ethAmount = BridgeUtils.convertIotaToERC20Decimal( - IERC20Metadata(config.tokenAddressOf(BridgeUtils.USDT)).decimals(), - config.tokenIotaDecimalOf(BridgeUtils.USDT), - iotaAmount - ); - assertEq(iotaAmount, ethAmount); - - // BTC - assertEq(IERC20Metadata(wBTC).decimals(), 8); - iotaAmount = 3_000_000_00; // 3 BTC - ethAmount = BridgeUtils.convertIotaToERC20Decimal( - IERC20Metadata(config.tokenAddressOf(BridgeUtils.BTC)).decimals(), - config.tokenIotaDecimalOf(BridgeUtils.BTC), - iotaAmount - ); - assertEq(iotaAmount, ethAmount); - } - - function testEncodeMessage() public { - bytes memory moveEncodedMessage = abi.encodePacked( - hex"5355495f4252494447455f4d45535341474500010000000000000000012080ab1ee086210a3a37355300ca24672e81062fcdb5ced6618dab203f6a3b291c0b14b18f79fe671db47393315ffdb377da4ea1b7af96010084d71700000000" - ); - - uint64 nonce = 0; - uint8 iotaChainId = 1; - - bytes memory payload = abi.encodePacked( - hex"2080ab1ee086210a3a37355300ca24672e81062fcdb5ced6618dab203f6a3b291c0b14b18f79fe671db47393315ffdb377da4ea1b7af96010084d71700000000" - ); - - bytes memory abiEncodedMessage = BridgeUtils.encodeMessage( - BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: nonce, - chainID: iotaChainId, - payload: payload - }) - ); - - assertEq( - keccak256(moveEncodedMessage), - keccak256(abiEncodedMessage), - "Encoded messages do not match" - ); - } - - function testDecodeTransferTokenPayload() public { - // 20: sender length 1 bytes - // 80ab1ee086210a3a37355300ca24672e81062fcdb5ced6618dab203f6a3b291c: sender 32 bytes - // 0b: target chain 1 bytes - // 14: target address length 1 bytes - // b18f79fe671db47393315ffdb377da4ea1b7af96: target address 20 bytes - // 02: token id 1 byte - // 000000c70432b1dd: amount 8 bytes - bytes memory payload = - hex"2080ab1ee086210a3a37355300ca24672e81062fcdb5ced6618dab203f6a3b291c0b14b18f79fe671db47393315ffdb377da4ea1b7af9602000000c70432b1dd"; - - BridgeUtils.TokenTransferPayload memory _payload = - BridgeUtils.decodeTokenTransferPayload(payload); - - assertEq(_payload.senderAddressLength, uint8(32)); - assertEq( - _payload.senderAddress, - hex"80ab1ee086210a3a37355300ca24672e81062fcdb5ced6618dab203f6a3b291c" - ); - assertEq(_payload.targetChain, uint8(11)); - assertEq(_payload.recipientAddressLength, uint8(20)); - assertEq(_payload.recipientAddress, 0xb18f79Fe671db47393315fFDB377Da4Ea1B7AF96); - assertEq(_payload.tokenID, BridgeUtils.ETH); - assertEq(_payload.amount, uint64(854768923101)); - } - - function testDecodeBlocklistPayload() public { - bytes memory payload = - hex"010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5"; - (bool blocklisting, address[] memory members) = BridgeUtils.decodeBlocklistPayload(payload); - - assertEq(members.length, 2); - assertEq(members[0], 0x68B43fD906C0B8F024a18C56e06744F7c6157c65); - assertEq(members[1], 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5); - assertFalse(blocklisting); - } - - function testDecodeUpdateLimitPayload() public { - bytes memory payload = hex"0c00000002540be400"; - (uint8 sourceChainID, uint64 newLimit) = BridgeUtils.decodeUpdateLimitPayload(payload); - assertEq(sourceChainID, 12); - assertEq(newLimit, 100 * USD_VALUE_MULTIPLIER); - } - - function testDecodeUpdateTokenPricePayload() public { - bytes memory payload = hex"01000000003b9aca00"; - (uint8 _tokenID, uint64 _newPrice) = BridgeUtils.decodeUpdateTokenPricePayload(payload); - - assertEq(_tokenID, 1); - assertEq(_newPrice, 10 * USD_VALUE_MULTIPLIER); - } - - function testDecodeEmergencyOpPayload() public { - bytes memory payload = hex"01"; - bool pausing = BridgeUtils.decodeEmergencyOpPayload(payload); - assertFalse(pausing); - } - - function testDecodeUpgradePayload() public { - bytes memory payload = abi.encode( - address(100), - address(200), - hex"5355495f4252494447455f4d455353414745050100000000000000000c" - ); - - (address proxy, address newImp, bytes memory _calldata) = - BridgeUtils.decodeUpgradePayload(payload); - - assertEq(proxy, address(100)); - assertEq(newImp, address(200)); - assertEq(_calldata, hex"5355495f4252494447455f4d455353414745050100000000000000000c"); - } - - function testDecodeUpgradePayloadWithNoArgsRegressionTest() public { - bytes memory initV2Message = - hex"5355495f4252494447455f4d4553534147450501000000000000007b0c00000000000000000000000006060606060606060606060606060606060606060000000000000000000000000909090909090909090909090909090909090909000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000045cd8a76b00000000000000000000000000000000000000000000000000000000"; - - bytes memory initV2Payload = - hex"00000000000000000000000006060606060606060606060606060606060606060000000000000000000000000909090909090909090909090909090909090909000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000045cd8a76b00000000000000000000000000000000000000000000000000000000"; - - bytes4 initV2CallData = bytes4(keccak256(bytes("initializeV2()"))); - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 123, - chainID: 12, - payload: initV2Payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - assertEq(encodedMessage, initV2Message); - - (address proxy, address newImp, bytes memory _calldata) = - BridgeUtils.decodeUpgradePayload(initV2Payload); - - assertEq(proxy, address(0x0606060606060606060606060606060606060606)); - assertEq(newImp, address(0x0909090909090909090909090909090909090909)); - assertEq(bytes4(_calldata), initV2CallData); - } - - function testDecodeUpgradePayloadWith1ArgRegressionTest() public { - bytes memory newMockFunc1Message = - hex"5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024417795ef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000"; - - bytes memory newMockFunc1Payload = - hex"0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024417795ef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000"; - - bytes4 newMockFunc1CallData = bytes4(keccak256(bytes("newMockFunction(bool)"))); - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 123, - chainID: 12, - payload: newMockFunc1Payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - assertEq(encodedMessage, newMockFunc1Message); - - (address proxy, address newImp, bytes memory _calldata) = - BridgeUtils.decodeUpgradePayload(newMockFunc1Payload); - - assertEq(proxy, address(0x0606060606060606060606060606060606060606)); - assertEq(newImp, address(0x0909090909090909090909090909090909090909)); - assertEq(bytes4(_calldata), newMockFunc1CallData); - } - - function testDecodeUpgradePayloadWith2ArgsRegressionTest() public { - bytes memory newMockFunc2Message = - hex"5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000"; - - bytes memory newMockFunc2Payload = - hex"0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000"; - - bytes4 newMockFunc2CallData = bytes4(keccak256(bytes("newMockFunction(bool,uint8)"))); - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 123, - chainID: 12, - payload: newMockFunc2Payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - assertEq(encodedMessage, newMockFunc2Message); - - (address proxy, address newImp, bytes memory _calldata) = - BridgeUtils.decodeUpgradePayload(newMockFunc2Payload); - - assertEq(proxy, address(0x0606060606060606060606060606060606060606)); - assertEq(newImp, address(0x0909090909090909090909090909090909090909)); - assertEq(bytes4(_calldata), newMockFunc2CallData); - } - - function testDecodeUpgradePayloadWithNoCalldataRegressionTest() public { - bytes memory emptyCalldataMessage = - hex"5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"; - - bytes memory emptyCalldataPayload = - hex"0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"; - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 123, - chainID: 12, - payload: emptyCalldataPayload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - assertEq(encodedMessage, emptyCalldataMessage); - - (address proxy, address newImp, bytes memory _calldata) = - BridgeUtils.decodeUpgradePayload(emptyCalldataPayload); - - assertEq(proxy, address(0x0606060606060606060606060606060606060606)); - assertEq(newImp, address(0x0909090909090909090909090909090909090909)); - assertEq(_calldata, bytes(hex"")); - } - - function testrequiredStakeInvalidType() public { - uint8 invalidType = 100; - vm.expectRevert("BridgeUtils: Invalid message type"); - BridgeUtils.requiredStake(BridgeUtils.Message(invalidType, 0, 0, 1, bytes(hex"00"))); - } -} diff --git a/bridge/evm/test/CommitteeUpgradeableTest.t.sol b/bridge/evm/test/CommitteeUpgradeableTest.t.sol deleted file mode 100644 index 5722c568515..00000000000 --- a/bridge/evm/test/CommitteeUpgradeableTest.t.sol +++ /dev/null @@ -1,351 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import "openzeppelin-foundry-upgrades/Options.sol"; -import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; -import "./mocks/MockIotaBridgeV2.sol"; -import "../contracts/BridgeCommittee.sol"; -import "../contracts/IotaBridge.sol"; -import "./BridgeBaseTest.t.sol"; -import "forge-std/Test.sol"; - -contract CommitteeUpgradeableTest is BridgeBaseTest { - MockIotaBridgeV2 bridgeV2; - uint8 _chainID = 12; - - // This function is called before each unit test - function setUp() public { - setUpBridgeTest(); - address[] memory _committeeMembers = new address[](5); - uint16[] memory _stake = new uint16[](5); - _committeeMembers[0] = committeeMemberA; - _committeeMembers[1] = committeeMemberB; - _committeeMembers[2] = committeeMemberC; - _committeeMembers[3] = committeeMemberD; - _committeeMembers[4] = committeeMemberE; - _stake[0] = 1000; - _stake[1] = 1000; - _stake[2] = 1000; - _stake[3] = 2002; - _stake[4] = 4998; - - uint8[] memory _supportedDestinationChains = new uint8[](1); - _supportedDestinationChains[0] = 0; - - Options memory opts; - opts.unsafeSkipAllChecks = true; - - // deploy bridge committee - address _committee = Upgrades.deployUUPSProxy( - "BridgeCommittee.sol", - abi.encodeCall( - BridgeCommittee.initialize, (_committeeMembers, _stake, minStakeRequired) - ), - opts - ); - - committee = BridgeCommittee(_committee); - - // deploy bridge config - address _config = Upgrades.deployUUPSProxy( - "BridgeConfig.sol", - abi.encodeCall( - BridgeConfig.initialize, - (_committee, _chainID, supportedTokens, tokenPrices, _supportedDestinationChains) - ), - opts - ); - - committee.initializeConfig(_config); - - // deploy iota bridge - address _bridge = Upgrades.deployUUPSProxy( - "IotaBridge.sol", - abi.encodeCall(IotaBridge.initialize, (_committee, address(0), address(0))), - opts - ); - - bridge = IotaBridge(_bridge); - bridgeV2 = new MockIotaBridgeV2(); - } - - function testUpgradeWithSignaturesSuccess() public { - bytes memory initializer = abi.encodeCall(MockIotaBridgeV2.initializeV2, ()); - bytes memory payload = abi.encode(address(bridge), address(bridgeV2), initializer); - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 0, - chainID: _chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - assertFalse(bridge.paused()); - bridge.upgradeWithSignatures(signatures, message); - assertTrue(bridge.paused()); - assertEq(Upgrades.getImplementationAddress(address(bridge)), address(bridgeV2)); - } - - function testUpgradeWithSignaturesInsufficientStakeAmount() public { - // Create message - bytes memory initializer = abi.encodeCall(MockIotaBridgeV2.initializeV2, ()); - bytes memory payload = abi.encode(address(bridge), address(bridgeV2), initializer); - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 0, - chainID: _chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](2); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - vm.expectRevert(bytes("BridgeCommittee: Insufficient stake amount")); - bridge.upgradeWithSignatures(signatures, message); - } - - function testUpgradeWithSignaturesMessageDoesNotMatchType() public { - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 0, - chainID: _chainID, - payload: abi.encode(0) - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("MessageVerifier: message does not match type")); - bridge.upgradeWithSignatures(signatures, message); - } - - function testUpgradeWithSignaturesInvalidNonce() public { - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 10, - chainID: _chainID, - payload: abi.encode(0) - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("MessageVerifier: Invalid nonce")); - bridge.upgradeWithSignatures(signatures, message); - } - - function testUpgradeWithSignaturesERC1967UpgradeNewImplementationIsNotUUPS() public { - bytes memory initializer = abi.encodeCall(MockIotaBridgeV2.initializeV2, ()); - bytes memory payload = abi.encode(address(bridge), address(this), initializer); - - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 0, - chainID: _chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - assertFalse(bridge.paused()); - vm.expectRevert( - abi.encodeWithSelector( - ERC1967Utils.ERC1967InvalidImplementation.selector, address(this) - ) - ); - bridge.upgradeWithSignatures(signatures, message); - } - - function testUpgradeWithSignaturesInvalidProxyAddress() public { - bytes memory initializer = abi.encodeCall(MockIotaBridgeV2.initializeV2, ()); - bytes memory payload = abi.encode(address(this), address(bridgeV2), initializer); - - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 0, - chainID: _chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("CommitteeUpgradeable: Invalid proxy address")); - bridge.upgradeWithSignatures(signatures, message); - } - - // An e2e upgrade regression test covering message ser/de and signature verification - function testUpgradeRegressionTestWithV2Initializer() public { - bytes memory messagePrefix = hex"5355495f4252494447455f4d455353414745050100000000000000000c"; - - bytes memory initV2CallData = - hex"000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000045cd8a76b00000000000000000000000000000000000000000000000000000000"; - - bytes memory payload = abi.encodePacked( - abi.encode(address(bridge)), abi.encode(address(bridgeV2)), initV2CallData - ); - - bytes memory encodedMessage = abi.encodePacked(messagePrefix, payload); - - bytes32 messageHash = keccak256(encodedMessage); - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 0, - chainID: _chainID, - payload: payload - }); - - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - bridge.upgradeWithSignatures(signatures, message); - - assertTrue(bridge.paused()); - assertEq(Upgrades.getImplementationAddress(address(bridge)), address(bridgeV2)); - } - - function testUpgradeRegressionTestWith1CalldataArg() public { - bytes memory messagePrefix = hex"5355495f4252494447455f4d455353414745050100000000000000000c"; - - bytes memory newMockFunc1CallData = - hex"00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024417795ef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000"; - - bytes memory payload = abi.encodePacked( - abi.encode(address(bridge)), abi.encode(address(bridgeV2)), newMockFunc1CallData - ); - - bytes memory encodedMessage = abi.encodePacked(messagePrefix, payload); - - bytes32 messageHash = keccak256(encodedMessage); - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 0, - chainID: _chainID, - payload: payload - }); - - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - bridge.upgradeWithSignatures(signatures, message); - - MockIotaBridgeV2 newBridgeV2 = MockIotaBridgeV2(address(bridge)); - assertTrue(newBridgeV2.isPausing()); - assertEq(Upgrades.getImplementationAddress(address(bridge)), address(bridgeV2)); - } - - function testUpgradeRegressionTestWith2CalldataArg() public { - bytes memory messagePrefix = hex"5355495f4252494447455f4d455353414745050100000000000000000c"; - - bytes memory newMockFunc2CallData = - hex"00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000"; - - bytes memory payload = abi.encodePacked( - abi.encode(address(bridge)), abi.encode(address(bridgeV2)), newMockFunc2CallData - ); - - bytes memory encodedMessage = abi.encodePacked(messagePrefix, payload); - - bytes32 messageHash = keccak256(encodedMessage); - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 0, - chainID: _chainID, - payload: payload - }); - - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - bridge.upgradeWithSignatures(signatures, message); - - MockIotaBridgeV2 newBridgeV2 = MockIotaBridgeV2(address(bridge)); - assertTrue(newBridgeV2.isPausing()); - assertEq(newBridgeV2.mock(), 42); - assertEq(Upgrades.getImplementationAddress(address(bridge)), address(bridgeV2)); - } - - function testUpgradeRegressionTestWithNoCalldata() public { - bytes memory messagePrefix = hex"5355495f4252494447455f4d455353414745050100000000000000000c"; - - bytes memory emptyCallData = - hex"00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"; - - bytes memory payload = abi.encodePacked( - abi.encode(address(bridge)), abi.encode(address(bridgeV2)), emptyCallData - ); - - bytes memory encodedMessage = abi.encodePacked(messagePrefix, payload); - - bytes32 messageHash = keccak256(encodedMessage); - - // Create upgrade message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 0, - chainID: _chainID, - payload: payload - }); - - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - bridge.upgradeWithSignatures(signatures, message); - - MockIotaBridgeV2(address(bridge)); - assertEq(Upgrades.getImplementationAddress(address(bridge)), address(bridgeV2)); - } -} diff --git a/bridge/evm/test/IotaBridgeTest.t.sol b/bridge/evm/test/IotaBridgeTest.t.sol deleted file mode 100644 index 11e26000e37..00000000000 --- a/bridge/evm/test/IotaBridgeTest.t.sol +++ /dev/null @@ -1,990 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./BridgeBaseTest.t.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import "../contracts/interfaces/IIotaBridge.sol"; -import "./mocks/MockIotaBridgeV2.sol"; - -contract IotaBridgeTest is BridgeBaseTest, IIotaBridge { - // This function is called before each unit test - function setUp() public { - setUpBridgeTest(); - } - - function testIotaBridgeInitialization() public { - assertEq(address(bridge.committee()), address(committee)); - assertEq(address(bridge.vault()), address(vault)); - } - - function testTransferBridgedTokensWithSignaturesTokenDailyLimitExceeded() public { - uint8 senderAddressLength = 32; - bytes memory senderAddress = abi.encode(0); - uint8 targetChain = chainID; - uint8 recipientAddressLength = 20; - address recipientAddress = bridgerA; - uint8 tokenID = BridgeUtils.ETH; - uint64 amount = 1_000_000 * USD_VALUE_MULTIPLIER; - bytes memory payload = abi.encodePacked( - senderAddressLength, - senderAddress, - targetChain, - recipientAddressLength, - recipientAddress, - tokenID, - amount - ); - - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 1, - chainID: 0, - payload: payload - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("IotaBridge: Amount exceeds bridge limit")); - bridge.transferBridgedTokensWithSignatures(signatures, message); - } - - function testTransferBridgedTokensWithSignaturesInvalidTargetChain() public { - uint8 senderAddressLength = 32; - bytes memory senderAddress = abi.encode(0); - uint8 targetChain = 0; - uint8 recipientAddressLength = 20; - address recipientAddress = bridgerA; - uint8 tokenID = BridgeUtils.ETH; - uint64 amount = 10000; - bytes memory payload = abi.encodePacked( - senderAddressLength, - senderAddress, - targetChain, - recipientAddressLength, - recipientAddress, - tokenID, - amount - ); - - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 1, - chainID: 1, - payload: payload - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("IotaBridge: Target chain not supported")); - bridge.transferBridgedTokensWithSignatures(signatures, message); - } - - function testTransferBridgedTokensWithSignaturesInsufficientStakeAmount() public { - // Create transfer message - BridgeUtils.TokenTransferPayload memory payload = BridgeUtils.TokenTransferPayload({ - senderAddressLength: 0, - senderAddress: abi.encode(0), - targetChain: 1, - recipientAddressLength: 0, - recipientAddress: bridgerA, - tokenID: BridgeUtils.ETH, - // This is IOTA amount (eth decimal 8) - amount: 100_000_000 - }); - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 1, - chainID: chainID, - payload: abi.encode(payload) - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](2); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - vm.expectRevert(bytes("BridgeCommittee: Insufficient stake amount")); - bridge.transferBridgedTokensWithSignatures(signatures, message); - } - - function testTransferBridgedTokensWithSignaturesMessageDoesNotMatchType() public { - // Create transfer message - BridgeUtils.TokenTransferPayload memory payload = BridgeUtils.TokenTransferPayload({ - senderAddressLength: 0, - senderAddress: abi.encode(0), - targetChain: 1, - recipientAddressLength: 0, - recipientAddress: bridgerA, - tokenID: BridgeUtils.ETH, - // This is IOTA amount (eth decimal 8) - amount: 100_000_000 - }); - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 1, - chainID: chainID, - payload: abi.encode(payload) - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](2); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - vm.expectRevert(bytes("MessageVerifier: message does not match type")); - bridge.transferBridgedTokensWithSignatures(signatures, message); - } - - function testTransferWETHWithValidSignatures() public { - // Fill vault with WETH - changePrank(deployer); - IWETH9(wETH).deposit{value: 10 ether}(); - // IWETH9(wETH).withdraw(1 ether); - IERC20(wETH).transfer(address(vault), 10 ether); - // Create transfer payload - uint8 senderAddressLength = 32; - bytes memory senderAddress = abi.encode(0); - uint8 targetChain = chainID; - uint8 recipientAddressLength = 20; - address recipientAddress = bridgerA; - uint8 tokenID = BridgeUtils.ETH; - uint64 amount = 100000000; // 1 ether in iota decimals - bytes memory payload = abi.encodePacked( - senderAddressLength, - senderAddress, - targetChain, - recipientAddressLength, - recipientAddress, - tokenID, - amount - ); - - // Create transfer message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 1, - chainID: 0, - payload: payload - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - uint256 aBalance = bridgerA.balance; - bridge.transferBridgedTokensWithSignatures(signatures, message); - assertEq(bridgerA.balance, aBalance + 1 ether); - - vm.expectRevert(bytes("IotaBridge: Message already processed")); - bridge.transferBridgedTokensWithSignatures(signatures, message); - } - - function testTransferUSDCWithValidSignatures() public { - // Fill vault with USDC - changePrank(USDCWhale); - IERC20(USDC).transfer(address(vault), 100_000_000); - changePrank(deployer); - - // Create transfer payload - uint8 senderAddressLength = 32; - bytes memory senderAddress = abi.encode(0); - uint8 targetChain = chainID; - uint8 recipientAddressLength = 20; - address recipientAddress = bridgerA; - uint8 tokenID = BridgeUtils.USDC; - uint64 amount = 1_000_000; - bytes memory payload = abi.encodePacked( - senderAddressLength, - senderAddress, - targetChain, - recipientAddressLength, - recipientAddress, - tokenID, - amount - ); - - // Create transfer message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 1, - chainID: 0, - payload: payload - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - assert(IERC20(USDC).balanceOf(bridgerA) == 0); - bridge.transferBridgedTokensWithSignatures(signatures, message); - assert(IERC20(USDC).balanceOf(bridgerA) == 1_000_000); - } - - function testExecuteEmergencyOpWithSignaturesInvalidOpCode() public { - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 0, - chainID: chainID, - payload: hex"02" - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("BridgeUtils: Invalid op code")); - bridge.executeEmergencyOpWithSignatures(signatures, message); - } - - function testExecuteEmergencyOpWithSignaturesInvalidNonce() public { - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 1, - chainID: chainID, - payload: bytes(hex"00") - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("MessageVerifier: Invalid nonce")); - bridge.executeEmergencyOpWithSignatures(signatures, message); - } - - function testExecuteEmergencyOpWithSignaturesMessageDoesNotMatchType() public { - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 0, - chainID: chainID, - payload: abi.encode(0) - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](4); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - vm.expectRevert(bytes("MessageVerifier: message does not match type")); - bridge.executeEmergencyOpWithSignatures(signatures, message); - } - - function testExecuteEmergencyOpWithSignaturesInvalidSignatures() public { - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 0, - chainID: chainID, - payload: bytes(hex"01") - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - bytes[] memory signatures = new bytes[](2); - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - vm.expectRevert(bytes("BridgeCommittee: Insufficient stake amount")); - bridge.executeEmergencyOpWithSignatures(signatures, message); - } - - function testFreezeBridgeEmergencyOp() public { - // Create emergency op message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 0, - chainID: chainID, - payload: bytes(hex"00") - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - assertFalse(bridge.paused()); - bridge.executeEmergencyOpWithSignatures(signatures, message); - assertTrue(bridge.paused()); - } - - function testUnfreezeBridgeEmergencyOp() public { - testFreezeBridgeEmergencyOp(); - // Create emergency op message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 1, - chainID: chainID, - payload: bytes(hex"01") - }); - - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes32 messageHash = keccak256(encodedMessage); - - bytes[] memory signatures = new bytes[](4); - - signatures[0] = getSignature(messageHash, committeeMemberPkA); - signatures[1] = getSignature(messageHash, committeeMemberPkB); - signatures[2] = getSignature(messageHash, committeeMemberPkC); - signatures[3] = getSignature(messageHash, committeeMemberPkD); - - bridge.executeEmergencyOpWithSignatures(signatures, message); - assertFalse(bridge.paused()); - } - - function testBridgeERC20UnsupportedToken() public { - vm.expectRevert(bytes("IotaBridge: Unsupported token")); - bridge.bridgeERC20( - 255, 1 ether, hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", 0 - ); - } - - function testBridgeERC20InsufficientAllowance() public { - vm.expectRevert(bytes("IotaBridge: Insufficient allowance")); - bridge.bridgeERC20( - BridgeUtils.ETH, - type(uint256).max, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", - 0 - ); - } - - function testBridgeERC20InvalidRecipientAddress() public { - vm.expectRevert(bytes("IotaBridge: Invalid recipient address length")); - bridge.bridgeERC20( - BridgeUtils.ETH, - 1 ether, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3", - 0 - ); - } - - function testBridgeEthInvalidRecipientAddress() public { - vm.expectRevert(bytes("IotaBridge: Invalid recipient address length")); - bridge.bridgeETH{value: 1 ether}( - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3", 0 - ); - } - - function testBridgeWETH() public { - changePrank(deployer); - IWETH9(wETH).deposit{value: 10 ether}(); - IERC20(wETH).approve(address(bridge), 10 ether); - assertEq(IERC20(wETH).balanceOf(address(vault)), 0); - uint256 balance = IERC20(wETH).balanceOf(deployer); - - // assert emitted event - vm.expectEmit(true, true, true, false); - emit TokensDeposited( - chainID, - 0, // nonce - 0, // destination chain id - BridgeUtils.ETH, - 1_00_000_000, // 1 ether - deployer, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4" - ); - - bridge.bridgeERC20( - BridgeUtils.ETH, - 1 ether, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", - 0 - ); - assertEq(IERC20(wETH).balanceOf(address(vault)), 1 ether); - assertEq(IERC20(wETH).balanceOf(deployer), balance - 1 ether); - assertEq(bridge.nonces(BridgeUtils.TOKEN_TRANSFER), 1); - - // Now test rounding. For ETH, the last 10 digits are rounded - vm.expectEmit(true, true, true, false); - emit TokensDeposited( - chainID, - 1, // nonce - 0, // destination chain id - BridgeUtils.ETH, - 2.00000001 ether, - deployer, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4" - ); - // 2_000_000_011_000_000_888 is rounded to 2.00000001 eth - bridge.bridgeERC20( - BridgeUtils.ETH, - 2_000_000_011_000_000_888, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", - 0 - ); - assertEq(IERC20(wETH).balanceOf(address(vault)), 3_000_000_011_000_000_888); - assertEq(IERC20(wETH).balanceOf(deployer), balance - 3_000_000_011_000_000_888); - assertEq(bridge.nonces(BridgeUtils.TOKEN_TRANSFER), 2); - } - - function testBridgeUSDC() public { - changePrank(USDCWhale); - - uint256 usdcAmount = 1000000; - - // approve - IERC20(USDC).approve(address(bridge), usdcAmount); - - assertEq(IERC20(USDC).balanceOf(address(vault)), 0); - uint256 balance = IERC20(USDC).balanceOf(USDCWhale); - - // assert emitted event - vm.expectEmit(true, true, true, false); - emit TokensDeposited( - chainID, - 0, // nonce - 0, // destination chain id - BridgeUtils.USDC, - 1_000_000, // 1 ether - USDCWhale, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4" - ); - bridge.bridgeERC20( - BridgeUtils.USDC, - usdcAmount, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", - 0 - ); - - assertEq(IERC20(USDC).balanceOf(USDCWhale), balance - usdcAmount); - assertEq(IERC20(USDC).balanceOf(address(vault)), usdcAmount); - } - - function testBridgeUSDT() public { - changePrank(USDTWhale); - - uint256 usdtAmount = 1000000; - - // approve - bytes4 selector = bytes4(keccak256("approve(address,uint256)")); - bytes memory data = abi.encodeWithSelector(selector, address(bridge), usdtAmount); - (bool success, bytes memory returnData) = USDT.call(data); - require(success, "Call failed"); - - assertEq(IERC20(USDT).balanceOf(address(vault)), 0); - uint256 balance = IERC20(USDT).balanceOf(USDTWhale); - - // assert emitted event - vm.expectEmit(true, true, true, false); - emit TokensDeposited( - chainID, - 0, // nonce - 0, // destination chain id - BridgeUtils.USDT, - 1_000_000, // 1 ether - USDTWhale, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4" - ); - bridge.bridgeERC20( - BridgeUtils.USDT, - usdtAmount, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", - 0 - ); - - assertEq(IERC20(USDT).balanceOf(USDTWhale), balance - usdtAmount); - assertEq(IERC20(USDT).balanceOf(address(vault)), usdtAmount); - } - - function testBridgeBTC() public { - changePrank(wBTCWhale); - - uint256 wbtcAmount = 1000000; - - // approve - IERC20(wBTC).approve(address(bridge), wbtcAmount); - - assertEq(IERC20(wBTC).balanceOf(address(vault)), 0); - uint256 balance = IERC20(wBTC).balanceOf(wBTCWhale); - - // assert emitted event - vm.expectEmit(true, true, true, false); - emit TokensDeposited( - chainID, - 0, // nonce - 0, // destination chain id - BridgeUtils.BTC, - 1_000_000, // 1 ether - wBTCWhale, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4" - ); - bridge.bridgeERC20( - BridgeUtils.BTC, - wbtcAmount, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", - 0 - ); - - assertEq(IERC20(wBTC).balanceOf(wBTCWhale), balance - wbtcAmount); - assertEq(IERC20(wBTC).balanceOf(address(vault)), wbtcAmount); - } - - function testBridgeEth() public { - changePrank(deployer); - assertEq(IERC20(wETH).balanceOf(address(vault)), 0); - uint256 balance = deployer.balance; - - // assert emitted event - vm.expectEmit(true, true, true, false); - emit IIotaBridge.TokensDeposited( - chainID, - 0, // nonce - 0, // destination chain id - BridgeUtils.ETH, - 1_000_000_00, // 1 ether - deployer, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4" - ); - - bridge.bridgeETH{value: 1 ether}( - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", 0 - ); - assertEq(IERC20(wETH).balanceOf(address(vault)), 1 ether); - assertEq(deployer.balance, balance - 1 ether); - assertEq(bridge.nonces(BridgeUtils.TOKEN_TRANSFER), 1); - } - - function testBridgeVaultReentrancy() public { - changePrank(address(bridge)); - - ReentrantAttack reentrantAttack = new ReentrantAttack(address(vault)); - vault.transferOwnership(address(reentrantAttack)); - // Fill vault with WETH - changePrank(deployer); - IWETH9(wETH).deposit{value: 10 ether}(); - IERC20(wETH).transfer(address(vault), 10 ether); - vm.expectRevert("ETH transfer failed"); - reentrantAttack.attack(); - } - - function testIotaBridgeInvalidERC20DecimalConversion() public { - IERC20(wETH).approve(address(bridge), 10 ether); - vm.expectRevert(bytes("BridgeUtils: Insufficient amount provided")); - bridge.bridgeERC20( - BridgeUtils.ETH, - 1, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", - 0 - ); - } - - function testIotaBridgeInvalidEthDecimalConversion() public { - vm.expectRevert(bytes("BridgeUtils: Insufficient amount provided")); - bridge.bridgeETH{value: 1}( - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", 0 - ); - } - - function testIotaBridgeInvalidERC20Transfer() public { - vm.expectRevert(bytes("BridgeUtils: Insufficient amount provided")); - bridge.bridgeERC20( - BridgeUtils.USDC, - 0, - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", - 0 - ); - } - - function testIotaBridgeInvalidETHTransfer() public { - vm.expectRevert(bytes("BridgeUtils: Insufficient amount provided")); - bridge.bridgeETH{value: 0}( - hex"06bb77410cd326430fa2036c8282dbb54a6f8640cea16ef5eff32d638718b3e4", 0 - ); - } - - // An e2e token transfer regression test covering message ser/de and signature verification - function testTransferIotaToEthRegressionTest() public { - address[] memory _committee = new address[](4); - uint16[] memory _stake = new uint16[](4); - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - - committee.initialize(_committee, _stake, minStakeRequired); - vault = new BridgeVault(wETH); - tokenPrices = new uint64[](5); - tokenPrices[0] = 1 * USD_VALUE_MULTIPLIER; // IOTA PRICE - tokenPrices[1] = 1 * USD_VALUE_MULTIPLIER; // BTC PRICE - tokenPrices[2] = 1 * USD_VALUE_MULTIPLIER; // ETH PRICE - tokenPrices[3] = 1 * USD_VALUE_MULTIPLIER; // USDC PRICE - tokenPrices[4] = 1 * USD_VALUE_MULTIPLIER; // USDT PRICE - - // deploy bridge config with 11 chainID - address[] memory _supportedTokens = new address[](5); - _supportedTokens[0] = address(0); - _supportedTokens[1] = wBTC; - _supportedTokens[2] = wETH; - _supportedTokens[3] = USDC; - _supportedTokens[4] = USDT; - uint8 supportedChainID = 1; - uint8[] memory _supportedDestinationChains = new uint8[](1); - _supportedDestinationChains[0] = 1; - BridgeConfig _config = new BridgeConfig(); - _config.initialize( - address(committee), 11, _supportedTokens, tokenPrices, _supportedDestinationChains - ); - - committee.initializeConfig(address(_config)); - - skip(2 days); - - uint64[] memory totalLimits = new uint64[](1); - totalLimits[0] = 100 * USD_VALUE_MULTIPLIER; - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), _supportedDestinationChains, totalLimits); - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - vault.transferOwnership(address(bridge)); - limiter.transferOwnership(address(bridge)); - - // Fill vault with WETH - changePrank(deployer); - IWETH9(wETH).deposit{value: 10 ether}(); - IERC20(wETH).transfer(address(vault), 10 ether); - address recipientAddress = 0xb18f79Fe671db47393315fFDB377Da4Ea1B7AF96; - - bytes memory payload = - hex"2080ab1ee086210a3a37355300ca24672e81062fcdb5ced6618dab203f6a3b291c0b14b18f79fe671db47393315ffdb377da4ea1b7af960200000000000186a0"; - // Create transfer message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.TOKEN_TRANSFER, - version: 1, - nonce: 1, - chainID: supportedChainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d45535341474500010000000000000001012080ab1ee086210a3a37355300ca24672e81062fcdb5ced6618dab203f6a3b291c0b14b18f79fe671db47393315ffdb377da4ea1b7af960200000000000186a0"; - - assertEq(encodedMessage, expectedEncodedMessage); - - bytes[] memory signatures = new bytes[](2); - - signatures[0] = - hex"e1cf11b380855ff1d4a451ebc2fd68477cf701b7d4ec88da3082709fe95201a5061b4b60cf13815a80ba9dfead23e220506aa74c4a863ba045d95715b4cc6b6e00"; - signatures[1] = - hex"8ba9ec92c2d5a44ecc123182f689b901a93921fd35f581354fea20b25a0ded6d055b96a64bdda77dd5a62b93d29abe93640aa3c1a136348093cd7a2418c6bfa301"; - - uint256 aBalance = recipientAddress.balance; - committee.verifySignatures(signatures, message); - - bridge.transferBridgedTokensWithSignatures(signatures, message); - assertEq(recipientAddress.balance, aBalance + 0.001 ether); - } - - // An e2e emergency op regression test covering message ser/de - function testEmergencyOpRegressionTest() public { - address[] memory _committee = new address[](4); - uint16[] memory _stake = new uint16[](4); - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - vault = new BridgeVault(wETH); - tokenPrices = new uint64[](5); - tokenPrices[0] = 1 * USD_VALUE_MULTIPLIER; // IOTA PRICE - tokenPrices[1] = 1 * USD_VALUE_MULTIPLIER; // BTC PRICE - tokenPrices[2] = 1 * USD_VALUE_MULTIPLIER; // ETH PRICE - tokenPrices[3] = 1 * USD_VALUE_MULTIPLIER; // USDC PRICE - tokenPrices[4] = 1 * USD_VALUE_MULTIPLIER; // USDT PRICE - uint8 _chainID = 2; - uint8[] memory _supportedDestinationChains = new uint8[](1); - _supportedDestinationChains[0] = 0; - address[] memory _supportedTokens = new address[](5); - _supportedTokens[0] = address(0); - _supportedTokens[1] = wBTC; - _supportedTokens[2] = wETH; - _supportedTokens[3] = USDC; - _supportedTokens[4] = USDT; - config = new BridgeConfig(); - config.initialize( - address(committee), _chainID, _supportedTokens, tokenPrices, _supportedDestinationChains - ); - - committee.initializeConfig(address(config)); - - uint64[] memory totalLimits = new uint64[](1); - totalLimits[0] = 100 * USD_VALUE_MULTIPLIER; - skip(2 days); - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), _supportedDestinationChains, totalLimits); - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - - bytes memory payload = hex"00"; - // Create emergency op message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 55, - chainID: _chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d455353414745020100000000000000370200"; - - assertEq(encodedMessage, expectedEncodedMessage); - } - - // An e2e emergency op regression test covering message ser/de and signature verification - function testEmergencyOpRegressionTestWithSigVerification() public { - address[] memory _committee = new address[](4); - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - uint16[] memory _stake = new uint16[](4); - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - - uint8 chainID = 11; - - config = new BridgeConfig(); - config.initialize( - address(committee), chainID, supportedTokens, tokenPrices, supportedChains - ); - - committee.initializeConfig(address(config)); - - vault = new BridgeVault(wETH); - - uint64[] memory totalLimits = new uint64[](1); - totalLimits[0] = 100 * USD_VALUE_MULTIPLIER; - - skip(2 days); - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), supportedChains, totalLimits); - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - - assertFalse(bridge.paused()); - - // pause - bytes memory payload = hex"00"; - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d455353414745020100000000000000000b00"; - - assertEq(encodedMessage, expectedEncodedMessage); - - bytes[] memory signatures = new bytes[](1); - - signatures[0] = - hex"859db4dff22e43821b9b451e88bc7489aec3381d3e4fb5d8cbf025a84d34964a2bd556e0a86e13cb5b2d0fa52f08d02e4b62b9e6d9e07d8f8451d4c19430806d01"; - - bridge.executeEmergencyOpWithSignatures(signatures, message); - assertTrue(bridge.paused()); - - // unpause - payload = hex"01"; - message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 1, - chainID: chainID, - payload: payload - }); - encodedMessage = BridgeUtils.encodeMessage(message); - expectedEncodedMessage = hex"5355495f4252494447455f4d455353414745020100000000000000010b01"; - - assertEq(encodedMessage, expectedEncodedMessage); - - signatures = new bytes[](3); - - signatures[0] = - hex"de5ca964c5aa1aa323cc480cd6de46eae980a1670a5fe8e12e31f724d0bcec6516e54b516737bb6ed6ccad775370c14d46f2e10100e9d16851d2050bf2349c6401"; - signatures[1] = - hex"fe8006e2013eaa7b8af0e5ac9f2890c2b2bd375d343684b2604ac6acd4142ccf5c9ec1914bce53a005232ef880bf0f597eed319d41d80e92d035c8314e1198ff00"; - signatures[2] = - hex"f5749ac37e11f22da0622082c9e63a91dc7b5c59cfdaa86438d9f6a53bbacf6b763126f1a20a826d7dff73252cf2fd68da67b9caec4d3c24a07fbd566a7a6bec00"; - - bridge.executeEmergencyOpWithSignatures(signatures, message); - assertFalse(bridge.paused()); - - // reusing the sig from nonce 0 will revert - payload = hex"00"; - message = BridgeUtils.Message({ - messageType: BridgeUtils.EMERGENCY_OP, - version: 1, - nonce: 0, - chainID: chainID, - payload: payload - }); - - signatures = new bytes[](1); - - signatures[0] = - hex"859db4dff22e43821b9b451e88bc7489aec3381d3e4fb5d8cbf025a84d34964a2bd556e0a86e13cb5b2d0fa52f08d02e4b62b9e6d9e07d8f8451d4c19430806d01"; - - vm.expectRevert(bytes("MessageVerifier: Invalid nonce")); - bridge.executeEmergencyOpWithSignatures(signatures, message); - - assertFalse(bridge.paused()); - } - - // An e2e upgrade regression test covering message ser/de and signature verification - function testUpgradeRegressionTest() public { - address[] memory _committee = new address[](4); - uint16[] memory _stake = new uint16[](4); - _committee[0] = 0x68B43fD906C0B8F024a18C56e06744F7c6157c65; - _committee[1] = 0xaCAEf39832CB995c4E049437A3E2eC6a7bad1Ab5; - _committee[2] = 0x8061f127910e8eF56F16a2C411220BaD25D61444; - _committee[3] = 0x508F3F1ff45F4ca3D8e86CDCC91445F00aCC59fC; - _stake[0] = 2500; - _stake[1] = 2500; - _stake[2] = 2500; - _stake[3] = 2500; - committee = new BridgeCommittee(); - committee.initialize(_committee, _stake, minStakeRequired); - vault = new BridgeVault(wETH); - tokenPrices = new uint64[](5); - tokenPrices[0] = 1 * USD_VALUE_MULTIPLIER; // IOTA PRICE - tokenPrices[1] = 1 * USD_VALUE_MULTIPLIER; // BTC PRICE - tokenPrices[2] = 1 * USD_VALUE_MULTIPLIER; // ETH PRICE - tokenPrices[3] = 1 * USD_VALUE_MULTIPLIER; // USDC PRICE - tokenPrices[4] = 1 * USD_VALUE_MULTIPLIER; // USDT PRICE - uint8 _chainID = 12; - uint8[] memory _supportedDestinationChains = new uint8[](1); - _supportedDestinationChains[0] = 0; - address[] memory _supportedTokens = new address[](5); - _supportedTokens[0] = address(0); - _supportedTokens[1] = wBTC; - _supportedTokens[2] = wETH; - _supportedTokens[3] = USDC; - _supportedTokens[4] = USDT; - config = new BridgeConfig(); - config.initialize( - address(committee), _chainID, _supportedTokens, tokenPrices, _supportedDestinationChains - ); - - committee.initializeConfig(address(config)); - - skip(2 days); - uint64[] memory totalLimits = new uint64[](1); - totalLimits[0] = 1_000_000 * USD_VALUE_MULTIPLIER; - limiter = new BridgeLimiter(); - limiter.initialize(address(committee), _supportedDestinationChains, totalLimits); - bridge = new IotaBridge(); - bridge.initialize(address(committee), address(vault), address(limiter)); - vault.transferOwnership(address(bridge)); - limiter.transferOwnership(address(bridge)); - - // Fill vault with WETH - changePrank(deployer); - IWETH9(wETH).deposit{value: 10 ether}(); - IERC20(wETH).transfer(address(vault), 10 ether); - - bytes memory payload = - hex"00000000000000000000000006060606060606060606060606060606060606060000000000000000000000000909090909090909090909090909090909090909000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000045cd8a76b00000000000000000000000000000000000000000000000000000000"; - // Create transfer message - BridgeUtils.Message memory message = BridgeUtils.Message({ - messageType: BridgeUtils.UPGRADE, - version: 1, - nonce: 123, - chainID: _chainID, - payload: payload - }); - bytes memory encodedMessage = BridgeUtils.encodeMessage(message); - bytes memory expectedEncodedMessage = - hex"5355495f4252494447455f4d4553534147450501000000000000007b0c00000000000000000000000006060606060606060606060606060606060606060000000000000000000000000909090909090909090909090909090909090909000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000045cd8a76b00000000000000000000000000000000000000000000000000000000"; - - assertEq(encodedMessage, expectedEncodedMessage); - - (address proxy, address newImp, bytes memory _calldata) = - BridgeUtils.decodeUpgradePayload(payload); - - assertEq(proxy, address(0x0606060606060606060606060606060606060606)); - assertEq(newImp, address(0x0909090909090909090909090909090909090909)); - assertEq(_calldata, hex"5cd8a76b"); - } -} - -contract ReentrantAttack { - IBridgeVault public vault; - bool private attackInitiated; - - constructor(address _vault) { - vault = IBridgeVault(_vault); - } - - receive() external payable { - if (!attackInitiated) { - attackInitiated = true; - vault.transferETH(payable(address(this)), 100); - } - } - - function attack() external payable { - attackInitiated = false; - vault.transferETH(payable(address(this)), 100); - } -} diff --git a/bridge/evm/test/mocks/MockIotaBridgeV2.sol b/bridge/evm/test/mocks/MockIotaBridgeV2.sol deleted file mode 100644 index fc533c6afc9..00000000000 --- a/bridge/evm/test/mocks/MockIotaBridgeV2.sol +++ /dev/null @@ -1,38 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "../../contracts/IotaBridge.sol"; - -contract MockIotaBridgeV2 is IotaBridge { - uint8 public mock; - bool public isPausing; - - function initializeV2() external { - _pause(); - } - - function initializeV2Params(uint256 value, bool _override, string memory _event) external { - if (_override) { - _pause(); - } else if (value == 42) { - _pause(); - } - - emit MockEvent(_event); - } - - function newMockFunction(bool _pausing) external { - isPausing = _pausing; - } - - function newMockFunction(bool _pausing, uint8 _mock) external { - mock = _mock; - isPausing = _pausing; - } - - // used to ignore for forge coverage - function testSkip() external view {} - - event MockEvent(string _event); -} diff --git a/bridge/evm/test/mocks/MockTokens.sol b/bridge/evm/test/mocks/MockTokens.sol deleted file mode 100644 index daf0a74d3ca..00000000000 --- a/bridge/evm/test/mocks/MockTokens.sol +++ /dev/null @@ -1,135 +0,0 @@ -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract MockWBTC is ERC20 { - constructor() ERC20("Wrapped Bitcoin", "wBTC") {} - - function mint(address to, uint256 amount) public virtual { - _mint(to, amount); - } - - function burn(address form, uint256 amount) public virtual { - _burn(form, amount); - } - - function decimals() public view virtual override returns (uint8) { - return 8; - } - - function testSkip() public {} -} - -contract MockUSDC is ERC20 { - constructor() ERC20("USD Coin", "USDC") {} - - function mint(address to, uint256 amount) public virtual { - _mint(to, amount); - } - - function burn(address form, uint256 amount) public virtual { - _burn(form, amount); - } - - function decimals() public view virtual override returns (uint8) { - return 6; - } - - function testSkip() public {} -} - -contract MockUSDT is ERC20 { - constructor() ERC20("Tether", "USDT") {} - - function mint(address to, uint256 amount) public virtual { - _mint(to, amount); - } - - function burn(address form, uint256 amount) public virtual { - _burn(form, amount); - } - - function decimals() public view virtual override returns (uint8) { - return 6; - } - - function testSkip() public {} -} - -contract MockKA is ERC20 { - constructor() ERC20("Ka Coin", "KA") {} - - function mint(address to, uint256 amount) public virtual { - _mint(to, amount); - } - - function burn(address form, uint256 amount) public virtual { - _burn(form, amount); - } - - function decimals() public view virtual override returns (uint8) { - return 9; - } - - function testSkip() public {} -} - -contract WETH { - string public name = "Wrapped Ether"; - string public symbol = "WETH"; - uint8 public decimals = 18; - - event Approval(address indexed src, address indexed guy, uint256 wad); - event Transfer(address indexed src, address indexed dst, uint256 wad); - event Deposit(address indexed dst, uint256 wad); - event Withdrawal(address indexed src, uint256 wad); - - mapping(address => uint256) public balanceOf; - mapping(address => mapping(address => uint256)) public allowance; - - function deposit() public payable { - balanceOf[msg.sender] += msg.value; - emit Deposit(msg.sender, msg.value); - } - - function withdraw(uint256 wad) public { - require(balanceOf[msg.sender] >= wad); - balanceOf[msg.sender] -= wad; - payable(msg.sender).transfer(wad); - emit Withdrawal(msg.sender, wad); - } - - function totalSupply() public view returns (uint256) { - return address(this).balance; - } - - function approve(address guy, uint256 wad) public returns (bool) { - allowance[msg.sender][guy] = wad; - emit Approval(msg.sender, guy, wad); - return true; - } - - function transfer(address dst, uint256 wad) public returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - - function transferFrom(address src, address dst, uint256 wad) public returns (bool) { - require(balanceOf[src] >= wad); - - if (src != msg.sender) { - require(allowance[src][msg.sender] >= wad); - allowance[src][msg.sender] -= wad; - } - - balanceOf[src] -= wad; - balanceOf[dst] += wad; - - emit Transfer(src, dst, wad); - - return true; - } - - function testSkip() public {} -} diff --git a/bridge/move/tokens/btc/Move.toml b/bridge/move/tokens/btc/Move.toml deleted file mode 100644 index cf125c51346..00000000000 --- a/bridge/move/tokens/btc/Move.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "BridgedBTC" -version = "0.0.1" -edition = "legacy" - -[dependencies] -MoveStdlib = { local = "../../../../crates/iota-framework/packages/move-stdlib" } -Iota = { local = "../../../../crates/iota-framework/packages/iota-framework" } - -[addresses] -bridged_btc = "0x0" diff --git a/bridge/move/tokens/btc/sources/btc.move b/bridge/move/tokens/btc/sources/btc.move deleted file mode 100644 index 8789dbb7cb9..00000000000 --- a/bridge/move/tokens/btc/sources/btc.move +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridged_btc::btc { - use std::option; - - use iota::coin; - use iota::transfer; - use iota::tx_context; - use iota::tx_context::TxContext; - - struct BTC has drop {} - - const DECIMAL: u8 = 8; - - fun init(otw: BTC, ctx: &mut TxContext) { - let (treasury_cap, metadata) = coin::create_currency( - otw, - DECIMAL, - b"BTC", - b"Bitcoin", - b"Bridged Bitcoin token", - option::none(), - ctx - ); - transfer::public_freeze_object(metadata); - transfer::public_transfer(treasury_cap, tx_context::sender(ctx)); - } -} diff --git a/bridge/move/tokens/eth/Move.toml b/bridge/move/tokens/eth/Move.toml deleted file mode 100644 index c1666e6decc..00000000000 --- a/bridge/move/tokens/eth/Move.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "BridgedETH" -version = "0.0.1" -edition = "legacy" - -[dependencies] -MoveStdlib = { local = "../../../../crates/iota-framework/packages/move-stdlib" } -Iota = { local = "../../../../crates/iota-framework/packages/iota-framework" } - -[addresses] -bridged_eth = "0x0" diff --git a/bridge/move/tokens/eth/sources/eth.move b/bridge/move/tokens/eth/sources/eth.move deleted file mode 100644 index 043a3a716a1..00000000000 --- a/bridge/move/tokens/eth/sources/eth.move +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridged_eth::eth { - use std::option; - - use iota::coin; - use iota::transfer; - use iota::tx_context; - use iota::tx_context::TxContext; - - struct ETH has drop {} - - const DECIMAL: u8 = 8; - - fun init(otw: ETH, ctx: &mut TxContext) { - let (treasury_cap, metadata) = coin::create_currency( - otw, - DECIMAL, - b"ETH", - b"Ethereum", - b"Bridged Ethereum token", - option::none(), - ctx - ); - transfer::public_freeze_object(metadata); - transfer::public_transfer(treasury_cap, tx_context::sender(ctx)) - } -} diff --git a/bridge/move/tokens/mock/ka/Move.toml b/bridge/move/tokens/mock/ka/Move.toml deleted file mode 100644 index ba168569e91..00000000000 --- a/bridge/move/tokens/mock/ka/Move.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "BridgedKa" -version = "0.0.1" -edition = "legacy" - -[dependencies] -MoveStdlib = { local = "../../../../../crates/iota-framework/packages/move-stdlib" } -Iota = { local = "../../../../../crates/iota-framework/packages/iota-framework" } - -[addresses] -bridged_ka = "0x0" diff --git a/bridge/move/tokens/mock/ka/sources/ka.move b/bridge/move/tokens/mock/ka/sources/ka.move deleted file mode 100644 index 5d17843f323..00000000000 --- a/bridge/move/tokens/mock/ka/sources/ka.move +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridged_ka::ka { - use std::option; - - use iota::coin; - use iota::transfer; - use iota::tx_context; - use iota::tx_context::TxContext; - - struct KA has drop {} - - const DECIMAL: u8 = 9; - - fun init(otw: KA, ctx: &mut TxContext) { - let (treasury_cap, metadata) = coin::create_currency( - otw, - DECIMAL, - b"Ka", - b"Ka Coin", - b"Ka, a mock token", - option::none(), - ctx - ); - transfer::public_freeze_object(metadata); - transfer::public_transfer(treasury_cap, tx_context::sender(ctx)); - } -} diff --git a/bridge/move/tokens/usdc/Move.toml b/bridge/move/tokens/usdc/Move.toml deleted file mode 100644 index 8d58d974221..00000000000 --- a/bridge/move/tokens/usdc/Move.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "BridgedUSDC" -version = "0.0.1" -edition = "legacy" - -[dependencies] -MoveStdlib = { local = "../../../../crates/iota-framework/packages/move-stdlib" } -Iota = { local = "../../../../crates/iota-framework/packages/iota-framework" } - -[addresses] -bridged_usdc = "0x0" diff --git a/bridge/move/tokens/usdc/sources/usdc.move b/bridge/move/tokens/usdc/sources/usdc.move deleted file mode 100644 index e6de9ee2f2d..00000000000 --- a/bridge/move/tokens/usdc/sources/usdc.move +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridged_usdc::usdc { - use std::option; - - use iota::coin; - use iota::transfer; - use iota::tx_context; - use iota::tx_context::TxContext; - - struct USDC has drop {} - - const DECIMAL: u8 = 6; - - fun init(otw: USDC, ctx: &mut TxContext) { - let (treasury_cap, metadata) = coin::create_currency( - otw, - DECIMAL, - b"USDC", - b"USD Coin", - b"Bridged USD Coin token", - option::none(), - ctx - ); - transfer::public_freeze_object(metadata); - transfer::public_transfer(treasury_cap, tx_context::sender(ctx)); - } -} diff --git a/bridge/move/tokens/usdt/Move.toml b/bridge/move/tokens/usdt/Move.toml deleted file mode 100644 index de3680f66a9..00000000000 --- a/bridge/move/tokens/usdt/Move.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "BridgedUSDT" -version = "0.0.1" -edition = "legacy" - -[dependencies] -MoveStdlib = { local = "../../../../crates/iota-framework/packages/move-stdlib" } -Iota = { local = "../../../../crates/iota-framework/packages/iota-framework" } - -[addresses] -bridged_usdt = "0x0" diff --git a/bridge/move/tokens/usdt/sources/usdt.move b/bridge/move/tokens/usdt/sources/usdt.move deleted file mode 100644 index 2a2d455cb7b..00000000000 --- a/bridge/move/tokens/usdt/sources/usdt.move +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridged_usdt::usdt { - use std::option; - - use iota::coin; - use iota::transfer; - use iota::tx_context; - use iota::tx_context::TxContext; - - struct USDT has drop {} - - const DECIMAL: u8 = 6; - - fun init(otw: USDT, ctx: &mut TxContext) { - let (treasury_cap, metadata) = coin::create_currency( - otw, - DECIMAL, - b"USDT", - b"Tether", - b"Bridged Tether token", - option::none(), - ctx - ); - transfer::public_freeze_object(metadata); - transfer::public_transfer(treasury_cap, tx_context::sender(ctx)); - } -} diff --git a/consensus/core/src/authority_node.rs b/consensus/core/src/authority_node.rs index 27de9a7466e..35d584041cf 100644 --- a/consensus/core/src/authority_node.rs +++ b/consensus/core/src/authority_node.rs @@ -444,7 +444,7 @@ mod tests { let temp_dir = TempDir::new().unwrap(); let parameters = Parameters { - db_path: temp_dir.into_path(), + db_path: temp_dir.keep(), ..Default::default() }; let txn_verifier = NoopTransactionVerifier {}; diff --git a/consensus/core/src/context.rs b/consensus/core/src/context.rs index 42bfba6af78..52760c0a4df 100644 --- a/consensus/core/src/context.rs +++ b/consensus/core/src/context.rs @@ -68,7 +68,7 @@ impl Context { AuthorityIndex::new_for_test(0), committee, Parameters { - db_path: temp_dir.into_path(), + db_path: temp_dir.keep(), ..Default::default() }, ProtocolConfig::get_for_max_version_UNSAFE(), diff --git a/crates/iota-archival/src/tests.rs b/crates/iota-archival/src/tests.rs index eb031bc229d..882c07c52e9 100644 --- a/crates/iota-archival/src/tests.rs +++ b/crates/iota-archival/src/tests.rs @@ -50,7 +50,7 @@ struct TestState { fn temp_dir() -> std::path::PathBuf { tempdir() .expect("Failed to open temporary directory") - .into_path() + .keep() } async fn write_new_checkpoints_to_store( diff --git a/crates/iota-aws-orchestrator/src/settings.rs b/crates/iota-aws-orchestrator/src/settings.rs index 85eef212b51..f175799eb05 100644 --- a/crates/iota-aws-orchestrator/src/settings.rs +++ b/crates/iota-aws-orchestrator/src/settings.rs @@ -179,7 +179,7 @@ impl Settings { #[cfg(test)] pub fn new_for_test() -> Self { // Create a temporary public key file. - let mut path = tempfile::tempdir().unwrap().into_path(); + let mut path = tempfile::tempdir().unwrap().keep(); path.push("test_public_key.pub"); let public_key = "This is a fake public key for tests"; fs::write(&path, public_key).unwrap(); diff --git a/crates/iota-benchmark/tests/simtest.rs b/crates/iota-benchmark/tests/simtest.rs index 663e7c2df5d..c1808aa6e4c 100644 --- a/crates/iota-benchmark/tests/simtest.rs +++ b/crates/iota-benchmark/tests/simtest.rs @@ -559,7 +559,7 @@ mod test { #[sim_test(config = "test_config()")] async fn test_data_ingestion_pipeline() { - let path = nondeterministic!(TempDir::new().unwrap()).into_path(); + let path = nondeterministic!(TempDir::new().unwrap()).keep(); let test_cluster = Arc::new( init_test_cluster_builder(4, 1000) .with_data_ingestion_dir(path.clone()) diff --git a/crates/iota-bridge-cli/Cargo.toml b/crates/iota-bridge-cli/Cargo.toml deleted file mode 100644 index 878cf6ef25d..00000000000 --- a/crates/iota-bridge-cli/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "iota-bridge-cli" -version.workspace = true -authors = ["IOTA Foundation "] -edition = "2021" -license = "Apache-2.0" -publish = false - -[dependencies] -# external dependencies -anyhow.workspace = true -clap.workspace = true -ethers = "2.0" -fastcrypto.workspace = true -futures.workspace = true -reqwest.workspace = true -serde.workspace = true -serde_json.workspace = true -serde_with.workspace = true -tokio.workspace = true -tracing.workspace = true - -# internal dependencies -iota-bridge.workspace = true -iota-config.workspace = true -iota-json-rpc-types.workspace = true -iota-keys.workspace = true -iota-sdk.workspace = true -iota-types.workspace = true -move-core-types.workspace = true -shared-crypto.workspace = true -telemetry-subscribers.workspace = true diff --git a/crates/iota-bridge-cli/src/lib.rs b/crates/iota-bridge-cli/src/lib.rs deleted file mode 100644 index 3e3264f153a..00000000000 --- a/crates/iota-bridge-cli/src/lib.rs +++ /dev/null @@ -1,738 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{path::PathBuf, str::FromStr, sync::Arc}; - -use anyhow::anyhow; -use clap::*; -use ethers::{ - providers::Middleware, - types::{Address as EthAddress, U256}, -}; -use fastcrypto::{ - encoding::{Encoding, Hex}, - hash::{HashFunction, Keccak256}, -}; -use iota_bridge::{ - abi::{EthBridgeCommittee, EthIotaBridge, eth_iota_bridge}, - crypto::BridgeAuthorityPublicKeyBytes, - error::BridgeResult, - iota_client::IotaBridgeClient, - types::{ - AddTokensOnEvmAction, AddTokensOnIotaAction, AssetPriceUpdateAction, - BlocklistCommitteeAction, BlocklistType, BridgeAction, EmergencyAction, - EmergencyActionType, EvmContractUpgradeAction, LimitUpdateAction, - }, - utils::{EthSigner, get_eth_signer_client}, -}; -use iota_config::Config; -use iota_json_rpc_types::IotaObjectDataOptions; -use iota_keys::keypair_file::read_key; -use iota_sdk::IotaClientBuilder; -use iota_types::{ - BRIDGE_PACKAGE_ID, TypeTag, - base_types::{IotaAddress, ObjectID, ObjectRef}, - bridge::{BRIDGE_MODULE_NAME, BridgeChainId}, - crypto::{IotaKeyPair, Signature}, - programmable_transaction_builder::ProgrammableTransactionBuilder, - transaction::{ObjectArg, Transaction, TransactionData}, -}; -use move_core_types::ident_str; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; -use shared_crypto::intent::{Intent, IntentMessage}; -use tracing::info; - -pub const SEPOLIA_BRIDGE_PROXY_ADDR: &str = "0xAE68F87938439afEEDd6552B0E83D2CbC2473623"; - -#[derive(Parser)] -pub struct Args { - #[command(subcommand)] - pub command: BridgeCommand, -} - -#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)] -pub enum Network { - Testnet, -} - -#[derive(Parser)] -pub enum BridgeCommand { - CreateBridgeValidatorKey { - path: PathBuf, - }, - CreateBridgeClientKey { - path: PathBuf, - #[arg(long, default_value = "false")] - use_ecdsa: bool, - }, - /// Read bridge key from a file and print related information - /// If `is-validator-key` is true, the key must be a secp256k1 key - ExamineKey { - path: PathBuf, - #[arg(long)] - is_validator_key: bool, - }, - CreateBridgeNodeConfigTemplate { - path: PathBuf, - #[arg(long)] - run_client: bool, - }, - /// Governance client to facilitate and execute Bridge governance actions - Governance { - /// Path of BridgeCliConfig - #[arg(long)] - config_path: PathBuf, - #[arg(long)] - chain_id: u8, - #[command(subcommand)] - cmd: GovernanceClientCommands, - /// If true, only collect signatures but not execute on chain - #[arg(long)] - dry_run: bool, - }, - /// View current status of Eth bridge - ViewEthBridge { - #[arg(long)] - network: Option, - #[arg(long)] - bridge_proxy: Option, - #[arg(long)] - eth_rpc_url: String, - }, - /// View current list of registered validators - ViewBridgeRegistration { - #[arg(long)] - iota_rpc_url: String, - }, - /// View current status of IOTA bridge - ViewIotaBridge { - #[arg(long)] - iota_rpc_url: String, - #[arg(long, default_value = "false")] - hex: bool, - #[arg(long, default_value = "false")] - ping: bool, - }, - /// Client to facilitate and execute Bridge actions - Client { - /// Path of BridgeCliConfig - #[arg(long)] - config_path: PathBuf, - #[command(subcommand)] - cmd: BridgeClientCommands, - }, -} - -#[derive(Parser)] -pub enum GovernanceClientCommands { - EmergencyButton { - #[arg(name = "nonce", long)] - nonce: u64, - #[arg(name = "action-type", long)] - action_type: EmergencyActionType, - }, - UpdateCommitteeBlocklist { - #[arg(name = "nonce", long)] - nonce: u64, - #[arg(name = "blocklist-type", long)] - blocklist_type: BlocklistType, - #[arg(name = "pubkey-hex", use_value_delimiter = true, long)] - pubkeys_hex: Vec, - }, - UpdateLimit { - #[arg(name = "nonce", long)] - nonce: u64, - #[arg(name = "sending-chain", long)] - sending_chain: u8, - #[arg(name = "new-usd-limit", long)] - new_usd_limit: u64, - }, - UpdateAssetPrice { - #[arg(name = "nonce", long)] - nonce: u64, - #[arg(name = "token-id", long)] - token_id: u8, - #[arg(name = "new-usd-price", long)] - new_usd_price: u64, - }, - AddTokensOnIota { - #[arg(name = "nonce", long)] - nonce: u64, - #[arg(name = "token-ids", use_value_delimiter = true, long)] - token_ids: Vec, - #[arg(name = "token-type-names", use_value_delimiter = true, long)] - token_type_names: Vec, - #[arg(name = "token-prices", use_value_delimiter = true, long)] - token_prices: Vec, - }, - AddTokensOnEvm { - #[arg(name = "nonce", long)] - nonce: u64, - #[arg(name = "token-ids", use_value_delimiter = true, long)] - token_ids: Vec, - #[arg(name = "token-type-names", use_value_delimiter = true, long)] - token_addresses: Vec, - #[arg(name = "token-prices", use_value_delimiter = true, long)] - token_prices: Vec, - #[arg(name = "token-iota-decimals", use_value_delimiter = true, long)] - token_iota_decimals: Vec, - }, - #[command(name = "upgrade-evm-contract")] - UpgradeEVMContract { - #[arg(name = "nonce", long)] - nonce: u64, - #[arg(name = "proxy-address", long)] - proxy_address: EthAddress, - /// The address of the new implementation contract - #[arg(name = "implementation-address", long)] - implementation_address: EthAddress, - /// Function selector with params types, e.g. `foo(uint256,bool,string)` - #[arg(name = "function-selector", long)] - function_selector: Option, - /// Params to be passed to the function, e.g. `420,false,hello` - #[arg(name = "params", use_value_delimiter = true, long)] - params: Vec, - }, -} - -pub fn make_action(chain_id: BridgeChainId, cmd: &GovernanceClientCommands) -> BridgeAction { - match cmd { - GovernanceClientCommands::EmergencyButton { nonce, action_type } => { - BridgeAction::EmergencyAction(EmergencyAction { - nonce: *nonce, - chain_id, - action_type: *action_type, - }) - } - GovernanceClientCommands::UpdateCommitteeBlocklist { - nonce, - blocklist_type, - pubkeys_hex, - } => BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - nonce: *nonce, - chain_id, - blocklist_type: *blocklist_type, - members_to_update: pubkeys_hex.clone(), - }), - GovernanceClientCommands::UpdateLimit { - nonce, - sending_chain, - new_usd_limit, - } => { - let sending_chain_id = - BridgeChainId::try_from(*sending_chain).expect("Invalid sending chain id"); - BridgeAction::LimitUpdateAction(LimitUpdateAction { - nonce: *nonce, - chain_id, - sending_chain_id, - new_usd_limit: *new_usd_limit, - }) - } - GovernanceClientCommands::UpdateAssetPrice { - nonce, - token_id, - new_usd_price, - } => BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction { - nonce: *nonce, - chain_id, - token_id: *token_id, - new_usd_price: *new_usd_price, - }), - GovernanceClientCommands::AddTokensOnIota { - nonce, - token_ids, - token_type_names, - token_prices, - } => { - assert_eq!(token_ids.len(), token_type_names.len()); - assert_eq!(token_ids.len(), token_prices.len()); - BridgeAction::AddTokensOnIotaAction(AddTokensOnIotaAction { - nonce: *nonce, - chain_id, - native: false, // only foreign tokens are supported now - token_ids: token_ids.clone(), - token_type_names: token_type_names.clone(), - token_prices: token_prices.clone(), - }) - } - GovernanceClientCommands::AddTokensOnEvm { - nonce, - token_ids, - token_addresses, - token_prices, - token_iota_decimals, - } => { - assert_eq!(token_ids.len(), token_addresses.len()); - assert_eq!(token_ids.len(), token_prices.len()); - assert_eq!(token_ids.len(), token_iota_decimals.len()); - BridgeAction::AddTokensOnEvmAction(AddTokensOnEvmAction { - nonce: *nonce, - native: true, // only eth native tokens are supported now - chain_id, - token_ids: token_ids.clone(), - token_addresses: token_addresses.clone(), - token_prices: token_prices.clone(), - token_iota_decimals: token_iota_decimals.clone(), - }) - } - GovernanceClientCommands::UpgradeEVMContract { - nonce, - proxy_address, - implementation_address, - function_selector, - params, - } => { - let call_data = match function_selector { - Some(function_selector) => encode_call_data(function_selector, params), - None => vec![], - }; - BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - nonce: *nonce, - chain_id, - proxy_address: *proxy_address, - new_impl_address: *implementation_address, - call_data, - }) - } - } -} - -fn encode_call_data(function_selector: &str, params: &[String]) -> Vec { - let left = function_selector - .find('(') - .expect("Invalid function selector, no left parentheses"); - let right = function_selector - .find(')') - .expect("Invalid function selector, no right parentheses"); - let param_types = function_selector[left + 1..right] - .split(',') - .map(|x| x.trim()) - .collect::>(); - - assert_eq!(param_types.len(), params.len(), "Invalid number of params"); - - let mut call_data = Keccak256::digest(function_selector).digest[0..4].to_vec(); - let mut tokens = vec![]; - for (param, param_type) in params.iter().zip(param_types.iter()) { - match param_type.to_lowercase().as_str() { - "uint256" => { - tokens.push(ethers::abi::Token::Uint( - ethers::types::U256::from_dec_str(param).expect("Invalid U256"), - )); - } - "bool" => { - tokens.push(ethers::abi::Token::Bool(match param.as_str() { - "true" => true, - "false" => false, - _ => panic!("Invalid bool in params"), - })); - } - "string" => { - tokens.push(ethers::abi::Token::String(param.clone())); - } - // TODO: need to support more types if needed - _ => panic!("Invalid param type"), - } - } - if !tokens.is_empty() { - call_data.extend(ethers::abi::encode(&tokens)); - } - call_data -} - -pub fn select_contract_address( - config: &LoadedBridgeCliConfig, - cmd: &GovernanceClientCommands, -) -> EthAddress { - match cmd { - GovernanceClientCommands::EmergencyButton { .. } => config.eth_bridge_proxy_address, - GovernanceClientCommands::UpdateCommitteeBlocklist { .. } => { - config.eth_bridge_committee_proxy_address - } - GovernanceClientCommands::UpdateLimit { .. } => config.eth_bridge_limiter_proxy_address, - GovernanceClientCommands::UpdateAssetPrice { .. } => config.eth_bridge_config_proxy_address, - GovernanceClientCommands::UpgradeEVMContract { proxy_address, .. } => *proxy_address, - GovernanceClientCommands::AddTokensOnIota { .. } => unreachable!(), - GovernanceClientCommands::AddTokensOnEvm { .. } => config.eth_bridge_config_proxy_address, - } -} - -#[serde_as] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct BridgeCliConfig { - /// Rpc url for IOTA fullnode, used for query stuff and submit transactions. - pub iota_rpc_url: String, - /// Rpc url for Eth fullnode, used for query stuff. - pub eth_rpc_url: String, - /// Proxy address for IotaBridge deployed on Eth - pub eth_bridge_proxy_address: EthAddress, - /// Path of the file where private key is stored. The content could be any - /// of the following: - /// - Base64 encoded `flag || privkey` for ECDSA key - /// - Base64 encoded `privkey` for Raw key - /// - Hex encoded `privkey` for Raw key - /// At least one of `iota_key_path` or `eth_key_path` must be provided. - /// If only one is provided, it will be used for both IOTA and Eth. - pub iota_key_path: Option, - /// See `iota_key_path`. Must be Secp256k1 key. - pub eth_key_path: Option, -} - -impl Config for BridgeCliConfig {} - -pub struct LoadedBridgeCliConfig { - /// Rpc url for IOTA fullnode, used for query stuff and submit transactions. - pub iota_rpc_url: String, - /// Rpc url for Eth fullnode, used for query stuff. - pub eth_rpc_url: String, - /// Proxy address for IotaBridge deployed on Eth - pub eth_bridge_proxy_address: EthAddress, - /// Proxy address for BridgeCommittee deployed on Eth - pub eth_bridge_committee_proxy_address: EthAddress, - /// Proxy address for BridgeConfig deployed on Eth - pub eth_bridge_config_proxy_address: EthAddress, - /// Proxy address for BridgeLimiter deployed on Eth - pub eth_bridge_limiter_proxy_address: EthAddress, - /// Key pair for IOTA operations - iota_key: IotaKeyPair, - /// Key pair for Eth operations, must be Secp256k1 key - eth_signer: EthSigner, -} - -impl LoadedBridgeCliConfig { - pub async fn load(cli_config: BridgeCliConfig) -> anyhow::Result { - if cli_config.eth_key_path.is_none() && cli_config.iota_key_path.is_none() { - return Err(anyhow!( - "At least one of `iota_key_path` or `eth_key_path` must be provided" - )); - } - let iota_key = if let Some(iota_key_path) = &cli_config.iota_key_path { - Some(read_key(iota_key_path, false)?) - } else { - None - }; - let eth_key = if let Some(eth_key_path) = &cli_config.eth_key_path { - let eth_key = read_key(eth_key_path, true)?; - Some(eth_key) - } else { - None - }; - let (eth_key, iota_key) = { - if eth_key.is_none() { - let iota_key = iota_key.unwrap(); - if !matches!(iota_key, IotaKeyPair::Secp256k1(_)) { - return Err(anyhow!("Eth key must be an ECDSA key")); - } - (iota_key.copy(), iota_key) - } else if iota_key.is_none() { - let eth_key = eth_key.unwrap(); - (eth_key.copy(), eth_key) - } else { - (eth_key.unwrap(), iota_key.unwrap()) - } - }; - - let provider = Arc::new( - ethers::prelude::Provider::::try_from(&cli_config.eth_rpc_url) - .unwrap() - .interval(std::time::Duration::from_millis(2000)), - ); - let private_key = Hex::encode(eth_key.to_bytes_no_flag()); - let eth_signer = get_eth_signer_client(&cli_config.eth_rpc_url, &private_key).await?; - let iota_bridge = EthIotaBridge::new(cli_config.eth_bridge_proxy_address, provider.clone()); - let eth_bridge_committee_proxy_address: EthAddress = iota_bridge.committee().call().await?; - let eth_bridge_limiter_proxy_address: EthAddress = iota_bridge.limiter().call().await?; - let eth_committee = - EthBridgeCommittee::new(eth_bridge_committee_proxy_address, provider.clone()); - let eth_bridge_committee_proxy_address: EthAddress = iota_bridge.committee().call().await?; - let eth_bridge_config_proxy_address: EthAddress = eth_committee.config().call().await?; - - let eth_address = eth_signer.address(); - let eth_chain_id = provider.get_chainid().await?; - let iota_address = IotaAddress::from(&iota_key.public()); - println!("Using IOTA address: {:?}", iota_address); - println!("Using Eth address: {:?}", eth_address); - println!("Using Eth chain: {:?}", eth_chain_id); - - Ok(Self { - iota_rpc_url: cli_config.iota_rpc_url, - eth_rpc_url: cli_config.eth_rpc_url, - eth_bridge_proxy_address: cli_config.eth_bridge_proxy_address, - eth_bridge_committee_proxy_address, - eth_bridge_limiter_proxy_address, - eth_bridge_config_proxy_address, - iota_key, - eth_signer, - }) - } -} - -impl LoadedBridgeCliConfig { - pub fn eth_signer(self: &LoadedBridgeCliConfig) -> &EthSigner { - &self.eth_signer - } - - pub async fn get_iota_account_info( - self: &LoadedBridgeCliConfig, - ) -> anyhow::Result<(IotaKeyPair, IotaAddress, ObjectRef)> { - let pubkey = self.iota_key.public(); - let iota_client_address = IotaAddress::from(&pubkey); - let iota_sdk_client = IotaClientBuilder::default() - .build(self.iota_rpc_url.clone()) - .await?; - let gases = iota_sdk_client - .coin_read_api() - .get_coins(iota_client_address, None, None, None) - .await? - .data; - // TODO: is 5 IOTA a good number? - let gas = gases - .into_iter() - .find(|coin| coin.balance >= 5_000_000_000) - .ok_or(anyhow!( - "Did not find gas object with enough balance for {}", - iota_client_address - ))?; - println!("Using Gas object: {}", gas.coin_object_id); - Ok((self.iota_key.copy(), iota_client_address, gas.object_ref())) - } -} -#[derive(Parser)] -pub enum BridgeClientCommands { - DepositNativeEtherOnEth { - #[arg(long)] - ether_amount: f64, - #[arg(long)] - target_chain: u8, - #[arg(long)] - iota_recipient_address: IotaAddress, - }, - DepositOnIota { - #[arg(long)] - coin_object_id: ObjectID, - #[arg(long)] - coin_type: String, - #[arg(long)] - target_chain: u8, - #[arg(long)] - recipient_address: EthAddress, - }, - ClaimOnEth { - #[arg(long)] - seq_num: u64, - }, -} - -impl BridgeClientCommands { - pub async fn handle( - self, - config: &LoadedBridgeCliConfig, - iota_bridge_client: IotaBridgeClient, - ) -> anyhow::Result<()> { - match self { - BridgeClientCommands::DepositNativeEtherOnEth { - ether_amount, - target_chain, - iota_recipient_address, - } => { - let eth_iota_bridge = EthIotaBridge::new( - config.eth_bridge_proxy_address, - Arc::new(config.eth_signer().clone()), - ); - // Note: even with f64 there may still be loss of precision even there are a lot - // of 0s - let int_part = ether_amount.trunc() as u64; - let frac_part = ether_amount.fract(); - let int_wei = U256::from(int_part) * U256::exp10(18); - let frac_wei = U256::from((frac_part * 1_000_000_000_000_000_000f64) as u64); - let amount = int_wei + frac_wei; - let eth_tx = eth_iota_bridge - .bridge_eth(iota_recipient_address.to_vec().into(), target_chain) - .value(amount); - let pending_tx = eth_tx.send().await.unwrap(); - let tx_receipt = pending_tx.await.unwrap().unwrap(); - info!( - "Deposited {ether_amount} Ethers to {:?} (target chain {target_chain}). Receipt: {:?}", - iota_recipient_address, tx_receipt, - ); - Ok(()) - } - BridgeClientCommands::ClaimOnEth { seq_num } => { - claim_on_eth(seq_num, config, iota_bridge_client) - .await - .map_err(|e| anyhow!("{:?}", e)) - } - BridgeClientCommands::DepositOnIota { - coin_object_id, - coin_type, - target_chain, - recipient_address, - } => { - let target_chain = BridgeChainId::try_from(target_chain).expect("Invalid chain id"); - let coin_type = TypeTag::from_str(&coin_type).expect("Invalid coin type"); - deposit_on_iota( - coin_object_id, - coin_type, - target_chain, - recipient_address, - config, - iota_bridge_client, - ) - .await - } - } - } -} - -async fn deposit_on_iota( - coin_object_id: ObjectID, - coin_type: TypeTag, - target_chain: BridgeChainId, - recipient_address: EthAddress, - config: &LoadedBridgeCliConfig, - iota_bridge_client: IotaBridgeClient, -) -> anyhow::Result<()> { - let target_chain = target_chain as u8; - let iota_client = iota_bridge_client.iota_client(); - let bridge_object_arg = iota_bridge_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - let rgp = iota_client - .governance_api() - .get_reference_gas_price() - .await - .unwrap(); - let sender = IotaAddress::from(&config.iota_key.public()); - let gas_obj_ref = iota_client - .coin_read_api() - .select_coins(sender, None, 1_000_000_000, vec![]) - .await? - .first() - .ok_or(anyhow!("No coin found for address {}", sender))? - .object_ref(); - let coin_obj_ref = iota_client - .read_api() - .get_object_with_options(coin_object_id, IotaObjectDataOptions::default()) - .await? - .data - .unwrap() - .object_ref(); - - let mut builder = ProgrammableTransactionBuilder::new(); - let arg_target_chain = builder.pure(target_chain).unwrap(); - let arg_target_address = builder.pure(recipient_address.as_bytes()).unwrap(); - let arg_token = builder - .obj(ObjectArg::ImmOrOwnedObject(coin_obj_ref)) - .unwrap(); - let arg_bridge = builder.obj(bridge_object_arg).unwrap(); - - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - BRIDGE_MODULE_NAME.to_owned(), - ident_str!("send_token").to_owned(), - vec![coin_type], - vec![arg_bridge, arg_target_chain, arg_target_address, arg_token], - ); - let pt = builder.finish(); - let tx_data = - TransactionData::new_programmable(sender, vec![gas_obj_ref], pt, 500_000_000, rgp); - let sig = Signature::new_secure( - &IntentMessage::new(Intent::iota_transaction(), tx_data.clone()), - &config.iota_key, - ); - let signed_tx = Transaction::from_data(tx_data, vec![sig]); - let tx_digest = *signed_tx.digest(); - info!(?tx_digest, "Sending deposit transaction to IOTA."); - let resp = iota_bridge_client - .execute_transaction_block_with_effects(signed_tx) - .await - .expect("Failed to execute transaction block"); - if !resp.status_ok().unwrap() { - return Err(anyhow!("Transaction {:?} failed: {:?}", tx_digest, resp)); - } - let events = resp.events.unwrap(); - info!( - ?tx_digest, - "Deposit transaction succeeded. Events: {:?}", events - ); - Ok(()) -} - -async fn claim_on_eth( - seq_num: u64, - config: &LoadedBridgeCliConfig, - iota_bridge_client: IotaBridgeClient, -) -> BridgeResult<()> { - let iota_chain_id = iota_bridge_client.get_bridge_summary().await?.chain_id; - let parsed_message = iota_bridge_client - .get_parsed_token_transfer_message(iota_chain_id, seq_num) - .await?; - if parsed_message.is_none() { - println!("No record found for seq_num: {seq_num}, chain id: {iota_chain_id}"); - return Ok(()); - } - let parsed_message = parsed_message.unwrap(); - let sigs = iota_bridge_client - .get_token_transfer_action_onchain_signatures_until_success(iota_chain_id, seq_num) - .await; - if sigs.is_none() { - println!("No signatures found for seq_num: {seq_num}, chain id: {iota_chain_id}"); - return Ok(()); - } - let signatures = sigs - .unwrap() - .into_iter() - .map(|sig: Vec| ethers::types::Bytes::from(sig)) - .collect::>(); - - let eth_iota_bridge = EthIotaBridge::new( - config.eth_bridge_proxy_address, - Arc::new(config.eth_signer().clone()), - ); - let message = eth_iota_bridge::Message::from(parsed_message); - let tx = eth_iota_bridge.transfer_bridged_tokens_with_signatures(signatures, message); - let _eth_claim_tx_receipt = tx.send().await.unwrap().await.unwrap().unwrap(); - info!("IOTA to Eth bridge transfer claimed"); - Ok(()) -} - -#[cfg(test)] -mod tests { - use ethers::abi::FunctionExt; - - use super::*; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_encode_call_data() { - let abi_json = - std::fs::read_to_string("../iota-bridge/abi/tests/mock_iota_bridge_v2.json").unwrap(); - let abi: ethers::abi::Abi = serde_json::from_str(&abi_json).unwrap(); - - let function_selector = "initializeV2Params(uint256,bool,string)"; - let params = vec!["420".to_string(), "false".to_string(), "hello".to_string()]; - let call_data = encode_call_data(function_selector, ¶ms); - - let function = abi - .functions() - .find(|f| { - let selector = f.selector(); - call_data.starts_with(selector.as_ref()) - }) - .expect("Function not found"); - - // Decode the data excluding the selector - let tokens = function.decode_input(&call_data[4..]).unwrap(); - assert_eq!( - tokens, - vec![ - ethers::abi::Token::Uint(ethers::types::U256::from_dec_str("420").unwrap()), - ethers::abi::Token::Bool(false), - ethers::abi::Token::String("hello".to_string()) - ] - ) - } -} diff --git a/crates/iota-bridge-cli/src/main.rs b/crates/iota-bridge-cli/src/main.rs deleted file mode 100644 index cd6d90fb8ec..00000000000 --- a/crates/iota-bridge-cli/src/main.rs +++ /dev/null @@ -1,589 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - collections::HashMap, - str::{FromStr, from_utf8}, - sync::Arc, - time::Duration, -}; - -use clap::*; -use ethers::{providers::Middleware, types::Address as EthAddress}; -use fastcrypto::encoding::{Encoding, Hex}; -use iota_bridge::{ - client::bridge_authority_aggregator::BridgeAuthorityAggregator, - crypto::{BridgeAuthorityPublicKey, BridgeAuthorityPublicKeyBytes}, - eth_transaction_builder::build_eth_transaction, - iota_client::IotaClient, - iota_transaction_builder::build_iota_transaction, - types::BridgeActionType, - utils::{ - EthBridgeContracts, examine_key, generate_bridge_authority_key_and_write_to_file, - generate_bridge_client_key_and_write_to_file, - generate_bridge_node_config_and_write_to_file, get_eth_contracts, - }, -}; -use iota_bridge_cli::{ - Args, BridgeCliConfig, BridgeCommand, LoadedBridgeCliConfig, Network, - SEPOLIA_BRIDGE_PROXY_ADDR, make_action, select_contract_address, -}; -use iota_config::Config; -use iota_sdk::{IotaClient as IotaSdkClient, IotaClientBuilder}; -use iota_types::{ - base_types::IotaAddress, - bridge::{BridgeChainId, MoveTypeCommitteeMember, MoveTypeCommitteeMemberRegistration}, - committee::TOTAL_VOTING_POWER, - crypto::{AuthorityPublicKeyBytes, Signature, ToFromBytes}, - transaction::Transaction, -}; -use shared_crypto::intent::{Intent, IntentMessage}; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - // Init logging - let (_guard, _filter_handle) = telemetry_subscribers::TelemetryConfig::new() - .with_env() - .init(); - let args = Args::parse(); - - match args.command { - BridgeCommand::CreateBridgeValidatorKey { path } => { - generate_bridge_authority_key_and_write_to_file(&path)?; - println!("Bridge validator key generated at {}", path.display()); - } - BridgeCommand::CreateBridgeClientKey { path, use_ecdsa } => { - generate_bridge_client_key_and_write_to_file(&path, use_ecdsa)?; - println!("Bridge client key generated at {}", path.display()); - } - BridgeCommand::ExamineKey { - path, - is_validator_key, - } => { - examine_key(&path, is_validator_key)?; - } - BridgeCommand::CreateBridgeNodeConfigTemplate { path, run_client } => { - generate_bridge_node_config_and_write_to_file(&path, run_client)?; - println!( - "Bridge node config template generated at {}", - path.display() - ); - } - - BridgeCommand::Governance { - config_path, - chain_id, - cmd, - dry_run, - } => { - let chain_id = BridgeChainId::try_from(chain_id).expect("Invalid chain id"); - println!("Chain ID: {:?}", chain_id); - let config = BridgeCliConfig::load(config_path).expect("Couldn't load BridgeCliConfig"); - let config = LoadedBridgeCliConfig::load(config).await?; - let iota_bridge_client = IotaClient::::new(&config.iota_rpc_url).await?; - - let (iota_key, iota_address, gas_object_ref) = config - .get_iota_account_info() - .await - .expect("Failed to get iota account info"); - let bridge_summary = iota_bridge_client - .get_bridge_summary() - .await - .expect("Failed to get bridge summary"); - let bridge_committee = Arc::new( - iota_bridge_client - .get_bridge_committee() - .await - .expect("Failed to get bridge committee"), - ); - let agg = BridgeAuthorityAggregator::new(bridge_committee); - - // Handle IOTA Side - if chain_id.is_iota_chain() { - let iota_chain_id = BridgeChainId::try_from(bridge_summary.chain_id).unwrap(); - assert_eq!( - iota_chain_id, chain_id, - "Chain ID mismatch, expected: {:?}, got from url: {:?}", - chain_id, iota_chain_id - ); - // Create BridgeAction - let iota_action = make_action(iota_chain_id, &cmd); - println!("Action to execute on IOTA: {:?}", iota_action); - let certified_action = agg - .request_committee_signatures(iota_action) - .await - .expect("Failed to request committee signatures"); - if dry_run { - println!("Dryrun succeeded."); - return Ok(()); - } - let bridge_arg = iota_bridge_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - let rgp = iota_bridge_client - .get_reference_gas_price_until_success() - .await; - let id_token_map = iota_bridge_client.get_token_id_map().await.unwrap(); - let tx = build_iota_transaction( - iota_address, - &gas_object_ref, - certified_action, - bridge_arg, - &id_token_map, - rgp, - ) - .expect("Failed to build iota transaction"); - let iota_sig = Signature::new_secure( - &IntentMessage::new(Intent::iota_transaction(), tx.clone()), - &iota_key, - ); - let tx = Transaction::from_data(tx, vec![iota_sig]); - let resp = iota_bridge_client - .execute_transaction_block_with_effects(tx) - .await - .expect("Failed to execute transaction block with effects"); - if resp.status_ok().unwrap() { - println!("IOTA Transaction succeeded: {:?}", resp.digest); - } else { - println!( - "IOTA Transaction failed: {:?}. Effects: {:?}", - resp.digest, resp.effects - ); - } - return Ok(()); - } - - // Handle eth side - // TODO assert chain id returned from rpc matches chain_id - let eth_signer_client = config.eth_signer(); - // Create BridgeAction - let eth_action = make_action(chain_id, &cmd); - println!("Action to execute on Eth: {:?}", eth_action); - // Create Eth Signer Client - // TODO if a validator is blocklisted on eth, ignore their signatures? - let certified_action = agg - .request_committee_signatures(eth_action) - .await - .expect("Failed to request committee signatures"); - if dry_run { - println!("Dryrun succeeded."); - return Ok(()); - } - let contract_address = select_contract_address(&config, &cmd); - let tx = build_eth_transaction( - contract_address, - eth_signer_client.clone(), - certified_action, - ) - .await - .expect("Failed to build eth transaction"); - println!("sending Eth tx: {:?}", tx); - match tx.send().await { - Ok(tx_hash) => { - println!("Transaction sent with hash: {:?}", tx_hash); - } - Err(err) => { - let revert = err.as_revert(); - println!("Transaction reverted: {:?}", revert); - } - }; - - return Ok(()); - } - - BridgeCommand::ViewEthBridge { - network, - bridge_proxy, - eth_rpc_url, - } => { - let bridge_proxy = match network { - Some(Network::Testnet) => { - Ok(EthAddress::from_str(SEPOLIA_BRIDGE_PROXY_ADDR).unwrap()) - } - None => bridge_proxy.ok_or(anyhow::anyhow!( - "Network or bridge proxy address must be provided" - )), - }?; - let provider = Arc::new( - ethers::prelude::Provider::::try_from(eth_rpc_url) - .unwrap() - .interval(std::time::Duration::from_millis(2000)), - ); - let chain_id = provider.get_chainid().await?; - let EthBridgeContracts { - bridge, - committee, - limiter, - vault, - config, - } = get_eth_contracts(bridge_proxy, &provider).await?; - let message_type = BridgeActionType::EvmContractUpgrade as u8; - let bridge_upgrade_next_nonce: u64 = bridge.nonces(message_type).call().await?; - let committee_upgrade_next_nonce: u64 = committee.nonces(message_type).call().await?; - let limiter_upgrade_next_nonce: u64 = limiter.nonces(message_type).call().await?; - let config_upgrade_next_nonce: u64 = config.nonces(message_type).call().await?; - - let token_transfer_next_nonce: u64 = bridge - .nonces(BridgeActionType::TokenTransfer as u8) - .call() - .await?; - let blocklist_update_nonce: u64 = committee - .nonces(BridgeActionType::UpdateCommitteeBlocklist as u8) - .call() - .await?; - let emergency_button_nonce: u64 = bridge - .nonces(BridgeActionType::EmergencyButton as u8) - .call() - .await?; - let limit_update_nonce: u64 = limiter - .nonces(BridgeActionType::LimitUpdate as u8) - .call() - .await?; - let asset_price_update_nonce: u64 = config - .nonces(BridgeActionType::AssetPriceUpdate as u8) - .call() - .await?; - let add_tokens_nonce: u64 = config - .nonces(BridgeActionType::AddTokensOnEvm as u8) - .call() - .await?; - - let print = OutputEthBridge { - chain_id: chain_id.as_u64(), - bridge_proxy: bridge.address(), - committee_proxy: committee.address(), - limiter_proxy: limiter.address(), - config_proxy: config.address(), - vault: vault.address(), - nonces: Nonces { - token_transfer: token_transfer_next_nonce, - blocklist_update: blocklist_update_nonce, - emergency_button: emergency_button_nonce, - limit_update: limit_update_nonce, - asset_price_update: asset_price_update_nonce, - add_evm_tokens: add_tokens_nonce, - contract_upgrade_bridge: bridge_upgrade_next_nonce, - contract_upgrade_committee: committee_upgrade_next_nonce, - contract_upgrade_limiter: limiter_upgrade_next_nonce, - contract_upgrade_config: config_upgrade_next_nonce, - }, - }; - println!("{}", serde_json::to_string_pretty(&print).unwrap()); - return Ok(()); - } - - BridgeCommand::ViewBridgeRegistration { iota_rpc_url } => { - let iota_bridge_client = IotaClient::::new(&iota_rpc_url).await?; - let bridge_summary = iota_bridge_client - .get_bridge_summary() - .await - .map_err(|e| anyhow::anyhow!("Failed to get bridge summary: {:?}", e))?; - let move_type_bridge_committee = bridge_summary.committee; - let iota_client = IotaClientBuilder::default().build(iota_rpc_url).await?; - let stakes = iota_client - .governance_api() - .get_committee_info(None) - .await? - .validators - .into_iter() - .collect::>(); - - // Those names are used for getting the stake of committee members, hence we use - // committee members here - let names = iota_client - .governance_api() - .get_latest_iota_system_state() - .await? - .iter_committee_members() - .map(|summary| { - let authority_key = - AuthorityPublicKeyBytes::from_bytes(&summary.authority_pubkey_bytes) - .expect("Failed to convert authority key"); - (summary.iota_address, (authority_key, summary.name.clone())) - }) - .collect::>(); - let mut authorities = vec![]; - let mut output_wrapper = Output::::default(); - for (_, member) in move_type_bridge_committee.member_registration { - let MoveTypeCommitteeMemberRegistration { - iota_address, - bridge_pubkey_bytes, - http_rest_url, - } = member; - let Ok(pubkey) = BridgeAuthorityPublicKey::from_bytes(&bridge_pubkey_bytes) else { - output_wrapper.add_error(format!( - "Invalid bridge pubkey for committee member {}: {:?}", - iota_address, bridge_pubkey_bytes - )); - continue; - }; - let eth_address = BridgeAuthorityPublicKeyBytes::from(&pubkey).to_eth_address(); - let Ok(url) = from_utf8(&http_rest_url) else { - output_wrapper.add_error(format!( - "Invalid bridge http url for committee member {}: {:?}", - iota_address, http_rest_url - )); - continue; - }; - let url = url.to_string(); - - let (authority_key, name) = names.get(&iota_address).unwrap(); - let stake = stakes.get(authority_key).unwrap(); - authorities.push((name, iota_address, pubkey, eth_address, url, stake)); - } - let total_stake = authorities - .iter() - .map(|(_, _, _, _, _, stake)| **stake) - .sum::(); - let mut output = OutputIotaBridgeRegistration { - total_registered_stake: total_stake as f32 / TOTAL_VOTING_POWER as f32 * 100.0, - ..Default::default() - }; - for (name, iota_address, pubkey, eth_address, url, stake) in authorities { - output.committee.push(OutputMember { - name: name.clone(), - iota_address, - eth_address, - pubkey: Hex::encode(pubkey.as_bytes()), - url, - stake: *stake, - blocklisted: None, - status: None, - }); - } - output_wrapper.inner = output; - println!("{}", serde_json::to_string_pretty(&output_wrapper).unwrap()); - } - - BridgeCommand::ViewIotaBridge { - iota_rpc_url, - hex, - ping, - } => { - let iota_bridge_client = IotaClient::::new(&iota_rpc_url).await?; - let bridge_summary = iota_bridge_client - .get_bridge_summary() - .await - .map_err(|e| anyhow::anyhow!("Failed to get bridge summary: {:?}", e))?; - let move_type_bridge_committee = bridge_summary.committee; - let iota_client = IotaClientBuilder::default().build(iota_rpc_url).await?; - - // Aligned with the `ViewBridgeRegistration` command we fetch the names of the - // committee members. - let names = iota_client - .governance_api() - .get_latest_iota_system_state() - .await? - .iter_committee_members() - .map(|summary| (summary.iota_address, summary.name.clone())) - .collect::>(); - let mut authorities = vec![]; - let mut ping_tasks = vec![]; - let client = reqwest::Client::builder() - .connect_timeout(Duration::from_secs(10)) - .timeout(Duration::from_secs(10)) - .build() - .unwrap(); - let mut output_wrapper = Output::::default(); - for (_, member) in move_type_bridge_committee.members { - let MoveTypeCommitteeMember { - iota_address, - bridge_pubkey_bytes, - voting_power, - http_rest_url, - blocklisted, - } = member; - let Ok(pubkey) = BridgeAuthorityPublicKey::from_bytes(&bridge_pubkey_bytes) else { - output_wrapper.add_error(format!( - "Invalid bridge pubkey for bridge authority {}: {:?}", - iota_address, bridge_pubkey_bytes - )); - continue; - }; - let eth_address = BridgeAuthorityPublicKeyBytes::from(&pubkey).to_eth_address(); - let Ok(url) = from_utf8(&http_rest_url) else { - output_wrapper.add_error(format!( - "Invalid bridge http url for bridge authority: {}: {:?}", - iota_address, http_rest_url - )); - continue; - }; - let url = url.to_string(); - - let name = names.get(&iota_address).unwrap(); - if ping { - let client_clone = client.clone(); - ping_tasks.push(client_clone.get(url.clone()).send()); - } - authorities.push(( - name, - iota_address, - pubkey, - eth_address, - url, - voting_power, - blocklisted, - )); - } - let total_stake = authorities - .iter() - .map(|(_, _, _, _, _, stake, _)| *stake) - .sum::(); - let mut output = OutputIotaBridge { - total_stake: total_stake as f32 / TOTAL_VOTING_POWER as f32 * 100.0, - ..Default::default() - }; - let ping_tasks_resp = if !ping_tasks.is_empty() { - futures::future::join_all(ping_tasks) - .await - .into_iter() - .map(|resp| { - Some(match resp { - Ok(resp) => resp.status().is_success(), - Err(_e) => false, - }) - }) - .collect::>() - } else { - vec![None; authorities.len()] - }; - let mut total_online_stake = 0; - for ((name, iota_address, pubkey, eth_address, url, stake, blocklisted), ping_resp) in - authorities.into_iter().zip(ping_tasks_resp) - { - let pubkey = if hex { - Hex::encode(pubkey.as_bytes()) - } else { - pubkey.to_string() - }; - match ping_resp { - Some(resp) => { - if resp { - total_online_stake += stake; - } - output.committee.push(OutputMember { - name: name.clone(), - iota_address, - eth_address, - pubkey, - url, - stake, - blocklisted: Some(blocklisted), - status: Some(if resp { - "online".to_string() - } else { - "offline".to_string() - }), - }); - } - None => { - output.committee.push(OutputMember { - name: name.clone(), - iota_address, - eth_address, - pubkey, - url, - stake, - blocklisted: Some(blocklisted), - status: None, - }); - } - } - } - if ping { - output.total_online_stake = - Some(total_online_stake as f32 / TOTAL_VOTING_POWER as f32 * 100.0); - } - - // sequence nonces - for (type_, nonce) in bridge_summary.sequence_nums { - output - .nonces - .insert(BridgeActionType::try_from(type_).unwrap(), nonce); - } - - output_wrapper.inner = output; - println!("{}", serde_json::to_string_pretty(&output_wrapper).unwrap()); - } - BridgeCommand::Client { config_path, cmd } => { - let config = BridgeCliConfig::load(config_path).expect("Couldn't load BridgeCliConfig"); - let config = LoadedBridgeCliConfig::load(config).await?; - let iota_bridge_client = IotaClient::::new(&config.iota_rpc_url).await?; - cmd.handle(&config, iota_bridge_client).await?; - return Ok(()); - } - } - - Ok(()) -} - -#[derive(serde::Serialize, Default)] -struct OutputEthBridge { - chain_id: u64, - bridge_proxy: EthAddress, - committee_proxy: EthAddress, - limiter_proxy: EthAddress, - config_proxy: EthAddress, - vault: EthAddress, - nonces: Nonces, -} - -#[derive(serde::Serialize, Default)] -struct Nonces { - token_transfer: u64, - blocklist_update: u64, - emergency_button: u64, - limit_update: u64, - asset_price_update: u64, - add_evm_tokens: u64, - contract_upgrade_bridge: u64, - contract_upgrade_committee: u64, - contract_upgrade_limiter: u64, - contract_upgrade_config: u64, -} - -#[derive(serde::Serialize, Default)] -struct Output { - #[serde(skip_serializing_if = "Option::is_none")] - errors: Option>, - inner: P, -} - -impl Output

{ - fn add_error(&mut self, error: String) { - if self.errors.is_none() { - self.errors = Some(vec![]); - } - self.errors.as_mut().unwrap().push(error); - } -} - -#[derive(serde::Serialize, Default)] -struct OutputIotaBridge { - total_stake: f32, - #[serde(skip_serializing_if = "Option::is_none")] - total_online_stake: Option, - committee: Vec, - nonces: HashMap, -} - -#[derive(serde::Serialize)] -struct OutputMember { - name: String, - iota_address: IotaAddress, - eth_address: EthAddress, - pubkey: String, - url: String, - stake: u64, - #[serde(skip_serializing_if = "Option::is_none")] - blocklisted: Option, - #[serde(skip_serializing_if = "Option::is_none")] - status: Option, -} - -#[derive(serde::Serialize, Default)] -struct OutputIotaBridgeRegistration { - total_registered_stake: f32, - committee: Vec, -} diff --git a/crates/iota-bridge-indexer/Cargo.toml b/crates/iota-bridge-indexer/Cargo.toml deleted file mode 100644 index 2d6a9bfd270..00000000000 --- a/crates/iota-bridge-indexer/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "iota-bridge-indexer" -version.workspace = true -authors = ["IOTA Foundation "] -edition = "2021" -license = "Apache-2.0" -publish = false - -[dependencies] -# external dependencies -anyhow.workspace = true -async-trait.workspace = true -backoff.workspace = true -bcs.workspace = true -clap.workspace = true -diesel = { workspace = true, features = ["postgres", "r2d2", "serde_json"] } -ethers = { version = "2.0", features = ["rustls", "ws"] } -futures.workspace = true -prometheus.workspace = true -serde.workspace = true -serde_yaml.workspace = true -tap.workspace = true -tokio = { workspace = true, features = ["full"] } -tracing.workspace = true - -# internal dependencies -iota-bridge.workspace = true -iota-config.workspace = true -iota-data-ingestion-core.workspace = true -iota-indexer-builder.workspace = true -iota-json-rpc-types.workspace = true -iota-metrics.workspace = true -iota-sdk.workspace = true -iota-types.workspace = true -telemetry-subscribers.workspace = true - -[dev-dependencies] -iota-test-transaction-builder.workspace = true - -[[bin]] -name = "bridge-indexer" -path = "src/main.rs" diff --git a/crates/iota-bridge-indexer/config.yaml b/crates/iota-bridge-indexer/config.yaml deleted file mode 100644 index 2363925284f..00000000000 --- a/crates/iota-bridge-indexer/config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# config.yaml format: - -# URL of the remote store -# remote_store_url: -# URL for Ethereum RPC -# eth_rpc_url: -# Database connection URL -# db_url: -# Path to the checkpoints -# checkpoints_path: -# Number of concurrent operations -# concurrency: 1 -# Bridge genesis checkpoint in IOTA, for testnet = 43917829 -# bridge_genesis_checkpoint: -# Ethereum to IOTA bridge contract address -# eth_iota_bridge_contract_address: -# Starting block number -# start_block: -# Client metric URL -# metric_url: -# Client metric port -# metric_port: -# checkpoint size of each backfill worker, use 432000 for 1 worker per day, assume 5 checkpoint per second -# back_fill_lot_size: -# Optional starting checkpoint for realtime ingestion task -# resume_from_checkpoint: diff --git a/crates/iota-bridge-indexer/diesel.toml b/crates/iota-bridge-indexer/diesel.toml deleted file mode 100644 index 4731681a0a7..00000000000 --- a/crates/iota-bridge-indexer/diesel.toml +++ /dev/null @@ -1,9 +0,0 @@ -# For documentation on how to configure this file, -# see https://diesel.rs/guides/configuring-diesel-cli - -[print_schema] -file = "src/schema.rs" -# custom_type_derives = ["diesel::query_builder::QueryId"] - -[migrations_directory] -dir = "src/migrations" diff --git a/crates/iota-bridge-indexer/src/config.rs b/crates/iota-bridge-indexer/src/config.rs deleted file mode 100644 index 841d5a2d340..00000000000 --- a/crates/iota-bridge-indexer/src/config.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::env; - -use serde::{Deserialize, Serialize}; - -/// config as loaded from `config.yaml`. -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IndexerConfig { - pub remote_store_url: String, - pub eth_rpc_url: String, - #[serde(default = "default_db_url")] - pub db_url: String, - pub eth_ws_url: String, - pub checkpoints_path: String, - pub concurrency: u64, - pub bridge_genesis_checkpoint: u64, - pub eth_iota_bridge_contract_address: String, - pub start_block: u64, - pub metric_url: String, - pub metric_port: u16, - pub iota_rpc_url: Option, - pub back_fill_lot_size: u64, - pub resume_from_checkpoint: Option, -} - -impl iota_config::Config for IndexerConfig {} - -pub fn default_db_url() -> String { - env::var("DB_URL").expect("db_url must be set in config or via the $DB_URL env var") -} diff --git a/crates/iota-bridge-indexer/src/eth_bridge_indexer.rs b/crates/iota-bridge-indexer/src/eth_bridge_indexer.rs deleted file mode 100644 index 2a602de49b4..00000000000 --- a/crates/iota-bridge-indexer/src/eth_bridge_indexer.rs +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - collections::{HashMap, HashSet}, - str::FromStr, - sync::Arc, - time::Duration, -}; - -use anyhow::Error; -use async_trait::async_trait; -use ethers::{ - prelude::Transaction, - providers::{Http, Middleware, Provider, StreamExt, Ws}, - types::{Address as EthAddress, Block, Filter, H256}, -}; -use iota_bridge::{ - abi::{EthBridgeEvent, EthIotaBridgeEvents}, - error::BridgeError, - eth_client::EthClient, - metered_eth_provider::MeteredEthHttpProvider, - metrics::BridgeMetrics, - retry_with_max_elapsed_time, - types::{EthEvent, RawEthLog}, -}; -use iota_indexer_builder::indexer_builder::{DataMapper, DataSender, Datasource}; -use iota_metrics::spawn_monitored_task; -use tokio::task::JoinHandle; -use tracing::info; - -use crate::{ - BridgeDataSource, ProcessedTxnData, TokenTransfer, TokenTransferData, TokenTransferStatus, - metrics::BridgeIndexerMetrics, -}; - -type RawEthData = (RawEthLog, Block, Transaction); - -pub struct EthSubscriptionDatasource { - bridge_address: EthAddress, - eth_ws_url: String, - indexer_metrics: BridgeIndexerMetrics, -} - -impl EthSubscriptionDatasource { - pub fn new( - eth_iota_bridge_contract_address: String, - eth_ws_url: String, - indexer_metrics: BridgeIndexerMetrics, - ) -> Result { - let bridge_address = EthAddress::from_str(ð_iota_bridge_contract_address)?; - Ok(Self { - bridge_address, - eth_ws_url, - indexer_metrics, - }) - } -} -#[async_trait] -impl Datasource for EthSubscriptionDatasource { - async fn start_data_retrieval( - &self, - starting_checkpoint: u64, - target_checkpoint: u64, - data_sender: DataSender, - ) -> Result>, Error> { - let filter = Filter::new() - .address(self.bridge_address) - .from_block(starting_checkpoint) - .to_block(target_checkpoint); - - let eth_ws_url = self.eth_ws_url.clone(); - let indexer_metrics: BridgeIndexerMetrics = self.indexer_metrics.clone(); - - let handle = spawn_monitored_task!(async move { - let eth_ws_client = Provider::::connect(ð_ws_url).await?; - - let mut cached_blocks: HashMap> = HashMap::new(); - - let mut stream = eth_ws_client.subscribe_logs(&filter).await?; - while let Some(log) = stream.next().await { - let raw_log = RawEthLog { - block_number: log - .block_number - .ok_or(BridgeError::Provider( - "Provider returns log without block_number".into(), - )) - .unwrap() - .as_u64(), - tx_hash: log - .transaction_hash - .ok_or(BridgeError::Provider( - "Provider returns log without transaction_hash".into(), - )) - .unwrap(), - log, - }; - - let block_number = raw_log.block_number(); - - let block = if let Some(cached_block) = cached_blocks.get(&block_number) { - cached_block.clone() - } else { - let Ok(Ok(Some(block))) = retry_with_max_elapsed_time!( - eth_ws_client.get_block(block_number), - Duration::from_secs(30000) - ) else { - panic!("Unable to get block from provider"); - }; - - cached_blocks.insert(block_number, block.clone()); - block - }; - - let Ok(Ok(Some(transaction))) = retry_with_max_elapsed_time!( - eth_ws_client.get_transaction(raw_log.tx_hash), - Duration::from_secs(30000) - ) else { - panic!("Unable to get transaction from provider"); - }; - - data_sender - .send((block_number, vec![(raw_log, block, transaction)])) - .await?; - - indexer_metrics - .latest_committed_eth_block - .set(block_number as i64); - } - - Ok::<_, Error>(()) - }); - Ok(handle) - } -} - -pub struct EthSyncDatasource { - bridge_address: EthAddress, - eth_http_url: String, - indexer_metrics: BridgeIndexerMetrics, - bridge_metrics: Arc, -} - -impl EthSyncDatasource { - pub fn new( - eth_iota_bridge_contract_address: String, - eth_http_url: String, - indexer_metrics: BridgeIndexerMetrics, - bridge_metrics: Arc, - ) -> Result { - let bridge_address = EthAddress::from_str(ð_iota_bridge_contract_address)?; - Ok(Self { - bridge_address, - eth_http_url, - indexer_metrics, - bridge_metrics, - }) - } -} -#[async_trait] -impl Datasource for EthSyncDatasource { - async fn start_data_retrieval( - &self, - starting_checkpoint: u64, - target_checkpoint: u64, - data_sender: DataSender, - ) -> Result>, Error> { - let client: Arc> = Arc::new( - EthClient::::new( - &self.eth_http_url, - HashSet::from_iter(vec![self.bridge_address]), - self.bridge_metrics.clone(), - ) - .await?, - ); - - let provider = Arc::new( - Provider::::try_from(&self.eth_http_url)? - .interval(std::time::Duration::from_millis(2000)), - ); - - let bridge_address = self.bridge_address; - let indexer_metrics: BridgeIndexerMetrics = self.indexer_metrics.clone(); - let client = Arc::clone(&client); - let provider = Arc::clone(&provider); - - let handle = spawn_monitored_task!(async move { - let mut cached_blocks: HashMap> = HashMap::new(); - - let Ok(Ok(logs)) = retry_with_max_elapsed_time!( - client.get_raw_events_in_range( - bridge_address, - starting_checkpoint, - target_checkpoint - ), - Duration::from_secs(30000) - ) else { - panic!("Unable to get logs from provider"); - }; - - let mut data = Vec::new(); - let mut first_block = 0; - - for log in logs { - let block = if let Some(cached_block) = cached_blocks.get(&log.block_number) { - cached_block.clone() - } else { - let Ok(Ok(Some(block))) = retry_with_max_elapsed_time!( - provider.get_block(log.block_number), - Duration::from_secs(30000) - ) else { - panic!("Unable to get block from provider"); - }; - - cached_blocks.insert(log.block_number, block.clone()); - block - }; - - if first_block == 0 { - first_block = log.block_number; - } - - let Ok(Ok(Some(transaction))) = retry_with_max_elapsed_time!( - provider.get_transaction(log.tx_hash), - Duration::from_secs(30000) - ) else { - panic!("Unable to get transaction from provider"); - }; - - data.push((log, block, transaction)); - } - - data_sender.send((target_checkpoint, data)).await?; - - indexer_metrics - .last_synced_eth_block - .set(first_block as i64); - - Ok::<_, Error>(()) - }); - - Ok(handle) - } -} - -#[derive(Clone)] -pub struct EthDataMapper { - pub metrics: BridgeIndexerMetrics, -} - -impl DataMapper<(E, Block, Transaction), ProcessedTxnData> for EthDataMapper { - fn map( - &self, - (log, block, transaction): (E, Block, Transaction), - ) -> Result, Error> { - let eth_bridge_event = EthBridgeEvent::try_from_log(log.log()); - if eth_bridge_event.is_none() { - return Ok(vec![]); - } - self.metrics.total_eth_bridge_transactions.inc(); - let bridge_event = eth_bridge_event.unwrap(); - let timestamp_ms = block.timestamp.as_u64() * 1000; - let gas = transaction.gas; - - let transfer = match bridge_event { - EthBridgeEvent::EthIotaBridgeEvents(bridge_event) => match bridge_event { - EthIotaBridgeEvents::TokensDepositedFilter(bridge_event) => { - info!("Observed Eth Deposit at block: {}", log.block_number()); - self.metrics.total_eth_token_deposited.inc(); - ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: bridge_event.source_chain_id, - nonce: bridge_event.nonce, - block_height: log.block_number(), - timestamp_ms, - txn_hash: transaction.hash.as_bytes().to_vec(), - txn_sender: bridge_event.sender_address.as_bytes().to_vec(), - status: TokenTransferStatus::Deposited, - gas_usage: gas.as_u64() as i64, - data_source: BridgeDataSource::Eth, - data: Some(TokenTransferData { - sender_address: bridge_event.sender_address.as_bytes().to_vec(), - destination_chain: bridge_event.destination_chain_id, - recipient_address: bridge_event.recipient_address.to_vec(), - token_id: bridge_event.token_id, - amount: bridge_event.iota_adjusted_amount, - }), - }) - } - EthIotaBridgeEvents::TokensClaimedFilter(bridge_event) => { - info!("Observed Eth Claim at block: {}", log.block_number()); - self.metrics.total_eth_token_transfer_claimed.inc(); - ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: bridge_event.source_chain_id, - nonce: bridge_event.nonce, - block_height: log.block_number(), - timestamp_ms, - txn_hash: transaction.hash.as_bytes().to_vec(), - txn_sender: bridge_event.sender_address.to_vec(), - status: TokenTransferStatus::Claimed, - gas_usage: gas.as_u64() as i64, - data_source: BridgeDataSource::Eth, - data: None, - }) - } - EthIotaBridgeEvents::PausedFilter(_) - | EthIotaBridgeEvents::UnpausedFilter(_) - | EthIotaBridgeEvents::UpgradedFilter(_) - | EthIotaBridgeEvents::InitializedFilter(_) => { - // TODO: handle these events - self.metrics.total_eth_bridge_txn_other.inc(); - return Ok(vec![]); - } - }, - EthBridgeEvent::EthBridgeCommitteeEvents(_) - | EthBridgeEvent::EthBridgeLimiterEvents(_) - | EthBridgeEvent::EthBridgeConfigEvents(_) - | EthBridgeEvent::EthCommitteeUpgradeableContractEvents(_) => { - // TODO: handle these events - self.metrics.total_eth_bridge_txn_other.inc(); - return Ok(vec![]); - } - }; - Ok(vec![transfer]) - } -} diff --git a/crates/iota-bridge-indexer/src/iota_bridge_indexer.rs b/crates/iota-bridge-indexer/src/iota_bridge_indexer.rs deleted file mode 100644 index 82673332753..00000000000 --- a/crates/iota-bridge-indexer/src/iota_bridge_indexer.rs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::{Error, anyhow}; -use async_trait::async_trait; -use diesel::{ - Connection, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper, - TextExpressionMethods, dsl::now, -}; -use iota_bridge::events::{ - MoveTokenDepositedEvent, MoveTokenTransferApproved, MoveTokenTransferClaimed, -}; -use iota_indexer_builder::{ - Task, - indexer_builder::{DataMapper, IndexerProgressStore, Persistent}, - iota_datasource::CheckpointTxnData, -}; -use iota_types::{ - BRIDGE_ADDRESS, IOTA_BRIDGE_OBJECT_ID, effects::TransactionEffectsAPI, event::Event, - execution_status::ExecutionStatus, full_checkpoint_content::CheckpointTransaction, -}; -use tracing::info; - -use crate::{ - BridgeDataSource, IotaTxnError, ProcessedTxnData, TokenTransfer, TokenTransferData, - TokenTransferStatus, - metrics::BridgeIndexerMetrics, - models, - postgres_manager::PgPool, - schema, - schema::{ - iota_error_transactions, - progress_store::{columns, dsl}, - token_transfer, token_transfer_data, - }, -}; - -/// Persistent layer impl -#[derive(Clone)] -pub struct PgBridgePersistent { - pool: PgPool, -} - -impl PgBridgePersistent { - pub fn new(pool: PgPool) -> Self { - Self { pool } - } -} - -// TODO: this is shared between IOTA and ETH, move to different file. -#[async_trait] -impl Persistent for PgBridgePersistent { - async fn write(&self, data: Vec) -> Result<(), Error> { - if data.is_empty() { - return Ok(()); - } - let connection = &mut self.pool.get()?; - connection.transaction(|conn| { - for d in data { - match d { - ProcessedTxnData::TokenTransfer(t) => { - diesel::insert_into(token_transfer::table) - .values(&t.to_db()) - .on_conflict_do_nothing() - .execute(conn)?; - - if let Some(d) = t.to_data_maybe() { - diesel::insert_into(token_transfer_data::table) - .values(&d) - .on_conflict_do_nothing() - .execute(conn)?; - } - } - ProcessedTxnData::Error(e) => { - diesel::insert_into(iota_error_transactions::table) - .values(&e.to_db()) - .on_conflict_do_nothing() - .execute(conn)?; - } - } - } - Ok(()) - }) - } -} - -#[async_trait] -impl IndexerProgressStore for PgBridgePersistent { - async fn load_progress(&self, task_name: String) -> anyhow::Result { - let mut conn = self.pool.get()?; - let cp: Option = dsl::progress_store - .find(&task_name) - .select(models::ProgressStore::as_select()) - .first(&mut conn) - .optional()?; - Ok(cp - .ok_or(anyhow!("Cannot found progress for task {task_name}"))? - .checkpoint as u64) - } - - async fn save_progress( - &mut self, - task_name: String, - checkpoint_number: u64, - ) -> anyhow::Result<()> { - let mut conn = self.pool.get()?; - diesel::insert_into(schema::progress_store::table) - .values(&models::ProgressStore { - task_name, - checkpoint: checkpoint_number as i64, - // Target checkpoint and timestamp will only be written for new entries - target_checkpoint: i64::MAX, - // Timestamp is defaulted to current time in DB if None - timestamp: None, - }) - .on_conflict(dsl::task_name) - .do_update() - .set(( - columns::checkpoint.eq(checkpoint_number as i64), - columns::timestamp.eq(now), - )) - .execute(&mut conn)?; - Ok(()) - } - - async fn tasks(&self, prefix: &str) -> Result, anyhow::Error> { - let mut conn = self.pool.get()?; - // get all unfinished tasks - let cp: Vec = dsl::progress_store - // TODO: using like could be error prone, change the progress store schema to stare the - // task name properly. - .filter(columns::task_name.like(format!("{prefix} - %"))) - .filter(columns::checkpoint.lt(columns::target_checkpoint)) - .order_by(columns::target_checkpoint.desc()) - .load(&mut conn)?; - Ok(cp.into_iter().map(|d| d.into()).collect()) - } - - async fn register_task( - &mut self, - task_name: String, - checkpoint: u64, - target_checkpoint: u64, - ) -> Result<(), anyhow::Error> { - let mut conn = self.pool.get()?; - diesel::insert_into(schema::progress_store::table) - .values(models::ProgressStore { - task_name, - checkpoint: checkpoint as i64, - target_checkpoint: target_checkpoint as i64, - // Timestamp is defaulted to current time in DB if None - timestamp: None, - }) - .execute(&mut conn)?; - Ok(()) - } - - async fn update_task(&mut self, task: Task) -> Result<(), anyhow::Error> { - let mut conn = self.pool.get()?; - diesel::update(dsl::progress_store.filter(columns::task_name.eq(task.task_name))) - .set(( - columns::checkpoint.eq(task.checkpoint as i64), - columns::target_checkpoint.eq(task.target_checkpoint as i64), - columns::timestamp.eq(now), - )) - .execute(&mut conn)?; - Ok(()) - } -} - -/// Data mapper impl -#[derive(Clone)] -pub struct IotaBridgeDataMapper { - pub metrics: BridgeIndexerMetrics, -} - -impl DataMapper for IotaBridgeDataMapper { - fn map( - &self, - (data, checkpoint_num, timestamp_ms): CheckpointTxnData, - ) -> Result, Error> { - self.metrics.total_iota_bridge_transactions.inc(); - if !data - .input_objects - .iter() - .any(|obj| obj.id() == IOTA_BRIDGE_OBJECT_ID) - { - return Ok(vec![]); - } - - match &data.events { - Some(events) => { - let token_transfers = events.data.iter().try_fold(vec![], |mut result, ev| { - if let Some(data) = process_iota_event(ev, &data, checkpoint_num, timestamp_ms)? - { - result.push(data); - } - Ok::<_, anyhow::Error>(result) - })?; - - if !token_transfers.is_empty() { - info!( - "IOTA: Extracted {} bridge token transfer data entries for tx {}.", - token_transfers.len(), - data.transaction.digest() - ); - } - Ok(token_transfers) - } - None => { - if let ExecutionStatus::Failure { error, command } = data.effects.status() { - Ok(vec![ProcessedTxnData::Error(IotaTxnError { - tx_digest: *data.transaction.digest(), - sender: data.transaction.sender_address(), - timestamp_ms, - failure_status: error.to_string(), - cmd_idx: command.map(|idx| idx as u64), - })]) - } else { - Ok(vec![]) - } - } - } - } -} - -fn process_iota_event( - ev: &Event, - tx: &CheckpointTransaction, - checkpoint: u64, - timestamp_ms: u64, -) -> Result, anyhow::Error> { - Ok(if ev.type_.address == BRIDGE_ADDRESS { - match ev.type_.name.as_str() { - "TokenDepositedEvent" => { - info!("Observed IOTA Deposit {:?}", ev); - // todo: metrics.total_iota_token_deposited.inc(); - let move_event: MoveTokenDepositedEvent = bcs::from_bytes(&ev.contents)?; - Some(ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: move_event.source_chain, - nonce: move_event.seq_num, - block_height: checkpoint, - timestamp_ms, - txn_hash: tx.transaction.digest().inner().to_vec(), - txn_sender: ev.sender.to_vec(), - status: TokenTransferStatus::Deposited, - gas_usage: tx.effects.gas_cost_summary().net_gas_usage(), - data_source: BridgeDataSource::Iota, - data: Some(TokenTransferData { - destination_chain: move_event.target_chain, - sender_address: move_event.sender_address.clone(), - recipient_address: move_event.target_address.clone(), - token_id: move_event.token_type, - amount: move_event.amount_iota_adjusted, - }), - })) - } - "TokenTransferApproved" => { - info!("Observed IOTA Approval {:?}", ev); - // todo: metrics.total_iota_token_transfer_approved.inc(); - let event: MoveTokenTransferApproved = bcs::from_bytes(&ev.contents)?; - Some(ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: event.message_key.source_chain, - nonce: event.message_key.bridge_seq_num, - block_height: checkpoint, - timestamp_ms, - txn_hash: tx.transaction.digest().inner().to_vec(), - txn_sender: ev.sender.to_vec(), - status: TokenTransferStatus::Approved, - gas_usage: tx.effects.gas_cost_summary().net_gas_usage(), - data_source: BridgeDataSource::Iota, - data: None, - })) - } - "TokenTransferClaimed" => { - info!("Observed IOTA Claim {:?}", ev); - // todo: metrics.total_iota_token_transfer_claimed.inc(); - let event: MoveTokenTransferClaimed = bcs::from_bytes(&ev.contents)?; - Some(ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: event.message_key.source_chain, - nonce: event.message_key.bridge_seq_num, - block_height: checkpoint, - timestamp_ms, - txn_hash: tx.transaction.digest().inner().to_vec(), - txn_sender: ev.sender.to_vec(), - status: TokenTransferStatus::Claimed, - gas_usage: tx.effects.gas_cost_summary().net_gas_usage(), - data_source: BridgeDataSource::Iota, - data: None, - })) - } - _ => { - // todo: metrics.total_iota_bridge_txn_other.inc(); - None - } - } - } else { - None - }) -} diff --git a/crates/iota-bridge-indexer/src/iota_transaction_handler.rs b/crates/iota-bridge-indexer/src/iota_transaction_handler.rs deleted file mode 100644 index 751f218985a..00000000000 --- a/crates/iota-bridge-indexer/src/iota_transaction_handler.rs +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::time::Duration; - -use anyhow::Result; -use futures::StreamExt; -use iota_bridge::events::{ - MoveTokenDepositedEvent, MoveTokenTransferApproved, MoveTokenTransferClaimed, -}; -use iota_json_rpc_types::IotaTransactionBlockEffectsAPI; -use iota_metrics::metered_channel::{Receiver, ReceiverStream}; -use iota_types::{BRIDGE_ADDRESS, digests::TransactionDigest}; -use tracing::{error, info}; - -use crate::{ - BridgeDataSource, ProcessedTxnData, TokenTransfer, TokenTransferData, TokenTransferStatus, - metrics::BridgeIndexerMetrics, - postgres_manager::{PgPool, update_iota_progress_store, write}, - types::RetrievedTransaction, -}; - -pub(crate) const COMMIT_BATCH_SIZE: usize = 10; - -pub async fn handle_iota_transactions_loop( - pg_pool: PgPool, - rx: Receiver<(Vec, Option)>, - metrics: BridgeIndexerMetrics, -) { - let checkpoint_commit_batch_size = std::env::var("COMMIT_BATCH_SIZE") - .unwrap_or(COMMIT_BATCH_SIZE.to_string()) - .parse::() - .unwrap(); - let mut stream = ReceiverStream::new(rx).ready_chunks(checkpoint_commit_batch_size); - while let Some(batch) = stream.next().await { - // unwrap: batch must not be empty - let (txns, cursor) = batch.last().cloned().unwrap(); - let data = batch - .into_iter() - // TODO: letting it panic so we can capture errors, but we should handle this more - // gracefully - .flat_map(|(chunk, _)| process_transactions(chunk, &metrics).unwrap()) - .collect::>(); - - // write batched transaction data to DB - if !data.is_empty() { - // unwrap: token_transfers is not empty - let last_ckp = txns.last().map(|tx| tx.checkpoint).unwrap_or_default(); - while let Err(err) = write(&pg_pool, data.clone()) { - error!("Failed to write iota transactions to DB: {:?}", err); - tokio::time::sleep(Duration::from_secs(5)).await; - } - info!("Wrote {} bridge transaction data to DB", data.len()); - metrics.last_committed_iota_checkpoint.set(last_ckp as i64); - } - - // update iota progress store using the latest cursor - if let Some(cursor) = cursor { - while let Err(err) = update_iota_progress_store(&pg_pool, cursor) { - error!("Failed to update iota progress tore DB: {:?}", err); - tokio::time::sleep(Duration::from_secs(5)).await; - } - info!("Updated iota transaction cursor to {}", cursor); - } - } - unreachable!("Channel closed unexpectedly"); -} - -fn process_transactions( - txns: Vec, - metrics: &BridgeIndexerMetrics, -) -> Result> { - txns.into_iter().try_fold(vec![], |mut result, tx| { - result.append(&mut into_token_transfers(tx, metrics)?); - Ok(result) - }) -} - -pub fn into_token_transfers( - tx: RetrievedTransaction, - metrics: &BridgeIndexerMetrics, -) -> Result> { - let mut transfers = Vec::new(); - let tx_digest = tx.tx_digest; - let timestamp_ms = tx.timestamp_ms; - let checkpoint_num = tx.checkpoint; - let effects = tx.effects; - for ev in tx.events.data { - if ev.type_.address != BRIDGE_ADDRESS { - continue; - } - match ev.type_.name.as_str() { - "TokenDepositedEvent" => { - info!("Observed IOTA Deposit {:?}", ev); - metrics.total_iota_token_deposited.inc(); - let move_event: MoveTokenDepositedEvent = bcs::from_bytes(ev.bcs.bytes())?; - transfers.push(ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: move_event.source_chain, - nonce: move_event.seq_num, - block_height: checkpoint_num, - timestamp_ms, - txn_hash: tx_digest.inner().to_vec(), - txn_sender: ev.sender.to_vec(), - status: TokenTransferStatus::Deposited, - gas_usage: effects.gas_cost_summary().net_gas_usage(), - data_source: BridgeDataSource::Iota, - data: Some(TokenTransferData { - destination_chain: move_event.target_chain, - sender_address: move_event.sender_address.clone(), - recipient_address: move_event.target_address.clone(), - token_id: move_event.token_type, - amount: move_event.amount_iota_adjusted, - }), - })); - } - "TokenTransferApproved" => { - info!("Observed IOTA Approval {:?}", ev); - metrics.total_iota_token_transfer_approved.inc(); - let event: MoveTokenTransferApproved = bcs::from_bytes(ev.bcs.bytes())?; - transfers.push(ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: event.message_key.source_chain, - nonce: event.message_key.bridge_seq_num, - block_height: checkpoint_num, - timestamp_ms, - txn_hash: tx_digest.inner().to_vec(), - txn_sender: ev.sender.to_vec(), - status: TokenTransferStatus::Approved, - gas_usage: effects.gas_cost_summary().net_gas_usage(), - data_source: BridgeDataSource::Iota, - data: None, - })); - } - "TokenTransferClaimed" => { - info!("Observed IOTA Claim {:?}", ev); - metrics.total_iota_token_transfer_claimed.inc(); - let event: MoveTokenTransferClaimed = bcs::from_bytes(ev.bcs.bytes())?; - transfers.push(ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: event.message_key.source_chain, - nonce: event.message_key.bridge_seq_num, - block_height: checkpoint_num, - timestamp_ms, - txn_hash: tx_digest.inner().to_vec(), - txn_sender: ev.sender.to_vec(), - status: TokenTransferStatus::Claimed, - gas_usage: effects.gas_cost_summary().net_gas_usage(), - data_source: BridgeDataSource::Iota, - data: None, - })); - } - _ => { - metrics.total_iota_bridge_txn_other.inc(); - } - } - } - if !transfers.is_empty() { - info!( - ?tx_digest, - "IOTA: Extracted {} bridge token transfer data entries", - transfers.len(), - ); - } - Ok(transfers) -} diff --git a/crates/iota-bridge-indexer/src/iota_transaction_queries.rs b/crates/iota-bridge-indexer/src/iota_transaction_queries.rs deleted file mode 100644 index 87010b47875..00000000000 --- a/crates/iota-bridge-indexer/src/iota_transaction_queries.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{sync::Arc, time::Duration}; - -use iota_bridge::{metrics::BridgeMetrics, retry_with_max_elapsed_time}; -use iota_json_rpc_types::{ - IotaTransactionBlockResponseOptions, IotaTransactionBlockResponseQuery, TransactionFilter, -}; -use iota_sdk::IotaClient; -use iota_types::{IOTA_BRIDGE_OBJECT_ID, digests::TransactionDigest}; -use tracing::{error, info}; - -use crate::types::RetrievedTransaction; - -const QUERY_DURATION: Duration = Duration::from_secs(1); -const SLEEP_DURATION: Duration = Duration::from_secs(5); - -pub async fn start_iota_tx_polling_task( - iota_client: IotaClient, - mut cursor: Option, - tx: iota_metrics::metered_channel::Sender<( - Vec, - Option, - )>, - metrics: Arc, -) { - info!("Starting IOTA transaction polling task from {:?}", cursor); - loop { - let Ok(Ok(results)) = retry_with_max_elapsed_time!( - iota_client.read_api().query_transaction_blocks( - IotaTransactionBlockResponseQuery { - filter: Some(TransactionFilter::InputObject(IOTA_BRIDGE_OBJECT_ID)), - options: Some(IotaTransactionBlockResponseOptions::full_content()), - }, - cursor, - None, - false, - ), - Duration::from_secs(600) - ) else { - error!("Failed to query bridge transactions after retry"); - continue; - }; - info!("Retrieved {} bridge transactions", results.data.len()); - let txes = match results - .data - .into_iter() - .map(RetrievedTransaction::try_from) - .collect::>>() - { - Ok(data) => data, - Err(e) => { - // TODO: Sometimes fullnode does not return checkpoint strangely. We retry - // instead of panicking. - error!( - "Failed to convert retrieved transactions to sanitized format: {}", - e - ); - tokio::time::sleep(SLEEP_DURATION).await; - continue; - } - }; - if txes.is_empty() { - // When there is no more new data, we are caught up, no need to stress the - // fullnode - tokio::time::sleep(QUERY_DURATION).await; - continue; - } - // Unwrap: txes is not empty - let ckp = txes.last().unwrap().checkpoint; - tx.send((txes, results.next_cursor)) - .await - .expect("Failed to send transaction block to process"); - metrics.last_synced_iota_checkpoint.set(ckp as i64); - cursor = results.next_cursor; - } -} diff --git a/crates/iota-bridge-indexer/src/lib.rs b/crates/iota-bridge-indexer/src/lib.rs deleted file mode 100644 index a84498f9f37..00000000000 --- a/crates/iota-bridge-indexer/src/lib.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt::{Display, Formatter}; - -use iota_types::base_types::{IotaAddress, TransactionDigest}; - -use crate::models::{ - IotaErrorTransactions, TokenTransfer as DBTokenTransfer, - TokenTransferData as DBTokenTransferData, -}; - -pub mod config; -pub mod iota_transaction_handler; -pub mod iota_transaction_queries; -pub mod metrics; -pub mod models; -pub mod postgres_manager; -pub mod schema; -pub mod types; - -pub mod eth_bridge_indexer; -pub mod iota_bridge_indexer; - -#[derive(Clone)] -pub enum ProcessedTxnData { - TokenTransfer(TokenTransfer), - Error(IotaTxnError), -} - -#[derive(Clone)] -pub struct IotaTxnError { - tx_digest: TransactionDigest, - sender: IotaAddress, - timestamp_ms: u64, - failure_status: String, - cmd_idx: Option, -} - -#[derive(Clone)] -pub struct TokenTransfer { - chain_id: u8, - nonce: u64, - block_height: u64, - timestamp_ms: u64, - txn_hash: Vec, - txn_sender: Vec, - status: TokenTransferStatus, - gas_usage: i64, - data_source: BridgeDataSource, - data: Option, -} - -#[derive(Clone)] -pub struct TokenTransferData { - sender_address: Vec, - destination_chain: u8, - recipient_address: Vec, - token_id: u8, - amount: u64, -} - -impl TokenTransfer { - fn to_db(&self) -> DBTokenTransfer { - DBTokenTransfer { - chain_id: self.chain_id as i32, - nonce: self.nonce as i64, - block_height: self.block_height as i64, - timestamp_ms: self.timestamp_ms as i64, - txn_hash: self.txn_hash.clone(), - txn_sender: self.txn_sender.clone(), - status: self.status.to_string(), - gas_usage: self.gas_usage, - data_source: self.data_source.to_string(), - } - } - - fn to_data_maybe(&self) -> Option { - self.data.as_ref().map(|data| DBTokenTransferData { - chain_id: self.chain_id as i32, - nonce: self.nonce as i64, - block_height: self.block_height as i64, - timestamp_ms: self.timestamp_ms as i64, - txn_hash: self.txn_hash.clone(), - sender_address: data.sender_address.clone(), - destination_chain: data.destination_chain as i32, - recipient_address: data.recipient_address.clone(), - token_id: data.token_id as i32, - amount: data.amount as i64, - }) - } -} - -impl IotaTxnError { - fn to_db(&self) -> IotaErrorTransactions { - IotaErrorTransactions { - txn_digest: self.tx_digest.inner().to_vec(), - sender_address: self.sender.to_vec(), - timestamp_ms: self.timestamp_ms as i64, - failure_status: self.failure_status.clone(), - cmd_idx: self.cmd_idx.map(|idx| idx as i64), - } - } -} - -#[derive(Clone)] -pub(crate) enum TokenTransferStatus { - Deposited, - Approved, - Claimed, -} - -impl Display for TokenTransferStatus { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = match self { - TokenTransferStatus::Deposited => "Deposited", - TokenTransferStatus::Approved => "Approved", - TokenTransferStatus::Claimed => "Claimed", - }; - write!(f, "{str}") - } -} - -#[derive(Clone)] -enum BridgeDataSource { - Iota, - Eth, -} - -impl Display for BridgeDataSource { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = match self { - BridgeDataSource::Eth => "ETH", - BridgeDataSource::Iota => "IOTA", - }; - write!(f, "{str}") - } -} diff --git a/crates/iota-bridge-indexer/src/main.rs b/crates/iota-bridge-indexer/src/main.rs deleted file mode 100644 index 7fe337d82b3..00000000000 --- a/crates/iota-bridge-indexer/src/main.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{env, path::PathBuf, sync::Arc}; - -use anyhow::Result; -use clap::*; -use ethers::providers::{Http, Middleware, Provider}; -use iota_bridge::metrics::BridgeMetrics; -use iota_bridge_indexer::{ - config::IndexerConfig, - eth_bridge_indexer::{EthDataMapper, EthSubscriptionDatasource, EthSyncDatasource}, - iota_bridge_indexer::{IotaBridgeDataMapper, PgBridgePersistent}, - iota_transaction_handler::handle_iota_transactions_loop, - iota_transaction_queries::start_iota_tx_polling_task, - metrics::BridgeIndexerMetrics, - postgres_manager::{get_connection_pool, read_iota_progress_store}, -}; -use iota_config::Config; -use iota_data_ingestion_core::DataIngestionMetrics; -use iota_indexer_builder::{ - indexer_builder::{BackfillStrategy, IndexerBuilder}, - iota_datasource::IotaCheckpointDatasource, -}; -use iota_metrics::{ - metered_channel::channel, spawn_logged_monitored_task, start_prometheus_server, -}; -use iota_sdk::IotaClientBuilder; -use tokio::task::JoinHandle; -use tracing::info; - -#[derive(Parser, Clone, Debug)] -struct Args { - /// Path to a yaml config - #[arg(long, short)] - config_path: Option, -} - -#[tokio::main] -async fn main() -> Result<()> { - let _guard = telemetry_subscribers::TelemetryConfig::new() - .with_env() - .init(); - - let args = Args::parse(); - - // load config - let config_path = if let Some(path) = args.config_path { - path - } else { - env::current_dir() - .expect("Couldn't get current directory") - .join("config.yaml") - }; - let config = IndexerConfig::load(&config_path)?; - - // Init metrics server - let registry_service = start_prometheus_server( - format!("{}:{}", config.metric_url, config.metric_port,) - .parse() - .unwrap_or_else(|err| panic!("Failed to parse metric address: {}", err)), - ); - let registry = registry_service.default_registry(); - - iota_metrics::init_metrics(®istry); - - info!( - "Metrics server started at {}::{}", - config.metric_url, config.metric_port - ); - let indexer_meterics = BridgeIndexerMetrics::new(®istry); - let ingestion_metrics = DataIngestionMetrics::new(®istry); - let bridge_metrics = Arc::new(BridgeMetrics::new(®istry)); - - let db_url = config.db_url.clone(); - let datastore = PgBridgePersistent::new(get_connection_pool(db_url.clone())); - - let provider = Arc::new( - Provider::::try_from(config.eth_rpc_url.clone())? - .interval(std::time::Duration::from_millis(2000)), - ); - - let current_block = provider.get_block_number().await?.as_u64(); - let subscription_end_block = u64::MAX; - - // Start the eth subscription indexer - - let eth_subscription_datasource = EthSubscriptionDatasource::new( - config.eth_iota_bridge_contract_address.clone(), - config.eth_ws_url.clone(), - indexer_meterics.clone(), - )?; - let eth_subscription_indexer = IndexerBuilder::new( - "EthBridgeSubscriptionIndexer", - eth_subscription_datasource, - EthDataMapper { - metrics: indexer_meterics.clone(), - }, - ) - .with_backfill_strategy(BackfillStrategy::Disabled) - .build(current_block, subscription_end_block, datastore.clone()); - let subscription_indexer_fut = spawn_logged_monitored_task!(eth_subscription_indexer.start()); - - // Start the eth sync indexer - let eth_sync_datasource = EthSyncDatasource::new( - config.eth_iota_bridge_contract_address.clone(), - config.eth_rpc_url.clone(), - indexer_meterics.clone(), - bridge_metrics.clone(), - )?; - let eth_sync_indexer = IndexerBuilder::new( - "EthBridgeSyncIndexer", - eth_sync_datasource, - EthDataMapper { - metrics: indexer_meterics.clone(), - }, - ) - .with_backfill_strategy(BackfillStrategy::Partitioned { task_size: 1000 }) - .disable_live_task() - .build(current_block, config.start_block, datastore.clone()); - let sync_indexer_fut = spawn_logged_monitored_task!(eth_sync_indexer.start()); - - if let Some(iota_rpc_url) = config.iota_rpc_url.clone() { - // Todo: impl datasource for iota RPC datasource - start_processing_iota_checkpoints_by_querying_txns( - iota_rpc_url, - db_url.clone(), - indexer_meterics.clone(), - bridge_metrics, - ) - .await?; - } else { - let iota_checkpoint_datasource = IotaCheckpointDatasource::new( - config.remote_store_url, - config.concurrency as usize, - config.checkpoints_path.clone().into(), - ingestion_metrics.clone(), - ); - let indexer = IndexerBuilder::new( - "IotaBridgeIndexer", - iota_checkpoint_datasource, - IotaBridgeDataMapper { - metrics: indexer_meterics.clone(), - }, - ) - .build( - config - .resume_from_checkpoint - .unwrap_or(config.bridge_genesis_checkpoint), - config.bridge_genesis_checkpoint, - datastore, - ); - indexer.start().await?; - } - // We are not waiting for the iota tasks to finish here, which is ok. - futures::future::join_all(vec![subscription_indexer_fut, sync_indexer_fut]).await; - - Ok(()) -} - -async fn start_processing_iota_checkpoints_by_querying_txns( - iota_rpc_url: String, - db_url: String, - indexer_metrics: BridgeIndexerMetrics, - bridge_metrics: Arc, -) -> Result>> { - let pg_pool = get_connection_pool(db_url.clone()); - let (tx, rx) = channel( - 100, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["iota_transaction_processing_queue"]), - ); - let mut handles = vec![]; - let cursor = - read_iota_progress_store(&pg_pool).expect("Failed to read cursor from iota progress store"); - let iota_client = IotaClientBuilder::default().build(iota_rpc_url).await?; - handles.push(spawn_logged_monitored_task!( - start_iota_tx_polling_task(iota_client, cursor, tx, bridge_metrics), - "start_iota_tx_polling_task" - )); - handles.push(spawn_logged_monitored_task!( - handle_iota_transactions_loop(pg_pool.clone(), rx, indexer_metrics.clone()), - "handle_iota_transactions_loop" - )); - Ok(handles) -} diff --git a/crates/iota-bridge-indexer/src/metrics.rs b/crates/iota-bridge-indexer/src/metrics.rs deleted file mode 100644 index 0fd001253c4..00000000000 --- a/crates/iota-bridge-indexer/src/metrics.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use prometheus::{ - IntCounter, IntGauge, Registry, register_int_counter_with_registry, - register_int_gauge_with_registry, -}; - -#[derive(Clone, Debug)] -pub struct BridgeIndexerMetrics { - pub(crate) total_iota_bridge_transactions: IntCounter, - pub(crate) total_iota_token_deposited: IntCounter, - pub(crate) total_iota_token_transfer_approved: IntCounter, - pub(crate) total_iota_token_transfer_claimed: IntCounter, - pub(crate) total_iota_bridge_txn_other: IntCounter, - pub(crate) total_eth_bridge_transactions: IntCounter, - pub(crate) total_eth_token_deposited: IntCounter, - pub(crate) total_eth_token_transfer_claimed: IntCounter, - pub(crate) total_eth_bridge_txn_other: IntCounter, - pub(crate) last_committed_iota_checkpoint: IntGauge, - pub(crate) latest_committed_eth_block: IntGauge, - pub(crate) last_synced_eth_block: IntGauge, -} - -impl BridgeIndexerMetrics { - pub fn new(registry: &Registry) -> Self { - Self { - total_iota_bridge_transactions: register_int_counter_with_registry!( - "total_iota_bridge_transactions", - "Total number of iota bridge transactions", - registry, - ) - .unwrap(), - total_iota_token_deposited: register_int_counter_with_registry!( - "total_iota_token_deposited", - "Total number of iota token deposited transactions", - registry, - ) - .unwrap(), - total_iota_token_transfer_approved: register_int_counter_with_registry!( - "total_iota_token_transfer_approved", - "Total number of iota token approved transactions", - registry, - ) - .unwrap(), - total_iota_token_transfer_claimed: register_int_counter_with_registry!( - "total_iota_token_transfer_claimed", - "Total number of iota token claimed transactions", - registry, - ) - .unwrap(), - total_iota_bridge_txn_other: register_int_counter_with_registry!( - "total_iota_bridge_txn_other", - "Total number of other iota bridge transactions", - registry, - ) - .unwrap(), - total_eth_bridge_transactions: register_int_counter_with_registry!( - "total_eth_bridge_transactions", - "Total number of eth bridge transactions", - registry, - ) - .unwrap(), - total_eth_token_deposited: register_int_counter_with_registry!( - "total_eth_token_deposited", - "Total number of eth token deposited transactions", - registry, - ) - .unwrap(), - total_eth_token_transfer_claimed: register_int_counter_with_registry!( - "total_eth_token_transfer_claimed", - "Total number of eth token claimed transactions", - registry, - ) - .unwrap(), - total_eth_bridge_txn_other: register_int_counter_with_registry!( - "total_eth_bridge_txn_other", - "Total number of other eth bridge transactions", - registry, - ) - .unwrap(), - last_committed_iota_checkpoint: register_int_gauge_with_registry!( - "last_committed_iota_checkpoint", - "The latest iota checkpoint that indexer committed to DB", - registry, - ) - .unwrap(), - latest_committed_eth_block: register_int_gauge_with_registry!( - "last_committed_eth_block", - "The latest eth block that indexer committed to DB", - registry, - ) - .unwrap(), - last_synced_eth_block: register_int_gauge_with_registry!( - "last_synced_eth_block", - "The last eth block that indexer committed to DB", - registry, - ) - .unwrap(), - } - } - - pub fn new_for_testing() -> Self { - let registry = Registry::new(); - Self::new(®istry) - } -} diff --git a/crates/iota-bridge-indexer/src/migrations/00000000000000_diesel_initial_setup/down.sql b/crates/iota-bridge-indexer/src/migrations/00000000000000_diesel_initial_setup/down.sql deleted file mode 100644 index eb62a1bf35c..00000000000 --- a/crates/iota-bridge-indexer/src/migrations/00000000000000_diesel_initial_setup/down.sql +++ /dev/null @@ -1,4 +0,0 @@ --- This file should undo anything in `up.sql` -DROP TABLE IF EXISTS token_transfer; -DROP TABLE IF EXISTS token_transfer_data; -DROP TABLE IF EXISTS progress_store; diff --git a/crates/iota-bridge-indexer/src/migrations/00000000000000_diesel_initial_setup/up.sql b/crates/iota-bridge-indexer/src/migrations/00000000000000_diesel_initial_setup/up.sql deleted file mode 100644 index 0353280bf0b..00000000000 --- a/crates/iota-bridge-indexer/src/migrations/00000000000000_diesel_initial_setup/up.sql +++ /dev/null @@ -1,47 +0,0 @@ -CREATE TABLE token_transfer_data -( - chain_id INT NOT NULL, - nonce BIGINT NOT NULL, - block_height BIGINT NOT NULL, - timestamp_ms BIGINT NOT NULL, - txn_hash bytea NOT NULL, - sender_address bytea NOT NULL, - destination_chain INT NOT NULL, - recipient_address bytea NOT NULL, - token_id INT NOT NULL, - amount BIGINT NOT NULL, - PRIMARY KEY(chain_id, nonce) -); -CREATE INDEX token_transfer_data_block_height ON token_transfer_data (block_height); -CREATE INDEX token_transfer_data_timestamp_ms ON token_transfer_data (timestamp_ms); -CREATE INDEX token_transfer_data_sender_address ON token_transfer_data (sender_address); -CREATE INDEX token_transfer_data_destination_chain ON token_transfer_data (destination_chain); -CREATE INDEX token_transfer_data_token_id ON token_transfer_data (token_id); - -CREATE TABLE token_transfer -( - chain_id INT NOT NULL, - nonce BIGINT NOT NULL, - status TEXT NOT NULL, - block_height BIGINT NOT NULL, - timestamp_ms BIGINT NOT NULL, - txn_hash bytea NOT NULL, - txn_sender bytea NOT NULL, - gas_usage BIGINT NOT NULL, - data_source TEXT NOT NULL, - PRIMARY KEY(chain_id, nonce, status) -); -CREATE INDEX token_transfer_block_height ON token_transfer (block_height); -CREATE INDEX token_transfer_timestamp_ms ON token_transfer (timestamp_ms); - -CREATE TABLE progress_store -( - task_name TEXT PRIMARY KEY, - checkpoint BIGINT NOT NULL -); - -CREATE TABLE iota_progress_store -( - id INT PRIMARY KEY, -- dummy value - txn_digest bytea NOT NULL -); diff --git a/crates/iota-bridge-indexer/src/migrations/2024-06-25-112132_smart_progress_store/down.sql b/crates/iota-bridge-indexer/src/migrations/2024-06-25-112132_smart_progress_store/down.sql deleted file mode 100644 index b783386149b..00000000000 --- a/crates/iota-bridge-indexer/src/migrations/2024-06-25-112132_smart_progress_store/down.sql +++ /dev/null @@ -1,5 +0,0 @@ -alter table progress_store - drop column target_checkpoint; - -alter table progress_store - drop column timestamp; \ No newline at end of file diff --git a/crates/iota-bridge-indexer/src/migrations/2024-06-25-112132_smart_progress_store/up.sql b/crates/iota-bridge-indexer/src/migrations/2024-06-25-112132_smart_progress_store/up.sql deleted file mode 100644 index 0b983212c2a..00000000000 --- a/crates/iota-bridge-indexer/src/migrations/2024-06-25-112132_smart_progress_store/up.sql +++ /dev/null @@ -1,5 +0,0 @@ -alter table progress_store - add target_checkpoint BIGINT default 9223372036854775807 not null; - -alter table progress_store - add timestamp timestamp default now() not null; \ No newline at end of file diff --git a/crates/iota-bridge-indexer/src/migrations/2024-07-02-101842_error_transaction/down.sql b/crates/iota-bridge-indexer/src/migrations/2024-07-02-101842_error_transaction/down.sql deleted file mode 100644 index d96181179b7..00000000000 --- a/crates/iota-bridge-indexer/src/migrations/2024-07-02-101842_error_transaction/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS iota_error_transactions; diff --git a/crates/iota-bridge-indexer/src/migrations/2024-07-02-101842_error_transaction/up.sql b/crates/iota-bridge-indexer/src/migrations/2024-07-02-101842_error_transaction/up.sql deleted file mode 100644 index 234814260c4..00000000000 --- a/crates/iota-bridge-indexer/src/migrations/2024-07-02-101842_error_transaction/up.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE iota_error_transactions -( - txn_digest bytea PRIMARY KEY, - sender_address bytea NOT NULL, - timestamp_ms BIGINT NOT NULL, - failure_status text NOT NULL, - cmd_idx BIGINT -); \ No newline at end of file diff --git a/crates/iota-bridge-indexer/src/models.rs b/crates/iota-bridge-indexer/src/models.rs deleted file mode 100644 index fece0f133dc..00000000000 --- a/crates/iota-bridge-indexer/src/models.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use diesel::{Identifiable, Insertable, Queryable, Selectable, data_types::PgTimestamp}; -use iota_indexer_builder::Task; - -use crate::schema::{ - iota_error_transactions, iota_progress_store, progress_store, token_transfer, - token_transfer_data, -}; - -#[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] -#[diesel(table_name = progress_store, primary_key(task_name))] -pub struct ProgressStore { - pub task_name: String, - pub checkpoint: i64, - pub target_checkpoint: i64, - pub timestamp: Option, -} - -impl From for Task { - fn from(value: ProgressStore) -> Self { - Self { - task_name: value.task_name, - checkpoint: value.checkpoint as u64, - target_checkpoint: value.target_checkpoint as u64, - // Ok to unwrap, timestamp is defaulted to now() in database - timestamp: value.timestamp.expect("Timestamp not set").0 as u64, - } - } -} - -#[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] -#[diesel(table_name = iota_progress_store, primary_key(txn_digest))] -pub struct IotaProgressStore { - pub id: i32, // Dummy value - pub txn_digest: Vec, -} - -#[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] -#[diesel(table_name = token_transfer, primary_key(chain_id, nonce))] -pub struct TokenTransfer { - pub chain_id: i32, - pub nonce: i64, - pub status: String, - pub block_height: i64, - pub timestamp_ms: i64, - pub txn_hash: Vec, - pub txn_sender: Vec, - pub gas_usage: i64, - pub data_source: String, -} - -#[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] -#[diesel(table_name = token_transfer_data, primary_key(chain_id, nonce))] -pub struct TokenTransferData { - pub chain_id: i32, - pub nonce: i64, - pub block_height: i64, - pub timestamp_ms: i64, - pub txn_hash: Vec, - pub sender_address: Vec, - pub destination_chain: i32, - pub recipient_address: Vec, - pub token_id: i32, - pub amount: i64, -} - -#[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] -#[diesel(table_name = iota_error_transactions, primary_key(txn_digest))] -pub struct IotaErrorTransactions { - pub txn_digest: Vec, - pub sender_address: Vec, - pub timestamp_ms: i64, - pub failure_status: String, - pub cmd_idx: Option, -} diff --git a/crates/iota-bridge-indexer/src/postgres_manager.rs b/crates/iota-bridge-indexer/src/postgres_manager.rs deleted file mode 100644 index 1a102010764..00000000000 --- a/crates/iota-bridge-indexer/src/postgres_manager.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use diesel::{ - BoolExpressionMethods, Connection, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, - SelectableHelper, - pg::PgConnection, - r2d2::{ConnectionManager, Pool}, - result::Error, -}; -use iota_types::digests::TransactionDigest; - -use crate::{ - ProcessedTxnData, - models::{IotaProgressStore, TokenTransfer as DBTokenTransfer}, - schema, - schema::{ - iota_error_transactions, iota_progress_store::txn_digest, token_transfer, - token_transfer_data, - }, -}; - -pub(crate) type PgPool = Pool>; - -const IOTA_PROGRESS_STORE_DUMMY_KEY: i32 = 1; - -pub fn get_connection_pool(database_url: String) -> PgPool { - let manager = ConnectionManager::::new(database_url); - Pool::builder() - .test_on_check_out(true) - .build(manager) - .expect("Could not build Postgres DB connection pool") -} - -// TODO: add retry logic -pub fn write(pool: &PgPool, token_txns: Vec) -> Result<(), anyhow::Error> { - if token_txns.is_empty() { - return Ok(()); - } - let (transfers, data, errors) = token_txns.iter().fold( - (vec![], vec![], vec![]), - |(mut transfers, mut data, mut errors), d| { - match d { - ProcessedTxnData::TokenTransfer(t) => { - transfers.push(t.to_db()); - if let Some(d) = t.to_data_maybe() { - data.push(d) - } - } - ProcessedTxnData::Error(e) => errors.push(e.to_db()), - } - (transfers, data, errors) - }, - ); - - let connection = &mut pool.get()?; - connection.transaction(|conn| { - diesel::insert_into(token_transfer_data::table) - .values(&data) - .on_conflict_do_nothing() - .execute(conn)?; - diesel::insert_into(token_transfer::table) - .values(&transfers) - .on_conflict_do_nothing() - .execute(conn)?; - diesel::insert_into(iota_error_transactions::table) - .values(&errors) - .on_conflict_do_nothing() - .execute(conn) - })?; - Ok(()) -} - -pub fn update_iota_progress_store( - pool: &PgPool, - tx_digest: TransactionDigest, -) -> Result<(), anyhow::Error> { - let mut conn = pool.get()?; - diesel::insert_into(schema::iota_progress_store::table) - .values(&IotaProgressStore { - id: IOTA_PROGRESS_STORE_DUMMY_KEY, - txn_digest: tx_digest.inner().to_vec(), - }) - .on_conflict(schema::iota_progress_store::dsl::id) - .do_update() - .set(txn_digest.eq(tx_digest.inner().to_vec())) - .execute(&mut conn)?; - Ok(()) -} - -pub fn read_iota_progress_store(pool: &PgPool) -> anyhow::Result> { - let mut conn = pool.get()?; - let val: Option = - crate::schema::iota_progress_store::dsl::iota_progress_store - .select(IotaProgressStore::as_select()) - .first(&mut conn) - .optional()?; - match val { - Some(val) => Ok(Some(TransactionDigest::try_from( - val.txn_digest.as_slice(), - )?)), - None => Ok(None), - } -} - -pub fn get_latest_eth_token_transfer( - pool: &PgPool, - finalized: bool, -) -> Result, Error> { - use crate::schema::token_transfer::dsl::*; - - let connection = &mut pool.get().unwrap(); - - if finalized { - token_transfer - .filter(data_source.eq("ETH").and(status.eq("Deposited"))) - .order(block_height.desc()) - .first::(connection) - .optional() - } else { - token_transfer - .filter(status.eq("DepositedUnfinalized")) - .order(block_height.desc()) - .first::(connection) - .optional() - } -} diff --git a/crates/iota-bridge-indexer/src/schema.rs b/crates/iota-bridge-indexer/src/schema.rs deleted file mode 100644 index 70d58d9c7c1..00000000000 --- a/crates/iota-bridge-indexer/src/schema.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 -// @generated automatically by Diesel CLI. - -diesel::table! { - progress_store (task_name) { - task_name -> Text, - checkpoint -> Int8, - target_checkpoint -> Int8, - timestamp -> Nullable, - } -} - -diesel::table! { - iota_progress_store (id) { - id -> Int4, - txn_digest -> Bytea, - } -} - -diesel::table! { - iota_error_transactions (txn_digest) { - txn_digest -> Bytea, - sender_address -> Bytea, - timestamp_ms -> Int8, - failure_status -> Text, - cmd_idx -> Nullable, - } -} - -diesel::table! { - token_transfer (chain_id, nonce, status) { - chain_id -> Int4, - nonce -> Int8, - status -> Text, - block_height -> Int8, - timestamp_ms -> Int8, - txn_hash -> Bytea, - txn_sender -> Bytea, - gas_usage -> Int8, - data_source -> Text, - } -} - -diesel::table! { - token_transfer_data (chain_id, nonce) { - chain_id -> Int4, - nonce -> Int8, - block_height -> Int8, - timestamp_ms -> Int8, - txn_hash -> Bytea, - sender_address -> Bytea, - destination_chain -> Int4, - recipient_address -> Bytea, - token_id -> Int4, - amount -> Int8, - } -} - -diesel::allow_tables_to_appear_in_same_query!( - progress_store, - iota_error_transactions, - iota_progress_store, - token_transfer, - token_transfer_data, -); diff --git a/crates/iota-bridge-indexer/src/types.rs b/crates/iota-bridge-indexer/src/types.rs deleted file mode 100644 index 69acf3c4e0d..00000000000 --- a/crates/iota-bridge-indexer/src/types.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use iota_json_rpc_types::{ - IotaTransactionBlockEffects, IotaTransactionBlockEvents, IotaTransactionBlockResponse, -}; -use iota_types::digests::TransactionDigest; - -#[derive(Clone)] -pub struct RetrievedTransaction { - pub tx_digest: TransactionDigest, - pub events: IotaTransactionBlockEvents, - pub checkpoint: u64, - pub timestamp_ms: u64, - pub effects: IotaTransactionBlockEffects, -} - -impl TryFrom for RetrievedTransaction { - type Error = anyhow::Error; - fn try_from(response: IotaTransactionBlockResponse) -> Result { - Ok(RetrievedTransaction { - tx_digest: response.digest, - events: response - .events - .ok_or(anyhow::anyhow!("missing events in responses"))?, - checkpoint: response - .checkpoint - .ok_or(anyhow::anyhow!("missing checkpoint in responses"))?, - timestamp_ms: response - .timestamp_ms - .ok_or(anyhow::anyhow!("missing timestamp_ms in responses"))?, - effects: response - .effects - .ok_or(anyhow::anyhow!("missing effects in responses"))?, - }) - } -} diff --git a/crates/iota-bridge/Cargo.toml b/crates/iota-bridge/Cargo.toml deleted file mode 100644 index be47c33ea8d..00000000000 --- a/crates/iota-bridge/Cargo.toml +++ /dev/null @@ -1,60 +0,0 @@ -[package] -name = "iota-bridge" -version.workspace = true -authors = ["IOTA Foundation "] -edition = "2021" -license = "Apache-2.0" -publish = false - -[dependencies] -# external dependencies -anyhow.workspace = true -arc-swap.workspace = true -async-trait.workspace = true -axum.workspace = true -backoff.workspace = true -bcs.workspace = true -clap.workspace = true -enum_dispatch.workspace = true -ethers = "2.0" -eyre.workspace = true -fastcrypto.workspace = true -futures.workspace = true -lru.workspace = true -num_enum.workspace = true -once_cell.workspace = true -prometheus.workspace = true -rand.workspace = true -reqwest.workspace = true -serde.workspace = true -serde_json.workspace = true -serde_with.workspace = true -tap.workspace = true -tempfile.workspace = true -tokio = { workspace = true, features = ["full"] } -tracing.workspace = true -url.workspace = true - -# internal dependencies -bin-version.workspace = true -iota-authority-aggregation.workspace = true -iota-config.workspace = true -iota-json-rpc-api.workspace = true -iota-json-rpc-types.workspace = true -iota-keys.workspace = true -iota-metrics.workspace = true -iota-sdk.workspace = true -iota-test-transaction-builder.workspace = true -iota-types.workspace = true -move-core-types.workspace = true -shared-crypto.workspace = true -telemetry-subscribers.workspace = true -typed-store.workspace = true - -[dev-dependencies] -# external dependencies -hex-literal = "0.3.4" -maplit = "1.0.2" - -# internal dependencies -test-cluster.workspace = true diff --git a/crates/iota-bridge/abi/bridge_committee.json b/crates/iota-bridge/abi/bridge_committee.json deleted file mode 100644 index 23ff17267c1..00000000000 --- a/crates/iota-bridge/abi/bridge_committee.json +++ /dev/null @@ -1,427 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "ERC1967InvalidImplementation", - "type": "error" - }, - { - "inputs": [], - "name": "ERC1967NonPayable", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [], - "name": "UUPSUnauthorizedCallContext", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "slot", - "type": "bytes32" - } - ], - "name": "UUPSUnsupportedProxiableUUID", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address[]", - "name": "updatedMembers", - "type": "address[]" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isBlocklisted", - "type": "bool" - } - ], - "name": "BlocklistUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "Upgraded", - "type": "event" - }, - { - "inputs": [], - "name": "UPGRADE_INTERFACE_VERSION", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "committeeMember", - "type": "address" - } - ], - "name": "blocklist", - "outputs": [ - { - "internalType": "bool", - "name": "isBlocklisted", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "committee", - "outputs": [ - { - "internalType": "contract IBridgeCommittee", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "committeeMember", - "type": "address" - } - ], - "name": "committeeIndex", - "outputs": [ - { - "internalType": "uint8", - "name": "index", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "committeeMember", - "type": "address" - } - ], - "name": "committeeStake", - "outputs": [ - { - "internalType": "uint16", - "name": "stakeAmount", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "config", - "outputs": [ - { - "internalType": "contract IBridgeConfig", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "committee", - "type": "address[]" - }, - { - "internalType": "uint16[]", - "name": "stake", - "type": "uint16[]" - }, - { - "internalType": "uint16", - "name": "minStakeRequired", - "type": "uint16" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_config", - "type": "address" - } - ], - "name": "initializeConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - } - ], - "name": "nonces", - "outputs": [ - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxiableUUID", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "updateBlocklistWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newImplementation", - "type": "address" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "upgradeToAndCall", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "upgradeWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "verifySignatures", - "outputs": [], - "stateMutability": "view", - "type": "function" - } -] diff --git a/crates/iota-bridge/abi/bridge_committee_upgradeable.json b/crates/iota-bridge/abi/bridge_committee_upgradeable.json deleted file mode 100644 index 9bd3d262465..00000000000 --- a/crates/iota-bridge/abi/bridge_committee_upgradeable.json +++ /dev/null @@ -1,212 +0,0 @@ -[ - { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "committee", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "contract IBridgeCommittee" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "nonces", - "inputs": [ - { - "name": "messageType", - "type": "uint8", - "internalType": "uint8" - } - ], - "outputs": [ - { - "name": "nonce", - "type": "uint64", - "internalType": "uint64" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "proxiableUUID", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "bytes32", - "internalType": "bytes32" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "upgradeToAndCall", - "inputs": [ - { - "name": "newImplementation", - "type": "address", - "internalType": "address" - }, - { - "name": "data", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [], - "stateMutability": "payable" - }, - { - "type": "function", - "name": "upgradeWithSignatures", - "inputs": [ - { - "name": "signatures", - "type": "bytes[]", - "internalType": "bytes[]" - }, - { - "name": "message", - "type": "tuple", - "internalType": "struct BridgeUtils.Message", - "components": [ - { - "name": "messageType", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "version", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "nonce", - "type": "uint64", - "internalType": "uint64" - }, - { - "name": "chainID", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "payload", - "type": "bytes", - "internalType": "bytes" - } - ] - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "Initialized", - "inputs": [ - { - "name": "version", - "type": "uint64", - "indexed": false, - "internalType": "uint64" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Upgraded", - "inputs": [ - { - "name": "implementation", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "AddressEmptyCode", - "inputs": [ - { - "name": "target", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967InvalidImplementation", - "inputs": [ - { - "name": "implementation", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] - }, - { - "type": "error", - "name": "NotInitializing", - "inputs": [] - }, - { - "type": "error", - "name": "ReentrancyGuardReentrantCall", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", - "inputs": [ - { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" - } - ] - } -] \ No newline at end of file diff --git a/crates/iota-bridge/abi/bridge_config.json b/crates/iota-bridge/abi/bridge_config.json deleted file mode 100644 index eb3ef51df20..00000000000 --- a/crates/iota-bridge/abi/bridge_config.json +++ /dev/null @@ -1,560 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "ERC1967InvalidImplementation", - "type": "error" - }, - { - "inputs": [], - "name": "ERC1967NonPayable", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [], - "name": "UUPSUnauthorizedCallContext", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "slot", - "type": "bytes32" - } - ], - "name": "UUPSUnsupportedProxiableUUID", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "address", - "name": "tokenAddress", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "iotaDecimal", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint64", - "name": "tokenPrice", - "type": "uint64" - } - ], - "name": "TokenAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint64", - "name": "tokenPrice", - "type": "uint64" - } - ], - "name": "TokenPriceUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "Upgraded", - "type": "event" - }, - { - "inputs": [], - "name": "UPGRADE_INTERFACE_VERSION", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "addTokensWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "chainID", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "committee", - "outputs": [ - { - "internalType": "contract IBridgeCommittee", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_committee", - "type": "address" - }, - { - "internalType": "uint8", - "name": "_chainID", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "_supportedTokens", - "type": "address[]" - }, - { - "internalType": "uint64[]", - "name": "_tokenPrices", - "type": "uint64[]" - }, - { - "internalType": "uint8[]", - "name": "_supportedChains", - "type": "uint8[]" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "chainId", - "type": "uint8" - } - ], - "name": "isChainSupported", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - } - ], - "name": "isTokenSupported", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - } - ], - "name": "nonces", - "outputs": [ - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxiableUUID", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "chainId", - "type": "uint8" - } - ], - "name": "supportedChains", - "outputs": [ - { - "internalType": "bool", - "name": "isSupported", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - } - ], - "name": "supportedTokens", - "outputs": [ - { - "internalType": "address", - "name": "tokenAddress", - "type": "address" - }, - { - "internalType": "uint8", - "name": "iotaDecimal", - "type": "uint8" - }, - { - "internalType": "bool", - "name": "native", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - } - ], - "name": "tokenAddressOf", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - } - ], - "name": "tokenPriceOf", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - } - ], - "name": "tokenPrices", - "outputs": [ - { - "internalType": "uint64", - "name": "tokenPrice", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - } - ], - "name": "tokenIotaDecimalOf", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "updateTokenPriceWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newImplementation", - "type": "address" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "upgradeToAndCall", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "upgradeWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/crates/iota-bridge/abi/bridge_limiter.json b/crates/iota-bridge/abi/bridge_limiter.json deleted file mode 100644 index fb0169a17e3..00000000000 --- a/crates/iota-bridge/abi/bridge_limiter.json +++ /dev/null @@ -1,605 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "ERC1967InvalidImplementation", - "type": "error" - }, - { - "inputs": [], - "name": "ERC1967NonPayable", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [], - "name": "UUPSUnauthorizedCallContext", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "slot", - "type": "bytes32" - } - ], - "name": "UUPSUnsupportedProxiableUUID", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint32", - "name": "hourUpdated", - "type": "uint32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "HourlyTransferAmountUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "sourceChainID", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint64", - "name": "newLimit", - "type": "uint64" - } - ], - "name": "LimitUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "Upgraded", - "type": "event" - }, - { - "inputs": [], - "name": "UPGRADE_INTERFACE_VERSION", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "calculateAmountInUSD", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - } - ], - "name": "calculateWindowAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "total", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "chainHourTimestamp", - "type": "uint256" - } - ], - "name": "chainHourlyTransferAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "totalAmountBridged", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - } - ], - "name": "chainLimits", - "outputs": [ - { - "internalType": "uint64", - "name": "totalLimit", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "committee", - "outputs": [ - { - "internalType": "contract IBridgeCommittee", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "currentHour", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "hourTimestamp", - "type": "uint32" - } - ], - "name": "getChainHourTimestampKey", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_committee", - "type": "address" - }, - { - "internalType": "uint8[]", - "name": "chainIDs", - "type": "uint8[]" - }, - { - "internalType": "uint64[]", - "name": "_totalLimits", - "type": "uint64[]" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - } - ], - "name": "nonces", - "outputs": [ - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - } - ], - "name": "oldestChainTimestamp", - "outputs": [ - { - "internalType": "uint32", - "name": "oldestHourTimestamp", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxiableUUID", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "recordBridgeTransfers", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "updateLimitWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newImplementation", - "type": "address" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "upgradeToAndCall", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "upgradeWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "willAmountExceedLimit", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "willUSDAmountExceedLimit", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - } -] diff --git a/crates/iota-bridge/abi/bridge_vault.json b/crates/iota-bridge/abi/bridge_vault.json deleted file mode 100644 index 71a0893fd92..00000000000 --- a/crates/iota-bridge/abi/bridge_vault.json +++ /dev/null @@ -1,145 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "_wETH", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenAddress", - "type": "address" - }, - { - "internalType": "address", - "name": "recipientAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferERC20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address payable", - "name": "recipientAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferETH", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "wETH", - "outputs": [ - { - "internalType": "contract IWETH9", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] diff --git a/crates/iota-bridge/abi/erc20.json b/crates/iota-bridge/abi/erc20.json deleted file mode 100644 index 5829d4b2816..00000000000 --- a/crates/iota-bridge/abi/erc20.json +++ /dev/null @@ -1,358 +0,0 @@ -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "allowance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientAllowance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientBalance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "approver", - "type": "address" - } - ], - "name": "ERC20InvalidApprover", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - } - ], - "name": "ERC20InvalidReceiver", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "ERC20InvalidSender", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "ERC20InvalidSpender", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "form", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "burn", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "mint", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "testSkip", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/crates/iota-bridge/abi/iota_bridge.json b/crates/iota-bridge/abi/iota_bridge.json deleted file mode 100644 index 4d21ce8406b..00000000000 --- a/crates/iota-bridge/abi/iota_bridge.json +++ /dev/null @@ -1,581 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "ERC1967InvalidImplementation", - "type": "error" - }, - { - "inputs": [], - "name": "ERC1967NonPayable", - "type": "error" - }, - { - "inputs": [], - "name": "EnforcedPause", - "type": "error" - }, - { - "inputs": [], - "name": "ExpectedPause", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [], - "name": "UUPSUnauthorizedCallContext", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "slot", - "type": "bytes32" - } - ], - "name": "UUPSUnsupportedProxiableUUID", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint8", - "name": "sourceChainID", - "type": "uint8" - }, - { - "indexed": true, - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "indexed": true, - "internalType": "uint8", - "name": "destinationChainID", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "erc20AdjustedAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "senderAddress", - "type": "bytes" - }, - { - "indexed": false, - "internalType": "address", - "name": "recipientAddress", - "type": "address" - } - ], - "name": "TokensClaimed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint8", - "name": "sourceChainID", - "type": "uint8" - }, - { - "indexed": true, - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "indexed": true, - "internalType": "uint8", - "name": "destinationChainID", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint64", - "name": "iotaAdjustedAmount", - "type": "uint64" - }, - { - "indexed": false, - "internalType": "address", - "name": "senderAddress", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "recipientAddress", - "type": "bytes" - } - ], - "name": "TokensDeposited", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Unpaused", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "Upgraded", - "type": "event" - }, - { - "inputs": [], - "name": "UPGRADE_INTERFACE_VERSION", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "tokenID", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "recipientAddress", - "type": "bytes" - }, - { - "internalType": "uint8", - "name": "destinationChainID", - "type": "uint8" - } - ], - "name": "bridgeERC20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "recipientAddress", - "type": "bytes" - }, - { - "internalType": "uint8", - "name": "destinationChainID", - "type": "uint8" - } - ], - "name": "bridgeETH", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "committee", - "outputs": [ - { - "internalType": "contract IBridgeCommittee", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "executeEmergencyOpWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_committee", - "type": "address" - }, - { - "internalType": "address", - "name": "_vault", - "type": "address" - }, - { - "internalType": "address", - "name": "_limiter", - "type": "address" - }, - { - "internalType": "address", - "name": "_wETH", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - } - ], - "name": "isTransferProcessed", - "outputs": [ - { - "internalType": "bool", - "name": "isProcessed", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "limiter", - "outputs": [ - { - "internalType": "contract IBridgeLimiter", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - } - ], - "name": "nonces", - "outputs": [ - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxiableUUID", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "transferBridgedTokensWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newImplementation", - "type": "address" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "upgradeToAndCall", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "version", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "nonce", - "type": "uint64" - }, - { - "internalType": "uint8", - "name": "chainID", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "payload", - "type": "bytes" - } - ], - "internalType": "struct BridgeUtils.Message", - "name": "message", - "type": "tuple" - } - ], - "name": "upgradeWithSignatures", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "vault", - "outputs": [ - { - "internalType": "contract IBridgeVault", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "wETH", - "outputs": [ - { - "internalType": "contract IWETH9", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - } -] diff --git a/crates/iota-bridge/abi/tests/mock_iota_bridge_v2.json b/crates/iota-bridge/abi/tests/mock_iota_bridge_v2.json deleted file mode 100644 index 67b31c095f5..00000000000 --- a/crates/iota-bridge/abi/tests/mock_iota_bridge_v2.json +++ /dev/null @@ -1,692 +0,0 @@ -[ - { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "bridgeERC20", - "inputs": [ - { - "name": "tokenID", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "amount", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "recipientAddress", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "destinationChainID", - "type": "uint8", - "internalType": "uint8" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "bridgeETH", - "inputs": [ - { - "name": "recipientAddress", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "destinationChainID", - "type": "uint8", - "internalType": "uint8" - } - ], - "outputs": [], - "stateMutability": "payable" - }, - { - "type": "function", - "name": "committee", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "contract IBridgeCommittee" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "executeEmergencyOpWithSignatures", - "inputs": [ - { - "name": "signatures", - "type": "bytes[]", - "internalType": "bytes[]" - }, - { - "name": "message", - "type": "tuple", - "internalType": "struct BridgeUtils.Message", - "components": [ - { - "name": "messageType", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "version", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "nonce", - "type": "uint64", - "internalType": "uint64" - }, - { - "name": "chainID", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "payload", - "type": "bytes", - "internalType": "bytes" - } - ] - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "initialize", - "inputs": [ - { - "name": "_committee", - "type": "address", - "internalType": "address" - }, - { - "name": "_vault", - "type": "address", - "internalType": "address" - }, - { - "name": "_limiter", - "type": "address", - "internalType": "address" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "initializeV2", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "initializeV2Params", - "inputs": [ - { - "name": "value", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "_override", - "type": "bool", - "internalType": "bool" - }, - { - "name": "_event", - "type": "string", - "internalType": "string" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "isPausing", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "bool", - "internalType": "bool" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "isTransferProcessed", - "inputs": [ - { - "name": "nonce", - "type": "uint64", - "internalType": "uint64" - } - ], - "outputs": [ - { - "name": "isProcessed", - "type": "bool", - "internalType": "bool" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "limiter", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "contract IBridgeLimiter" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "mock", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint8", - "internalType": "uint8" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "newMockFunction", - "inputs": [ - { - "name": "_pausing", - "type": "bool", - "internalType": "bool" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "newMockFunction", - "inputs": [ - { - "name": "_pausing", - "type": "bool", - "internalType": "bool" - }, - { - "name": "_mock", - "type": "uint8", - "internalType": "uint8" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "nonces", - "inputs": [ - { - "name": "messageType", - "type": "uint8", - "internalType": "uint8" - } - ], - "outputs": [ - { - "name": "nonce", - "type": "uint64", - "internalType": "uint64" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "paused", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "bool", - "internalType": "bool" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "proxiableUUID", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "bytes32", - "internalType": "bytes32" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "testSkip", - "inputs": [], - "outputs": [], - "stateMutability": "view" - }, - { - "type": "function", - "name": "transferBridgedTokensWithSignatures", - "inputs": [ - { - "name": "signatures", - "type": "bytes[]", - "internalType": "bytes[]" - }, - { - "name": "message", - "type": "tuple", - "internalType": "struct BridgeUtils.Message", - "components": [ - { - "name": "messageType", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "version", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "nonce", - "type": "uint64", - "internalType": "uint64" - }, - { - "name": "chainID", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "payload", - "type": "bytes", - "internalType": "bytes" - } - ] - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "upgradeToAndCall", - "inputs": [ - { - "name": "newImplementation", - "type": "address", - "internalType": "address" - }, - { - "name": "data", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [], - "stateMutability": "payable" - }, - { - "type": "function", - "name": "upgradeWithSignatures", - "inputs": [ - { - "name": "signatures", - "type": "bytes[]", - "internalType": "bytes[]" - }, - { - "name": "message", - "type": "tuple", - "internalType": "struct BridgeUtils.Message", - "components": [ - { - "name": "messageType", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "version", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "nonce", - "type": "uint64", - "internalType": "uint64" - }, - { - "name": "chainID", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "payload", - "type": "bytes", - "internalType": "bytes" - } - ] - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "vault", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "contract IBridgeVault" - } - ], - "stateMutability": "view" - }, - { - "type": "event", - "name": "Initialized", - "inputs": [ - { - "name": "version", - "type": "uint64", - "indexed": false, - "internalType": "uint64" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "MockEvent", - "inputs": [ - { - "name": "_event", - "type": "string", - "indexed": false, - "internalType": "string" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Paused", - "inputs": [ - { - "name": "account", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "TokensClaimed", - "inputs": [ - { - "name": "sourceChainID", - "type": "uint8", - "indexed": true, - "internalType": "uint8" - }, - { - "name": "nonce", - "type": "uint64", - "indexed": true, - "internalType": "uint64" - }, - { - "name": "destinationChainID", - "type": "uint8", - "indexed": true, - "internalType": "uint8" - }, - { - "name": "tokenID", - "type": "uint8", - "indexed": false, - "internalType": "uint8" - }, - { - "name": "erc20AdjustedAmount", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - }, - { - "name": "senderAddress", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - }, - { - "name": "recipientAddress", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "TokensDeposited", - "inputs": [ - { - "name": "sourceChainID", - "type": "uint8", - "indexed": true, - "internalType": "uint8" - }, - { - "name": "nonce", - "type": "uint64", - "indexed": true, - "internalType": "uint64" - }, - { - "name": "destinationChainID", - "type": "uint8", - "indexed": true, - "internalType": "uint8" - }, - { - "name": "tokenID", - "type": "uint8", - "indexed": false, - "internalType": "uint8" - }, - { - "name": "iotaAdjustedAmount", - "type": "uint64", - "indexed": false, - "internalType": "uint64" - }, - { - "name": "senderAddress", - "type": "address", - "indexed": false, - "internalType": "address" - }, - { - "name": "recipientAddress", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Unpaused", - "inputs": [ - { - "name": "account", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Upgraded", - "inputs": [ - { - "name": "implementation", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "AddressEmptyCode", - "inputs": [ - { - "name": "target", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "AddressInsufficientBalance", - "inputs": [ - { - "name": "account", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967InvalidImplementation", - "inputs": [ - { - "name": "implementation", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "EnforcedPause", - "inputs": [] - }, - { - "type": "error", - "name": "ExpectedPause", - "inputs": [] - }, - { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] - }, - { - "type": "error", - "name": "NotInitializing", - "inputs": [] - }, - { - "type": "error", - "name": "ReentrancyGuardReentrantCall", - "inputs": [] - }, - { - "type": "error", - "name": "SafeERC20FailedOperation", - "inputs": [ - { - "name": "token", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", - "inputs": [ - { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" - } - ] - } -] diff --git a/crates/iota-bridge/src/abi.rs b/crates/iota-bridge/src/abi.rs deleted file mode 100644 index 85fc4cdb09d..00000000000 --- a/crates/iota-bridge/src/abi.rs +++ /dev/null @@ -1,564 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use ethers::{ - abi::RawLog, - contract::{EthLogDecode, abigen}, - types::{Address as EthAddress, Log}, -}; -use iota_types::{base_types::IotaAddress, bridge::BridgeChainId}; -use serde::{Deserialize, Serialize}; - -use crate::{ - encoding::{ - ADD_TOKENS_ON_EVM_MESSAGE_VERSION, ASSET_PRICE_UPDATE_MESSAGE_VERSION, - BridgeMessageEncoding, COMMITTEE_BLOCKLIST_MESSAGE_VERSION, - EMERGENCY_BUTTON_MESSAGE_VERSION, EVM_CONTRACT_UPGRADE_MESSAGE_VERSION, - LIMIT_UPDATE_MESSAGE_VERSION, TOKEN_TRANSFER_MESSAGE_VERSION, - }, - error::{BridgeError, BridgeResult}, - types::{ - AddTokensOnEvmAction, AssetPriceUpdateAction, BlocklistCommitteeAction, BridgeAction, - BridgeActionType, EmergencyAction, EthLog, EthToIotaBridgeAction, EvmContractUpgradeAction, - IotaToEthBridgeAction, LimitUpdateAction, ParsedTokenTransferMessage, - }, -}; - -macro_rules! gen_eth_events { - ($($contract:ident, $contract_event:ident, $abi_path:literal),* $(,)?) => { - $( - abigen!( - $contract, - $abi_path, - event_derives(serde::Deserialize, serde::Serialize) - ); - )* - - #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] - pub enum EthBridgeEvent { - $( - $contract_event($contract_event), - )* - } - - impl EthBridgeEvent { - pub fn try_from_eth_log(log: &EthLog) -> Option { - Self::try_from_log(&log.log) - } - - pub fn try_from_log(log: &Log) -> Option { - let raw_log = RawLog { - topics: log.topics.clone(), - data: log.data.to_vec(), - }; - - $( - if let Ok(decoded) = $contract_event::decode_log(&raw_log) { - return Some(EthBridgeEvent::$contract_event(decoded)); - } - )* - - None - } - } - }; - - // For contracts that don't have Events - ($($contract:ident, $abi_path:literal),* $(,)?) => { - $( - abigen!( - $contract, - $abi_path, - event_derives(serde::Deserialize, serde::Serialize) - ); - )* - }; -} - -#[rustfmt::skip] -gen_eth_events!( - EthIotaBridge, EthIotaBridgeEvents, "abi/iota_bridge.json", - EthBridgeCommittee, EthBridgeCommitteeEvents, "abi/bridge_committee.json", - EthBridgeLimiter, EthBridgeLimiterEvents, "abi/bridge_limiter.json", - EthBridgeConfig, EthBridgeConfigEvents, "abi/bridge_config.json", - EthCommitteeUpgradeableContract, EthCommitteeUpgradeableContractEvents, "abi/bridge_committee_upgradeable.json" -); - -gen_eth_events!(EthBridgeVault, "abi/bridge_vault.json"); - -abigen!( - EthERC20, - "abi/erc20.json", - event_derives(serde::Deserialize, serde::Serialize) -); - -impl EthBridgeEvent { - pub fn try_into_bridge_action( - self, - eth_tx_hash: ethers::types::H256, - eth_event_index: u16, - ) -> BridgeResult> { - Ok(match self { - EthBridgeEvent::EthIotaBridgeEvents(event) => { - match event { - EthIotaBridgeEvents::TokensDepositedFilter(event) => { - let bridge_event = match EthToIotaTokenBridgeV1::try_from(&event) { - Ok(bridge_event) => { - if bridge_event.iota_adjusted_amount == 0 { - return Err(BridgeError::ZeroValueBridgeTransfer(format!( - "Manual intervention is required: {}", - eth_tx_hash - ))); - } - bridge_event - } - // This only happens when solidity code does not align with rust code. - // When this happens in production, there is a risk of stuck bridge - // transfers. We log error here. - // TODO: add metrics and alert - Err(e) => { - return Err(BridgeError::Generic(format!( - "Manual intervention is required. Failed to convert TokensDepositedFilter log to EthToIotaTokenBridgeV1. This indicates incorrect parameters or a bug in the code: {:?}. Err: {:?}", - event, e - ))); - } - }; - - Some(BridgeAction::EthToIotaBridgeAction(EthToIotaBridgeAction { - eth_tx_hash, - eth_event_index, - eth_bridge_event: bridge_event, - })) - } - EthIotaBridgeEvents::TokensClaimedFilter(_event) => None, - EthIotaBridgeEvents::PausedFilter(_event) => None, - EthIotaBridgeEvents::UnpausedFilter(_event) => None, - EthIotaBridgeEvents::UpgradedFilter(_event) => None, - EthIotaBridgeEvents::InitializedFilter(_event) => None, - } - } - EthBridgeEvent::EthBridgeCommitteeEvents(event) => match event { - EthBridgeCommitteeEvents::BlocklistUpdatedFilter(_event) => None, - EthBridgeCommitteeEvents::InitializedFilter(_event) => None, - EthBridgeCommitteeEvents::UpgradedFilter(_event) => None, - }, - EthBridgeEvent::EthBridgeLimiterEvents(event) => match event { - EthBridgeLimiterEvents::LimitUpdatedFilter(_event) => None, - EthBridgeLimiterEvents::InitializedFilter(_event) => None, - EthBridgeLimiterEvents::UpgradedFilter(_event) => None, - EthBridgeLimiterEvents::HourlyTransferAmountUpdatedFilter(_event) => None, - EthBridgeLimiterEvents::OwnershipTransferredFilter(_event) => None, - }, - EthBridgeEvent::EthBridgeConfigEvents(event) => match event { - EthBridgeConfigEvents::InitializedFilter(_event) => None, - EthBridgeConfigEvents::UpgradedFilter(_event) => None, - EthBridgeConfigEvents::TokenAddedFilter(_event) => None, - EthBridgeConfigEvents::TokenPriceUpdatedFilter(_event) => None, - }, - EthBridgeEvent::EthCommitteeUpgradeableContractEvents(event) => match event { - EthCommitteeUpgradeableContractEvents::InitializedFilter(_event) => None, - EthCommitteeUpgradeableContractEvents::UpgradedFilter(_event) => None, - }, - }) - } -} - -/// The event emitted when tokens are deposited into the bridge on Ethereum. -/// Sanity checked version of TokensDepositedFilter -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Hash)] -pub struct EthToIotaTokenBridgeV1 { - pub nonce: u64, - pub iota_chain_id: BridgeChainId, - pub eth_chain_id: BridgeChainId, - pub iota_address: IotaAddress, - pub eth_address: EthAddress, - pub token_id: u8, - pub iota_adjusted_amount: u64, -} - -impl TryFrom<&TokensDepositedFilter> for EthToIotaTokenBridgeV1 { - type Error = BridgeError; - fn try_from(event: &TokensDepositedFilter) -> BridgeResult { - Ok(Self { - nonce: event.nonce, - iota_chain_id: BridgeChainId::try_from(event.destination_chain_id)?, - eth_chain_id: BridgeChainId::try_from(event.source_chain_id)?, - iota_address: IotaAddress::from_bytes(event.recipient_address.as_ref())?, - eth_address: event.sender_address, - token_id: event.token_id, - iota_adjusted_amount: event.iota_adjusted_amount, - }) - } -} - -//////////////////////////////////////////////////////////////////////// -// Eth Message Conversion // -//////////////////////////////////////////////////////////////////////// - -impl From for eth_iota_bridge::Message { - fn from(action: IotaToEthBridgeAction) -> Self { - eth_iota_bridge::Message { - message_type: BridgeActionType::TokenTransfer as u8, - version: TOKEN_TRANSFER_MESSAGE_VERSION, - nonce: action.iota_bridge_event.nonce, - chain_id: action.iota_bridge_event.iota_chain_id as u8, - payload: action.as_payload_bytes().into(), - } - } -} - -impl From for eth_iota_bridge::Message { - fn from(parsed_message: ParsedTokenTransferMessage) -> Self { - eth_iota_bridge::Message { - message_type: BridgeActionType::TokenTransfer as u8, - version: parsed_message.message_version, - nonce: parsed_message.seq_num, - chain_id: parsed_message.source_chain as u8, - payload: parsed_message.payload.into(), - } - } -} - -impl From for eth_iota_bridge::Message { - fn from(action: EmergencyAction) -> Self { - eth_iota_bridge::Message { - message_type: BridgeActionType::EmergencyButton as u8, - version: EMERGENCY_BUTTON_MESSAGE_VERSION, - nonce: action.nonce, - chain_id: action.chain_id as u8, - payload: action.as_payload_bytes().into(), - } - } -} - -impl From for eth_bridge_committee::Message { - fn from(action: BlocklistCommitteeAction) -> Self { - eth_bridge_committee::Message { - message_type: BridgeActionType::UpdateCommitteeBlocklist as u8, - version: COMMITTEE_BLOCKLIST_MESSAGE_VERSION, - nonce: action.nonce, - chain_id: action.chain_id as u8, - payload: action.as_payload_bytes().into(), - } - } -} - -impl From for eth_bridge_limiter::Message { - fn from(action: LimitUpdateAction) -> Self { - eth_bridge_limiter::Message { - message_type: BridgeActionType::LimitUpdate as u8, - version: LIMIT_UPDATE_MESSAGE_VERSION, - nonce: action.nonce, - chain_id: action.chain_id as u8, - payload: action.as_payload_bytes().into(), - } - } -} - -impl From for eth_bridge_config::Message { - fn from(action: AssetPriceUpdateAction) -> Self { - eth_bridge_config::Message { - message_type: BridgeActionType::AssetPriceUpdate as u8, - version: ASSET_PRICE_UPDATE_MESSAGE_VERSION, - nonce: action.nonce, - chain_id: action.chain_id as u8, - payload: action.as_payload_bytes().into(), - } - } -} - -impl From for eth_bridge_config::Message { - fn from(action: AddTokensOnEvmAction) -> Self { - eth_bridge_config::Message { - message_type: BridgeActionType::AddTokensOnEvm as u8, - version: ADD_TOKENS_ON_EVM_MESSAGE_VERSION, - nonce: action.nonce, - chain_id: action.chain_id as u8, - payload: action.as_payload_bytes().into(), - } - } -} - -impl From for eth_committee_upgradeable_contract::Message { - fn from(action: EvmContractUpgradeAction) -> Self { - eth_committee_upgradeable_contract::Message { - message_type: BridgeActionType::EvmContractUpgrade as u8, - version: EVM_CONTRACT_UPGRADE_MESSAGE_VERSION, - nonce: action.nonce, - chain_id: action.chain_id as u8, - payload: action.as_payload_bytes().into(), - } - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use ethers::types::TxHash; - use fastcrypto::encoding::{Encoding, Hex}; - use hex_literal::hex; - use iota_types::{bridge::TOKEN_ID_ETH, crypto::ToFromBytes}; - - use super::*; - use crate::{ - crypto::BridgeAuthorityPublicKeyBytes, - types::{BlocklistType, EmergencyActionType}, - }; - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_eth_message_conversion_emergency_action_regression() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - - let action = EmergencyAction { - nonce: 2, - chain_id: BridgeChainId::EthSepolia, - action_type: EmergencyActionType::Pause, - }; - let message: eth_iota_bridge::Message = action.into(); - assert_eq!( - message, - eth_iota_bridge::Message { - message_type: BridgeActionType::EmergencyButton as u8, - version: EMERGENCY_BUTTON_MESSAGE_VERSION, - nonce: 2, - chain_id: BridgeChainId::EthSepolia as u8, - payload: vec![0].into(), - } - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_eth_message_conversion_update_blocklist_action_regression() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes( - &Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4") - .unwrap(), - ) - .unwrap(); - let action = BlocklistCommitteeAction { - nonce: 0, - chain_id: BridgeChainId::EthSepolia, - blocklist_type: BlocklistType::Blocklist, - members_to_update: vec![pub_key_bytes], - }; - let message: eth_bridge_committee::Message = action.into(); - assert_eq!( - message, - eth_bridge_committee::Message { - message_type: BridgeActionType::UpdateCommitteeBlocklist as u8, - version: COMMITTEE_BLOCKLIST_MESSAGE_VERSION, - nonce: 0, - chain_id: BridgeChainId::EthSepolia as u8, - payload: Hex::decode("000168b43fd906c0b8f024a18c56e06744f7c6157c65") - .unwrap() - .into(), - } - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_eth_message_conversion_update_limit_action_regression() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let action = LimitUpdateAction { - nonce: 2, - chain_id: BridgeChainId::EthSepolia, - sending_chain_id: BridgeChainId::IotaTestnet, - new_usd_limit: 4200000, - }; - let message: eth_bridge_limiter::Message = action.into(); - assert_eq!( - message, - eth_bridge_limiter::Message { - message_type: BridgeActionType::LimitUpdate as u8, - version: LIMIT_UPDATE_MESSAGE_VERSION, - nonce: 2, - chain_id: BridgeChainId::EthSepolia as u8, - payload: Hex::decode("010000000000401640").unwrap().into(), - } - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_eth_message_conversion_contract_upgrade_action_regression() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let action = EvmContractUpgradeAction { - nonce: 2, - chain_id: BridgeChainId::EthSepolia, - proxy_address: EthAddress::repeat_byte(1), - new_impl_address: EthAddress::repeat_byte(2), - call_data: Vec::from("deadbeef"), - }; - let message: eth_committee_upgradeable_contract::Message = action.into(); - assert_eq!( - message, - eth_committee_upgradeable_contract::Message { - message_type: BridgeActionType::EvmContractUpgrade as u8, - version: EVM_CONTRACT_UPGRADE_MESSAGE_VERSION, - nonce: 2, - chain_id: BridgeChainId::EthSepolia as u8, - payload: Hex::decode("0x00000000000000000000000001010101010101010101010101010101010101010000000000000000000000000202020202020202020202020202020202020202000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000086465616462656566000000000000000000000000000000000000000000000000").unwrap().into(), - } - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_eth_message_conversion_update_price_action_regression() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let action = AssetPriceUpdateAction { - nonce: 2, - chain_id: BridgeChainId::EthSepolia, - token_id: TOKEN_ID_ETH, - new_usd_price: 80000000, - }; - let message: eth_bridge_config::Message = action.into(); - assert_eq!( - message, - eth_bridge_config::Message { - message_type: BridgeActionType::AssetPriceUpdate as u8, - version: ASSET_PRICE_UPDATE_MESSAGE_VERSION, - nonce: 2, - chain_id: BridgeChainId::EthSepolia as u8, - payload: Hex::decode("020000000004c4b400").unwrap().into(), - } - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_eth_message_conversion_add_tokens_on_evm_action_regression() -> anyhow::Result<()> { - let action = AddTokensOnEvmAction { - nonce: 5, - chain_id: BridgeChainId::EthCustom, - native: true, - token_ids: vec![99, 100, 101], - token_addresses: vec![ - EthAddress::repeat_byte(1), - EthAddress::repeat_byte(2), - EthAddress::repeat_byte(3), - ], - token_iota_decimals: vec![5, 6, 7], - token_prices: vec![1_000_000_000, 2_000_000_000, 3_000_000_000], - }; - let message: eth_bridge_config::Message = action.into(); - assert_eq!( - message, - eth_bridge_config::Message { - message_type: BridgeActionType::AddTokensOnEvm as u8, - version: ADD_TOKENS_ON_EVM_MESSAGE_VERSION, - nonce: 5, - chain_id: BridgeChainId::EthCustom as u8, - payload: Hex::decode("0103636465030101010101010101010101010101010101010101020202020202020202020202020202020202020203030303030303030303030303030303030303030305060703000000003b9aca00000000007735940000000000b2d05e00").unwrap().into(), - } - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_token_deposit_eth_log_to_iota_bridge_event_regression() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let tx_hash = TxHash::random(); - let action = EthLog { - block_number: 33, - tx_hash, - log_index_in_tx: 1, - log: Log { - address: EthAddress::repeat_byte(1), - topics: vec![ - hex!("a0f1d54820817ede8517e70a3d0a9197c015471c5360d2119b759f0359858ce6").into(), - hex!("000000000000000000000000000000000000000000000000000000000000000c").into(), - hex!("0000000000000000000000000000000000000000000000000000000000000000").into(), - hex!("0000000000000000000000000000000000000000000000000000000000000002").into(), - ], - data: ethers::types::Bytes::from( - Hex::decode("0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000fa56ea0000000000000000000000000014dc79964da2c08b23698b3d3cc7ca32193d9955000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000203b1eb23133e94d08d0da9303cfd38e7d4f8f6951f235daa62cd64ea5b6d96d77").unwrap(), - ), - block_hash: None, - block_number: None, - transaction_hash: Some(tx_hash), - transaction_index: Some(ethers::types::U64::from(0)), - log_index: Some(ethers::types::U256::from(1)), - transaction_log_index: None, - log_type: None, - removed: Some(false), - } - }; - let event = EthBridgeEvent::try_from_eth_log(&action).unwrap(); - assert_eq!( - event, - EthBridgeEvent::EthIotaBridgeEvents(EthIotaBridgeEvents::TokensDepositedFilter( - TokensDepositedFilter { - source_chain_id: 12, - nonce: 0, - destination_chain_id: 2, - token_id: 2, - iota_adjusted_amount: 4200000000, - sender_address: EthAddress::from_str( - "0x14dc79964da2c08b23698b3d3cc7ca32193d9955" - ) - .unwrap(), - recipient_address: ethers::types::Bytes::from( - Hex::decode( - "0x3b1eb23133e94d08d0da9303cfd38e7d4f8f6951f235daa62cd64ea5b6d96d77" - ) - .unwrap(), - ), - } - )) - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_0_iota_amount_conversion_for_eth_event() { - let e = EthBridgeEvent::EthIotaBridgeEvents(EthIotaBridgeEvents::TokensDepositedFilter( - TokensDepositedFilter { - source_chain_id: BridgeChainId::EthSepolia as u8, - nonce: 0, - destination_chain_id: BridgeChainId::IotaTestnet as u8, - token_id: 2, - iota_adjusted_amount: 1, - sender_address: EthAddress::random(), - recipient_address: ethers::types::Bytes::from( - IotaAddress::random_for_testing_only().to_vec(), - ), - }, - )); - assert!( - e.try_into_bridge_action(TxHash::random(), 0) - .unwrap() - .is_some() - ); - - let e = EthBridgeEvent::EthIotaBridgeEvents(EthIotaBridgeEvents::TokensDepositedFilter( - TokensDepositedFilter { - source_chain_id: BridgeChainId::EthSepolia as u8, - nonce: 0, - destination_chain_id: BridgeChainId::IotaTestnet as u8, - token_id: 2, - iota_adjusted_amount: 0, // <------------ - sender_address: EthAddress::random(), - recipient_address: ethers::types::Bytes::from( - IotaAddress::random_for_testing_only().to_vec(), - ), - }, - )); - match e.try_into_bridge_action(TxHash::random(), 0).unwrap_err() { - BridgeError::ZeroValueBridgeTransfer(_) => {} - e => panic!("Unexpected error: {:?}", e), - } - } -} diff --git a/crates/iota-bridge/src/action_executor.rs b/crates/iota-bridge/src/action_executor.rs deleted file mode 100644 index d98e551456a..00000000000 --- a/crates/iota-bridge/src/action_executor.rs +++ /dev/null @@ -1,1582 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! BridgeActionExecutor receives BridgeActions (from BridgeOrchestrator), -//! collects bridge authority signatures and submit signatures on chain. - -use std::{collections::HashMap, sync::Arc}; - -use arc_swap::ArcSwap; -use iota_json_rpc_types::{ - IotaExecutionStatus, IotaTransactionBlockEffectsAPI, IotaTransactionBlockResponse, -}; -use iota_metrics::spawn_logged_monitored_task; -use iota_types::{ - TypeTag, - base_types::{IotaAddress, ObjectID, ObjectRef}, - crypto::{IotaKeyPair, Signature}, - digests::TransactionDigest, - gas_coin::GasCoin, - object::Owner, - transaction::{ObjectArg, Transaction}, -}; -use shared_crypto::intent::{Intent, IntentMessage}; -use tokio::{sync::Semaphore, time::Duration}; -use tracing::{Instrument, error, info, instrument, warn}; - -use crate::{ - client::bridge_authority_aggregator::BridgeAuthorityAggregator, - error::BridgeError, - events::{ - TokenTransferAlreadyApproved, TokenTransferAlreadyClaimed, TokenTransferApproved, - TokenTransferClaimed, - }, - iota_client::{IotaClient, IotaClientInner}, - iota_transaction_builder::build_iota_transaction, - metrics::BridgeMetrics, - retry_with_max_elapsed_time, - storage::BridgeOrchestratorTables, - types::{BridgeAction, BridgeActionStatus, IsBridgePaused, VerifiedCertifiedBridgeAction}, -}; - -pub const CHANNEL_SIZE: usize = 1000; -pub const SIGNING_CONCURRENCY: usize = 10; - -// delay schedule: at most 16 times including the initial attempt -// 0.1s, 0.2s, 0.4s, 0.8s, 1.6s, 3.2s, 6.4s, 12.8s, 25.6s, 51.2s, 102.4s, -// 204.8s, 409.6s, 819.2s, 1638.4s -pub const MAX_SIGNING_ATTEMPTS: u64 = 16; -pub const MAX_EXECUTION_ATTEMPTS: u64 = 16; - -async fn delay(attempt_times: u64) { - let delay_ms = 100 * (2 ^ attempt_times); - tokio::time::sleep(tokio::time::Duration::from_millis(delay_ms)).await; -} - -#[derive(Debug)] -pub struct BridgeActionExecutionWrapper(pub BridgeAction, pub u64); - -#[derive(Debug)] -pub struct CertifiedBridgeActionExecutionWrapper(pub VerifiedCertifiedBridgeAction, pub u64); - -pub trait BridgeActionExecutorTrait { - fn run( - self, - ) -> ( - Vec>, - iota_metrics::metered_channel::Sender, - ); -} - -pub struct BridgeActionExecutor { - iota_client: Arc>, - bridge_auth_agg: Arc>, - key: IotaKeyPair, - iota_address: IotaAddress, - gas_object_id: ObjectID, - store: Arc, - bridge_object_arg: ObjectArg, - iota_token_type_tags: Arc>>, - bridge_pause_rx: tokio::sync::watch::Receiver, - metrics: Arc, -} - -impl BridgeActionExecutorTrait for BridgeActionExecutor -where - C: IotaClientInner + 'static, -{ - fn run( - self, - ) -> ( - Vec>, - iota_metrics::metered_channel::Sender, - ) { - let (tasks, sender, _) = self.run_inner(); - (tasks, sender) - } -} - -impl BridgeActionExecutor -where - C: IotaClientInner + 'static, -{ - pub async fn new( - iota_client: Arc>, - bridge_auth_agg: Arc>, - store: Arc, - key: IotaKeyPair, - iota_address: IotaAddress, - gas_object_id: ObjectID, - iota_token_type_tags: Arc>>, - bridge_pause_rx: tokio::sync::watch::Receiver, - metrics: Arc, - ) -> Self { - let bridge_object_arg = iota_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - Self { - iota_client, - bridge_auth_agg, - store, - key, - gas_object_id, - iota_address, - bridge_object_arg, - iota_token_type_tags, - bridge_pause_rx, - metrics, - } - } - - fn run_inner( - self, - ) -> ( - Vec>, - iota_metrics::metered_channel::Sender, - iota_metrics::metered_channel::Sender, - ) { - let key = self.key; - - let (sender, receiver) = iota_metrics::metered_channel::channel( - CHANNEL_SIZE, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["executor_signing_queue"]), - ); - - let (execution_tx, execution_rx) = iota_metrics::metered_channel::channel( - CHANNEL_SIZE, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["executor_execution_queue"]), - ); - let execution_tx_clone = execution_tx.clone(); - let sender_clone = sender.clone(); - let store_clone = self.store.clone(); - let client_clone = self.iota_client.clone(); - let mut tasks = vec![]; - let metrics = self.metrics.clone(); - tasks.push(spawn_logged_monitored_task!( - Self::run_signature_aggregation_loop( - client_clone, - self.bridge_auth_agg, - store_clone, - sender_clone, - receiver, - execution_tx_clone, - metrics, - ) - )); - - let metrics = self.metrics.clone(); - let execution_tx_clone = execution_tx.clone(); - tasks.push(spawn_logged_monitored_task!( - Self::run_onchain_execution_loop( - self.iota_client.clone(), - key, - self.iota_address, - self.gas_object_id, - self.store.clone(), - execution_tx_clone, - execution_rx, - self.bridge_object_arg, - self.iota_token_type_tags, - self.bridge_pause_rx, - metrics, - ) - )); - (tasks, sender, execution_tx) - } - - async fn run_signature_aggregation_loop( - iota_client: Arc>, - auth_agg: Arc>, - store: Arc, - signing_queue_sender: iota_metrics::metered_channel::Sender, - mut signing_queue_receiver: iota_metrics::metered_channel::Receiver< - BridgeActionExecutionWrapper, - >, - execution_queue_sender: iota_metrics::metered_channel::Sender< - CertifiedBridgeActionExecutionWrapper, - >, - metrics: Arc, - ) { - info!("Starting run_signature_aggregation_loop"); - let semaphore = Arc::new(Semaphore::new(SIGNING_CONCURRENCY)); - while let Some(action) = signing_queue_receiver.recv().await { - Self::handle_signing_task( - &semaphore, - &auth_agg, - &signing_queue_sender, - &execution_queue_sender, - &iota_client, - &store, - action, - &metrics, - ) - .await; - } - } - - async fn should_proceed_signing(iota_client: &Arc>) -> bool { - let Ok(Ok(is_paused)) = - retry_with_max_elapsed_time!(iota_client.is_bridge_paused(), Duration::from_secs(600)) - else { - error!("Failed to get bridge status after retry"); - return false; - }; - !is_paused - } - - #[instrument(level = "error", skip_all, fields(action_key=?action.0.key(), attempt_times=?action.1))] - async fn handle_signing_task( - semaphore: &Arc, - auth_agg: &Arc>, - signing_queue_sender: &iota_metrics::metered_channel::Sender, - execution_queue_sender: &iota_metrics::metered_channel::Sender< - CertifiedBridgeActionExecutionWrapper, - >, - iota_client: &Arc>, - store: &Arc, - action: BridgeActionExecutionWrapper, - metrics: &Arc, - ) { - metrics.action_executor_signing_queue_received_actions.inc(); - let action_key = action.0.key(); - info!("Received action for signing: {:?}", action.0); - - // TODO: this is a temporary fix to avoid signing when the bridge is paused. - // but the way is implemented is not ideal: - // 1. it should check the direction - // 2. should use a better mechanism to check the bridge status instead of - // polling for each action - let should_proceed = Self::should_proceed_signing(iota_client).await; - if !should_proceed { - metrics.action_executor_signing_queue_skipped_actions.inc(); - warn!("skipping signing task: {:?}", action_key); - return; - } - - let auth_agg_clone = auth_agg.clone(); - let signing_queue_sender_clone = signing_queue_sender.clone(); - let execution_queue_sender_clone = execution_queue_sender.clone(); - let iota_client_clone = iota_client.clone(); - let store_clone = store.clone(); - let metrics_clone = metrics.clone(); - let semaphore_clone = semaphore.clone(); - spawn_logged_monitored_task!( - Self::request_signatures( - semaphore_clone, - iota_client_clone, - auth_agg_clone, - action, - store_clone, - signing_queue_sender_clone, - execution_queue_sender_clone, - metrics_clone, - ) - .instrument(tracing::debug_span!("request_signatures", action_key=?action_key)), - "request_signatures" - ); - } - - // Checks if the action is already processed on chain. - // If yes, skip this action and remove it from the pending log. - // Returns true if the action is already processed. - async fn handle_already_processed_token_transfer_action_maybe( - iota_client: &Arc>, - action: &BridgeAction, - store: &Arc, - metrics: &Arc, - ) -> bool { - let status = iota_client - .get_token_transfer_action_onchain_status_until_success( - action.chain_id() as u8, - action.seq_number(), - ) - .await; - match status { - BridgeActionStatus::Approved | BridgeActionStatus::Claimed => { - info!( - "Action already approved or claimed, removing action from pending logs: {:?}", - action - ); - metrics.action_executor_already_processed_actions.inc(); - store - .remove_pending_actions(&[action.digest()]) - .unwrap_or_else(|e| { - panic!("Write to DB should not fail: {:?}", e); - }); - true - } - // Although theoretically a legit IotaToEthBridgeAction should not have - // status `NotFound` - BridgeActionStatus::Pending | BridgeActionStatus::NotFound => false, - } - } - - // TODO: introduce a way to properly stagger the handling - // for various validators. - async fn request_signatures( - semaphore: Arc, - iota_client: Arc>, - auth_agg: Arc>, - action: BridgeActionExecutionWrapper, - store: Arc, - signing_queue_sender: iota_metrics::metered_channel::Sender, - execution_queue_sender: iota_metrics::metered_channel::Sender< - CertifiedBridgeActionExecutionWrapper, - >, - metrics: Arc, - ) { - let _permit = semaphore - .acquire() - .await - .expect("semaphore should not be closed"); - info!("requesting signatures"); - let BridgeActionExecutionWrapper(action, attempt_times) = action; - - // Only token transfer action should reach here - match &action { - BridgeAction::IotaToEthBridgeAction(_) | BridgeAction::EthToIotaBridgeAction(_) => (), - _ => unreachable!("Non token transfer action should not reach here"), - }; - - // If the action is already processed, skip it. - if Self::handle_already_processed_token_transfer_action_maybe( - &iota_client, - &action, - &store, - &metrics, - ) - .await - { - return; - } - match auth_agg - .load() - .request_committee_signatures(action.clone()) - .await - { - Ok(certificate) => { - info!("Sending certificate to execution"); - execution_queue_sender - .send(CertifiedBridgeActionExecutionWrapper(certificate, 0)) - .await - .unwrap_or_else(|e| { - panic!("Sending to execution queue should not fail: {:?}", e); - }); - } - Err(e) => { - warn!("Failed to collect sigs for bridge action: {:?}", e); - metrics.err_signature_aggregation.inc(); - - // TODO: spawn a task for this - if attempt_times >= MAX_SIGNING_ATTEMPTS { - error!( - "Manual intervention is required. Failed to collect sigs for bridge action after {MAX_SIGNING_ATTEMPTS} attempts: {:?}", - e - ); - return; - } - delay(attempt_times).await; - signing_queue_sender - .send(BridgeActionExecutionWrapper(action, attempt_times + 1)) - .await - .unwrap_or_else(|e| { - panic!("Sending to signing queue should not fail: {:?}", e); - }); - } - } - } - - // Before calling this function, `key` and `iota_address` need to be - // verified to match. - async fn run_onchain_execution_loop( - iota_client: Arc>, - iota_key: IotaKeyPair, - iota_address: IotaAddress, - gas_object_id: ObjectID, - store: Arc, - execution_queue_sender: iota_metrics::metered_channel::Sender< - CertifiedBridgeActionExecutionWrapper, - >, - mut execution_queue_receiver: iota_metrics::metered_channel::Receiver< - CertifiedBridgeActionExecutionWrapper, - >, - bridge_object_arg: ObjectArg, - iota_token_type_tags: Arc>>, - bridge_pause_rx: tokio::sync::watch::Receiver, - metrics: Arc, - ) { - info!("Starting run_onchain_execution_loop"); - while let Some(certificate_wrapper) = execution_queue_receiver.recv().await { - // When bridge is paused, skip execution. - // Skipped actions will be picked up upon node restarting - // if bridge is unpaused. - if *bridge_pause_rx.borrow() { - warn!("Bridge is paused, skipping execution"); - metrics - .action_executor_execution_queue_skipped_actions_due_to_pausing - .inc(); - continue; - } - Self::handle_execution_task( - certificate_wrapper, - &iota_client, - &iota_key, - &iota_address, - gas_object_id, - &store, - &execution_queue_sender, - &bridge_object_arg, - &iota_token_type_tags, - &metrics, - ) - .await; - } - panic!("Execution queue closed unexpectedly"); - } - - #[instrument(level = "error", skip_all, fields(action_key=?certificate_wrapper.0.data().key(), attempt_times=?certificate_wrapper.1))] - async fn handle_execution_task( - certificate_wrapper: CertifiedBridgeActionExecutionWrapper, - iota_client: &Arc>, - iota_key: &IotaKeyPair, - iota_address: &IotaAddress, - gas_object_id: ObjectID, - store: &Arc, - execution_queue_sender: &iota_metrics::metered_channel::Sender< - CertifiedBridgeActionExecutionWrapper, - >, - bridge_object_arg: &ObjectArg, - iota_token_type_tags: &ArcSwap>, - metrics: &Arc, - ) { - metrics - .action_executor_execution_queue_received_actions - .inc(); - let CertifiedBridgeActionExecutionWrapper(certificate, attempt_times) = certificate_wrapper; - let action = certificate.data(); - let action_key = action.key(); - - info!("Received certified action for execution: {:?}", action); - - // TODO check gas coin balance here. If gas balance too low, do not proceed. - let (gas_coin, gas_object_ref) = - Self::get_gas_data_assert_ownership(*iota_address, gas_object_id, iota_client).await; - metrics.gas_coin_balance.set(gas_coin.value() as i64); - - let ceriticate_clone = certificate.clone(); - - // Check once: if the action is already processed, skip it. - if Self::handle_already_processed_token_transfer_action_maybe( - iota_client, - action, - store, - metrics, - ) - .await - { - info!("Action already processed, skipping"); - return; - } - - info!("Building IOTA transaction"); - let rgp = iota_client.get_reference_gas_price_until_success().await; - let tx_data = match build_iota_transaction( - *iota_address, - &gas_object_ref, - ceriticate_clone, - *bridge_object_arg, - iota_token_type_tags.load().as_ref(), - rgp, - ) { - Ok(tx_data) => tx_data, - Err(err) => { - metrics.err_build_iota_transaction.inc(); - error!( - "Manual intervention is required. Failed to build transaction for action {:?}: {:?}", - action, err - ); - // This should not happen, but in case it does, we do not want to - // panic, instead we log here for manual intervention. - return; - } - }; - let sig = Signature::new_secure( - &IntentMessage::new(Intent::iota_transaction(), &tx_data), - iota_key, - ); - let signed_tx = Transaction::from_data(tx_data, vec![sig]); - let tx_digest = *signed_tx.digest(); - - // Check twice: If the action is already processed, skip it. - if Self::handle_already_processed_token_transfer_action_maybe( - iota_client, - action, - store, - metrics, - ) - .await - { - info!("Action already processed, skipping"); - return; - } - - info!(?tx_digest, ?gas_object_ref, "Sending transaction to IOTA"); - match iota_client - .execute_transaction_block_with_effects(signed_tx) - .await - { - Ok(resp) => { - Self::handle_execution_effects(tx_digest, resp, store, action, metrics).await - } - - // If the transaction did not go through, retry up to a certain times. - Err(err) => { - error!( - ?action_key, - ?tx_digest, - "IOTA transaction failed at signing: {err:?}" - ); - metrics.err_iota_transaction_submission.inc(); - let metrics_clone = metrics.clone(); - // Do this in a separate task so we won't deadlock here - let sender_clone = execution_queue_sender.clone(); - spawn_logged_monitored_task!(async move { - // If it fails for too many times, log and ask for manual intervention. - metrics_clone - .err_iota_transaction_submission_too_many_failures - .inc(); - if attempt_times >= MAX_EXECUTION_ATTEMPTS { - error!("Manual intervention is required. Failed to collect execute transaction for bridge action after {MAX_EXECUTION_ATTEMPTS} attempts: {:?}", err); - return; - } - delay(attempt_times).await; - sender_clone - .send(CertifiedBridgeActionExecutionWrapper( - certificate, - attempt_times + 1, - )) - .await - .unwrap_or_else(|e| { - panic!("Sending to execution queue should not fail: {:?}", e); - }); - info!("Re-enqueued certificate for execution"); - }.instrument(tracing::debug_span!("reenqueue_execution_task", action_key=?action_key))); - } - } - } - - // TODO: do we need a mechanism to periodically read pending actions from DB? - async fn handle_execution_effects( - tx_digest: TransactionDigest, - response: IotaTransactionBlockResponse, - store: &Arc, - action: &BridgeAction, - metrics: &Arc, - ) { - let effects = response - .effects - .clone() - .expect("We requested effects but got None."); - let status = effects.status(); - match status { - IotaExecutionStatus::Success => { - let events = response.events.expect("We requested events but got None."); - // If the transaction is successful, there must be either - // TokenTransferAlreadyClaimed or TokenTransferClaimed event. - assert!( - events.data.iter().any(|e| e.type_ - == *TokenTransferAlreadyClaimed.get().unwrap() - || e.type_ == *TokenTransferClaimed.get().unwrap() - || e.type_ == *TokenTransferApproved.get().unwrap() - || e.type_ == *TokenTransferAlreadyApproved.get().unwrap()), - "Expected TokenTransferAlreadyClaimed, TokenTransferClaimed, TokenTransferApproved or TokenTransferAlreadyApproved event but got: {:?}", - events, - ); - info!(?tx_digest, "IOTA transaction executed successfully"); - store - .remove_pending_actions(&[action.digest()]) - .unwrap_or_else(|e| { - panic!("Write to DB should not fail: {:?}", e); - }) - } - IotaExecutionStatus::Failure { error } => { - // In practice the transaction could fail because of running out of gas, but - // really should not be due to other reasons. - // This means manual intervention is needed. So we do not push them back to - // the execution queue because retries are mostly likely going to fail anyway. - // After human examination, the node should be restarted and fetch them from - // WAL. - - metrics.err_iota_transaction_execution.inc(); - error!( - ?tx_digest, - "Manual intervention is needed. IOTA transaction executed and failed with error: {error:?}" - ); - } - } - } - - /// Panics if the gas object is not owned by the address. - async fn get_gas_data_assert_ownership( - iota_address: IotaAddress, - gas_object_id: ObjectID, - iota_client: &IotaClient, - ) -> (GasCoin, ObjectRef) { - let (gas_coin, gas_obj_ref, owner) = iota_client - .get_gas_data_panic_if_not_gas(gas_object_id) - .await; - - // TODO: when we add multiple gas support in the future we could discard - // transferred gas object instead. - assert_eq!( - owner, - Owner::AddressOwner(iota_address), - "Gas object {:?} is no longer owned by address {}", - gas_object_id, - iota_address - ); - (gas_coin, gas_obj_ref) - } -} - -pub async fn submit_to_executor( - tx: &iota_metrics::metered_channel::Sender, - action: BridgeAction, -) -> Result<(), BridgeError> { - tx.send(BridgeActionExecutionWrapper(action, 0)) - .await - .map_err(|e| BridgeError::Generic(e.to_string())) -} - -#[cfg(test)] -mod tests { - use std::{ - collections::{BTreeMap, HashMap}, - str::FromStr, - }; - - use fastcrypto::traits::KeyPair; - use iota_json_rpc_types::{ - IotaEvent, IotaTransactionBlockEffects, IotaTransactionBlockEvents, - IotaTransactionBlockResponse, - }; - use iota_types::{ - TypeTag, base_types::random_object_ref, crypto::get_key_pair, gas_coin::GasCoin, - transaction::TransactionData, - }; - use prometheus::Registry; - - use super::*; - use crate::{ - crypto::{ - BridgeAuthorityKeyPair, BridgeAuthorityPublicKeyBytes, - BridgeAuthorityRecoverableSignature, - }, - events::init_all_struct_tags, - iota_mock_client::IotaMockClient, - server::mock_handler::BridgeRequestMockHandler, - test_utils::{ - DUMMY_MUTABLE_BRIDGE_OBJECT_ARG, get_test_authorities_and_run_mock_bridge_server, - get_test_eth_to_iota_bridge_action, get_test_iota_to_eth_bridge_action, - sign_action_with_key, - }, - types::{ - BRIDGE_PAUSED, BridgeCommittee, BridgeCommitteeValiditySignInfo, CertifiedBridgeAction, - }, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_onchain_execution_loop() { - let SetupData { - signing_tx, - iota_client_mock, - mut tx_subscription, - store, - secrets, - dummy_iota_key, - mock0, - mock1, - mock2, - mock3, - gas_object_ref, - iota_address, - iota_token_type_tags, - .. - } = setup().await; - let (action_certificate, _, _) = get_bridge_authority_approved_action( - vec![&mock0, &mock1, &mock2, &mock3], - vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], - None, - true, - ); - let action = action_certificate.data().clone(); - let id_token_map = (*iota_token_type_tags.load().clone()).clone(); - let tx_data = build_iota_transaction( - iota_address, - &gas_object_ref, - action_certificate, - DUMMY_MUTABLE_BRIDGE_OBJECT_ARG, - &id_token_map, - 1000, - ) - .unwrap(); - - let tx_digest = get_tx_digest(tx_data, &dummy_iota_key); - - let gas_coin = GasCoin::new_for_testing(1_000_000_000_000); // dummy gas coin - iota_client_mock.add_gas_object_info( - gas_coin.clone(), - gas_object_ref, - Owner::AddressOwner(iota_address), - ); - - // Mock the transaction to be successfully executed - let mut event = IotaEvent::random_for_testing(); - event.type_ = TokenTransferClaimed.get().unwrap().clone(); - let events = vec![event]; - mock_transaction_response( - &iota_client_mock, - tx_digest, - IotaExecutionStatus::Success, - Some(events), - true, - ); - - store.insert_pending_actions(&[action.clone()]).unwrap(); - assert_eq!( - store.get_all_pending_actions()[&action.digest()], - action.clone() - ); - - // Kick it - submit_to_executor(&signing_tx, action.clone()) - .await - .unwrap(); - - // Expect to see the transaction to be requested and successfully executed hence - // removed from WAL - tx_subscription.recv().await.unwrap(); - assert!(store.get_all_pending_actions().is_empty()); - - ///////////////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////// Test execution failure - ////////////////////////////////////// /////////////////////////////////// - ////////////////////////////////////// ///////////////////////////////////////// - ////////////////////////////////////// ////////////////// - - let (action_certificate, _, _) = get_bridge_authority_approved_action( - vec![&mock0, &mock1, &mock2, &mock3], - vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], - None, - true, - ); - - let action = action_certificate.data().clone(); - - let tx_data = build_iota_transaction( - iota_address, - &gas_object_ref, - action_certificate, - DUMMY_MUTABLE_BRIDGE_OBJECT_ARG, - &id_token_map, - 1000, - ) - .unwrap(); - let tx_digest = get_tx_digest(tx_data, &dummy_iota_key); - - // Mock the transaction to fail - mock_transaction_response( - &iota_client_mock, - tx_digest, - IotaExecutionStatus::Failure { - error: "failure is mother of success".to_string(), - }, - None, - true, - ); - - store.insert_pending_actions(&[action.clone()]).unwrap(); - assert_eq!( - store.get_all_pending_actions()[&action.digest()], - action.clone() - ); - - // Kick it - submit_to_executor(&signing_tx, action.clone()) - .await - .unwrap(); - - // Expect to see the transaction to be requested and but failed - tx_subscription.recv().await.unwrap(); - // The action is not removed from WAL because the transaction failed - assert_eq!( - store.get_all_pending_actions()[&action.digest()], - action.clone() - ); - - ///////////////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////// Test transaction failed at signing stage - //////////////////////////// /////////////////////////// /////////////// - //////////////////////////// /////////////////////////////////////////////////// - //////////////////////////// /// - - let (action_certificate, _, _) = get_bridge_authority_approved_action( - vec![&mock0, &mock1, &mock2, &mock3], - vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], - None, - true, - ); - - let action = action_certificate.data().clone(); - - let tx_data = build_iota_transaction( - iota_address, - &gas_object_ref, - action_certificate, - DUMMY_MUTABLE_BRIDGE_OBJECT_ARG, - &id_token_map, - 1000, - ) - .unwrap(); - let tx_digest = get_tx_digest(tx_data, &dummy_iota_key); - mock_transaction_error( - &iota_client_mock, - tx_digest, - BridgeError::Generic("some random error".to_string()), - true, - ); - - store.insert_pending_actions(&[action.clone()]).unwrap(); - assert_eq!( - store.get_all_pending_actions()[&action.digest()], - action.clone() - ); - - // Kick it - submit_to_executor(&signing_tx, action.clone()) - .await - .unwrap(); - - // Failure will trigger retry, we wait for 2 requests before checking WAL log - let tx_digest = tx_subscription.recv().await.unwrap(); - assert_eq!(tx_subscription.recv().await.unwrap(), tx_digest); - - // The retry is still going on, action still in WAL - assert!( - store - .get_all_pending_actions() - .contains_key(&action.digest()) - ); - - // Now let it succeed - let mut event = IotaEvent::random_for_testing(); - event.type_ = TokenTransferClaimed.get().unwrap().clone(); - let events = vec![event]; - mock_transaction_response( - &iota_client_mock, - tx_digest, - IotaExecutionStatus::Success, - Some(events), - true, - ); - - // Give it 1 second to retry and succeed - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - // The action is successful and should be removed from WAL now - assert!( - !store - .get_all_pending_actions() - .contains_key(&action.digest()) - ); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_signature_aggregation_loop() { - let SetupData { - signing_tx, - iota_client_mock, - mut tx_subscription, - store, - secrets, - dummy_iota_key, - mock0, - mock1, - mock2, - mock3, - gas_object_ref, - iota_address, - iota_token_type_tags, - .. - } = setup().await; - let id_token_map = (*iota_token_type_tags.load().clone()).clone(); - let (action_certificate, iota_tx_digest, iota_tx_event_index) = - get_bridge_authority_approved_action( - vec![&mock0, &mock1, &mock2, &mock3], - vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], - None, - true, - ); - let action = action_certificate.data().clone(); - mock_bridge_authority_signing_errors( - vec![&mock0, &mock1, &mock2], - iota_tx_digest, - iota_tx_event_index, - ); - let mut sigs = mock_bridge_authority_sigs( - vec![&mock3], - &action, - vec![&secrets[3]], - iota_tx_digest, - iota_tx_event_index, - ); - - let gas_coin = GasCoin::new_for_testing(1_000_000_000_000); // dummy gas coin - iota_client_mock.add_gas_object_info( - gas_coin, - gas_object_ref, - Owner::AddressOwner(iota_address), - ); - store.insert_pending_actions(&[action.clone()]).unwrap(); - assert_eq!( - store.get_all_pending_actions()[&action.digest()], - action.clone() - ); - - // Kick it - submit_to_executor(&signing_tx, action.clone()) - .await - .unwrap(); - - // Wait until the transaction is retried at least once (instead of deing - // dropped) - loop { - let requested_times = - mock0.get_iota_token_events_requested(iota_tx_digest, iota_tx_event_index); - if requested_times >= 2 { - break; - } - tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; - } - // Nothing is sent to execute yet - assert_eq!( - tx_subscription.try_recv().unwrap_err(), - tokio::sync::broadcast::error::TryRecvError::Empty - ); - // Still in WAL - assert_eq!( - store.get_all_pending_actions()[&action.digest()], - action.clone() - ); - - // Let authorities sign the action too. Now we are above the threshold - let sig_from_2 = mock_bridge_authority_sigs( - vec![&mock2], - &action, - vec![&secrets[2]], - iota_tx_digest, - iota_tx_event_index, - ); - sigs.extend(sig_from_2); - let certified_action = CertifiedBridgeAction::new_from_data_and_sig( - action.clone(), - BridgeCommitteeValiditySignInfo { signatures: sigs }, - ); - let action_certificate = VerifiedCertifiedBridgeAction::new_from_verified(certified_action); - let tx_data = build_iota_transaction( - iota_address, - &gas_object_ref, - action_certificate, - DUMMY_MUTABLE_BRIDGE_OBJECT_ARG, - &id_token_map, - 1000, - ) - .unwrap(); - let tx_digest = get_tx_digest(tx_data, &dummy_iota_key); - - let mut event = IotaEvent::random_for_testing(); - event.type_ = TokenTransferClaimed.get().unwrap().clone(); - let events = vec![event]; - mock_transaction_response( - &iota_client_mock, - tx_digest, - IotaExecutionStatus::Success, - Some(events), - true, - ); - - // Expect to see the transaction to be requested and succeed - assert_eq!(tx_subscription.recv().await.unwrap(), tx_digest); - // The action is removed from WAL - assert!( - !store - .get_all_pending_actions() - .contains_key(&action.digest()) - ); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_skip_request_signature_if_already_processed_on_chain() { - let SetupData { - signing_tx, - iota_client_mock, - mut tx_subscription, - store, - mock0, - mock1, - mock2, - mock3, - .. - } = setup().await; - - let iota_tx_digest = TransactionDigest::random(); - let iota_tx_event_index = 0; - let action = get_test_iota_to_eth_bridge_action( - Some(iota_tx_digest), - Some(iota_tx_event_index), - None, - None, - None, - None, - None, - ); - mock_bridge_authority_signing_errors( - vec![&mock0, &mock1, &mock2, &mock3], - iota_tx_digest, - iota_tx_event_index, - ); - store.insert_pending_actions(&[action.clone()]).unwrap(); - assert_eq!( - store.get_all_pending_actions()[&action.digest()], - action.clone() - ); - - // Kick it - submit_to_executor(&signing_tx, action.clone()) - .await - .unwrap(); - let action_digest = action.digest(); - - // Wait for 1 second. It should still in the process of retrying requesting sigs - // because we mock errors above. - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - tx_subscription.try_recv().unwrap_err(); - // And the action is still in WAL - assert!(store.get_all_pending_actions().contains_key(&action_digest)); - - iota_client_mock.set_action_onchain_status(&action, BridgeActionStatus::Approved); - - // The next retry will see the action is already processed on chain and remove - // it from WAL - let now = std::time::Instant::now(); - while store.get_all_pending_actions().contains_key(&action_digest) { - if now.elapsed().as_secs() > 10 { - panic!("Timeout waiting for action to be removed from WAL"); - } - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - } - tx_subscription.try_recv().unwrap_err(); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_skip_tx_submission_if_already_processed_on_chain() { - let SetupData { - execution_tx, - iota_client_mock, - mut tx_subscription, - store, - secrets, - dummy_iota_key, - mock0, - mock1, - mock2, - mock3, - gas_object_ref, - iota_address, - iota_token_type_tags, - .. - } = setup().await; - let id_token_map = (*iota_token_type_tags.load().clone()).clone(); - let (action_certificate, _, _) = get_bridge_authority_approved_action( - vec![&mock0, &mock1, &mock2, &mock3], - vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], - None, - true, - ); - - let action = action_certificate.data().clone(); - let arg = DUMMY_MUTABLE_BRIDGE_OBJECT_ARG; - let tx_data = build_iota_transaction( - iota_address, - &gas_object_ref, - action_certificate.clone(), - arg, - &id_token_map, - 1000, - ) - .unwrap(); - let tx_digest = get_tx_digest(tx_data, &dummy_iota_key); - mock_transaction_error( - &iota_client_mock, - tx_digest, - BridgeError::Generic("some random error".to_string()), - true, - ); - - let gas_coin = GasCoin::new_for_testing(1_000_000_000_000); // dummy gas coin - iota_client_mock.add_gas_object_info( - gas_coin.clone(), - gas_object_ref, - Owner::AddressOwner(iota_address), - ); - - iota_client_mock.set_action_onchain_status(&action, BridgeActionStatus::Pending); - - store.insert_pending_actions(&[action.clone()]).unwrap(); - assert_eq!( - store.get_all_pending_actions()[&action.digest()], - action.clone() - ); - - // Kick it (send to the execution queue, skipping the signing queue) - execution_tx - .send(CertifiedBridgeActionExecutionWrapper(action_certificate, 0)) - .await - .unwrap(); - - // Some requests come in and will fail. - tx_subscription.recv().await.unwrap(); - - // Set the action to be already approved on chain - iota_client_mock.set_action_onchain_status(&action, BridgeActionStatus::Approved); - - // The next retry will see the action is already processed on chain and remove - // it from WAL - let now = std::time::Instant::now(); - let action_digest = action.digest(); - while store.get_all_pending_actions().contains_key(&action_digest) { - if now.elapsed().as_secs() > 10 { - panic!("Timeout waiting for action to be removed from WAL"); - } - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - } - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_skip_tx_submission_if_bridge_is_paused() { - let SetupData { - execution_tx, - iota_client_mock, - mut tx_subscription, - store, - secrets, - dummy_iota_key, - mock0, - mock1, - mock2, - mock3, - gas_object_ref, - iota_address, - iota_token_type_tags, - bridge_pause_tx, - .. - } = setup().await; - let id_token_map: HashMap = (*iota_token_type_tags.load().clone()).clone(); - let (action_certificate, _, _) = get_bridge_authority_approved_action( - vec![&mock0, &mock1, &mock2, &mock3], - vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], - None, - true, - ); - - let action = action_certificate.data().clone(); - let arg = DUMMY_MUTABLE_BRIDGE_OBJECT_ARG; - let tx_data = build_iota_transaction( - iota_address, - &gas_object_ref, - action_certificate.clone(), - arg, - &id_token_map, - 1000, - ) - .unwrap(); - let tx_digest = get_tx_digest(tx_data, &dummy_iota_key); - mock_transaction_error( - &iota_client_mock, - tx_digest, - BridgeError::Generic("some random error".to_string()), - true, - ); - - let gas_coin = GasCoin::new_for_testing(1_000_000_000_000); // dummy gas coin - iota_client_mock.add_gas_object_info( - gas_coin.clone(), - gas_object_ref, - Owner::AddressOwner(iota_address), - ); - let action_digest = action.digest(); - iota_client_mock.set_action_onchain_status(&action, BridgeActionStatus::Pending); - - // assert bridge is unpaused now - assert!(!*bridge_pause_tx.borrow()); - - store.insert_pending_actions(&[action.clone()]).unwrap(); - assert_eq!( - store.get_all_pending_actions()[&action.digest()], - action.clone() - ); - - // Kick it (send to the execution queue, skipping the signing queue) - execution_tx - .send(CertifiedBridgeActionExecutionWrapper( - action_certificate.clone(), - 0, - )) - .await - .unwrap(); - - // Some requests come in - tx_subscription.recv().await.unwrap(); - - // Pause the bridge - bridge_pause_tx.send(BRIDGE_PAUSED).unwrap(); - - // Kick it again - execution_tx - .send(CertifiedBridgeActionExecutionWrapper(action_certificate, 0)) - .await - .unwrap(); - - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - // Nothing is sent to execute - assert_eq!( - tx_subscription.try_recv().unwrap_err(), - tokio::sync::broadcast::error::TryRecvError::Empty - ); - // Still in WAL - assert_eq!( - store.get_all_pending_actions()[&action_digest], - action.clone() - ); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_action_executor_handle_new_token() { - let new_token_id = 255u8; // token id that does not exist - let new_type_tag = TypeTag::from_str("0xbeef::beef::BEEF").unwrap(); - let SetupData { - execution_tx, - iota_client_mock, - mut tx_subscription, - secrets, - dummy_iota_key, - mock0, - mock1, - mock2, - mock3, - gas_object_ref, - iota_address, - iota_token_type_tags, - .. - } = setup().await; - let mut id_token_map: HashMap = (*iota_token_type_tags.load().clone()).clone(); - let (action_certificate, _, _) = get_bridge_authority_approved_action( - vec![&mock0, &mock1, &mock2, &mock3], - vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], - Some(new_token_id), - false, /* we need an eth -> iota action that entails the new token type tag in - * transaction building */ - ); - - let action = action_certificate.data().clone(); - let arg = DUMMY_MUTABLE_BRIDGE_OBJECT_ARG; - let tx_data = build_iota_transaction( - iota_address, - &gas_object_ref, - action_certificate.clone(), - arg, - &maplit::hashmap! { - new_token_id => new_type_tag.clone() - }, - 1000, - ) - .unwrap(); - let tx_digest = get_tx_digest(tx_data, &dummy_iota_key); - mock_transaction_error( - &iota_client_mock, - tx_digest, - BridgeError::Generic("some random error".to_string()), - true, - ); - - let gas_coin = GasCoin::new_for_testing(1_000_000_000_000); // dummy gas coin - iota_client_mock.add_gas_object_info( - gas_coin.clone(), - gas_object_ref, - Owner::AddressOwner(iota_address), - ); - iota_client_mock.set_action_onchain_status(&action, BridgeActionStatus::Pending); - - // Kick it (send to the execution queue, skipping the signing queue) - execution_tx - .send(CertifiedBridgeActionExecutionWrapper( - action_certificate.clone(), - 0, - )) - .await - .unwrap(); - - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - // Nothing is sent to execute, because the token id does not exist yet - assert_eq!( - tx_subscription.try_recv().unwrap_err(), - tokio::sync::broadcast::error::TryRecvError::Empty - ); - - // Now insert the new token id - id_token_map.insert(new_token_id, new_type_tag); - iota_token_type_tags.store(Arc::new(id_token_map)); - - // Kick it again - execution_tx - .send(CertifiedBridgeActionExecutionWrapper( - action_certificate.clone(), - 0, - )) - .await - .unwrap(); - - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - // The action is sent to execution - assert_eq!(tx_subscription.recv().await.unwrap(), tx_digest); - } - - fn mock_bridge_authority_sigs( - mocks: Vec<&BridgeRequestMockHandler>, - action: &BridgeAction, - secrets: Vec<&BridgeAuthorityKeyPair>, - iota_tx_digest: TransactionDigest, - iota_tx_event_index: u16, - ) -> BTreeMap { - assert_eq!(mocks.len(), secrets.len()); - let mut signed_actions = BTreeMap::new(); - for (mock, secret) in mocks.iter().zip(secrets.iter()) { - let signed_action = sign_action_with_key(action, secret); - mock.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Ok(signed_action.clone()), - ); - signed_actions.insert(secret.public().into(), signed_action.into_sig().signature); - } - signed_actions - } - - fn mock_bridge_authority_signing_errors( - mocks: Vec<&BridgeRequestMockHandler>, - iota_tx_digest: TransactionDigest, - iota_tx_event_index: u16, - ) { - for mock in mocks { - mock.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Err(BridgeError::RestAPI("small issue".into())), - ); - } - } - - /// Create a BridgeAction and mock authorities to return signatures - fn get_bridge_authority_approved_action( - mocks: Vec<&BridgeRequestMockHandler>, - secrets: Vec<&BridgeAuthorityKeyPair>, - token_id: Option, - iota_to_eth: bool, - ) -> (VerifiedCertifiedBridgeAction, TransactionDigest, u16) { - let iota_tx_digest = TransactionDigest::random(); - let iota_tx_event_index = 1; - let action = if iota_to_eth { - get_test_iota_to_eth_bridge_action( - Some(iota_tx_digest), - Some(iota_tx_event_index), - None, - None, - None, - None, - token_id, - ) - } else { - get_test_eth_to_iota_bridge_action(None, None, None, token_id) - }; - - let sigs = mock_bridge_authority_sigs( - mocks, - &action, - secrets, - iota_tx_digest, - iota_tx_event_index, - ); - let certified_action = CertifiedBridgeAction::new_from_data_and_sig( - action, - BridgeCommitteeValiditySignInfo { signatures: sigs }, - ); - ( - VerifiedCertifiedBridgeAction::new_from_verified(certified_action), - iota_tx_digest, - iota_tx_event_index, - ) - } - - fn get_tx_digest(tx_data: TransactionData, dummy_iota_key: &IotaKeyPair) -> TransactionDigest { - let sig = Signature::new_secure( - &IntentMessage::new(Intent::iota_transaction(), &tx_data), - dummy_iota_key, - ); - let signed_tx = Transaction::from_data(tx_data, vec![sig]); - *signed_tx.digest() - } - - /// Why is `wildcard` needed? This is because authority signatures - /// are part of transaction data. Depending on whose signatures - /// are included in what order, this may change the tx digest. - fn mock_transaction_response( - iota_client_mock: &IotaMockClient, - tx_digest: TransactionDigest, - status: IotaExecutionStatus, - events: Option>, - wildcard: bool, - ) { - let mut response = IotaTransactionBlockResponse::new(tx_digest); - let effects = IotaTransactionBlockEffects::new_for_testing(tx_digest, status); - if let Some(events) = events { - response.events = Some(IotaTransactionBlockEvents { data: events }); - } - response.effects = Some(effects); - if wildcard { - iota_client_mock.set_wildcard_transaction_response(Ok(response)); - } else { - iota_client_mock.add_transaction_response(tx_digest, Ok(response)); - } - } - - fn mock_transaction_error( - iota_client_mock: &IotaMockClient, - tx_digest: TransactionDigest, - error: BridgeError, - wildcard: bool, - ) { - if wildcard { - iota_client_mock.set_wildcard_transaction_response(Err(error)); - } else { - iota_client_mock.add_transaction_response(tx_digest, Err(error)); - } - } - - struct SetupData { - signing_tx: iota_metrics::metered_channel::Sender, - execution_tx: iota_metrics::metered_channel::Sender, - iota_client_mock: IotaMockClient, - tx_subscription: tokio::sync::broadcast::Receiver, - store: Arc, - secrets: Vec, - dummy_iota_key: IotaKeyPair, - mock0: BridgeRequestMockHandler, - mock1: BridgeRequestMockHandler, - mock2: BridgeRequestMockHandler, - mock3: BridgeRequestMockHandler, - #[expect(unused)] - handles: Vec>, - gas_object_ref: ObjectRef, - iota_address: IotaAddress, - iota_token_type_tags: Arc>>, - bridge_pause_tx: tokio::sync::watch::Sender, - } - - async fn setup() -> SetupData { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - init_all_struct_tags(); - - let (iota_address, kp): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let iota_key = IotaKeyPair::from(kp); - let gas_object_ref = random_object_ref(); - let temp_dir = tempfile::tempdir().unwrap(); - let store = BridgeOrchestratorTables::new(temp_dir.path()); - let iota_client_mock = IotaMockClient::default(); - let tx_subscription = iota_client_mock.subscribe_to_requested_transactions(); - let iota_client = Arc::new(IotaClient::new_for_testing(iota_client_mock.clone())); - - // The dummy key is used to sign transaction so we can get TransactionDigest - // easily. User signature is not part of the transaction so it does not - // matter which key it is. - let (_, dummy_kp): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let dummy_iota_key = IotaKeyPair::from(dummy_kp); - - let mock0 = BridgeRequestMockHandler::new(); - let mock1 = BridgeRequestMockHandler::new(); - let mock2 = BridgeRequestMockHandler::new(); - let mock3 = BridgeRequestMockHandler::new(); - - let (mut handles, authorities, secrets) = get_test_authorities_and_run_mock_bridge_server( - vec![2500, 2500, 2500, 2500], - vec![mock0.clone(), mock1.clone(), mock2.clone(), mock3.clone()], - ); - - let committee = BridgeCommittee::new(authorities).unwrap(); - - let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( - Arc::new(committee), - )))); - let metrics = Arc::new(BridgeMetrics::new(®istry)); - let iota_token_type_tags = iota_client.get_token_id_map().await.unwrap(); - let iota_token_type_tags = Arc::new(ArcSwap::new(Arc::new(iota_token_type_tags))); - let (bridge_pause_tx, bridge_pause_rx) = tokio::sync::watch::channel(false); - let executor = BridgeActionExecutor::new( - iota_client.clone(), - agg.clone(), - store.clone(), - iota_key, - iota_address, - gas_object_ref.0, - iota_token_type_tags.clone(), - bridge_pause_rx, - metrics, - ) - .await; - - let (executor_handle, signing_tx, execution_tx) = executor.run_inner(); - handles.extend(executor_handle); - - SetupData { - signing_tx, - execution_tx, - iota_client_mock, - tx_subscription, - store, - secrets, - dummy_iota_key, - mock0, - mock1, - mock2, - mock3, - handles, - gas_object_ref, - iota_address, - iota_token_type_tags, - bridge_pause_tx, - } - } -} diff --git a/crates/iota-bridge/src/client/bridge_authority_aggregator.rs b/crates/iota-bridge/src/client/bridge_authority_aggregator.rs deleted file mode 100644 index 9922c13edc0..00000000000 --- a/crates/iota-bridge/src/client/bridge_authority_aggregator.rs +++ /dev/null @@ -1,710 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! BridgeAuthorityAggregator aggregates signatures from BridgeCommittee. - -use std::{ - collections::{BTreeMap, BTreeSet, btree_map::Entry}, - sync::Arc, - time::Duration, -}; - -use iota_authority_aggregation::{ReduceOutput, quorum_map_then_reduce_with_timeout_and_prefs}; -use iota_types::{ - base_types::ConciseableName, - committee::{StakeUnit, TOTAL_VOTING_POWER}, -}; -use tracing::{error, info, warn}; - -use crate::{ - client::bridge_client::BridgeClient, - crypto::{BridgeAuthorityPublicKeyBytes, BridgeAuthoritySignInfo}, - error::{BridgeError, BridgeResult}, - types::{ - BridgeAction, BridgeCommittee, BridgeCommitteeValiditySignInfo, CertifiedBridgeAction, - VerifiedCertifiedBridgeAction, VerifiedSignedBridgeAction, - }, -}; - -pub struct BridgeAuthorityAggregator { - pub committee: Arc, - pub clients: Arc>>, -} - -impl BridgeAuthorityAggregator { - pub fn new(committee: Arc) -> Self { - let clients: BTreeMap> = committee - .members() - .iter() - .filter_map(|(name, authority)| { - if authority.is_blocklisted { - warn!("Ignored blocklisted authority {:?} (stake: {}) when creating BridgeAuthorityAggregator", name.concise(), authority.voting_power); - return None; - } - // TODO: we could also record bad stakes here and use in signature aggregation - match BridgeClient::new( - name.clone(), - committee.clone(), - ) { - Ok(client) => Some((name.clone(), Arc::new(client))), - Err(e) => { - error!( - "Failed to create BridgeClient for {:?}: {:?}", - name.concise(), - e - ); - None - } - } - }) - .collect::>(); - Self { - committee, - clients: Arc::new(clients), - } - } - - pub async fn request_committee_signatures( - &self, - action: BridgeAction, - ) -> BridgeResult { - let state = GetSigsState::new(action.approval_threshold(), self.committee.clone()); - request_sign_bridge_action_into_certification( - action, - self.committee.clone(), - self.clients.clone(), - state, - ) - .await - } -} - -#[derive(Debug)] -struct GetSigsState { - total_bad_stake: StakeUnit, - total_ok_stake: StakeUnit, - sigs: BTreeMap, - validity_threshold: StakeUnit, - committee: Arc, -} - -impl GetSigsState { - fn new(validity_threshold: StakeUnit, committee: Arc) -> Self { - Self { - committee, - total_bad_stake: 0, - total_ok_stake: 0, - sigs: BTreeMap::new(), - validity_threshold, - } - } - - fn handle_verified_signed_action( - &mut self, - name: BridgeAuthorityPublicKeyBytes, - stake: StakeUnit, - signed_action: VerifiedSignedBridgeAction, - ) -> BridgeResult> { - info!("Got signatures from {}, stake: {}", name.concise(), stake); - if !self.committee.is_active_member(&name) { - return Err(BridgeError::InvalidBridgeAuthority(name)); - } - - // safeguard here to assert passed in stake matches the stake in committee - // unwrap safe: if name is an active member then it must be in committee set - assert_eq!(stake, self.committee.member(&name).unwrap().voting_power); - - match self.sigs.entry(name.clone()) { - Entry::Vacant(e) => { - e.insert(signed_action.auth_sig().clone()); - self.total_ok_stake += stake; - } - Entry::Occupied(_e) => { - return Err(BridgeError::AuthoritySignatureDuplication(format!( - "Got signatures for the same authority twice: {}", - name.concise() - ))); - } - } - if self.total_ok_stake >= self.validity_threshold { - info!( - "Got enough signatures from {} validators with total_ok_stake {}", - self.sigs.len(), - self.total_ok_stake - ); - let signatures = self - .sigs - .iter() - .map(|(k, v)| (k.clone(), v.signature.clone())) - .collect::>(); - let sig_info = BridgeCommitteeValiditySignInfo { signatures }; - let certified_action: iota_types::message_envelope::Envelope< - BridgeAction, - BridgeCommitteeValiditySignInfo, - > = CertifiedBridgeAction::new_from_data_and_sig( - signed_action.into_inner().into_data(), - sig_info, - ); - // `BridgeClient` already verified individual signatures - Ok(Some(VerifiedCertifiedBridgeAction::new_from_verified( - certified_action, - ))) - } else { - Ok(None) - } - } - - fn add_bad_stake(&mut self, bad_stake: StakeUnit) { - self.total_bad_stake += bad_stake; - } - - fn is_too_many_error(&self) -> bool { - TOTAL_VOTING_POWER - self.total_bad_stake - self.committee.total_blocklisted_stake() - < self.validity_threshold - } -} - -async fn request_sign_bridge_action_into_certification( - action: BridgeAction, - committee: Arc, - clients: Arc>>, - state: GetSigsState, -) -> BridgeResult { - // `preferences` is used as a trick here to influence the order of validators to - // be requested. - // * if `Some(_)`, then we will request validators in the order of the voting - // power. - // * if `None`, we still refer to voting power, but they are shuffled by - // randomness. - // Because ethereum gas price is not negligible, when the signatures are to be - // verified on ethereum, we pass in `Some` to make sure the validators with - // higher voting power are requested first to save gas cost. - let preference = match action { - BridgeAction::IotaToEthBridgeAction(_) => Some(BTreeSet::new()), - BridgeAction::EthToIotaBridgeAction(_) => None, - _ => { - if action.chain_id().is_iota_chain() { - None - } else { - Some(BTreeSet::new()) - } - } - }; - let (result, _) = quorum_map_then_reduce_with_timeout_and_prefs( - committee, - clients, - preference.as_ref(), - state, - |_name, client| { - Box::pin(async move { client.request_sign_bridge_action(action.clone()).await }) - }, - |mut state, name, stake, result| { - Box::pin(async move { - match result { - Ok(verified_signed_action) => { - match state.handle_verified_signed_action( - name.clone(), - stake, - verified_signed_action, - ) { - Ok(Some(certified_action)) => { - return ReduceOutput::Success(certified_action); - } - Ok(None) => (), - Err(e) => { - error!( - "Failed to handle verified signed action from {}: {:?}", - name.concise(), - e - ); - state.add_bad_stake(stake); - } - } - } - Err(e) => { - warn!( - "Failed to get signature from {:?}. Error: {:?}", - name.concise(), - e - ); - state.add_bad_stake(stake); - } - }; - - // If bad stake (including blocklisted stake) is too high to reach validity threshold, return error - if state.is_too_many_error() { - ReduceOutput::Failed(state) - } else { - ReduceOutput::Continue(state) - } - }) - }, - // A herustic timeout, we expect the signing to finish within 5 seconds - Duration::from_secs(5), - ) - .await - .map_err(|state| { - error!( - "Failed to get enough signatures, bad stake: {}, blocklisted stake: {}, good stake: {}, validity threshold: {}", - state.total_bad_stake, - state.committee.total_blocklisted_stake(), - state.total_ok_stake, - state.validity_threshold, - ); - BridgeError::AuthoritySignatureAggregationTooManyErrors(format!( - "Failed to get enough signatures, bad stake: {}, blocklisted stake: {}, good stake: {}, validity threshold: {}", - state.total_bad_stake, - state.committee.total_blocklisted_stake(), - state.total_ok_stake, - state.validity_threshold, - )) - })?; - Ok(result) -} - -#[cfg(test)] -mod tests { - use std::collections::BTreeSet; - - use fastcrypto::traits::ToFromBytes; - use iota_types::{committee::VALIDITY_THRESHOLD, digests::TransactionDigest}; - - use super::*; - use crate::{ - crypto::BridgeAuthorityPublicKey, - server::mock_handler::BridgeRequestMockHandler, - test_utils::{ - get_test_authorities_and_run_mock_bridge_server, get_test_authority_and_key, - get_test_iota_to_eth_bridge_action, sign_action_with_key, - }, - types::BridgeCommittee, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_auth_agg_construction() { - telemetry_subscribers::init_for_testing(); - - let mut authorities = vec![]; - for _i in 0..4 { - let (authority, _, _) = get_test_authority_and_key(2500, 12345); - authorities.push(authority); - } - let committee = BridgeCommittee::new(authorities.clone()).unwrap(); - - let agg = BridgeAuthorityAggregator::new(Arc::new(committee)); - assert_eq!( - agg.clients.keys().cloned().collect::>(), - BTreeSet::from_iter(vec![ - authorities[0].pubkey_bytes(), - authorities[1].pubkey_bytes(), - authorities[2].pubkey_bytes(), - authorities[3].pubkey_bytes() - ]) - ); - - // authority 2 is blocklisted - authorities[2].is_blocklisted = true; - let committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let agg = BridgeAuthorityAggregator::new(Arc::new(committee)); - assert_eq!( - agg.clients.keys().cloned().collect::>(), - BTreeSet::from_iter(vec![ - authorities[0].pubkey_bytes(), - authorities[1].pubkey_bytes(), - authorities[3].pubkey_bytes() - ]) - ); - - // authority 3 has bad url - authorities[3].base_url = "".into(); - let committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let agg = BridgeAuthorityAggregator::new(Arc::new(committee)); - assert_eq!( - agg.clients.keys().cloned().collect::>(), - BTreeSet::from_iter(vec![ - authorities[0].pubkey_bytes(), - authorities[1].pubkey_bytes(), - authorities[3].pubkey_bytes() - ]) - ); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_auth_agg_ok() { - telemetry_subscribers::init_for_testing(); - - let mock0 = BridgeRequestMockHandler::new(); - let mock1 = BridgeRequestMockHandler::new(); - let mock2 = BridgeRequestMockHandler::new(); - let mock3 = BridgeRequestMockHandler::new(); - - // start servers - let (_handles, authorities, secrets) = get_test_authorities_and_run_mock_bridge_server( - vec![2500, 2500, 2500, 2500], - vec![mock0.clone(), mock1.clone(), mock2.clone(), mock3.clone()], - ); - - let committee = BridgeCommittee::new(authorities).unwrap(); - - let agg = BridgeAuthorityAggregator::new(Arc::new(committee)); - - let iota_tx_digest = TransactionDigest::random(); - let iota_tx_event_index = 0; - let nonce = 0; - let amount = 1000; - let action = get_test_iota_to_eth_bridge_action( - Some(iota_tx_digest), - Some(iota_tx_event_index), - Some(nonce), - Some(amount), - None, - None, - None, - ); - - // All authorities return signatures - mock0.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Ok(sign_action_with_key(&action, &secrets[0])), - ); - mock1.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Ok(sign_action_with_key(&action, &secrets[1])), - ); - mock2.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Ok(sign_action_with_key(&action, &secrets[2])), - ); - mock3.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Ok(sign_action_with_key(&action, &secrets[3])), - ); - agg.request_committee_signatures(action.clone()) - .await - .unwrap(); - - // 1 out of 4 authorities returns error - mock3.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Err(BridgeError::RestAPI("".into())), - ); - agg.request_committee_signatures(action.clone()) - .await - .unwrap(); - - // 2 out of 4 authorities returns error - mock2.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Err(BridgeError::RestAPI("".into())), - ); - agg.request_committee_signatures(action.clone()) - .await - .unwrap(); - - // 3 out of 4 authorities returns error - good stake below valdiity threshold - mock1.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Err(BridgeError::RestAPI("".into())), - ); - let err = agg - .request_committee_signatures(action.clone()) - .await - .unwrap_err(); - assert!(matches!( - err, - BridgeError::AuthoritySignatureAggregationTooManyErrors(_) - )); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_auth_agg_more_cases() { - telemetry_subscribers::init_for_testing(); - - let mock0 = BridgeRequestMockHandler::new(); - let mock1 = BridgeRequestMockHandler::new(); - let mock2 = BridgeRequestMockHandler::new(); - let mock3 = BridgeRequestMockHandler::new(); - - // start servers - let (_handles, mut authorities, secrets) = get_test_authorities_and_run_mock_bridge_server( - vec![2500, 2500, 2500, 2500], - vec![mock0.clone(), mock1.clone(), mock2.clone(), mock3.clone()], - ); - // 0 and 1 are blocklisted - authorities[0].is_blocklisted = true; - authorities[1].is_blocklisted = true; - - let committee = BridgeCommittee::new(authorities.clone()).unwrap(); - - let agg = BridgeAuthorityAggregator::new(Arc::new(committee)); - - let iota_tx_digest = TransactionDigest::random(); - let iota_tx_event_index = 0; - let nonce = 0; - let amount = 1000; - let action = get_test_iota_to_eth_bridge_action( - Some(iota_tx_digest), - Some(iota_tx_event_index), - Some(nonce), - Some(amount), - None, - None, - None, - ); - - // Only mock authority 2 and 3 to return signatures, such that if - // BridgeAuthorityAggregator requests to authority 0 and 1 (which should - // not happen) it will panic. - mock2.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Ok(sign_action_with_key(&action, &secrets[2])), - ); - mock3.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Ok(sign_action_with_key(&action, &secrets[3])), - ); - let certified = agg - .request_committee_signatures(action.clone()) - .await - .unwrap(); - let signers = certified - .auth_sig() - .signatures - .keys() - .cloned() - .collect::>(); - assert_eq!( - signers, - BTreeSet::from_iter(vec![ - authorities[2].pubkey_bytes(), - authorities[3].pubkey_bytes() - ]) - ); - - // if mock 3 returns error, then it won't reach validity threshold - mock3.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Err(BridgeError::RestAPI("".into())), - ); - let err = agg - .request_committee_signatures(action.clone()) - .await - .unwrap_err(); - assert!(matches!( - err, - BridgeError::AuthoritySignatureAggregationTooManyErrors(_) - )); - - // if mock 3 returns duplicated signature (by authority 2), `BridgeClient` will - // catch this - mock3.add_iota_event_response( - iota_tx_digest, - iota_tx_event_index, - Ok(sign_action_with_key(&action, &secrets[2])), - ); - let err = agg - .request_committee_signatures(action.clone()) - .await - .unwrap_err(); - assert!(matches!( - err, - BridgeError::AuthoritySignatureAggregationTooManyErrors(_) - )); - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_get_sigs_state() { - telemetry_subscribers::init_for_testing(); - - let mut authorities = vec![]; - let mut secrets = vec![]; - for _i in 0..4 { - let (authority, _, secret) = get_test_authority_and_key(2500, 12345); - authorities.push(authority); - secrets.push(secret); - } - - let committee = BridgeCommittee::new(authorities.clone()).unwrap(); - - let threshold = VALIDITY_THRESHOLD; - let mut state = GetSigsState::new(threshold, Arc::new(committee)); - - assert!(!state.is_too_many_error()); - - // bad stake: 2500 - state.add_bad_stake(2500); - assert!(!state.is_too_many_error()); - - // bad stake ; 5000 - state.add_bad_stake(2500); - assert!(!state.is_too_many_error()); - - // bad stake : 6666 - state.add_bad_stake(1666); - assert!(!state.is_too_many_error()); - - // bad stake : 6667 - too many errors - state.add_bad_stake(1); - assert!(state.is_too_many_error()); - - // Authority 0 is blocklisted, we lose 2500 stake - authorities[0].is_blocklisted = true; - let committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let threshold = VALIDITY_THRESHOLD; - let mut state = GetSigsState::new(threshold, Arc::new(committee)); - - assert!(!state.is_too_many_error()); - - // bad stake: 2500 + 2500 - state.add_bad_stake(2500); - assert!(!state.is_too_many_error()); - - // bad stake: 5000 + 2500 - too many errors - state.add_bad_stake(2500); - assert!(state.is_too_many_error()); - - // Below we test `handle_verified_signed_action` - authorities[0].is_blocklisted = false; - authorities[1].voting_power = 1; // set authority's voting power to minimal - authorities[2].voting_power = 4999; - authorities[3].is_blocklisted = true; // blocklist authority 3 - let committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let threshold = VALIDITY_THRESHOLD; - let mut state = GetSigsState::new(threshold, Arc::new(committee.clone())); - - let iota_tx_digest = TransactionDigest::random(); - let iota_tx_event_index = 0; - let nonce = 0; - let amount = 1000; - let action = get_test_iota_to_eth_bridge_action( - Some(iota_tx_digest), - Some(iota_tx_event_index), - Some(nonce), - Some(amount), - None, - None, - None, - ); - - let sig_0 = sign_action_with_key(&action, &secrets[0]); - // returns Ok(None) - assert!( - state - .handle_verified_signed_action( - authorities[0].pubkey_bytes().clone(), - authorities[0].voting_power, - VerifiedSignedBridgeAction::new_from_verified(sig_0.clone()) - ) - .unwrap() - .is_none() - ); - assert_eq!(state.total_ok_stake, 2500); - - // Handling a sig from an already signed authority would fail - let new_sig_0 = sign_action_with_key(&action, &secrets[0]); - // returns Err(BridgeError::AuthoritySignatureDuplication) - let err = state - .handle_verified_signed_action( - authorities[0].pubkey_bytes().clone(), - authorities[0].voting_power, - VerifiedSignedBridgeAction::new_from_verified(new_sig_0.clone()), - ) - .unwrap_err(); - assert!(matches!(err, BridgeError::AuthoritySignatureDuplication(_))); - assert_eq!(state.total_ok_stake, 2500); - - // Handling a sig from an authority not in committee would fail - let (unknown_authority, _, kp) = get_test_authority_and_key(2500, 12345); - let unknown_sig = sign_action_with_key(&action, &kp); - // returns Err(BridgeError::InvalidBridgeAuthority) - let err = state - .handle_verified_signed_action( - unknown_authority.pubkey_bytes().clone(), - authorities[0].voting_power, - VerifiedSignedBridgeAction::new_from_verified(unknown_sig.clone()), - ) - .unwrap_err(); - assert!(matches!(err, BridgeError::InvalidBridgeAuthority(_))); - assert_eq!(state.total_ok_stake, 2500); - - // Handling a blocklisted authority would fail - let sig_3 = sign_action_with_key(&action, &secrets[3]); - // returns Err(BridgeError::InvalidBridgeAuthority) - let err = state - .handle_verified_signed_action( - authorities[3].pubkey_bytes().clone(), - authorities[3].voting_power, - VerifiedSignedBridgeAction::new_from_verified(sig_3.clone()), - ) - .unwrap_err(); - assert!(matches!(err, BridgeError::InvalidBridgeAuthority(_))); - assert_eq!(state.total_ok_stake, 2500); - - // Collect signtuare from authority 1 (voting power = 1) - let sig_1 = sign_action_with_key(&action, &secrets[1]); - // returns Ok(None) - assert!( - state - .handle_verified_signed_action( - authorities[1].pubkey_bytes().clone(), - authorities[1].voting_power, - VerifiedSignedBridgeAction::new_from_verified(sig_1.clone()) - ) - .unwrap() - .is_none() - ); - assert_eq!(state.total_ok_stake, 2501); - - // Collect signature from authority 2 - reach validity threshold - let sig_2 = sign_action_with_key(&action, &secrets[2]); - // returns Ok(None) - let certificate = state - .handle_verified_signed_action( - authorities[2].pubkey_bytes().clone(), - authorities[2].voting_power, - VerifiedSignedBridgeAction::new_from_verified(sig_2.clone()), - ) - .unwrap() - .unwrap(); - assert_eq!(state.total_ok_stake, 7500); - - assert_eq!(certificate.data(), &action); - let signers = certificate - .auth_sig() - .signatures - .keys() - .cloned() - .collect::>(); - assert_eq!( - signers, - BTreeSet::from_iter(vec![ - authorities[0].pubkey_bytes(), - authorities[1].pubkey_bytes(), - authorities[2].pubkey_bytes() - ]) - ); - - for (pubkey, sig) in &certificate.auth_sig().signatures { - let sign_info = BridgeAuthoritySignInfo { - authority_pub_key: BridgeAuthorityPublicKey::from_bytes(pubkey.as_ref()).unwrap(), - signature: sig.clone(), - }; - assert!(sign_info.verify(&action, &committee).is_ok()); - } - } -} diff --git a/crates/iota-bridge/src/client/bridge_client.rs b/crates/iota-bridge/src/client/bridge_client.rs deleted file mode 100644 index cd65afb4891..00000000000 --- a/crates/iota-bridge/src/client/bridge_client.rs +++ /dev/null @@ -1,639 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! `BridgeClient` talks to BridgeNode. - -use std::{str::FromStr, sync::Arc}; - -use fastcrypto::{ - encoding::{Encoding, Hex}, - traits::ToFromBytes, -}; -use url::Url; - -use crate::{ - crypto::{BridgeAuthorityPublicKeyBytes, verify_signed_bridge_action}, - error::{BridgeError, BridgeResult}, - server::APPLICATION_JSON, - types::{BridgeAction, BridgeCommittee, VerifiedSignedBridgeAction}, -}; - -// Note: `base_url` is `Option` because -// `quorum_map_then_reduce_with_timeout_and_prefs` uses `[]` to get Client based -// on key. Therefore even when the URL is invalid we need to create a Client -// instance. TODO: In the future we can consider change -// `quorum_map_then_reduce_with_timeout_and_prefs` and its callsites to use -// `get` instead of `[]`. -#[derive(Clone, Debug)] -pub struct BridgeClient { - inner: reqwest::Client, - authority: BridgeAuthorityPublicKeyBytes, - committee: Arc, - base_url: Option, -} - -impl BridgeClient { - pub fn new( - authority_name: BridgeAuthorityPublicKeyBytes, - committee: Arc, - ) -> BridgeResult { - if !committee.is_active_member(&authority_name) { - return Err(BridgeError::InvalidBridgeAuthority(authority_name)); - } - // Unwrap safe: we passed the `is_active_member` check above - let member = committee.member(&authority_name).unwrap(); - Ok(Self { - inner: reqwest::Client::new(), - authority: authority_name.clone(), - base_url: Url::from_str(&member.base_url).ok(), - committee, - }) - } - - #[cfg(test)] - pub fn update_committee(&mut self, committee: Arc) { - self.committee = committee; - } - - // Important: the paths need to match the ones in server/mod.rs - fn bridge_action_to_path(event: &BridgeAction) -> String { - match event { - BridgeAction::IotaToEthBridgeAction(e) => format!( - "sign/bridge_tx/iota/eth/{}/{}", - e.iota_tx_digest, e.iota_tx_event_index - ), - BridgeAction::EthToIotaBridgeAction(e) => format!( - "sign/bridge_tx/eth/iota/{}/{}", - Hex::encode(e.eth_tx_hash.0), - e.eth_event_index - ), - BridgeAction::BlocklistCommitteeAction(a) => { - let chain_id = (a.chain_id as u8).to_string(); - let nonce = a.nonce.to_string(); - let type_ = (a.blocklist_type as u8).to_string(); - let keys = a - .members_to_update - .iter() - .map(|k| Hex::encode(k.as_bytes())) - .collect::>() - .join(","); - format!("sign/update_committee_blocklist/{chain_id}/{nonce}/{type_}/{keys}") - } - BridgeAction::EmergencyAction(a) => { - let chain_id = (a.chain_id as u8).to_string(); - let nonce = a.nonce.to_string(); - let type_ = (a.action_type as u8).to_string(); - format!("sign/emergency_button/{chain_id}/{nonce}/{type_}") - } - BridgeAction::LimitUpdateAction(a) => { - let chain_id = (a.chain_id as u8).to_string(); - let nonce = a.nonce.to_string(); - let sending_chain_id = (a.sending_chain_id as u8).to_string(); - let new_usd_limit = a.new_usd_limit.to_string(); - format!("sign/update_limit/{chain_id}/{nonce}/{sending_chain_id}/{new_usd_limit}") - } - BridgeAction::AssetPriceUpdateAction(a) => { - let chain_id = (a.chain_id as u8).to_string(); - let nonce = a.nonce.to_string(); - let token_id = a.token_id.to_string(); - let new_usd_price = a.new_usd_price.to_string(); - format!("sign/update_asset_price/{chain_id}/{nonce}/{token_id}/{new_usd_price}") - } - BridgeAction::EvmContractUpgradeAction(a) => { - let chain_id = (a.chain_id as u8).to_string(); - let nonce = a.nonce.to_string(); - let proxy_address = Hex::encode(a.proxy_address.as_bytes()); - let new_impl_address = Hex::encode(a.new_impl_address.as_bytes()); - let path = format!( - "sign/upgrade_evm_contract/{chain_id}/{nonce}/{proxy_address}/{new_impl_address}" - ); - if a.call_data.is_empty() { - path - } else { - let call_data = Hex::encode(a.call_data.clone()); - format!("{}/{}", path, call_data) - } - } - BridgeAction::AddTokensOnIotaAction(a) => { - let chain_id = (a.chain_id as u8).to_string(); - let nonce = a.nonce.to_string(); - let native = if a.native { "1" } else { "0" }; - let token_ids = a - .token_ids - .iter() - .map(|id| id.to_string()) - .collect::>() - .join(","); - let token_type_names = a - .token_type_names - .iter() - .map(|name| name.to_canonical_string(true)) - .collect::>() - .join(","); - let token_prices = a - .token_prices - .iter() - .map(|price| price.to_string()) - .collect::>() - .join(","); - format!( - "sign/add_tokens_on_iota/{chain_id}/{nonce}/{native}/{token_ids}/{token_type_names}/{token_prices}" - ) - } - BridgeAction::AddTokensOnEvmAction(a) => { - let chain_id = (a.chain_id as u8).to_string(); - let nonce = a.nonce.to_string(); - let native = if a.native { "1" } else { "0" }; - let token_ids = a - .token_ids - .iter() - .map(|id| id.to_string()) - .collect::>() - .join(","); - let token_addresses = a - .token_addresses - .iter() - .map(|name| format!("{:?}", name)) - .collect::>() - .join(","); - let token_iota_decimals = a - .token_iota_decimals - .iter() - .map(|id| id.to_string()) - .collect::>() - .join(","); - let token_prices = a - .token_prices - .iter() - .map(|price| price.to_string()) - .collect::>() - .join(","); - format!( - "sign/add_tokens_on_evm/{chain_id}/{nonce}/{native}/{token_ids}/{token_addresses}/{token_iota_decimals}/{token_prices}" - ) - } - } - } - - // Returns Ok(true) if the server is up and running - pub async fn ping(&self) -> BridgeResult { - if self.base_url.is_none() { - return Err(BridgeError::InvalidAuthorityUrl(self.authority.clone())); - } - // Unwrap safe: checked `self.base_url.is_none()` above - let url = self.base_url.clone().unwrap(); - Ok(self - .inner - .get(url) - .header(reqwest::header::ACCEPT, APPLICATION_JSON) - .send() - .await? - .error_for_status() - .is_ok()) - } - - pub async fn request_sign_bridge_action( - &self, - action: BridgeAction, - ) -> BridgeResult { - if self.base_url.is_none() { - return Err(BridgeError::InvalidAuthorityUrl(self.authority.clone())); - } - // Unwrap safe: checked `self.base_url.is_none()` above - let url = self - .base_url - .clone() - .unwrap() - .join(&Self::bridge_action_to_path(&action))?; - let resp = self - .inner - .get(url) - .header(reqwest::header::ACCEPT, APPLICATION_JSON) - .send() - .await?; - if !resp.status().is_success() { - let error_status = format!("{:?}", resp.error_for_status_ref()); - return Err(BridgeError::RestAPI(format!( - "request_sign_bridge_action failed with status {:?}: {:?}", - error_status, - resp.text().await? - ))); - } - let signed_bridge_action = resp.json().await?; - verify_signed_bridge_action( - &action, - signed_bridge_action, - &self.authority, - &self.committee, - ) - } -} - -#[cfg(test)] -mod tests { - use ethers::types::{Address as EthAddress, TxHash}; - use fastcrypto::{ - hash::{HashFunction, Keccak256}, - traits::KeyPair, - }; - use iota_types::{ - TypeTag, - base_types::IotaAddress, - bridge::{BridgeChainId, TOKEN_ID_BTC, TOKEN_ID_USDT}, - crypto::get_key_pair, - digests::TransactionDigest, - }; - use prometheus::Registry; - - use super::*; - use crate::{ - abi::EthToIotaTokenBridgeV1, - crypto::BridgeAuthoritySignInfo, - events::EmittedIotaToEthTokenBridgeV1, - server::mock_handler::BridgeRequestMockHandler, - test_utils::{ - get_test_authority_and_key, get_test_iota_to_eth_bridge_action, run_mock_bridge_server, - }, - types::SignedBridgeAction, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_client() { - telemetry_subscribers::init_for_testing(); - - let (mut authority, pubkey, _) = get_test_authority_and_key(10000, 12345); - - let pubkey_bytes = BridgeAuthorityPublicKeyBytes::from(&pubkey); - let committee = Arc::new(BridgeCommittee::new(vec![authority.clone()]).unwrap()); - let action = - get_test_iota_to_eth_bridge_action(None, Some(1), Some(1), Some(100), None, None, None); - - // Ok - let client = BridgeClient::new(pubkey_bytes.clone(), committee).unwrap(); - assert!(client.base_url.is_some()); - - // Ok - authority.base_url = "https://foo.iotabridge.io".to_string(); - let committee = Arc::new(BridgeCommittee::new(vec![authority.clone()]).unwrap()); - let client = BridgeClient::new(pubkey_bytes.clone(), committee.clone()).unwrap(); - assert!(client.base_url.is_some()); - - // Err, not in committee - let (_, kp2): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let pubkey2_bytes = BridgeAuthorityPublicKeyBytes::from(kp2.public()); - let err = BridgeClient::new(pubkey2_bytes, committee.clone()).unwrap_err(); - assert!(matches!(err, BridgeError::InvalidBridgeAuthority(_))); - - // invalid base url - authority.base_url = "127.0.0.1:12345".to_string(); // <-- bad, missing http:// - let committee = Arc::new(BridgeCommittee::new(vec![authority.clone()]).unwrap()); - let client = BridgeClient::new(pubkey_bytes.clone(), committee.clone()).unwrap(); - assert!(client.base_url.is_none()); - assert!(matches!( - client.ping().await.unwrap_err(), - BridgeError::InvalidAuthorityUrl(_) - )); - assert!(matches!( - client - .request_sign_bridge_action(action.clone()) - .await - .unwrap_err(), - BridgeError::InvalidAuthorityUrl(_) - )); - - // invalid base url - authority.base_url = "http://127.256.0.1:12345".to_string(); // <-- bad, invalid ipv4 address - let committee = Arc::new(BridgeCommittee::new(vec![authority.clone()]).unwrap()); - let client = BridgeClient::new(pubkey_bytes, committee.clone()).unwrap(); - assert!(client.base_url.is_none()); - assert!(matches!( - client.ping().await.unwrap_err(), - BridgeError::InvalidAuthorityUrl(_) - )); - assert!(matches!( - client - .request_sign_bridge_action(action.clone()) - .await - .unwrap_err(), - BridgeError::InvalidAuthorityUrl(_) - )); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_client_request_sign_action() { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - - let mock_handler = BridgeRequestMockHandler::new(); - - // start server - let (_handles, ports) = run_mock_bridge_server(vec![mock_handler.clone()]); - - let port = ports[0]; - - let (authority, _pubkey, secret) = get_test_authority_and_key(5000, port); - let (authority2, _pubkey2, secret2) = get_test_authority_and_key(5000, port - 1); - - let committee = BridgeCommittee::new(vec![authority.clone(), authority2.clone()]).unwrap(); - - let mut client = - BridgeClient::new(authority.pubkey_bytes(), Arc::new(committee.clone())).unwrap(); - - let tx_digest = TransactionDigest::random(); - let event_idx = 4; - - let action = get_test_iota_to_eth_bridge_action( - Some(tx_digest), - Some(event_idx), - Some(1), - Some(100), - None, - None, - None, - ); - let sig = BridgeAuthoritySignInfo::new(&action, &secret); - let signed_event = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig.clone()); - mock_handler.add_iota_event_response(tx_digest, event_idx, Ok(signed_event.clone())); - - // success - client - .request_sign_bridge_action(action.clone()) - .await - .unwrap(); - - // mismatched action would fail, this could happen when the authority fetched - // the wrong event - let action2 = get_test_iota_to_eth_bridge_action( - Some(tx_digest), - Some(event_idx), - Some(2), - Some(200), - None, - None, - None, - ); - let wrong_sig = BridgeAuthoritySignInfo::new(&action2, &secret); - let wrong_signed_action = - SignedBridgeAction::new_from_data_and_sig(action2.clone(), wrong_sig.clone()); - mock_handler.add_iota_event_response(tx_digest, event_idx, Ok(wrong_signed_action)); - let err = client - .request_sign_bridge_action(action.clone()) - .await - .unwrap_err(); - assert!(matches!(err, BridgeError::MismatchedAction)); - - // The action matches but the signature is wrong, fail - let wrong_signed_action = - SignedBridgeAction::new_from_data_and_sig(action.clone(), wrong_sig); - mock_handler.add_iota_event_response(tx_digest, event_idx, Ok(wrong_signed_action)); - let err = client - .request_sign_bridge_action(action.clone()) - .await - .unwrap_err(); - assert!(matches!( - err, - BridgeError::InvalidBridgeAuthoritySignature(..) - )); - - // sig from blocklisted authority would fail - let mut authority_blocklisted = authority.clone(); - authority_blocklisted.is_blocklisted = true; - let committee2 = Arc::new( - BridgeCommittee::new(vec![authority_blocklisted.clone(), authority2.clone()]).unwrap(), - ); - client.update_committee(committee2); - mock_handler.add_iota_event_response(tx_digest, event_idx, Ok(signed_event)); - - let err = client - .request_sign_bridge_action(action.clone()) - .await - .unwrap_err(); - assert!( - matches!(err, BridgeError::InvalidBridgeAuthority(pk) if pk == authority_blocklisted.pubkey_bytes()), - ); - - client.update_committee(committee.into()); - - // signed by a different authority in committee would fail - let sig2 = BridgeAuthoritySignInfo::new(&action, &secret2); - let signed_event2 = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig2.clone()); - mock_handler.add_iota_event_response(tx_digest, event_idx, Ok(signed_event2)); - let err = client - .request_sign_bridge_action(action.clone()) - .await - .unwrap_err(); - assert!(matches!(err, BridgeError::MismatchedAuthoritySigner)); - - // signed by a different key, not in committee, would fail - let (_, kp3): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let secret3 = Arc::pin(kp3); - let sig3 = BridgeAuthoritySignInfo::new(&action, &secret3); - let signed_event3 = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig3); - mock_handler.add_iota_event_response(tx_digest, event_idx, Ok(signed_event3)); - let err = client - .request_sign_bridge_action(action.clone()) - .await - .unwrap_err(); - assert!(matches!(err, BridgeError::MismatchedAuthoritySigner)); - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_action_path_regression_tests() { - let iota_tx_digest = TransactionDigest::random(); - let iota_tx_event_index = 5; - let action = BridgeAction::IotaToEthBridgeAction(crate::types::IotaToEthBridgeAction { - iota_tx_digest, - iota_tx_event_index, - iota_bridge_event: EmittedIotaToEthTokenBridgeV1 { - iota_chain_id: BridgeChainId::IotaCustom, - nonce: 1, - iota_address: IotaAddress::random_for_testing_only(), - eth_chain_id: BridgeChainId::EthSepolia, - eth_address: EthAddress::random(), - token_id: TOKEN_ID_USDT, - amount_iota_adjusted: 1, - }, - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - format!( - "sign/bridge_tx/iota/eth/{}/{}", - iota_tx_digest, iota_tx_event_index - ) - ); - - let eth_tx_hash = TxHash::random(); - let eth_event_index = 6; - let action = BridgeAction::EthToIotaBridgeAction(crate::types::EthToIotaBridgeAction { - eth_tx_hash, - eth_event_index, - eth_bridge_event: EthToIotaTokenBridgeV1 { - eth_chain_id: BridgeChainId::EthSepolia, - nonce: 1, - eth_address: EthAddress::random(), - iota_chain_id: BridgeChainId::IotaCustom, - iota_address: IotaAddress::random_for_testing_only(), - token_id: TOKEN_ID_USDT, - iota_adjusted_amount: 1, - }, - }); - - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - format!( - "sign/bridge_tx/eth/iota/{}/{}", - Hex::encode(eth_tx_hash.0), - eth_event_index - ) - ); - - let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes( - &Hex::decode("027f1178ff417fc9f5b8290bd8876f0a157a505a6c52db100a8492203ddd1d4279") - .unwrap(), - ) - .unwrap(); - - let action = - BridgeAction::BlocklistCommitteeAction(crate::types::BlocklistCommitteeAction { - chain_id: BridgeChainId::EthSepolia, - nonce: 1, - blocklist_type: crate::types::BlocklistType::Blocklist, - members_to_update: vec![pub_key_bytes.clone()], - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/update_committee_blocklist/11/1/0/027f1178ff417fc9f5b8290bd8876f0a157a505a6c52db100a8492203ddd1d4279", - ); - let pub_key_bytes2 = BridgeAuthorityPublicKeyBytes::from_bytes( - &Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4") - .unwrap(), - ) - .unwrap(); - let action = - BridgeAction::BlocklistCommitteeAction(crate::types::BlocklistCommitteeAction { - chain_id: BridgeChainId::EthSepolia, - nonce: 1, - blocklist_type: crate::types::BlocklistType::Blocklist, - members_to_update: vec![pub_key_bytes.clone(), pub_key_bytes2.clone()], - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/update_committee_blocklist/11/1/0/027f1178ff417fc9f5b8290bd8876f0a157a505a6c52db100a8492203ddd1d4279,02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4", - ); - - let action = BridgeAction::EmergencyAction(crate::types::EmergencyAction { - chain_id: BridgeChainId::IotaCustom, - nonce: 5, - action_type: crate::types::EmergencyActionType::Pause, - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/emergency_button/2/5/0", - ); - - let action = BridgeAction::LimitUpdateAction(crate::types::LimitUpdateAction { - chain_id: BridgeChainId::IotaCustom, - nonce: 10, - sending_chain_id: BridgeChainId::EthCustom, - new_usd_limit: 100, - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/update_limit/2/10/12/100", - ); - - let action = BridgeAction::AssetPriceUpdateAction(crate::types::AssetPriceUpdateAction { - chain_id: BridgeChainId::IotaCustom, - nonce: 8, - token_id: TOKEN_ID_BTC, - new_usd_price: 100_000_000, - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/update_asset_price/2/8/1/100000000", - ); - - let action = - BridgeAction::EvmContractUpgradeAction(crate::types::EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data: vec![], - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/upgrade_evm_contract/12/123/0606060606060606060606060606060606060606/0909090909090909090909090909090909090909", - ); - - let function_signature = "initializeV2()"; - let selector = &Keccak256::digest(function_signature).digest[0..4]; - let mut call_data = selector.to_vec(); - let action = - BridgeAction::EvmContractUpgradeAction(crate::types::EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data: call_data.clone(), - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/upgrade_evm_contract/12/123/0606060606060606060606060606060606060606/0909090909090909090909090909090909090909/5cd8a76b", - ); - - call_data.extend(ethers::abi::encode(&[ethers::abi::Token::Uint(42.into())])); - let action = - BridgeAction::EvmContractUpgradeAction(crate::types::EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data, - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/upgrade_evm_contract/12/123/0606060606060606060606060606060606060606/0909090909090909090909090909090909090909/5cd8a76b000000000000000000000000000000000000000000000000000000000000002a", - ); - - let action = BridgeAction::AddTokensOnIotaAction(crate::types::AddTokensOnIotaAction { - nonce: 3, - chain_id: BridgeChainId::IotaCustom, - native: false, - token_ids: vec![99, 100, 101], - token_type_names: vec![ - TypeTag::from_str("0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin1").unwrap(), - TypeTag::from_str("0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin2").unwrap(), - TypeTag::from_str("0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin3").unwrap(), - ], - token_prices: vec![1_000_000_000, 2_000_000_000, 3_000_000_000], - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/add_tokens_on_iota/2/3/0/99,100,101/0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin1,0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin2,0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin3/1000000000,2000000000,3000000000", - ); - - let action = BridgeAction::AddTokensOnEvmAction(crate::types::AddTokensOnEvmAction { - nonce: 0, - chain_id: BridgeChainId::EthCustom, - native: true, - token_ids: vec![99, 100, 101], - token_addresses: vec![ - EthAddress::repeat_byte(1), - EthAddress::repeat_byte(2), - EthAddress::repeat_byte(3), - ], - token_iota_decimals: vec![5, 6, 7], - token_prices: vec![1_000_000_000, 2_000_000_000, 3_000_000_000], - }); - assert_eq!( - BridgeClient::bridge_action_to_path(&action), - "sign/add_tokens_on_evm/12/0/1/99,100,101/0x0101010101010101010101010101010101010101,0x0202020202020202020202020202020202020202,0x0303030303030303030303030303030303030303/5,6,7/1000000000,2000000000,3000000000", - ); - } -} diff --git a/crates/iota-bridge/src/client/mod.rs b/crates/iota-bridge/src/client/mod.rs deleted file mode 100644 index 4f516992c65..00000000000 --- a/crates/iota-bridge/src/client/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub mod bridge_authority_aggregator; -pub mod bridge_client; diff --git a/crates/iota-bridge/src/config.rs b/crates/iota-bridge/src/config.rs deleted file mode 100644 index f1f37a81419..00000000000 --- a/crates/iota-bridge/src/config.rs +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{collections::HashSet, path::PathBuf, str::FromStr, sync::Arc}; - -use anyhow::anyhow; -use ethers::{providers::Middleware, types::Address as EthAddress}; -use futures::{StreamExt, future}; -use iota_config::Config; -use iota_json_rpc_types::Coin; -use iota_keys::keypair_file::read_key; -use iota_sdk::{IotaClient as IotaSdkClient, IotaClientBuilder, apis::CoinReadApi}; -use iota_types::{ - base_types::{IotaAddress, ObjectID, ObjectRef}, - bridge::BridgeChainId, - crypto::{IotaKeyPair, KeypairTraits}, - digests::{get_mainnet_chain_identifier, get_testnet_chain_identifier}, - event::EventID, - object::Owner, -}; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; -use tracing::info; - -use crate::{ - abi::EthBridgeConfig, - crypto::BridgeAuthorityKeyPair, - error::BridgeError, - eth_client::EthClient, - iota_client::IotaClient, - metered_eth_provider::{MeteredEthHttpProvider, new_metered_eth_provider}, - metrics::BridgeMetrics, - types::{BridgeAction, is_route_valid}, - utils::get_eth_contract_addresses, -}; - -#[serde_as] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct EthConfig { - /// Rpc url for Eth fullnode, used for query stuff. - pub eth_rpc_url: String, - /// The proxy address of IotaBridge - pub eth_bridge_proxy_address: String, - /// The expected BridgeChainId on Eth side. - pub eth_bridge_chain_id: u8, - /// The starting block for EthSyncer to monitor eth contracts. - /// It is required when `run_client` is true. Usually this is - /// the block number when the bridge contracts are deployed. - /// When BridgeNode starts, it reads the contract watermark from storage. - /// If the watermark is not found, it will start from this fallback block - /// number. If the watermark is found, it will start from the watermark. - /// this v.s.`eth_contracts_start_block_override`: - pub eth_contracts_start_block_fallback: Option, - /// The starting block for EthSyncer to monitor eth contracts. It overrides - /// the watermark in storage. This is useful when we want to reprocess the - /// events from a specific block number. - /// Note: this field has to be reset after starting the BridgeNode, - /// otherwise it will reprocess the events from this block number every - /// time it starts. - #[serde(skip_serializing_if = "Option::is_none")] - pub eth_contracts_start_block_override: Option, -} - -#[serde_as] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct IotaConfig { - /// Rpc url for IOTA fullnode, used for query stuff and submit transactions. - pub iota_rpc_url: String, - /// The expected BridgeChainId on IOTA side. - pub iota_bridge_chain_id: u8, - /// Path of the file where bridge client key (any IotaKeyPair) is stored. - /// If `run_client` is true, and this is None, then use - /// `bridge_authority_key_path` as client key. - #[serde(skip_serializing_if = "Option::is_none")] - pub bridge_client_key_path: Option, - /// The gas object to use for paying for gas fees for the client. It needs - /// to be owned by the address associated with bridge client key. If not - /// set and `run_client` is true, it will query and use the gas object - /// with highest amount for the account. - #[serde(skip_serializing_if = "Option::is_none")] - pub bridge_client_gas_object: Option, - /// Override the last processed EventID for bridge module `bridge`. - /// When set, IotaSyncer will start from this cursor (exclusively) instead - /// of the one in storage. If the cursor is not found in storage or - /// override, the query will start from genesis. Key: iota module, - /// Value: last processed EventID (tx_digest, event_seq). Note 1: This - /// field should be rarely used. Only use it when you understand how to - /// follow up. Note 2: the EventID needs to be valid, namely it must - /// exist and matches the filter. Otherwise, it will miss one event - /// because of fullnode Event query semantics. - #[serde(skip_serializing_if = "Option::is_none")] - pub iota_bridge_module_last_processed_event_id_override: Option, -} - -#[serde_as] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct BridgeNodeConfig { - /// The port that the server listens on. - pub server_listen_port: u16, - /// The port that for metrics server. - pub metrics_port: u16, - /// Path of the file where bridge authority key (Secp256k1) is stored. - pub bridge_authority_key_path: PathBuf, - /// Whether to run client. If true, `iota.bridge_client_key_path` - /// and `db_path` needs to be provided. - pub run_client: bool, - /// Path of the client storage. Required when `run_client` is true. - #[serde(skip_serializing_if = "Option::is_none")] - pub db_path: Option, - /// A list of approved governance actions. Action in this list will be - /// signed when requested by client. - pub approved_governance_actions: Vec, - /// IOTA configuration - pub iota: IotaConfig, - /// Eth configuration - pub eth: EthConfig, -} - -impl Config for BridgeNodeConfig {} - -impl BridgeNodeConfig { - pub async fn validate( - &self, - metrics: Arc, - ) -> anyhow::Result<(BridgeServerConfig, Option)> { - if !is_route_valid( - BridgeChainId::try_from(self.iota.iota_bridge_chain_id)?, - BridgeChainId::try_from(self.eth.eth_bridge_chain_id)?, - ) { - return Err(anyhow!( - "Route between IOTA chain id {} and Eth chain id {} is not valid", - self.iota.iota_bridge_chain_id, - self.eth.eth_bridge_chain_id, - )); - }; - - let bridge_authority_key = match read_key(&self.bridge_authority_key_path, true)? { - IotaKeyPair::Secp256k1(key) => key, - _ => unreachable!("we required secp256k1 key in `read_key`"), - }; - - // we do this check here instead of `prepare_for_iota` below because - // that is only called when `run_client` is true. - let iota_client = - Arc::new(IotaClient::::new(&self.iota.iota_rpc_url).await?); - let bridge_committee = iota_client - .get_bridge_committee() - .await - .map_err(|e| anyhow!("Error getting bridge committee: {:?}", e))?; - if !bridge_committee.is_active_member(&bridge_authority_key.public().into()) { - return Err(anyhow!( - "Bridge authority key is not part of bridge committee" - )); - } - - let (eth_client, eth_contracts) = self.prepare_for_eth(metrics).await?; - let bridge_summary = iota_client - .get_bridge_summary() - .await - .map_err(|e| anyhow!("Error getting bridge summary: {:?}", e))?; - if bridge_summary.chain_id != self.iota.iota_bridge_chain_id { - anyhow::bail!( - "Bridge chain id mismatch: expected {}, but connected to {}", - self.iota.iota_bridge_chain_id, - bridge_summary.chain_id - ); - } - - // Validate approved actions that must be governace actions - for action in &self.approved_governance_actions { - if !action.is_governace_action() { - anyhow::bail!(format!( - "{:?}", - BridgeError::ActionIsNotGovernanceAction(action.clone()) - )); - } - } - let approved_governance_actions = self.approved_governance_actions.clone(); - - let bridge_server_config = BridgeServerConfig { - key: bridge_authority_key, - metrics_port: self.metrics_port, - server_listen_port: self.server_listen_port, - iota_client: iota_client.clone(), - eth_client: eth_client.clone(), - approved_governance_actions, - }; - if !self.run_client { - return Ok((bridge_server_config, None)); - } - - // If client is enabled, prepare client config - let (bridge_client_key, client_iota_address, gas_object_ref) = - self.prepare_for_iota(iota_client.clone()).await?; - - let db_path = self - .db_path - .clone() - .ok_or(anyhow!("`db_path` is required when `run_client` is true"))?; - - let bridge_client_config = BridgeClientConfig { - iota_address: client_iota_address, - key: bridge_client_key, - gas_object_ref, - metrics_port: self.metrics_port, - iota_client: iota_client.clone(), - eth_client: eth_client.clone(), - db_path, - eth_contracts, - // in `prepare_for_eth` we check if this is None when `run_client` is true. Safe to - // unwrap here. - eth_contracts_start_block_fallback: self - .eth - .eth_contracts_start_block_fallback - .unwrap(), - eth_contracts_start_block_override: self.eth.eth_contracts_start_block_override, - iota_bridge_module_last_processed_event_id_override: self - .iota - .iota_bridge_module_last_processed_event_id_override, - }; - - Ok((bridge_server_config, Some(bridge_client_config))) - } - - async fn prepare_for_eth( - &self, - metrics: Arc, - ) -> anyhow::Result<(Arc>, Vec)> { - let bridge_proxy_address = EthAddress::from_str(&self.eth.eth_bridge_proxy_address)?; - let provider = Arc::new( - new_metered_eth_provider(&self.eth.eth_rpc_url, metrics.clone()) - .unwrap() - .interval(std::time::Duration::from_millis(2000)), - ); - let chain_id = provider.get_chainid().await?; - let (committee_address, limiter_address, vault_address, config_address) = - get_eth_contract_addresses(bridge_proxy_address, &provider).await?; - let config = EthBridgeConfig::new(config_address, provider.clone()); - - if self.run_client && self.eth.eth_contracts_start_block_fallback.is_none() { - return Err(anyhow!( - "eth_contracts_start_block_fallback is required when run_client is true" - )); - } - - // If bridge chain id is Eth Mainent or Sepolia, we expect to see chain - // identifier to match accordingly. - let bridge_chain_id: u8 = config.chain_id().call().await?; - if self.eth.eth_bridge_chain_id != bridge_chain_id { - return Err(anyhow!( - "Bridge chain id mismatch: expected {}, but connected to {}", - self.eth.eth_bridge_chain_id, - bridge_chain_id - )); - } - if bridge_chain_id == BridgeChainId::EthMainnet as u8 && chain_id.as_u64() != 1 { - anyhow::bail!( - "Expected Eth chain id 1, but connected to {}", - chain_id.as_u64() - ); - } - if bridge_chain_id == BridgeChainId::EthSepolia as u8 && chain_id.as_u64() != 11155111 { - anyhow::bail!( - "Expected Eth chain id 11155111, but connected to {}", - chain_id.as_u64() - ); - } - info!( - "Connected to Eth chain: {}, Bridge chain id: {}", - chain_id.as_u64(), - bridge_chain_id, - ); - - let eth_client = Arc::new( - EthClient::::new( - &self.eth.eth_rpc_url, - HashSet::from_iter(vec![ - bridge_proxy_address, - committee_address, - config_address, - limiter_address, - vault_address, - ]), - metrics, - ) - .await?, - ); - let contract_addresses = vec![ - bridge_proxy_address, - committee_address, - config_address, - limiter_address, - vault_address, - ]; - Ok((eth_client, contract_addresses)) - } - - async fn prepare_for_iota( - &self, - iota_client: Arc>, - ) -> anyhow::Result<(IotaKeyPair, IotaAddress, ObjectRef)> { - let bridge_client_key = match &self.iota.bridge_client_key_path { - None => read_key(&self.bridge_authority_key_path, true), - Some(path) => read_key(path, false), - }?; - - // If bridge chain id is IOTA Mainent or Testnet, we expect to see chain - // identifier to match accordingly. - let iota_identifier = iota_client - .get_chain_identifier() - .await - .map_err(|e| anyhow!("Error getting chain identifier from IOTA: {:?}", e))?; - if self.iota.iota_bridge_chain_id == BridgeChainId::IotaMainnet as u8 - && iota_identifier != get_mainnet_chain_identifier().to_string() - { - anyhow::bail!( - "Expected iota chain identifier {}, but connected to {}", - self.iota.iota_bridge_chain_id, - iota_identifier - ); - } - if self.iota.iota_bridge_chain_id == BridgeChainId::IotaTestnet as u8 - && iota_identifier != get_testnet_chain_identifier().to_string() - { - anyhow::bail!( - "Expected iota chain identifier {}, but connected to {}", - self.iota.iota_bridge_chain_id, - iota_identifier - ); - } - info!( - "Connected to IOTA chain: {}, Bridge chain id: {}", - iota_identifier, self.iota.iota_bridge_chain_id, - ); - - let client_iota_address = IotaAddress::from(&bridge_client_key.public()); - - // TODO: decide a minimal amount here - let gas_object_id = match self.iota.bridge_client_gas_object { - Some(id) => id, - None => { - let iota_client = IotaClientBuilder::default() - .build(&self.iota.iota_rpc_url) - .await?; - let coin = - pick_highest_balance_coin(iota_client.coin_read_api(), client_iota_address, 0) - .await?; - coin.coin_object_id - } - }; - let (gas_coin, gas_object_ref, owner) = iota_client - .get_gas_data_panic_if_not_gas(gas_object_id) - .await; - if owner != Owner::AddressOwner(client_iota_address) { - return Err(anyhow!( - "Gas object {:?} is not owned by bridge client key's associated iota address {:?}, but {:?}", - gas_object_id, - client_iota_address, - owner - )); - } - info!( - "Starting bridge client with address: {:?}, gas object {:?}, balance: {}", - client_iota_address, - gas_object_ref.0, - gas_coin.value() - ); - - Ok((bridge_client_key, client_iota_address, gas_object_ref)) - } -} - -pub struct BridgeServerConfig { - pub key: BridgeAuthorityKeyPair, - pub server_listen_port: u16, - pub metrics_port: u16, - pub iota_client: Arc>, - pub eth_client: Arc>, - /// A list of approved governance actions. Action in this list will be - /// signed when requested by client. - pub approved_governance_actions: Vec, -} - -// TODO: add gas balance alert threshold -pub struct BridgeClientConfig { - pub iota_address: IotaAddress, - pub key: IotaKeyPair, - pub gas_object_ref: ObjectRef, - pub metrics_port: u16, - pub iota_client: Arc>, - pub eth_client: Arc>, - pub db_path: PathBuf, - pub eth_contracts: Vec, - // See `BridgeNodeConfig` for the explanation of following two fields. - pub eth_contracts_start_block_fallback: u64, - pub eth_contracts_start_block_override: Option, - pub iota_bridge_module_last_processed_event_id_override: Option, -} - -#[serde_as] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct BridgeCommitteeConfig { - pub bridge_authority_port_and_key_path: Vec<(u64, PathBuf)>, -} - -impl Config for BridgeCommitteeConfig {} - -pub async fn pick_highest_balance_coin( - coin_read_api: &CoinReadApi, - address: IotaAddress, - minimal_amount: u64, -) -> anyhow::Result { - let mut highest_balance = 0; - let mut highest_balance_coin = None; - coin_read_api - .get_coins_stream(address, None) - .for_each(|coin: Coin| { - if coin.balance > highest_balance { - highest_balance = coin.balance; - highest_balance_coin = Some(coin.clone()); - } - future::ready(()) - }) - .await; - if highest_balance_coin.is_none() { - return Err(anyhow!("No IOTA coins found for address {:?}", address)); - } - if highest_balance < minimal_amount { - return Err(anyhow!( - "Found no single coin that has >= {} balance IOTA for address {:?}", - minimal_amount, - address, - )); - } - Ok(highest_balance_coin.unwrap()) -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct EthContractAddresses { - pub iota_bridge: EthAddress, - pub bridge_committee: EthAddress, - pub bridge_config: EthAddress, - pub bridge_limiter: EthAddress, - pub bridge_vault: EthAddress, -} diff --git a/crates/iota-bridge/src/crypto.rs b/crates/iota-bridge/src/crypto.rs deleted file mode 100644 index 2264f418739..00000000000 --- a/crates/iota-bridge/src/crypto.rs +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt::{Debug, Display, Formatter}; - -use ethers::{ - core::k256::{ecdsa::VerifyingKey, elliptic_curve::sec1::ToEncodedPoint}, - types::Address as EthAddress, -}; -use fastcrypto::{ - encoding::{Encoding, Hex}, - error::FastCryptoError, - hash::{HashFunction, Keccak256}, - secp256k1::{ - Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, - recoverable::Secp256k1RecoverableSignature, - }, - traits::{KeyPair, RecoverableSigner, ToFromBytes, VerifyRecoverable}, -}; -use iota_types::{base_types::ConciseableName, message_envelope::VerifiedEnvelope}; -use serde::{Deserialize, Serialize}; -use tap::TapFallible; - -use crate::{ - error::{BridgeError, BridgeResult}, - types::{BridgeAction, BridgeCommittee, SignedBridgeAction, VerifiedSignedBridgeAction}, -}; -pub type BridgeAuthorityKeyPair = Secp256k1KeyPair; -pub type BridgeAuthorityPublicKey = Secp256k1PublicKey; -pub type BridgeAuthorityRecoverableSignature = Secp256k1RecoverableSignature; - -#[derive(Ord, PartialOrd, PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] -pub struct BridgeAuthorityPublicKeyBytes(Secp256k1PublicKeyAsBytes); - -impl BridgeAuthorityPublicKeyBytes { - pub fn to_eth_address(&self) -> EthAddress { - // unwrap: the conversion should not fail - let pubkey = VerifyingKey::from_sec1_bytes(self.as_bytes()).unwrap(); - let affine: ðers::core::k256::AffinePoint = pubkey.as_ref(); - let encoded = affine.to_encoded_point(false); - let pubkey = &encoded.as_bytes()[1..]; - assert_eq!(pubkey.len(), 64, "raw public key must be 64 bytes"); - let hash = Keccak256::digest(pubkey).digest; - EthAddress::from_slice(&hash[12..]) - } -} - -impl From<&BridgeAuthorityPublicKey> for BridgeAuthorityPublicKeyBytes { - fn from(pk: &BridgeAuthorityPublicKey) -> Self { - Self(Secp256k1PublicKeyAsBytes::from(pk)) - } -} - -impl ToFromBytes for BridgeAuthorityPublicKeyBytes { - /// Parse an object from its byte representation - fn from_bytes(bytes: &[u8]) -> Result { - let pk = BridgeAuthorityPublicKey::from_bytes(bytes)?; - Ok(Self::from(&pk)) - } - - /// Borrow a byte slice representing the serialized form of this object - fn as_bytes(&self) -> &[u8] { - self.as_ref() - } -} - -/// implement `FromStr` for `BridgeAuthorityPublicKeyBytes` -/// to convert a hex-string to public key bytes. -impl std::str::FromStr for BridgeAuthorityPublicKeyBytes { - type Err = FastCryptoError; - fn from_str(s: &str) -> Result { - let bytes = Hex::decode(s).map_err(|e| { - FastCryptoError::GeneralError(format!("Failed to decode hex string: {}", e)) - })?; - Self::from_bytes(&bytes) - } -} - -pub struct ConciseBridgeAuthorityPublicKeyBytesRef<'a>(&'a BridgeAuthorityPublicKeyBytes); - -impl Debug for ConciseBridgeAuthorityPublicKeyBytesRef<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - let s = Hex::encode(self.0.0.0.get(0..4).ok_or(std::fmt::Error)?); - write!(f, "k#{}..", s) - } -} - -impl Display for ConciseBridgeAuthorityPublicKeyBytesRef<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - Debug::fmt(self, f) - } -} - -impl AsRef<[u8]> for BridgeAuthorityPublicKeyBytes { - fn as_ref(&self) -> &[u8] { - self.0.0.as_ref() - } -} - -impl<'a> ConciseableName<'a> for BridgeAuthorityPublicKeyBytes { - type ConciseTypeRef = ConciseBridgeAuthorityPublicKeyBytesRef<'a>; - type ConciseType = String; - - fn concise(&'a self) -> ConciseBridgeAuthorityPublicKeyBytesRef<'a> { - ConciseBridgeAuthorityPublicKeyBytesRef(self) - } - - fn concise_owned(&self) -> String { - format!("{:?}", ConciseBridgeAuthorityPublicKeyBytesRef(self)) - } -} - -// TODO: include epoch ID here to reduce race conditions? -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct BridgeAuthoritySignInfo { - pub authority_pub_key: BridgeAuthorityPublicKey, - pub signature: BridgeAuthorityRecoverableSignature, -} - -impl BridgeAuthoritySignInfo { - pub fn new(msg: &BridgeAction, secret: &BridgeAuthorityKeyPair) -> Self { - let msg_bytes = msg.to_bytes(); - Self { - authority_pub_key: secret.public().clone(), - signature: secret.sign_recoverable_with_hash::(&msg_bytes), - } - } - - pub fn verify(&self, msg: &BridgeAction, committee: &BridgeCommittee) -> BridgeResult<()> { - // 1. verify committee member is in the committee and not blocklisted - if !committee.is_active_member(&self.authority_pub_key_bytes()) { - return Err(BridgeError::InvalidBridgeAuthority( - self.authority_pub_key_bytes(), - )); - } - - // 2. verify signature - let msg_bytes = msg.to_bytes(); - - self.authority_pub_key - .verify_recoverable_with_hash::(&msg_bytes, &self.signature) - .map_err(|e| { - BridgeError::InvalidBridgeAuthoritySignature(( - self.authority_pub_key_bytes(), - e.to_string(), - )) - }) - } - - pub fn authority_pub_key_bytes(&self) -> BridgeAuthorityPublicKeyBytes { - BridgeAuthorityPublicKeyBytes::from(&self.authority_pub_key) - } -} - -/// Verifies a SignedBridgeAction (response from bridge authority to bridge -/// client) represents the right BridgeAction, and is signed by the right -/// authority. -pub fn verify_signed_bridge_action( - expected_action: &BridgeAction, - signed_action: SignedBridgeAction, - expected_signer: &BridgeAuthorityPublicKeyBytes, - committee: &BridgeCommittee, -) -> BridgeResult { - if signed_action.data() != expected_action { - return Err(BridgeError::MismatchedAction); - } - - let sig = signed_action.auth_sig(); - if &sig.authority_pub_key_bytes() != expected_signer { - return Err(BridgeError::MismatchedAuthoritySigner); - } - sig.verify(signed_action.data(), committee).tap_err(|e| { - tracing::error!( - "Failed to verify SignedBridgeEvent {:?}. Error {:?}", - signed_action, - e - ) - })?; - Ok(VerifiedEnvelope::new_from_verified(signed_action)) -} - -#[cfg(test)] -mod tests { - use std::{str::FromStr, sync::Arc}; - - use ethers::types::Address as EthAddress; - use fastcrypto::traits::{KeyPair, ToFromBytes}; - use iota_types::{ - base_types::IotaAddress, - bridge::{BridgeChainId, TOKEN_ID_ETH}, - crypto::get_key_pair, - digests::TransactionDigest, - }; - use prometheus::Registry; - - use super::*; - use crate::{ - events::EmittedIotaToEthTokenBridgeV1, - test_utils::{get_test_authority_and_key, get_test_iota_to_eth_bridge_action}, - types::{BridgeAction, BridgeAuthority, IotaToEthBridgeAction, SignedBridgeAction}, - }; - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_sign_and_verify_bridge_event_basic() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - - let (mut authority1, pubkey, secret) = get_test_authority_and_key(5000, 9999); - let pubkey_bytes = BridgeAuthorityPublicKeyBytes::from(&pubkey); - - let (authority2, pubkey2, _secret) = get_test_authority_and_key(5000, 9999); - let pubkey_bytes2 = BridgeAuthorityPublicKeyBytes::from(&pubkey2); - - let committee = BridgeCommittee::new(vec![authority1.clone(), authority2.clone()]).unwrap(); - - let action: BridgeAction = - get_test_iota_to_eth_bridge_action(None, Some(1), Some(1), Some(100), None, None, None); - - let sig = BridgeAuthoritySignInfo::new(&action, &secret); - - let signed_action = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig.clone()); - - // Verification should succeed - let _ = - verify_signed_bridge_action(&action, signed_action.clone(), &pubkey_bytes, &committee) - .unwrap(); - - // Verification should fail - mismatched signer - assert!(matches!( - verify_signed_bridge_action(&action, signed_action.clone(), &pubkey_bytes2, &committee) - .unwrap_err(), - BridgeError::MismatchedAuthoritySigner - )); - - let mismatched_action: BridgeAction = - get_test_iota_to_eth_bridge_action(None, Some(2), Some(3), Some(4), None, None, None); - // Verification should fail - mismatched action - assert!(matches!( - verify_signed_bridge_action( - &mismatched_action, - signed_action.clone(), - &pubkey_bytes2, - &committee - ) - .unwrap_err(), - BridgeError::MismatchedAction, - )); - - // Signature is invalid (signed over different message), verification should - // fail - let action2: BridgeAction = - get_test_iota_to_eth_bridge_action(None, Some(3), Some(5), Some(77), None, None, None); - - let invalid_sig = BridgeAuthoritySignInfo::new(&action2, &secret); - let signed_action = SignedBridgeAction::new_from_data_and_sig(action.clone(), invalid_sig); - let _ = verify_signed_bridge_action(&action, signed_action, &pubkey_bytes, &committee) - .unwrap_err(); - - // Signer is not in committee, verification should fail - let (_, kp2): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let pubkey_bytes_2 = BridgeAuthorityPublicKeyBytes::from(kp2.public()); - let secret2 = Arc::pin(kp2); - let sig2 = BridgeAuthoritySignInfo::new(&action, &secret2); - let signed_action2 = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig2); - let _ = verify_signed_bridge_action(&action, signed_action2, &pubkey_bytes_2, &committee) - .unwrap_err(); - - // Authority is blocklisted, verification should fail - authority1.is_blocklisted = true; - let committee = BridgeCommittee::new(vec![authority1, authority2]).unwrap(); - let signed_action = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig); - let _ = verify_signed_bridge_action(&action, signed_action, &pubkey_bytes, &committee) - .unwrap_err(); - - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_sig_verification_regression_test() { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - - let public_key_bytes = - Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4") - .unwrap(); - let pubkey1 = BridgeAuthorityPublicKey::from_bytes(&public_key_bytes).unwrap(); - let authority1 = BridgeAuthority { - pubkey: pubkey1.clone(), - voting_power: 2500, - is_blocklisted: false, - base_url: "".into(), - }; - - let public_key_bytes = - Hex::decode("027f1178ff417fc9f5b8290bd8876f0a157a505a6c52db100a8492203ddd1d4279") - .unwrap(); - let pubkey2 = BridgeAuthorityPublicKey::from_bytes(&public_key_bytes).unwrap(); - let authority2 = BridgeAuthority { - pubkey: pubkey2.clone(), - voting_power: 2500, - is_blocklisted: false, - base_url: "".into(), - }; - - let public_key_bytes = - Hex::decode("026f311bcd1c2664c14277c7a80e4857c690626597064f89edc33b8f67b99c6bc0") - .unwrap(); - let pubkey3 = BridgeAuthorityPublicKey::from_bytes(&public_key_bytes).unwrap(); - let authority3 = BridgeAuthority { - pubkey: pubkey3.clone(), - voting_power: 2500, - is_blocklisted: false, - base_url: "".into(), - }; - - let public_key_bytes = - Hex::decode("03a57b85771aedeb6d31c808be9a6e73194e4b70e679608f2bca68bcc684773736") - .unwrap(); - let pubkey4 = BridgeAuthorityPublicKey::from_bytes(&public_key_bytes).unwrap(); - let authority4 = BridgeAuthority { - pubkey: pubkey4.clone(), - voting_power: 2500, - is_blocklisted: false, - base_url: "".into(), - }; - - let committee = BridgeCommittee::new(vec![ - authority1.clone(), - authority2.clone(), - authority3.clone(), - authority4.clone(), - ]) - .unwrap(); - - let action = BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction { - iota_tx_digest: TransactionDigest::random(), - iota_tx_event_index: 0, - iota_bridge_event: EmittedIotaToEthTokenBridgeV1 { - nonce: 1, - iota_chain_id: BridgeChainId::IotaTestnet, - iota_address: IotaAddress::from_str( - "0x80ab1ee086210a3a37355300ca24672e81062fcdb5ced6618dab203f6a3b291c", - ) - .unwrap(), - eth_chain_id: BridgeChainId::EthSepolia, - eth_address: EthAddress::from_str("0xb18f79Fe671db47393315fFDB377Da4Ea1B7AF96") - .unwrap(), - token_id: TOKEN_ID_ETH, - amount_iota_adjusted: 100000u64, - }, - }); - let sig = BridgeAuthoritySignInfo { - authority_pub_key: pubkey1, - signature: BridgeAuthorityRecoverableSignature::from_bytes( - &Hex::decode("e1cf11b380855ff1d4a451ebc2fd68477cf701b7d4ec88da3082709fe95201a5061b4b60cf13815a80ba9dfead23e220506aa74c4a863ba045d95715b4cc6b6e00").unwrap(), - ).unwrap(), - }; - sig.verify(&action, &committee).unwrap(); - - let sig = BridgeAuthoritySignInfo { - authority_pub_key: pubkey4.clone(), - signature: BridgeAuthorityRecoverableSignature::from_bytes( - &Hex::decode("8ba9ec92c2d5a44ecc123182f689b901a93921fd35f581354fea20b25a0ded6d055b96a64bdda77dd5a62b93d29abe93640aa3c1a136348093cd7a2418c6bfa301").unwrap(), - ).unwrap(), - }; - sig.verify(&action, &committee).unwrap(); - - let sig = BridgeAuthoritySignInfo { - authority_pub_key: pubkey4, - signature: BridgeAuthorityRecoverableSignature::from_bytes( - // invalid sdig - &Hex::decode("8ba9ec92c2d5a44ecc123182f689b901a93921fd35f581354fea20b25a0ded6d055b96a64bdda77dd5a62b93d29abe93640aa3c1a136348093cd7a2418c6bfa302").unwrap(), - ).unwrap(), - }; - sig.verify(&action, &committee).unwrap_err(); - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_authority_public_key_bytes_to_eth_address() { - let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes( - &Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4") - .unwrap(), - ) - .unwrap(); - let addr = "0x68b43fd906c0b8f024a18c56e06744f7c6157c65" - .parse::() - .unwrap(); - assert_eq!(pub_key_bytes.to_eth_address(), addr); - - // Example from: https://github.com/gakonst/ethers-rs/blob/master/ethers-core/src/utils/mod.rs#L1235 - let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes( - &Hex::decode("0376698beebe8ee5c74d8cc50ab84ac301ee8f10af6f28d0ffd6adf4d6d3b9b762") - .unwrap(), - ) - .unwrap(); - let addr = "0Ac1dF02185025F65202660F8167210A80dD5086" - .parse::() - .unwrap(); - assert_eq!(pub_key_bytes.to_eth_address(), addr); - } -} diff --git a/crates/iota-bridge/src/e2e_tests/basic.rs b/crates/iota-bridge/src/e2e_tests/basic.rs deleted file mode 100644 index 8caffd1ca5c..00000000000 --- a/crates/iota-bridge/src/e2e_tests/basic.rs +++ /dev/null @@ -1,654 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - collections::{HashMap, HashSet}, - path::Path, - sync::Arc, -}; - -use anyhow::anyhow; -use eth_iota_bridge::EthIotaBridgeEvents; -use ethers::{prelude::*, types::Address as EthAddress}; -use iota_json_rpc_types::{ - IotaExecutionStatus, IotaTransactionBlockEffectsAPI, IotaTransactionBlockResponse, -}; -use iota_sdk::{IotaClient, wallet_context::WalletContext}; -use iota_types::{ - BRIDGE_PACKAGE_ID, TypeTag, - base_types::{IotaAddress, ObjectRef}, - bridge::{BRIDGE_MODULE_NAME, BridgeChainId, BridgeTokenMetadata, TOKEN_ID_ETH}, - programmable_transaction_builder::ProgrammableTransactionBuilder, - transaction::{ObjectArg, TransactionData}, -}; -use move_core_types::ident_str; -use tap::TapFallible; -use tracing::info; - -use crate::{ - abi::{EthBridgeEvent, EthERC20, EthIotaBridge, eth_iota_bridge}, - client::bridge_authority_aggregator::BridgeAuthorityAggregator, - e2e_tests::test_utils::{ - BridgeTestCluster, BridgeTestClusterBuilder, get_signatures, send_eth_tx_and_get_tx_receipt, - }, - eth_transaction_builder::build_eth_transaction, - events::{ - IotaBridgeEvent, IotaToEthTokenBridgeV1, TokenTransferApproved, TokenTransferClaimed, - }, - iota_client::IotaBridgeClient, - iota_transaction_builder::build_add_tokens_on_iota_transaction, - types::{AddTokensOnEvmAction, BridgeAction, BridgeActionStatus, IotaToEthBridgeAction}, - utils::{EthSigner, publish_and_register_coins_return_add_coins_on_iota_action}, -}; - -#[tokio::test(flavor = "multi_thread", worker_threads = 8)] -#[ignore = "https://github.com/iotaledger/iota/issues/3224"] -async fn test_bridge_from_eth_to_iota_to_eth() { - telemetry_subscribers::init_for_testing(); - - let eth_chain_id = BridgeChainId::EthCustom as u8; - let iota_chain_id = BridgeChainId::IotaCustom as u8; - - let mut bridge_test_cluster = BridgeTestClusterBuilder::new() - .with_eth_env(true) - .with_bridge_cluster(true) - .build() - .await; - - let (eth_signer, _) = bridge_test_cluster - .get_eth_signer_and_address() - .await - .unwrap(); - - let iota_address = bridge_test_cluster.iota_user_address(); - let amount = 42; - let iota_amount = amount * 100_000_000; - - initiate_bridge_eth_to_iota(&bridge_test_cluster, amount, 0) - .await - .unwrap(); - let events = bridge_test_cluster - .new_bridge_events( - HashSet::from_iter([ - TokenTransferApproved.get().unwrap().clone(), - TokenTransferClaimed.get().unwrap().clone(), - ]), - true, - ) - .await; - // There are exactly 1 approved and 1 claimed event - assert_eq!(events.len(), 2); - - let eth_coin = bridge_test_cluster - .iota_client() - .coin_read_api() - .get_all_coins(iota_address, None, None) - .await - .unwrap() - .data - .iter() - .find(|c| c.coin_type.contains("ETH")) - .expect("Recipient should have received ETH coin now") - .clone(); - assert_eq!(eth_coin.balance, iota_amount); - info!("Eth to iota bridge transfer finished"); - - // Now let the recipient send the coin back to ETH - let eth_address_1 = EthAddress::random(); - let nonce = 0; - - let iota_to_eth_bridge_action = initiate_bridge_iota_to_eth( - &bridge_test_cluster, - eth_address_1, - eth_coin.object_ref(), - nonce, - iota_amount, - ) - .await - .unwrap(); - let events = bridge_test_cluster - .new_bridge_events( - HashSet::from_iter([ - IotaToEthTokenBridgeV1.get().unwrap().clone(), - TokenTransferApproved.get().unwrap().clone(), - TokenTransferClaimed.get().unwrap().clone(), - ]), - true, - ) - .await; - // There are exactly 1 deposit and 1 approved event - assert_eq!(events.len(), 2); - - // Test `get_parsed_token_transfer_message` - let parsed_msg = bridge_test_cluster - .bridge_client() - .get_parsed_token_transfer_message(iota_chain_id, nonce) - .await - .unwrap() - .unwrap(); - assert_eq!(parsed_msg.source_chain as u8, iota_chain_id); - assert_eq!(parsed_msg.seq_num, nonce); - assert_eq!( - parsed_msg.parsed_payload.sender_address, - iota_address.to_vec() - ); - assert_eq!( - &parsed_msg.parsed_payload.target_address, - eth_address_1.as_bytes() - ); - assert_eq!(parsed_msg.parsed_payload.target_chain, eth_chain_id); - assert_eq!(parsed_msg.parsed_payload.token_type, TOKEN_ID_ETH); - assert_eq!(parsed_msg.parsed_payload.amount, iota_amount); - - let message = eth_iota_bridge::Message::from(iota_to_eth_bridge_action); - let signatures = - get_signatures(bridge_test_cluster.bridge_client(), nonce, iota_chain_id).await; - - let eth_iota_bridge = EthIotaBridge::new( - bridge_test_cluster.contracts().iota_bridge, - eth_signer.clone().into(), - ); - let call = eth_iota_bridge.transfer_bridged_tokens_with_signatures(signatures, message); - let eth_claim_tx_receipt = send_eth_tx_and_get_tx_receipt(call).await; - assert_eq!(eth_claim_tx_receipt.status.unwrap().as_u64(), 1); - info!("IOTA to Eth bridge transfer claimed"); - // Assert eth_address_1 has received ETH - assert_eq!( - eth_signer.get_balance(eth_address_1, None).await.unwrap(), - U256::from(amount) * U256::exp10(18) - ); -} - -// Test add new coins on both IOTA and Eth -// Also test bridge node handling `NewTokenEvent`` -#[tokio::test(flavor = "multi_thread", worker_threads = 8)] -#[ignore = "https://github.com/iotaledger/iota/issues/3224"] -async fn test_add_new_coins_on_iota_and_eth() { - telemetry_subscribers::init_for_testing(); - let mut bridge_test_cluster = BridgeTestClusterBuilder::new() - .with_eth_env(true) - .with_bridge_cluster(false) - .with_num_validators(3) - .build() - .await; - - let bridge_arg = bridge_test_cluster.get_mut_bridge_arg().await.unwrap(); - - // Register tokens on IOTA - let token_id = 5; - let token_iota_decimal = 9; // this needs to match ka.move - let token_price = 10000; - let sender = bridge_test_cluster.iota_user_address(); - info!("Published new token"); - let iota_action = publish_and_register_coins_return_add_coins_on_iota_action( - bridge_test_cluster.wallet(), - bridge_arg, - vec![Path::new("../../bridge/move/tokens/mock/ka").into()], - vec![token_id], - vec![token_price], - 1, // seq num - ) - .await; - let new_token_erc_address = bridge_test_cluster.contracts().ka; - let eth_action = BridgeAction::AddTokensOnEvmAction(AddTokensOnEvmAction { - nonce: 0, - chain_id: BridgeChainId::EthCustom, - native: true, - token_ids: vec![token_id], - token_addresses: vec![new_token_erc_address], - token_iota_decimals: vec![token_iota_decimal], - token_prices: vec![token_price], - }); - - info!("Starting bridge cluster"); - - bridge_test_cluster.set_approved_governance_actions_for_next_start(vec![ - vec![iota_action.clone(), eth_action.clone()], - vec![iota_action.clone()], - vec![eth_action.clone()], - ]); - bridge_test_cluster.start_bridge_cluster().await; - bridge_test_cluster - .wait_for_bridge_cluster_to_be_up(10) - .await; - info!("Bridge cluster is up"); - - let bridge_committee = Arc::new( - bridge_test_cluster - .bridge_client() - .get_bridge_committee() - .await - .expect("Failed to get bridge committee"), - ); - let agg = BridgeAuthorityAggregator::new(bridge_committee); - let certified_iota_action = agg - .request_committee_signatures(iota_action) - .await - .expect("Failed to request committee signatures for AddTokensOnIotaAction"); - let certified_eth_action = agg - .request_committee_signatures(eth_action.clone()) - .await - .expect("Failed to request committee signatures for AddTokensOnEvmAction"); - - let tx = build_add_tokens_on_iota_transaction( - sender, - &bridge_test_cluster - .wallet() - .get_one_gas_object_owned_by_address(sender) - .await - .unwrap() - .unwrap(), - certified_iota_action, - bridge_arg, - 1000, - ) - .unwrap(); - - let response = bridge_test_cluster.sign_and_execute_transaction(&tx).await; - let effects = response.effects.unwrap(); - assert_eq!(effects.status(), &IotaExecutionStatus::Success); - assert!(response.events.unwrap().data.iter().any(|e| { - let iota_bridge_event = IotaBridgeEvent::try_from_iota_event(e).unwrap().unwrap(); - match iota_bridge_event { - IotaBridgeEvent::NewTokenEvent(e) => { - assert_eq!(e.token_id, token_id); - true - } - _ => false, - } - })); - info!("Approved new token on IOTA"); - - // Assert new token is correctly added - let treasury_summary = bridge_test_cluster - .bridge_client() - .get_treasury_summary() - .await - .unwrap(); - assert_eq!(treasury_summary.id_token_type_map.len(), 5); // 4 + 1 new token - let (id, _type) = treasury_summary - .id_token_type_map - .iter() - .find(|(id, _)| id == &token_id) - .unwrap(); - let (_type, metadata) = treasury_summary - .supported_tokens - .iter() - .find(|(_type_, _)| _type == _type_) - .unwrap(); - assert_eq!( - metadata, - &BridgeTokenMetadata { - id: *id, - decimal_multiplier: 1_000_000_000, - notional_value: token_price, - native_token: false, - } - ); - - // Add new token on EVM - let config_address = bridge_test_cluster.contracts().bridge_config; - let eth_signer = bridge_test_cluster.get_eth_signer().await; - let eth_call = build_eth_transaction(config_address, eth_signer, certified_eth_action) - .await - .unwrap(); - let eth_receipt = send_eth_tx_and_get_tx_receipt(eth_call).await; - assert_eq!(eth_receipt.status.unwrap().as_u64(), 1); - - // Verify new tokens are added on EVM - let (address, dp, price) = bridge_test_cluster - .eth_env() - .get_supported_token(token_id) - .await; - assert_eq!(address, new_token_erc_address); - assert_eq!(dp, 9); - assert_eq!(price, token_price); - - initiate_bridge_erc20_to_iota( - &bridge_test_cluster, - 100, - new_token_erc_address, - token_id, - 0, - ) - .await - .unwrap(); -} - -pub(crate) async fn deposit_native_eth_to_sol_contract( - signer: &EthSigner, - contract_address: EthAddress, - iota_recipient_address: IotaAddress, - iota_chain_id: BridgeChainId, - amount: u64, -) -> ContractCall { - let contract = EthIotaBridge::new(contract_address, signer.clone().into()); - let iota_recipient_address = iota_recipient_address.to_vec().into(); - let amount = U256::from(amount) * U256::exp10(18); // 1 ETH - contract - .bridge_eth(iota_recipient_address, iota_chain_id as u8) - .value(amount) -} - -async fn deposit_eth_to_iota_package( - iota_client: &IotaClient, - iota_address: IotaAddress, - wallet_context: &WalletContext, - target_chain: BridgeChainId, - target_address: EthAddress, - token: ObjectRef, - bridge_object_arg: ObjectArg, - iota_token_type_tags: &HashMap, -) -> Result { - let mut builder = ProgrammableTransactionBuilder::new(); - let arg_target_chain = builder.pure(target_chain as u8).unwrap(); - let arg_target_address = builder.pure(target_address.as_bytes()).unwrap(); - let arg_token = builder.obj(ObjectArg::ImmOrOwnedObject(token)).unwrap(); - let arg_bridge = builder.obj(bridge_object_arg).unwrap(); - - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - BRIDGE_MODULE_NAME.to_owned(), - ident_str!("send_token").to_owned(), - vec![iota_token_type_tags.get(&TOKEN_ID_ETH).unwrap().clone()], - vec![arg_bridge, arg_target_chain, arg_target_address, arg_token], - ); - - let pt = builder.finish(); - let gas_object_ref = wallet_context - .get_one_gas_object_owned_by_address(iota_address) - .await - .unwrap() - .unwrap(); - let tx_data = TransactionData::new_programmable( - iota_address, - vec![gas_object_ref], - pt, - 500_000_000, - iota_client - .governance_api() - .get_reference_gas_price() - .await - .unwrap(), - ); - let tx = wallet_context.sign_transaction(&tx_data); - wallet_context.execute_transaction_may_fail(tx).await -} - -pub async fn initiate_bridge_erc20_to_iota( - bridge_test_cluster: &BridgeTestCluster, - amount_u64: u64, - token_address: EthAddress, - token_id: u8, - nonce: u64, -) -> Result<(), anyhow::Error> { - let (eth_signer, eth_address) = bridge_test_cluster - .get_eth_signer_and_address() - .await - .unwrap(); - - // First, mint ERC20 tokens to the signer - let contract = EthERC20::new(token_address, eth_signer.clone().into()); - let decimal = contract.decimals().await? as usize; - let amount = U256::from(amount_u64) * U256::exp10(decimal); - let iota_amount = amount.as_u64(); - let mint_call = contract.mint(eth_address, amount); - let mint_tx_receipt = send_eth_tx_and_get_tx_receipt(mint_call).await; - assert_eq!(mint_tx_receipt.status.unwrap().as_u64(), 1); - - // Second, set allowance - let allowance_call = contract.approve(bridge_test_cluster.contracts().iota_bridge, amount); - let allowance_tx_receipt = send_eth_tx_and_get_tx_receipt(allowance_call).await; - assert_eq!(allowance_tx_receipt.status.unwrap().as_u64(), 1); - - // Third, deposit to bridge - let iota_recipient_address = bridge_test_cluster.iota_user_address(); - let iota_chain_id = bridge_test_cluster.iota_chain_id(); - let eth_chain_id = bridge_test_cluster.eth_chain_id(); - - info!( - "Depositing ERC20 (token id:{}, token_address: {}) to Solidity contract", - token_id, token_address - ); - let contract = EthIotaBridge::new( - bridge_test_cluster.contracts().iota_bridge, - eth_signer.clone().into(), - ); - let deposit_call = contract.bridge_erc20( - token_id, - amount, - iota_recipient_address.to_vec().into(), - iota_chain_id as u8, - ); - let tx_receipt = send_eth_tx_and_get_tx_receipt(deposit_call).await; - let eth_bridge_event = tx_receipt - .logs - .iter() - .find_map(EthBridgeEvent::try_from_log) - .unwrap(); - let EthBridgeEvent::EthIotaBridgeEvents(EthIotaBridgeEvents::TokensDepositedFilter( - eth_bridge_event, - )) = eth_bridge_event - else { - unreachable!(); - }; - // assert eth log matches - assert_eq!(eth_bridge_event.source_chain_id, eth_chain_id as u8); - assert_eq!(eth_bridge_event.nonce, nonce); - assert_eq!(eth_bridge_event.destination_chain_id, iota_chain_id as u8); - assert_eq!(eth_bridge_event.token_id, token_id); - assert_eq!(eth_bridge_event.iota_adjusted_amount, iota_amount); - assert_eq!(eth_bridge_event.sender_address, eth_address); - assert_eq!( - eth_bridge_event.recipient_address, - iota_recipient_address.to_vec() - ); - info!( - "Deposited ERC20 (token id:{}, token_address: {}) to Solidity contract", - token_id, token_address - ); - - wait_for_transfer_action_status( - bridge_test_cluster.bridge_client(), - eth_chain_id, - nonce, - BridgeActionStatus::Claimed, - ) - .await - .tap_ok(|_| { - info!( - nonce, - token_id, amount_u64, "Eth to IOTA bridge transfer claimed" - ); - }) -} - -pub async fn initiate_bridge_eth_to_iota( - bridge_test_cluster: &BridgeTestCluster, - amount: u64, - nonce: u64, -) -> Result<(), anyhow::Error> { - info!("Depositing native Ether to Solidity contract, nonce: {nonce}, amount: {amount}"); - let (eth_signer, eth_address) = bridge_test_cluster - .get_eth_signer_and_address() - .await - .unwrap(); - - let iota_address = bridge_test_cluster.iota_user_address(); - let iota_chain_id = bridge_test_cluster.iota_chain_id(); - let eth_chain_id = bridge_test_cluster.eth_chain_id(); - let token_id = TOKEN_ID_ETH; - - let iota_amount = (U256::from(amount) * U256::exp10(8)).as_u64(); // DP for Ether on IOTA - - let eth_tx = deposit_native_eth_to_sol_contract( - ð_signer, - bridge_test_cluster.contracts().iota_bridge, - iota_address, - iota_chain_id, - amount, - ) - .await; - let tx_receipt = send_eth_tx_and_get_tx_receipt(eth_tx).await; - let eth_bridge_event = tx_receipt - .logs - .iter() - .find_map(EthBridgeEvent::try_from_log) - .unwrap(); - let EthBridgeEvent::EthIotaBridgeEvents(EthIotaBridgeEvents::TokensDepositedFilter( - eth_bridge_event, - )) = eth_bridge_event - else { - unreachable!(); - }; - // assert eth log matches - assert_eq!(eth_bridge_event.source_chain_id, eth_chain_id as u8); - assert_eq!(eth_bridge_event.nonce, nonce); - assert_eq!(eth_bridge_event.destination_chain_id, iota_chain_id as u8); - assert_eq!(eth_bridge_event.token_id, token_id); - assert_eq!(eth_bridge_event.iota_adjusted_amount, iota_amount); - assert_eq!(eth_bridge_event.sender_address, eth_address); - assert_eq!(eth_bridge_event.recipient_address, iota_address.to_vec()); - info!("Deposited Eth to Solidity contract"); - - wait_for_transfer_action_status( - bridge_test_cluster.bridge_client(), - eth_chain_id, - nonce, - BridgeActionStatus::Claimed, - ) - .await - .tap_ok(|_| { - info!("Eth to IOTA bridge transfer claimed"); - }) -} - -pub async fn initiate_bridge_iota_to_eth( - bridge_test_cluster: &BridgeTestCluster, - eth_address: EthAddress, - token: ObjectRef, - nonce: u64, - iota_amount: u64, -) -> Result { - let bridge_object_arg = bridge_test_cluster - .bridge_client() - .get_mutable_bridge_object_arg_must_succeed() - .await; - let iota_client = bridge_test_cluster.iota_client(); - let token_types = bridge_test_cluster - .bridge_client() - .get_token_id_map() - .await - .unwrap(); - let iota_address = bridge_test_cluster.iota_user_address(); - - let resp = match deposit_eth_to_iota_package( - iota_client, - iota_address, - bridge_test_cluster.wallet(), - bridge_test_cluster.eth_chain_id(), - eth_address, - token, - bridge_object_arg, - &token_types, - ) - .await - { - Ok(resp) => { - if !resp.status_ok().unwrap() { - return Err(anyhow!("IOTA TX error")); - } else { - resp - } - } - Err(e) => return Err(e), - }; - - let iota_events = resp.events.unwrap().data; - let bridge_event = iota_events - .iter() - .filter_map(|e| { - let iota_bridge_event = IotaBridgeEvent::try_from_iota_event(e).unwrap()?; - iota_bridge_event.try_into_bridge_action(e.id.tx_digest, e.id.event_seq as u16) - }) - .find_map(|e| { - if let BridgeAction::IotaToEthBridgeAction(a) = e { - Some(a) - } else { - None - } - }) - .unwrap(); - info!("Deposited Eth to move package"); - assert_eq!(bridge_event.iota_bridge_event.nonce, nonce); - assert_eq!( - bridge_event.iota_bridge_event.iota_chain_id, - bridge_test_cluster.iota_chain_id() - ); - assert_eq!( - bridge_event.iota_bridge_event.eth_chain_id, - bridge_test_cluster.eth_chain_id() - ); - assert_eq!(bridge_event.iota_bridge_event.iota_address, iota_address); - assert_eq!(bridge_event.iota_bridge_event.eth_address, eth_address); - assert_eq!(bridge_event.iota_bridge_event.token_id, TOKEN_ID_ETH); - assert_eq!( - bridge_event.iota_bridge_event.amount_iota_adjusted, - iota_amount - ); - - // Wait for the bridge action to be approved - wait_for_transfer_action_status( - bridge_test_cluster.bridge_client(), - bridge_test_cluster.iota_chain_id(), - nonce, - BridgeActionStatus::Approved, - ) - .await - .unwrap(); - info!("IOTA to Eth bridge transfer approved."); - - Ok(bridge_event) -} - -async fn wait_for_transfer_action_status( - iota_bridge_client: &IotaBridgeClient, - chain_id: BridgeChainId, - nonce: u64, - status: BridgeActionStatus, -) -> Result<(), anyhow::Error> { - // Wait for the bridge action to be approved - let now = std::time::Instant::now(); - info!( - "Waiting for onchain status {:?}. chain: {:?}, nonce: {nonce}", - status, chain_id as u8 - ); - loop { - let timer = std::time::Instant::now(); - let res = iota_bridge_client - .get_token_transfer_action_onchain_status_until_success(chain_id as u8, nonce) - .await; - info!( - "get_token_transfer_action_onchain_status_until_success took {:?}, status: {:?}", - timer.elapsed(), - res - ); - - if res == status { - info!( - "detected on chain status {:?}. chain: {:?}, nonce: {nonce}", - status, chain_id as u8 - ); - return Ok(()); - } - if now.elapsed().as_secs() > 60 { - return Err(anyhow!( - "Timeout waiting for token transfer action to be {:?}. chain_id: {chain_id:?}, nonce: {nonce}. Time elapsed: {:?}", - status, - now.elapsed(), - )); - } - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - } -} diff --git a/crates/iota-bridge/src/e2e_tests/complex.rs b/crates/iota-bridge/src/e2e_tests/complex.rs deleted file mode 100644 index 76ab551688c..00000000000 --- a/crates/iota-bridge/src/e2e_tests/complex.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::sync::Arc; - -use ethers::types::Address as EthAddress; -use iota_json_rpc_types::{IotaExecutionStatus, IotaTransactionBlockEffectsAPI}; -use iota_types::bridge::{BridgeChainId, TOKEN_ID_ETH}; -use tracing::info; - -use crate::{ - client::bridge_authority_aggregator::BridgeAuthorityAggregator, - e2e_tests::{ - basic::{initiate_bridge_eth_to_iota, initiate_bridge_iota_to_eth}, - test_utils::BridgeTestClusterBuilder, - }, - iota_transaction_builder::build_iota_transaction, - types::{BridgeAction, BridgeActionStatus, EmergencyAction, EmergencyActionType}, -}; - -#[tokio::test(flavor = "multi_thread", worker_threads = 16)] -#[ignore = "https://github.com/iotaledger/iota/issues/3224"] -async fn test_iota_bridge_paused() { - telemetry_subscribers::init_for_testing(); - - // approve pause action in bridge nodes - let pause_action = BridgeAction::EmergencyAction(EmergencyAction { - nonce: 0, - chain_id: BridgeChainId::IotaCustom, - action_type: EmergencyActionType::Pause, - }); - - let unpause_action = BridgeAction::EmergencyAction(EmergencyAction { - nonce: 1, - chain_id: BridgeChainId::IotaCustom, - action_type: EmergencyActionType::Unpause, - }); - - // Setup bridge test env - let bridge_test_cluster = BridgeTestClusterBuilder::new() - .with_eth_env(true) - .with_bridge_cluster(true) - .with_num_validators(4) - .with_approved_governance_actions(vec![ - vec![pause_action.clone(), unpause_action.clone()], - vec![unpause_action.clone()], - vec![unpause_action.clone()], - vec![], - ]) - .build() - .await; - - let bridge_client = bridge_test_cluster.bridge_client(); - let iota_address = bridge_test_cluster.iota_user_address(); - let iota_token_type_tags = bridge_client.get_token_id_map().await.unwrap(); - - // verify bridge are not paused - assert!(!bridge_client.get_bridge_summary().await.unwrap().is_frozen); - - // try bridge from eth and verify it works on iota - initiate_bridge_eth_to_iota(&bridge_test_cluster, 10, 0) - .await - .unwrap(); - // verify Eth was transferred to IOTA address - let eth_coin_type = iota_token_type_tags.get(&TOKEN_ID_ETH).unwrap(); - let eth_coin = bridge_client - .iota_client() - .coin_read_api() - .get_coins(iota_address, Some(eth_coin_type.to_string()), None, None) - .await - .unwrap() - .data; - assert_eq!(1, eth_coin.len()); - - // get pause bridge signatures from committee - let bridge_committee = Arc::new(bridge_client.get_bridge_committee().await.unwrap()); - let agg = BridgeAuthorityAggregator::new(bridge_committee); - let certified_action = agg - .request_committee_signatures(pause_action) - .await - .unwrap(); - - // execute pause bridge on iota - let gas = bridge_test_cluster - .wallet() - .get_one_gas_object_owned_by_address(iota_address) - .await - .unwrap() - .unwrap(); - - let tx = build_iota_transaction( - iota_address, - &gas, - certified_action, - bridge_client - .get_mutable_bridge_object_arg_must_succeed() - .await, - &iota_token_type_tags, - 1000, - ) - .unwrap(); - - let response = bridge_test_cluster.sign_and_execute_transaction(&tx).await; - assert_eq!( - response.effects.unwrap().status(), - &IotaExecutionStatus::Success - ); - info!("Bridge paused"); - - // verify bridge paused - assert!(bridge_client.get_bridge_summary().await.unwrap().is_frozen); - - // Transfer from eth to iota should fail on IOTA - let eth_to_iota_bridge_action = initiate_bridge_eth_to_iota(&bridge_test_cluster, 10, 1).await; - assert!(eth_to_iota_bridge_action.is_err()); - // message should not be recorded on IOTA when the bridge is paused - let res = bridge_test_cluster - .bridge_client() - .get_token_transfer_action_onchain_status_until_success( - bridge_test_cluster.eth_chain_id() as u8, - 1, - ) - .await; - assert_eq!(BridgeActionStatus::NotFound, res); - // Transfer from IOTA to eth should fail - let iota_to_eth_bridge_action = initiate_bridge_iota_to_eth( - &bridge_test_cluster, - EthAddress::random(), - eth_coin.first().unwrap().object_ref(), - 0, - 10, - ) - .await; - assert!(iota_to_eth_bridge_action.is_err()) -} diff --git a/crates/iota-bridge/src/e2e_tests/mod.rs b/crates/iota-bridge/src/e2e_tests/mod.rs deleted file mode 100644 index 0de1e3c71ab..00000000000 --- a/crates/iota-bridge/src/e2e_tests/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod basic; -mod complex; -pub mod test_utils; diff --git a/crates/iota-bridge/src/e2e_tests/test_utils.rs b/crates/iota-bridge/src/e2e_tests/test_utils.rs deleted file mode 100644 index a836ae9888f..00000000000 --- a/crates/iota-bridge/src/e2e_tests/test_utils.rs +++ /dev/null @@ -1,828 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - collections::{BTreeMap, HashSet}, - fs::{self, DirBuilder, File}, - io::{Read, Write}, - path::{Path, PathBuf}, - process::{Child, Command}, - str::FromStr, - sync::Arc, -}; - -use ethers::{prelude::*, types::Address as EthAddress}; -use iota_config::local_ip_utils::get_available_port; -use iota_json_rpc_types::{ - IotaEvent, IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, - IotaTransactionBlockResponseQuery, TransactionFilter, -}; -use iota_sdk::{IotaClient, wallet_context::WalletContext}; -use iota_test_transaction_builder::TestTransactionBuilder; -use iota_types::{ - IOTA_BRIDGE_OBJECT_ID, - base_types::IotaAddress, - bridge::BridgeChainId, - committee::TOTAL_VOTING_POWER, - crypto::{EncodeDecodeBase64, KeypairTraits, get_key_pair}, - digests::TransactionDigest, - transaction::{ObjectArg, TransactionData}, -}; -use move_core_types::language_storage::StructTag; -use prometheus::Registry; -use rand::{Rng, SeedableRng, rngs::SmallRng}; -use serde::{Deserialize, Serialize}; -use tempfile::tempdir; -use test_cluster::{TestCluster, TestClusterBuilder}; -use tokio::{join, task::JoinHandle}; -use tracing::{error, info}; - -use crate::{ - BRIDGE_ENABLE_PROTOCOL_VERSION, - abi::{EthBridgeCommittee, EthBridgeConfig}, - config::{BridgeNodeConfig, EthConfig, IotaConfig}, - crypto::{BridgeAuthorityKeyPair, BridgeAuthorityPublicKeyBytes}, - events::*, - iota_client::IotaBridgeClient, - node::run_bridge_node, - server::BridgeNodePublicMetadata, - types::BridgeAction, - utils::{EthSigner, get_eth_signer_client}, -}; - -const BRIDGE_COMMITTEE_NAME: &str = "BridgeCommittee"; -const IOTA_BRIDGE_NAME: &str = "IotaBridge"; -const BRIDGE_CONFIG_NAME: &str = "BridgeConfig"; -const BRIDGE_LIMITER_NAME: &str = "BridgeLimiter"; -const BRIDGE_VAULT_NAME: &str = "BridgeVault"; -const BTC_NAME: &str = "BTC"; -const ETH_NAME: &str = "ETH"; -const USDC_NAME: &str = "USDC"; -const USDT_NAME: &str = "USDT"; -const KA_NAME: &str = "KA"; - -pub const TEST_PK: &str = "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356"; - -/// A helper struct that holds TestCluster and other Bridge related -/// structs that are needed for testing. -pub struct BridgeTestCluster { - pub num_validators: usize, - pub test_cluster: TestCluster, - bridge_client: IotaBridgeClient, - eth_environment: EthBridgeEnvironment, - bridge_node_handles: Option>>, - approved_governance_actions_for_next_start: Option>>, - bridge_tx_cursor: Option, - eth_chain_id: BridgeChainId, - iota_chain_id: BridgeChainId, -} - -pub struct BridgeTestClusterBuilder { - with_eth_env: bool, - with_bridge_cluster: bool, - num_validators: usize, - approved_governance_actions: Option>>, - eth_chain_id: BridgeChainId, - iota_chain_id: BridgeChainId, -} - -impl Default for BridgeTestClusterBuilder { - fn default() -> Self { - Self::new() - } -} - -impl BridgeTestClusterBuilder { - pub fn new() -> Self { - BridgeTestClusterBuilder { - with_eth_env: false, - with_bridge_cluster: false, - num_validators: 4, - approved_governance_actions: None, - eth_chain_id: BridgeChainId::EthCustom, - iota_chain_id: BridgeChainId::IotaCustom, - } - } - - pub fn with_eth_env(mut self, with_eth_env: bool) -> Self { - self.with_eth_env = with_eth_env; - self - } - - pub fn with_bridge_cluster(mut self, with_bridge_cluster: bool) -> Self { - self.with_bridge_cluster = with_bridge_cluster; - self - } - - pub fn with_num_validators(mut self, num_validators: usize) -> Self { - self.num_validators = num_validators; - self - } - - pub fn with_approved_governance_actions( - mut self, - approved_governance_actions: Vec>, - ) -> Self { - assert_eq!(approved_governance_actions.len(), self.num_validators); - self.approved_governance_actions = Some(approved_governance_actions); - self - } - - pub fn with_iota_chain_id(mut self, chain_id: BridgeChainId) -> Self { - self.iota_chain_id = chain_id; - self - } - - pub fn with_eth_chain_id(mut self, chain_id: BridgeChainId) -> Self { - self.eth_chain_id = chain_id; - self - } - - pub async fn build(self) -> BridgeTestCluster { - init_all_struct_tags(); - std::env::set_var("__TEST_ONLY_CONSENSUS_USE_LONG_MIN_ROUND_DELAY", "1"); - let mut bridge_keys = vec![]; - let mut bridge_keys_copy = vec![]; - for _ in 0..self.num_validators { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - bridge_keys.push(kp.copy()); - bridge_keys_copy.push(kp); - } - let start_cluster_task = - tokio::task::spawn(Self::start_test_cluster(bridge_keys, self.num_validators)); - let start_eth_env_task = tokio::task::spawn(Self::start_eth_env(bridge_keys_copy)); - let (start_cluster_res, start_eth_env_res) = join!(start_cluster_task, start_eth_env_task); - let test_cluster = start_cluster_res.unwrap(); - let eth_environment = start_eth_env_res.unwrap(); - - let mut bridge_node_handles = None; - if self.with_bridge_cluster { - let approved_governace_actions = self - .approved_governance_actions - .clone() - .unwrap_or(vec![vec![]; self.num_validators]); - bridge_node_handles = Some( - start_bridge_cluster(&test_cluster, ð_environment, approved_governace_actions) - .await, - ); - } - let bridge_client = IotaBridgeClient::new(&test_cluster.fullnode_handle.rpc_url) - .await - .unwrap(); - BridgeTestCluster { - num_validators: self.num_validators, - test_cluster, - bridge_client, - eth_environment, - bridge_node_handles, - approved_governance_actions_for_next_start: self.approved_governance_actions, - bridge_tx_cursor: None, - iota_chain_id: self.iota_chain_id, - eth_chain_id: self.eth_chain_id, - } - } - - async fn start_test_cluster( - bridge_keys: Vec, - num_validators: usize, - ) -> TestCluster { - assert_eq!(bridge_keys.len(), num_validators); - let test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_num_validators(num_validators) - .with_protocol_version(BRIDGE_ENABLE_PROTOCOL_VERSION.into()) - .build_with_bridge(bridge_keys, true) - .await; - info!("Test cluster built"); - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; - info!("Bridge committee is finalized"); - test_cluster - } - - async fn start_eth_env(bridge_keys: Vec) -> EthBridgeEnvironment { - let anvil_port = get_available_port("127.0.0.1"); - let anvil_url = format!("http://127.0.0.1:{anvil_port}"); - let mut eth_environment = EthBridgeEnvironment::new(&anvil_url, anvil_port) - .await - .unwrap(); - // Give anvil a bit of time to start - tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; - let (eth_signer, eth_pk_hex) = eth_environment - .get_signer(TEST_PK) - .await - .unwrap_or_else(|e| panic!("Failed to get eth signer from anvil at {anvil_url}: {e}")); - let deployed_contracts = - deploy_sol_contract(&anvil_url, eth_signer, bridge_keys, eth_pk_hex).await; - info!("Deployed contracts: {:?}", deployed_contracts); - eth_environment.contracts = Some(deployed_contracts); - eth_environment - } -} - -impl BridgeTestCluster { - pub async fn get_eth_signer_and_private_key(&self) -> anyhow::Result<(EthSigner, String)> { - self.eth_environment.get_signer(TEST_PK).await - } - - pub async fn get_eth_signer_and_address(&self) -> anyhow::Result<(EthSigner, EthAddress)> { - let (eth_signer, _) = self.get_eth_signer_and_private_key().await?; - let eth_address = eth_signer.address(); - Ok((eth_signer, eth_address)) - } - - pub async fn get_eth_signer(&self) -> EthSigner { - let (eth_signer, _) = self.get_eth_signer_and_private_key().await.unwrap(); - eth_signer - } - - pub fn bridge_client(&self) -> &IotaBridgeClient { - &self.bridge_client - } - - pub fn iota_client(&self) -> &IotaClient { - &self.test_cluster.fullnode_handle.iota_client - } - - pub fn iota_user_address(&self) -> IotaAddress { - self.test_cluster.get_address_0() - } - - pub fn iota_chain_id(&self) -> BridgeChainId { - self.iota_chain_id - } - - pub fn eth_chain_id(&self) -> BridgeChainId { - self.eth_chain_id - } - - pub(crate) fn eth_env(&self) -> &EthBridgeEnvironment { - &self.eth_environment - } - - pub fn contracts(&self) -> &DeployedSolContracts { - self.eth_environment.contracts() - } - - pub fn iota_bridge_address(&self) -> String { - self.eth_environment.contracts().iota_bridge_address_hex() - } - - pub fn wallet_mut(&mut self) -> &mut WalletContext { - self.test_cluster.wallet_mut() - } - - pub fn wallet(&self) -> &WalletContext { - &self.test_cluster.wallet - } - - pub fn bridge_authority_key(&self, index: usize) -> BridgeAuthorityKeyPair { - self.test_cluster.bridge_authority_keys.as_ref().unwrap()[index].copy() - } - - pub fn iota_rpc_url(&self) -> String { - self.test_cluster.fullnode_handle.rpc_url.clone() - } - - pub fn eth_rpc_url(&self) -> String { - self.eth_environment.rpc_url.clone() - } - - pub async fn get_mut_bridge_arg(&self) -> Option { - self.test_cluster.get_mut_bridge_arg().await - } - - pub async fn test_transaction_builder_with_sender( - &self, - sender: IotaAddress, - ) -> TestTransactionBuilder { - self.test_cluster - .test_transaction_builder_with_sender(sender) - .await - } - - pub async fn wait_for_bridge_cluster_to_be_up(&self, timeout_sec: u64) { - self.test_cluster - .wait_for_bridge_cluster_to_be_up(timeout_sec) - .await; - } - - pub async fn sign_and_execute_transaction( - &self, - tx_data: &TransactionData, - ) -> IotaTransactionBlockResponse { - self.test_cluster - .sign_and_execute_transaction(tx_data) - .await - } - - pub fn set_approved_governance_actions_for_next_start( - &mut self, - approved_governance_actions: Vec>, - ) { - assert_eq!(approved_governance_actions.len(), self.num_validators); - self.approved_governance_actions_for_next_start = Some(approved_governance_actions); - } - - pub async fn start_bridge_cluster(&mut self) { - assert!(self.bridge_node_handles.is_none()); - let approved_governace_actions = self - .approved_governance_actions_for_next_start - .clone() - .unwrap_or(vec![vec![], vec![], vec![], vec![]]); - self.bridge_node_handles = Some( - start_bridge_cluster( - &self.test_cluster, - &self.eth_environment, - approved_governace_actions, - ) - .await, - ); - } - - /// Returns new bridge transaction. It advanaces the stored tx digest - /// cursor. When `assert_success` is true, it asserts all transactions - /// are successful. - pub async fn new_bridge_transactions( - &mut self, - assert_success: bool, - ) -> Vec { - let resps = self - .iota_client() - .read_api() - .query_transaction_blocks( - IotaTransactionBlockResponseQuery { - filter: Some(TransactionFilter::InputObject(IOTA_BRIDGE_OBJECT_ID)), - options: Some(IotaTransactionBlockResponseOptions::full_content()), - }, - self.bridge_tx_cursor, - None, - false, - ) - .await - .unwrap(); - self.bridge_tx_cursor = resps.next_cursor; - - for tx in &resps.data { - if assert_success { - assert!(tx.status_ok().unwrap()); - } - let events = &tx.events.as_ref().unwrap().data; - if events - .iter() - .any(|e| &e.type_ == TokenTransferApproved.get().unwrap()) - { - assert!( - events - .iter() - .any(|e| &e.type_ == TokenTransferClaimed.get().unwrap() - || &e.type_ == TokenTransferApproved.get().unwrap()) - ); - } else if events - .iter() - .any(|e| &e.type_ == TokenTransferAlreadyClaimed.get().unwrap()) - { - assert!( - events - .iter() - .all(|e| &e.type_ == TokenTransferAlreadyClaimed.get().unwrap() - || &e.type_ == TokenTransferAlreadyApproved.get().unwrap()) - ); - } - // TODO: check for other events e.g. TokenRegistrationEvent, - // NewTokenEvent etc - } - resps.data - } - - /// Returns events that are emitted in new bridge transaction and match - /// `event_types`. It advanaces the stored tx digest cursor. - /// See `new_bridge_transactions` for `assert_success`. - pub async fn new_bridge_events( - &mut self, - event_types: HashSet, - assert_success: bool, - ) -> Vec { - let txes = self.new_bridge_transactions(assert_success).await; - let events = txes - .iter() - .flat_map(|tx| { - tx.events - .as_ref() - .unwrap() - .data - .iter() - .filter(|e| event_types.contains(&e.type_)) - .cloned() - }) - .collect(); - events - } -} - -pub async fn get_eth_signer_client_e2e_test_only( - eth_rpc_url: &str, -) -> anyhow::Result<(EthSigner, String)> { - // This private key is derived from the default anvil setting. - // Mnemonic: test test test test test test test test test test test - // junk Derivation path: m/44'/60'/0'/0/ - // DO NOT USE IT ANYWHERE ELSE EXCEPT FOR RUNNING AUTOMATIC INTEGRATION TESTING - let url = eth_rpc_url.to_string(); - let private_key_0 = "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356"; - let signer_0 = get_eth_signer_client(&url, private_key_0).await?; - Ok((signer_0, private_key_0.to_string())) -} - -#[derive(Debug, Clone)] -pub struct DeployedSolContracts { - pub iota_bridge: EthAddress, - pub bridge_committee: EthAddress, - pub bridge_limiter: EthAddress, - pub bridge_vault: EthAddress, - pub bridge_config: EthAddress, - pub btc: EthAddress, - pub eth: EthAddress, - pub usdc: EthAddress, - pub usdt: EthAddress, - pub ka: EthAddress, -} - -impl DeployedSolContracts { - pub fn eth_address_to_hex(addr: EthAddress) -> String { - format!("{:x}", addr) - } - - pub fn iota_bridge_address_hex(&self) -> String { - Self::eth_address_to_hex(self.iota_bridge) - } -} - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -struct SolDeployConfig { - committee_member_stake: Vec, - committee_members: Vec, - min_committee_stake_required: u64, - source_chain_id: u64, - supported_chain_ids: Vec, - supported_chain_limits_in_dollars: Vec, - supported_tokens: Vec, - token_prices: Vec, -} - -pub(crate) async fn deploy_sol_contract( - anvil_url: &str, - eth_signer: EthSigner, - bridge_authority_keys: Vec, - eth_private_key_hex: String, -) -> DeployedSolContracts { - let sol_path = format!("{}/../../bridge/evm", env!("CARGO_MANIFEST_DIR")); - - // Write the deploy config to a temp file then provide it to the forge late - let deploy_config_path = tempfile::tempdir() - .unwrap() - .into_path() - .join("sol_deploy_config.json"); - let node_len = bridge_authority_keys.len(); - let stake = TOTAL_VOTING_POWER / (node_len as u64); - let committee_members = bridge_authority_keys - .iter() - .map(|k| { - format!( - "0x{:x}", - BridgeAuthorityPublicKeyBytes::from(&k.public).to_eth_address() - ) - }) - .collect::>(); - let mut committee_member_stake = vec![stake; node_len]; - // Adjust it so that the total stake is equal to TOTAL_VOTING_POWER - committee_member_stake[node_len - 1] = TOTAL_VOTING_POWER - stake * (node_len as u64 - 1); - let deploy_config = SolDeployConfig { - committee_member_stake: committee_member_stake.clone(), - committee_members: committee_members.clone(), - min_committee_stake_required: 10000, - source_chain_id: 12, - supported_chain_ids: vec![1, 2, 3], - supported_chain_limits_in_dollars: vec![ - 1000000000000000, - 1000000000000000, - 1000000000000000, - ], - supported_tokens: vec![], // this is set up in the deploy script - token_prices: vec![12800, 432518900, 25969600, 10000, 10000], - }; - - let serialized_config = serde_json::to_string_pretty(&deploy_config).unwrap(); - tracing::debug!( - "Serialized config written to {:?}: {:?}", - deploy_config_path, - serialized_config - ); - let mut file = File::create(deploy_config_path.clone()).unwrap(); - file.write_all(serialized_config.as_bytes()).unwrap(); - - // override for the deploy script - std::env::set_var("OVERRIDE_CONFIG_PATH", deploy_config_path.to_str().unwrap()); - std::env::set_var("PRIVATE_KEY", eth_private_key_hex); - std::env::set_var("ETHERSCAN_API_KEY", "n/a"); - - // We provide a unique out path for each run to avoid conflicts - let mut rng = SmallRng::from_entropy(); - let random_number = rng.gen::(); - let forge_out_path = PathBuf::from(format!("out-{random_number}")); - let _dir = TempDir::new( - PathBuf::from(sol_path.clone()) - .join(forge_out_path.clone()) - .as_path(), - ) - .unwrap(); - std::env::set_var("FOUNDRY_OUT", forge_out_path.to_str().unwrap()); - - info!("Deploying solidity contracts"); - Command::new("forge") - .current_dir(sol_path.clone()) - .arg("clean") - .status() - .expect("Failed to execute `forge clean`"); - - let mut child = Command::new("forge") - .current_dir(sol_path) - .arg("script") - .arg("script/deploy_bridge.s.sol") - .arg("--fork-url") - .arg(anvil_url) - .arg("--broadcast") - .arg("--ffi") - .arg("--chain") - .arg("31337") - .stdout(std::process::Stdio::piped()) // Capture stdout - .stderr(std::process::Stdio::piped()) // Capture stderr - .spawn() - .unwrap(); - - let mut stdout = child.stdout.take().expect("Failed to open stdout"); - let mut stderr = child.stderr.take().expect("Failed to open stderr"); - - // Read stdout/stderr to String - let mut s = String::new(); - stdout.read_to_string(&mut s).unwrap(); - let mut e = String::new(); - stderr.read_to_string(&mut e).unwrap(); - - // Wait for the child process to finish and collect its status - let status = child.wait().unwrap(); - if status.success() { - info!("Solidity contract deployment finished successfully"); - } else { - error!( - "Solidity contract deployment exited with code: {:?}", - status.code() - ); - } - println!("Stdout: {}", s); - println!("Stdout: {}", e); - - let mut deployed_contracts = BTreeMap::new(); - // Process the stdout to parse contract addresses - for line in s.lines() { - if line.contains("[Deployed]") { - let replaced_line = line.replace("[Deployed]", ""); - let trimmed_line = replaced_line.trim(); - let parts: Vec<&str> = trimmed_line.split(':').collect(); - if parts.len() == 2 { - let contract_name = parts[0].to_string().trim().to_string(); - let contract_address = EthAddress::from_str(parts[1].to_string().trim()).unwrap(); - deployed_contracts.insert(contract_name, contract_address); - } - } - } - - let contracts = DeployedSolContracts { - iota_bridge: deployed_contracts.remove(IOTA_BRIDGE_NAME).unwrap(), - bridge_committee: deployed_contracts.remove(BRIDGE_COMMITTEE_NAME).unwrap(), - bridge_config: deployed_contracts.remove(BRIDGE_CONFIG_NAME).unwrap(), - bridge_limiter: deployed_contracts.remove(BRIDGE_LIMITER_NAME).unwrap(), - bridge_vault: deployed_contracts.remove(BRIDGE_VAULT_NAME).unwrap(), - btc: deployed_contracts.remove(BTC_NAME).unwrap(), - eth: deployed_contracts.remove(ETH_NAME).unwrap(), - usdc: deployed_contracts.remove(USDC_NAME).unwrap(), - usdt: deployed_contracts.remove(USDT_NAME).unwrap(), - ka: deployed_contracts.remove(KA_NAME).unwrap(), - }; - let eth_bridge_committee = - EthBridgeCommittee::new(contracts.bridge_committee, eth_signer.clone().into()); - for (i, (m, s)) in committee_members - .iter() - .zip(committee_member_stake.iter()) - .enumerate() - { - let eth_address = EthAddress::from_str(m).unwrap(); - assert_eq!( - eth_bridge_committee - .committee_index(eth_address) - .await - .unwrap(), - i as u8 - ); - assert_eq!( - eth_bridge_committee - .committee_stake(eth_address) - .await - .unwrap(), - *s as u16 - ); - assert!(!eth_bridge_committee.blocklist(eth_address).await.unwrap()); - } - contracts -} - -#[derive(Debug)] -pub struct EthBridgeEnvironment { - pub rpc_url: String, - process: Child, - contracts: Option, -} - -impl EthBridgeEnvironment { - async fn new(anvil_url: &str, anvil_port: u16) -> anyhow::Result { - // Start eth node with anvil - let eth_environment_process = std::process::Command::new("anvil") - .arg("--port") - .arg(anvil_port.to_string()) - .arg("--block-time") - .arg("1") // 1 second block time - .arg("--slots-in-an-epoch") - .arg("3") // 3 slots in an epoch - .spawn() - .expect("Failed to start anvil"); - - Ok(EthBridgeEnvironment { - rpc_url: anvil_url.to_string(), - process: eth_environment_process, - contracts: None, - }) - } - - pub(crate) async fn get_signer( - &self, - private_key: &str, - ) -> anyhow::Result<(EthSigner, String)> { - let signer = get_eth_signer_client(&self.rpc_url, private_key).await?; - Ok((signer, private_key.to_string())) - } - - pub(crate) fn contracts(&self) -> &DeployedSolContracts { - self.contracts.as_ref().unwrap() - } - - pub(crate) fn get_bridge_config( - &self, - ) -> EthBridgeConfig> { - let provider = Arc::new( - ethers::prelude::Provider::::try_from(&self.rpc_url) - .unwrap() - .interval(std::time::Duration::from_millis(2000)), - ); - EthBridgeConfig::new(self.contracts().bridge_config, provider.clone()) - } - - pub(crate) async fn get_supported_token(&self, token_id: u8) -> (EthAddress, u8, u64) { - let config = self.get_bridge_config(); - let token_address = config.token_address_of(token_id).call().await.unwrap(); - let token_iota_decimal = config.token_iota_decimal_of(token_id).call().await.unwrap(); - let token_price = config.token_price_of(token_id).call().await.unwrap(); - (token_address, token_iota_decimal, token_price) - } -} - -impl Drop for EthBridgeEnvironment { - fn drop(&mut self) { - self.process.kill().unwrap(); - } -} - -pub(crate) async fn start_bridge_cluster( - test_cluster: &TestCluster, - eth_environment: &EthBridgeEnvironment, - approved_governance_actions: Vec>, -) -> Vec> { - let bridge_authority_keys = test_cluster - .bridge_authority_keys - .as_ref() - .unwrap() - .iter() - .map(|k| k.copy()) - .collect::>(); - let bridge_server_ports = test_cluster.bridge_server_ports.as_ref().unwrap(); - assert_eq!(bridge_authority_keys.len(), bridge_server_ports.len()); - assert_eq!( - bridge_authority_keys.len(), - approved_governance_actions.len() - ); - - let eth_bridge_contract_address = eth_environment - .contracts - .as_ref() - .unwrap() - .iota_bridge_address_hex(); - - let mut handles = vec![]; - for (i, ((kp, server_listen_port), approved_governance_actions)) in bridge_authority_keys - .iter() - .zip(bridge_server_ports.iter()) - .zip(approved_governance_actions.into_iter()) - .enumerate() - { - // prepare node config (server + client) - let tmp_dir = tempdir().unwrap().into_path().join(i.to_string()); - std::fs::create_dir_all(tmp_dir.clone()).unwrap(); - let db_path = tmp_dir.join("client_db"); - // write authority key to file - let authority_key_path = tmp_dir.join("bridge_authority_key"); - let base64_encoded = kp.encode_base64(); - std::fs::write(authority_key_path.clone(), base64_encoded).unwrap(); - - let config = BridgeNodeConfig { - server_listen_port: *server_listen_port, - metrics_port: get_available_port("127.0.0.1"), - bridge_authority_key_path: authority_key_path, - approved_governance_actions, - run_client: true, - db_path: Some(db_path), - eth: EthConfig { - eth_rpc_url: eth_environment.rpc_url.clone(), - eth_bridge_proxy_address: eth_bridge_contract_address.clone(), - eth_bridge_chain_id: BridgeChainId::EthCustom as u8, - eth_contracts_start_block_fallback: Some(0), - eth_contracts_start_block_override: None, - }, - iota: IotaConfig { - iota_rpc_url: test_cluster.fullnode_handle.rpc_url.clone(), - iota_bridge_chain_id: BridgeChainId::IotaCustom as u8, - bridge_client_key_path: None, - bridge_client_gas_object: None, - iota_bridge_module_last_processed_event_id_override: None, - }, - }; - // Spawn bridge node in memory - let config_clone = config.clone(); - handles.push( - run_bridge_node( - config_clone, - BridgeNodePublicMetadata::empty_for_testing(), - Registry::new(), - ) - .await - .unwrap(), - ); - } - handles -} - -pub(crate) async fn get_signatures( - iota_bridge_client: &IotaBridgeClient, - nonce: u64, - iota_chain_id: u8, -) -> Vec { - let sigs = iota_bridge_client - .get_token_transfer_action_onchain_signatures_until_success(iota_chain_id, nonce) - .await - .unwrap(); - - sigs.into_iter() - .map(|sig: Vec| Bytes::from(sig)) - .collect() -} - -pub(crate) async fn send_eth_tx_and_get_tx_receipt( - call: FunctionCall, -) -> TransactionReceipt -where - M: Middleware, - B: std::borrow::Borrow, - D: ethers::abi::Detokenize, -{ - call.send().await.unwrap().await.unwrap().unwrap() -} - -/// A simple struct to create a temporary directory that -/// will be removed when it goes out of scope. -struct TempDir { - path: PathBuf, -} - -impl TempDir { - fn new(dir_path: &Path) -> std::io::Result { - DirBuilder::new().recursive(true).create(dir_path)?; - Ok(TempDir { - path: dir_path.to_path_buf(), - }) - } -} - -impl Drop for TempDir { - fn drop(&mut self) { - fs::remove_dir_all(&self.path).unwrap(); - } -} diff --git a/crates/iota-bridge/src/encoding.rs b/crates/iota-bridge/src/encoding.rs deleted file mode 100644 index 8d6c640bcb1..00000000000 --- a/crates/iota-bridge/src/encoding.rs +++ /dev/null @@ -1,1013 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use enum_dispatch::enum_dispatch; -use ethers::types::Address as EthAddress; -use iota_types::base_types::IOTA_ADDRESS_LENGTH; - -use crate::types::{ - AddTokensOnEvmAction, AddTokensOnIotaAction, AssetPriceUpdateAction, BlocklistCommitteeAction, - BridgeAction, BridgeActionType, EmergencyAction, EthToIotaBridgeAction, - EvmContractUpgradeAction, IotaToEthBridgeAction, LimitUpdateAction, -}; - -pub const TOKEN_TRANSFER_MESSAGE_VERSION: u8 = 1; -pub const COMMITTEE_BLOCKLIST_MESSAGE_VERSION: u8 = 1; -pub const EMERGENCY_BUTTON_MESSAGE_VERSION: u8 = 1; -pub const LIMIT_UPDATE_MESSAGE_VERSION: u8 = 1; -pub const ASSET_PRICE_UPDATE_MESSAGE_VERSION: u8 = 1; -pub const EVM_CONTRACT_UPGRADE_MESSAGE_VERSION: u8 = 1; -pub const ADD_TOKENS_ON_IOTA_MESSAGE_VERSION: u8 = 1; -pub const ADD_TOKENS_ON_EVM_MESSAGE_VERSION: u8 = 1; - -pub const BRIDGE_MESSAGE_PREFIX: &[u8] = b"IOTA_BRIDGE_MESSAGE"; - -/// Encoded bridge message consists of the following fields: -/// 1. Message type (1 byte) -/// 2. Message version (1 byte) -/// 3. Nonce (8 bytes in big endian) -/// 4. Chain id (1 byte) -/// 4. Payload (variable length) -#[enum_dispatch] -pub trait BridgeMessageEncoding { - /// Convert the entire message to bytes - fn as_bytes(&self) -> Vec; - /// Convert the payload piece to bytes - fn as_payload_bytes(&self) -> Vec; -} - -impl BridgeMessageEncoding for IotaToEthBridgeAction { - fn as_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - let e = &self.iota_bridge_event; - // Add message type - bytes.push(BridgeActionType::TokenTransfer as u8); - // Add message version - bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION); - // Add nonce - bytes.extend_from_slice(&e.nonce.to_be_bytes()); - // Add source chain id - bytes.push(e.iota_chain_id as u8); - - // Add payload bytes - bytes.extend_from_slice(&self.as_payload_bytes()); - - bytes - } - - fn as_payload_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - let e = &self.iota_bridge_event; - - // Add source address length - bytes.push(IOTA_ADDRESS_LENGTH as u8); - // Add source address - bytes.extend_from_slice(&e.iota_address.to_vec()); - // Add dest chain id - bytes.push(e.eth_chain_id as u8); - // Add dest address length - bytes.push(EthAddress::len_bytes() as u8); - // Add dest address - bytes.extend_from_slice(e.eth_address.as_bytes()); - - // Add token id - bytes.push(e.token_id); - - // Add token amount - bytes.extend_from_slice(&e.amount_iota_adjusted.to_be_bytes()); - - bytes - } -} - -impl BridgeMessageEncoding for EthToIotaBridgeAction { - fn as_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - let e = &self.eth_bridge_event; - // Add message type - bytes.push(BridgeActionType::TokenTransfer as u8); - // Add message version - bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION); - // Add nonce - bytes.extend_from_slice(&e.nonce.to_be_bytes()); - // Add source chain id - bytes.push(e.eth_chain_id as u8); - - // Add payload bytes - bytes.extend_from_slice(&self.as_payload_bytes()); - - bytes - } - - fn as_payload_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - let e = &self.eth_bridge_event; - - // Add source address length - bytes.push(EthAddress::len_bytes() as u8); - // Add source address - bytes.extend_from_slice(e.eth_address.as_bytes()); - // Add dest chain id - bytes.push(e.iota_chain_id as u8); - // Add dest address length - bytes.push(IOTA_ADDRESS_LENGTH as u8); - // Add dest address - bytes.extend_from_slice(&e.iota_address.to_vec()); - - // Add token id - bytes.push(e.token_id); - - // Add token amount - bytes.extend_from_slice(&e.iota_adjusted_amount.to_be_bytes()); - - bytes - } -} - -impl BridgeMessageEncoding for BlocklistCommitteeAction { - fn as_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add message type - bytes.push(BridgeActionType::UpdateCommitteeBlocklist as u8); - // Add message version - bytes.push(COMMITTEE_BLOCKLIST_MESSAGE_VERSION); - // Add nonce - bytes.extend_from_slice(&self.nonce.to_be_bytes()); - // Add chain id - bytes.push(self.chain_id as u8); - - // Add payload bytes - bytes.extend_from_slice(&self.as_payload_bytes()); - - bytes - } - - fn as_payload_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - - // Add blocklist type - bytes.push(self.blocklist_type as u8); - // Add length of updated members. - // Unwrap: It should not overflow given what we have today. - bytes.push(u8::try_from(self.members_to_update.len()).unwrap()); - - // Add list of updated members - // Members are represented as pubkey derived evm addresses (20 bytes) - let members_bytes = self - .members_to_update - .iter() - .map(|m| m.to_eth_address().to_fixed_bytes().to_vec()) - .collect::>(); - for members_bytes in members_bytes { - bytes.extend_from_slice(&members_bytes); - } - - bytes - } -} - -impl BridgeMessageEncoding for EmergencyAction { - fn as_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add message type - bytes.push(BridgeActionType::EmergencyButton as u8); - // Add message version - bytes.push(EMERGENCY_BUTTON_MESSAGE_VERSION); - // Add nonce - bytes.extend_from_slice(&self.nonce.to_be_bytes()); - // Add chain id - bytes.push(self.chain_id as u8); - - // Add payload bytes - bytes.extend_from_slice(&self.as_payload_bytes()); - - bytes - } - - fn as_payload_bytes(&self) -> Vec { - vec![self.action_type as u8] - } -} - -impl BridgeMessageEncoding for LimitUpdateAction { - fn as_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add message type - bytes.push(BridgeActionType::LimitUpdate as u8); - // Add message version - bytes.push(LIMIT_UPDATE_MESSAGE_VERSION); - // Add nonce - bytes.extend_from_slice(&self.nonce.to_be_bytes()); - // Add chain id - bytes.push(self.chain_id as u8); - - // Add payload bytes - bytes.extend_from_slice(&self.as_payload_bytes()); - - bytes - } - - fn as_payload_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add sending chain id - bytes.push(self.sending_chain_id as u8); - // Add new usd limit - bytes.extend_from_slice(&self.new_usd_limit.to_be_bytes()); - bytes - } -} - -impl BridgeMessageEncoding for AssetPriceUpdateAction { - fn as_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add message type - bytes.push(BridgeActionType::AssetPriceUpdate as u8); - // Add message version - bytes.push(EMERGENCY_BUTTON_MESSAGE_VERSION); - // Add nonce - bytes.extend_from_slice(&self.nonce.to_be_bytes()); - // Add chain id - bytes.push(self.chain_id as u8); - - // Add payload bytes - bytes.extend_from_slice(&self.as_payload_bytes()); - - bytes - } - - fn as_payload_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add token id - bytes.push(self.token_id); - // Add new usd limit - bytes.extend_from_slice(&self.new_usd_price.to_be_bytes()); - bytes - } -} - -impl BridgeMessageEncoding for EvmContractUpgradeAction { - fn as_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add message type - bytes.push(BridgeActionType::EvmContractUpgrade as u8); - // Add message version - bytes.push(EVM_CONTRACT_UPGRADE_MESSAGE_VERSION); - // Add nonce - bytes.extend_from_slice(&self.nonce.to_be_bytes()); - // Add chain id - bytes.push(self.chain_id as u8); - - // Add payload bytes - bytes.extend_from_slice(&self.as_payload_bytes()); - - bytes - } - - fn as_payload_bytes(&self) -> Vec { - ethers::abi::encode(&[ - ethers::abi::Token::Address(self.proxy_address), - ethers::abi::Token::Address(self.new_impl_address), - ethers::abi::Token::Bytes(self.call_data.clone()), - ]) - } -} - -impl BridgeMessageEncoding for AddTokensOnIotaAction { - fn as_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add message type - bytes.push(BridgeActionType::AddTokensOnIota as u8); - // Add message version - bytes.push(ADD_TOKENS_ON_IOTA_MESSAGE_VERSION); - // Add nonce - bytes.extend_from_slice(&self.nonce.to_be_bytes()); - // Add chain id - bytes.push(self.chain_id as u8); - - // Add payload bytes - bytes.extend_from_slice(&self.as_payload_bytes()); - - bytes - } - - fn as_payload_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add native - bytes.push(self.native as u8); - // Add token ids - // Unwrap: bcs serialization should not fail - bytes.extend_from_slice(&bcs::to_bytes(&self.token_ids).unwrap()); - - // Add token type names - // Unwrap: bcs serialization should not fail - bytes.extend_from_slice( - &bcs::to_bytes( - &self - .token_type_names - .iter() - .map(|m| m.to_canonical_string(false)) - .collect::>(), - ) - .unwrap(), - ); - - // Add token prices - // Unwrap: bcs serialization should not fail - bytes.extend_from_slice(&bcs::to_bytes(&self.token_prices).unwrap()); - - bytes - } -} - -impl BridgeMessageEncoding for AddTokensOnEvmAction { - fn as_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add message type - bytes.push(BridgeActionType::AddTokensOnEvm as u8); - // Add message version - bytes.push(ADD_TOKENS_ON_EVM_MESSAGE_VERSION); - // Add nonce - bytes.extend_from_slice(&self.nonce.to_be_bytes()); - // Add chain id - bytes.push(self.chain_id as u8); - - // Add payload bytes - bytes.extend_from_slice(&self.as_payload_bytes()); - - bytes - } - - fn as_payload_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add native - bytes.push(self.native as u8); - // Add token ids - // Unwrap: bcs serialization should not fail - bytes.push(u8::try_from(self.token_ids.len()).unwrap()); - for token_id in &self.token_ids { - bytes.push(*token_id); - } - - // Add token addresses - // Unwrap: bcs serialization should not fail - bytes.push(u8::try_from(self.token_addresses.len()).unwrap()); - for token_address in &self.token_addresses { - bytes.extend_from_slice(&token_address.to_fixed_bytes()); - } - - // Add token iota decimals - // Unwrap: bcs serialization should not fail - bytes.push(u8::try_from(self.token_iota_decimals.len()).unwrap()); - for token_iota_decimal in &self.token_iota_decimals { - bytes.push(*token_iota_decimal); - } - - // Add token prices - // Unwrap: bcs serialization should not fail - bytes.push(u8::try_from(self.token_prices.len()).unwrap()); - for token_price in &self.token_prices { - bytes.extend_from_slice(&token_price.to_be_bytes()); - } - bytes - } -} - -impl BridgeAction { - /// Convert to message bytes to verify in Move and Solidity - pub fn to_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - // Add prefix - bytes.extend_from_slice(BRIDGE_MESSAGE_PREFIX); - // Add bytes from message itself - bytes.extend_from_slice(&self.as_bytes()); - bytes - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use ethers::{ - abi::ParamType, - types::{Address as EthAddress, TxHash}, - }; - use fastcrypto::{ - encoding::{Encoding, Hex}, - hash::{HashFunction, Keccak256}, - traits::ToFromBytes, - }; - use iota_types::{ - TypeTag, - base_types::{IotaAddress, TransactionDigest}, - bridge::{BridgeChainId, TOKEN_ID_BTC, TOKEN_ID_USDC}, - }; - use prometheus::Registry; - - use super::*; - use crate::{ - abi::EthToIotaTokenBridgeV1, - crypto::{BridgeAuthorityKeyPair, BridgeAuthorityPublicKeyBytes, BridgeAuthoritySignInfo}, - events::EmittedIotaToEthTokenBridgeV1, - types::{BlocklistType, EmergencyActionType, USD_MULTIPLIER}, - }; - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - let nonce = 54321u64; - let iota_tx_digest = TransactionDigest::random(); - let iota_chain_id = BridgeChainId::IotaTestnet; - let iota_tx_event_index = 1u16; - let eth_chain_id = BridgeChainId::EthSepolia; - let iota_address = IotaAddress::random_for_testing_only(); - let eth_address = EthAddress::random(); - let token_id = TOKEN_ID_USDC; - let amount_iota_adjusted = 1_000_000; - - let iota_bridge_event = EmittedIotaToEthTokenBridgeV1 { - nonce, - iota_chain_id, - eth_chain_id, - iota_address, - eth_address, - token_id, - amount_iota_adjusted, - }; - - let encoded_bytes = BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction { - iota_tx_digest, - iota_tx_event_index, - iota_bridge_event, - }) - .to_bytes(); - - // Construct the expected bytes - let prefix_bytes = BRIDGE_MESSAGE_PREFIX.to_vec(); // len: 18 - let message_type = vec![BridgeActionType::TokenTransfer as u8]; // len: 1 - let message_version = vec![TOKEN_TRANSFER_MESSAGE_VERSION]; // len: 1 - let nonce_bytes = nonce.to_be_bytes().to_vec(); // len: 8 - let source_chain_id_bytes = vec![iota_chain_id as u8]; // len: 1 - - let iota_address_length_bytes = vec![IOTA_ADDRESS_LENGTH as u8]; // len: 1 - let iota_address_bytes = iota_address.to_vec(); // len: 32 - let dest_chain_id_bytes = vec![eth_chain_id as u8]; // len: 1 - let eth_address_length_bytes = vec![EthAddress::len_bytes() as u8]; // len: 1 - let eth_address_bytes = eth_address.as_bytes().to_vec(); // len: 20 - - let token_id_bytes = vec![token_id]; // len: 1 - let token_amount_bytes = amount_iota_adjusted.to_be_bytes().to_vec(); // len: 8 - - let mut combined_bytes = Vec::new(); - combined_bytes.extend_from_slice(&prefix_bytes); - combined_bytes.extend_from_slice(&message_type); - combined_bytes.extend_from_slice(&message_version); - combined_bytes.extend_from_slice(&nonce_bytes); - combined_bytes.extend_from_slice(&source_chain_id_bytes); - combined_bytes.extend_from_slice(&iota_address_length_bytes); - combined_bytes.extend_from_slice(&iota_address_bytes); - combined_bytes.extend_from_slice(&dest_chain_id_bytes); - combined_bytes.extend_from_slice(ð_address_length_bytes); - combined_bytes.extend_from_slice(ð_address_bytes); - combined_bytes.extend_from_slice(&token_id_bytes); - combined_bytes.extend_from_slice(&token_amount_bytes); - - assert_eq!(combined_bytes, encoded_bytes); - - // Assert fixed length - // TODO: for each action type add a test to assert the length - assert_eq!( - combined_bytes.len(), - 18 + 1 + 1 + 8 + 1 + 1 + 32 + 1 + 20 + 1 + 1 + 8 - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding_regression_emitted_iota_to_eth_token_bridge_v1() - -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - let iota_tx_digest = TransactionDigest::random(); - let iota_tx_event_index = 1u16; - - let nonce = 10u64; - let iota_chain_id = BridgeChainId::IotaTestnet; - let eth_chain_id = BridgeChainId::EthSepolia; - let iota_address = IotaAddress::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000064", - ) - .unwrap(); - let eth_address = - EthAddress::from_str("0x00000000000000000000000000000000000000c8").unwrap(); - let token_id = TOKEN_ID_USDC; - let amount_iota_adjusted = 12345; - - let iota_bridge_event = EmittedIotaToEthTokenBridgeV1 { - nonce, - iota_chain_id, - eth_chain_id, - iota_address, - eth_address, - token_id, - amount_iota_adjusted, - }; - let encoded_bytes = BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction { - iota_tx_digest, - iota_tx_event_index, - iota_bridge_event, - }) - .to_bytes(); - assert_eq!( - encoded_bytes, - Hex::decode("5355495f4252494447455f4d4553534147450001000000000000000a012000000000000000000000000000000000000000000000000000000000000000640b1400000000000000000000000000000000000000c8030000000000003039").unwrap(), - ); - - let hash = Keccak256::digest(encoded_bytes).digest; - assert_eq!( - hash.to_vec(), - Hex::decode("6ab34c52b6264cbc12fe8c3874f9b08f8481d2e81530d136386646dbe2f8baf4") - .unwrap(), - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding_blocklist_update_v1() { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - - let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes( - &Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4") - .unwrap(), - ) - .unwrap(); - let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - nonce: 129, - chain_id: BridgeChainId::IotaCustom, - blocklist_type: BlocklistType::Blocklist, - members_to_update: vec![pub_key_bytes.clone()], - }); - let bytes = blocklist_action.to_bytes(); - // 5355495f4252494447455f4d455353414745: prefix - // 01: msg type - // 01: msg version - // 0000000000000081: nonce - // 03: chain id - // 00: blocklist type - // 01: length of updated members - // [ - // 68b43fd906c0b8f024a18c56e06744f7c6157c65 - // ]: blocklisted members abi-encoded - assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000008102000168b43fd906c0b8f024a18c56e06744f7c6157c65").unwrap()); - - let pub_key_bytes_2 = BridgeAuthorityPublicKeyBytes::from_bytes( - &Hex::decode("027f1178ff417fc9f5b8290bd8876f0a157a505a6c52db100a8492203ddd1d4279") - .unwrap(), - ) - .unwrap(); - // its evm address: 0xacaef39832cb995c4e049437a3e2ec6a7bad1ab5 - let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - nonce: 68, - chain_id: BridgeChainId::IotaCustom, - blocklist_type: BlocklistType::Unblocklist, - members_to_update: vec![pub_key_bytes.clone(), pub_key_bytes_2.clone()], - }); - let bytes = blocklist_action.to_bytes(); - // 5355495f4252494447455f4d455353414745: prefix - // 01: msg type - // 01: msg version - // 0000000000000044: nonce - // 02: chain id - // 01: blocklist type - // 02: length of updated members - // [ - // 68b43fd906c0b8f024a18c56e06744f7c6157c65 - // acaef39832cb995c4e049437a3e2ec6a7bad1ab5 - // ]: blocklisted members abi-encoded - assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000004402010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5").unwrap()); - - let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - nonce: 49, - chain_id: BridgeChainId::EthCustom, - blocklist_type: BlocklistType::Blocklist, - members_to_update: vec![pub_key_bytes.clone()], - }); - let bytes = blocklist_action.to_bytes(); - // 5355495f4252494447455f4d455353414745: prefix - // 01: msg type - // 01: msg version - // 0000000000000031: nonce - // 0c: chain id - // 00: blocklist type - // 01: length of updated members - // [ - // 68b43fd906c0b8f024a18c56e06744f7c6157c65 - // ]: blocklisted members abi-encoded - assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d455353414745010100000000000000310c000168b43fd906c0b8f024a18c56e06744f7c6157c65").unwrap()); - - let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - nonce: 94, - chain_id: BridgeChainId::EthSepolia, - blocklist_type: BlocklistType::Unblocklist, - members_to_update: vec![pub_key_bytes.clone(), pub_key_bytes_2.clone()], - }); - let bytes = blocklist_action.to_bytes(); - // 5355495f4252494447455f4d455353414745: prefix - // 01: msg type - // 01: msg version - // 000000000000005e: nonce - // 0b: chain id - // 01: blocklist type - // 02: length of updated members - // [ - // 00000000000000000000000068b43fd906c0b8f024a18c56e06744f7c6157c65 - // 000000000000000000000000acaef39832cb995c4e049437a3e2ec6a7bad1ab5 - // ]: blocklisted members abi-encoded - assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000005e0b010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5").unwrap()); - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding_emergency_action() { - let action = BridgeAction::EmergencyAction(EmergencyAction { - nonce: 55, - chain_id: BridgeChainId::IotaCustom, - action_type: EmergencyActionType::Pause, - }); - let bytes = action.to_bytes(); - // 5355495f4252494447455f4d455353414745: prefix - // 02: msg type - // 01: msg version - // 0000000000000037: nonce - // 03: chain id - // 00: action type - assert_eq!( - bytes, - Hex::decode("5355495f4252494447455f4d455353414745020100000000000000370200").unwrap() - ); - - let action = BridgeAction::EmergencyAction(EmergencyAction { - nonce: 56, - chain_id: BridgeChainId::EthSepolia, - action_type: EmergencyActionType::Unpause, - }); - let bytes = action.to_bytes(); - // 5355495f4252494447455f4d455353414745: prefix - // 02: msg type - // 01: msg version - // 0000000000000038: nonce - // 0b: chain id - // 01: action type - assert_eq!( - bytes, - Hex::decode("5355495f4252494447455f4d455353414745020100000000000000380b01").unwrap() - ); - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding_limit_update_action() { - let action = BridgeAction::LimitUpdateAction(LimitUpdateAction { - nonce: 15, - chain_id: BridgeChainId::IotaCustom, - sending_chain_id: BridgeChainId::EthCustom, - new_usd_limit: 1_000_000 * USD_MULTIPLIER, // $1M USD - }); - let bytes = action.to_bytes(); - // 5355495f4252494447455f4d455353414745: prefix - // 03: msg type - // 01: msg version - // 000000000000000f: nonce - // 03: chain id - // 0c: sending chain id - // 00000002540be400: new usd limit - assert_eq!( - bytes, - Hex::decode( - "5355495f4252494447455f4d4553534147450301000000000000000f020c00000002540be400" - ) - .unwrap() - ); - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding_asset_price_update_action() { - let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction { - nonce: 266, - chain_id: BridgeChainId::IotaCustom, - token_id: TOKEN_ID_BTC, - new_usd_price: 100_000 * USD_MULTIPLIER, // $100k USD - }); - let bytes = action.to_bytes(); - // 5355495f4252494447455f4d455353414745: prefix - // 04: msg type - // 01: msg version - // 000000000000010a: nonce - // 03: chain id - // 01: token id - // 000000003b9aca00: new usd price - assert_eq!( - bytes, - Hex::decode( - "5355495f4252494447455f4d4553534147450401000000000000010a0201000000003b9aca00" - ) - .unwrap() - ); - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding_evm_contract_upgrade_action() { - // Calldata with only the function selector and no parameters: `function - // initializeV2()` - let function_signature = "initializeV2()"; - let selector = &Keccak256::digest(function_signature).digest[0..4]; - let call_data = selector.to_vec(); - assert_eq!(Hex::encode(call_data.clone()), "5cd8a76b"); - - let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data, - }); - // 5355495f4252494447455f4d455353414745: prefix - // 05: msg type - // 01: msg version - // 000000000000007b: nonce - // 0c: chain id - // 0000000000000000000000000606060606060606060606060606060606060606: proxy - // address - // 0000000000000000000000000909090909090909090909090909090909090909: new impl - // address - // - // 0000000000000000000000000000000000000000000000000000000000000060 - // 0000000000000000000000000000000000000000000000000000000000000004 - // 5cd8a76b00000000000000000000000000000000000000000000000000000000: call data - assert_eq!( - Hex::encode(action.to_bytes().clone()), - "5355495f4252494447455f4d4553534147450501000000000000007b0c00000000000000000000000006060606060606060606060606060606060606060000000000000000000000000909090909090909090909090909090909090909000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000045cd8a76b00000000000000000000000000000000000000000000000000000000" - ); - - // Calldata with one parameter: `function newMockFunction(bool)` - let function_signature = "newMockFunction(bool)"; - let selector = &Keccak256::digest(function_signature).digest[0..4]; - let mut call_data = selector.to_vec(); - call_data.extend(ethers::abi::encode(&[ethers::abi::Token::Bool(true)])); - assert_eq!( - Hex::encode(call_data.clone()), - "417795ef0000000000000000000000000000000000000000000000000000000000000001" - ); - let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data, - }); - // 5355495f4252494447455f4d455353414745: prefix - // 05: msg type - // 01: msg version - // 000000000000007b: nonce - // 0c: chain id - // 0000000000000000000000000606060606060606060606060606060606060606: proxy - // address - // 0000000000000000000000000909090909090909090909090909090909090909: new impl - // address - // - // 0000000000000000000000000000000000000000000000000000000000000060 - // 0000000000000000000000000000000000000000000000000000000000000024 - // 417795ef00000000000000000000000000000000000000000000000000000000 - // 0000000100000000000000000000000000000000000000000000000000000000: call data - assert_eq!( - Hex::encode(action.to_bytes().clone()), - "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024417795ef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000" - ); - - // Calldata with two parameters: `function newerMockFunction(bool, uint8)` - let function_signature = "newMockFunction(bool,uint8)"; - let selector = &Keccak256::digest(function_signature).digest[0..4]; - let mut call_data = selector.to_vec(); - call_data.extend(ethers::abi::encode(&[ - ethers::abi::Token::Bool(true), - ethers::abi::Token::Uint(42u8.into()), - ])); - assert_eq!( - Hex::encode(call_data.clone()), - "be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a" - ); - let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data, - }); - // 5355495f4252494447455f4d455353414745: prefix - // 05: msg type - // 01: msg version - // 000000000000007b: nonce - // 0c: chain id - // 0000000000000000000000000606060606060606060606060606060606060606: proxy - // address - // 0000000000000000000000000909090909090909090909090909090909090909: new impl - // address - // - // 0000000000000000000000000000000000000000000000000000000000000060 - // 0000000000000000000000000000000000000000000000000000000000000044 - // be8fc25d00000000000000000000000000000000000000000000000000000000 - // 0000000100000000000000000000000000000000000000000000000000000000 - // 0000002a00000000000000000000000000000000000000000000000000000000: call data - assert_eq!( - Hex::encode(action.to_bytes().clone()), - "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000" - ); - - // Empty calldate - let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data: vec![], - }); - // 5355495f4252494447455f4d455353414745: prefix - // 05: msg type - // 01: msg version - // 000000000000007b: nonce - // 0c: chain id - // 0000000000000000000000000606060606060606060606060606060606060606: proxy - // address - // 0000000000000000000000000909090909090909090909090909090909090909: new impl - // address - // - // 0000000000000000000000000000000000000000000000000000000000000060 - // 0000000000000000000000000000000000000000000000000000000000000000: call data - let data = action.to_bytes(); - assert_eq!( - Hex::encode(data.clone()), - "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - ); - let types = vec![ParamType::Address, ParamType::Address, ParamType::Bytes]; - // Ensure that the call data (start from bytes 29) can be decoded - ethers::abi::decode(&types, &data[29..]).unwrap(); - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding_regression_eth_to_iota_token_bridge_v1() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - let eth_tx_hash = TxHash::random(); - let eth_event_index = 1u16; - - let nonce = 10u64; - let iota_chain_id = BridgeChainId::IotaTestnet; - let eth_chain_id = BridgeChainId::EthSepolia; - let iota_address = IotaAddress::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000064", - ) - .unwrap(); - let eth_address = - EthAddress::from_str("0x00000000000000000000000000000000000000c8").unwrap(); - let token_id = TOKEN_ID_USDC; - let iota_adjusted_amount = 12345; - - let eth_bridge_event = EthToIotaTokenBridgeV1 { - nonce, - iota_chain_id, - eth_chain_id, - iota_address, - eth_address, - token_id, - iota_adjusted_amount, - }; - let encoded_bytes = BridgeAction::EthToIotaBridgeAction(EthToIotaBridgeAction { - eth_tx_hash, - eth_event_index, - eth_bridge_event, - }) - .to_bytes(); - - assert_eq!( - encoded_bytes, - Hex::decode("5355495f4252494447455f4d4553534147450001000000000000000a0b1400000000000000000000000000000000000000c801200000000000000000000000000000000000000000000000000000000000000064030000000000003039").unwrap(), - ); - - let hash = Keccak256::digest(encoded_bytes).digest; - assert_eq!( - hash.to_vec(), - Hex::decode("b352508c301a37bb1b68a75dd0fc42b6f692b2650818631c8f8a4d4d3e5bef46") - .unwrap(), - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding_regression_add_coins_on_iota() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - - let action = BridgeAction::AddTokensOnIotaAction(AddTokensOnIotaAction { - nonce: 0, - chain_id: BridgeChainId::IotaCustom, - native: false, - token_ids: vec![1, 2, 3, 4], - token_type_names: vec![ - TypeTag::from_str("0x9b5e13bcd0cb23ff25c07698e89d48056c745338d8c9dbd033a4172b87027073::btc::BTC").unwrap(), - TypeTag::from_str("0x7970d71c03573f540a7157f0d3970e117effa6ae16cefd50b45c749670b24e6a::eth::ETH").unwrap(), - TypeTag::from_str("0x500e429a24478405d5130222b20f8570a746b6bc22423f14b4d4e6a8ea580736::usdc::USDC").unwrap(), - TypeTag::from_str("0x46bfe51da1bd9511919a92eb1154149b36c0f4212121808e13e3e5857d607a9c::usdt::USDT").unwrap(), - ], - token_prices: vec![ - 500_000_000u64, - 30_000_000u64, - 1_000u64, - 1_000u64, - ] - }); - let encoded_bytes = action.to_bytes(); - - assert_eq!( - Hex::encode(encoded_bytes), - "5355495f4252494447455f4d4553534147450601000000000000000002000401020304044a396235653133626364306362323366663235633037363938653839643438303536633734353333386438633964626430333361343137326238373032373037333a3a6274633a3a4254434a373937306437316330333537336635343061373135376630643339373065313137656666613661653136636566643530623435633734393637306232346536613a3a6574683a3a4554484c353030653432396132343437383430356435313330323232623230663835373061373436623662633232343233663134623464346536613865613538303733363a3a757364633a3a555344434c343662666535316461316264393531313931396139326562313135343134396233366330663432313231323138303865313365336535383537643630376139633a3a757364743a3a55534454040065cd1d0000000080c3c90100000000e803000000000000e803000000000000", - ); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_message_encoding_regression_add_coins_on_evm() -> anyhow::Result<()> { - let action = BridgeAction::AddTokensOnEvmAction(crate::types::AddTokensOnEvmAction { - nonce: 0, - chain_id: BridgeChainId::EthCustom, - native: true, - token_ids: vec![99, 100, 101], - token_addresses: vec![ - EthAddress::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap(), - EthAddress::from_str("0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84").unwrap(), - EthAddress::from_str("0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72").unwrap(), - ], - token_iota_decimals: vec![5, 6, 7], - token_prices: vec![1_000_000_000, 2_000_000_000, 3_000_000_000], - }); - let encoded_bytes = action.to_bytes(); - - assert_eq!( - Hex::encode(encoded_bytes), - "5355495f4252494447455f4d455353414745070100000000000000000c0103636465036b175474e89094c44da98b954eedeac495271d0fae7ab96520de3a18e5e111b5eaab095312d7fe84c18360217d8f7ab5e7c516566761ea12ce7f9d720305060703000000003b9aca00000000007735940000000000b2d05e00", - ); - // To generate regression test for sol contracts - let keys = get_bridge_encoding_regression_test_keys(); - for key in keys { - let pub_key = key.public.as_bytes(); - println!("pub_key: {:?}", Hex::encode(pub_key)); - println!( - "sig: {:?}", - Hex::encode( - BridgeAuthoritySignInfo::new(&action, &key) - .signature - .as_bytes() - ) - ); - } - Ok(()) - } - - fn get_bridge_encoding_regression_test_keys() -> Vec { - vec![ - BridgeAuthorityKeyPair::from_bytes( - &Hex::decode("e42c82337ce12d4a7ad6cd65876d91b2ab6594fd50cdab1737c91773ba7451db") - .unwrap(), - ) - .unwrap(), - BridgeAuthorityKeyPair::from_bytes( - &Hex::decode("1aacd610da3d0cc691a04b83b01c34c6c65cda0fe8d502df25ff4b3185c85687") - .unwrap(), - ) - .unwrap(), - BridgeAuthorityKeyPair::from_bytes( - &Hex::decode("53e7baf8378fbc62692e3056c2e10c6666ef8b5b3a53914830f47636d1678140") - .unwrap(), - ) - .unwrap(), - BridgeAuthorityKeyPair::from_bytes( - &Hex::decode("08b5350a091faabd5f25b6e290bfc3f505d43208775b9110dfed5ee6c7a653f0") - .unwrap(), - ) - .unwrap(), - ] - } -} diff --git a/crates/iota-bridge/src/error.rs b/crates/iota-bridge/src/error.rs deleted file mode 100644 index 64e60594bf5..00000000000 --- a/crates/iota-bridge/src/error.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::{crypto::BridgeAuthorityPublicKeyBytes, types::BridgeAction}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum BridgeError { - // The input is not an invalid transaction digest/hash - InvalidTxHash, - // The referenced transaction failed - OriginTxFailed, - // The referenced transaction does not exist - TxNotFound, - // Tx is not yet finalized - TxNotFinalized, - // No recognized bridge event in specified transaction and event position - NoBridgeEventsInTxPosition, - // Found a bridge event but not in a recognized Eth bridge contract - BridgeEventInUnrecognizedEthContract, - // Found a bridge event but not in a recognized IOTA bridge package - BridgeEventInUnrecognizedIotaPackage, - // Found BridgeEvent but not BridgeAction - BridgeEventNotActionable, - // Failure to serialize - BridgeSerialization(String), - // Internal Bridge error - Internal(String), - // Authority signature duplication - AuthoritySignatureDuplication(String), - // Too many errors when aggregating authority signatures - AuthoritySignatureAggregationTooManyErrors(String), - // Transient Ethereum provider error - TransientProvider(String), - // Ethereum provider error - Provider(String), - // TokenId is unknown - UnknownTokenId(u8), - // Invalid BridgeCommittee - InvalidBridgeCommittee(String), - // Invalid Bridge authority signature - InvalidBridgeAuthoritySignature((BridgeAuthorityPublicKeyBytes, String)), - // Entity is not in the Bridge committee or is blocklisted - InvalidBridgeAuthority(BridgeAuthorityPublicKeyBytes), - // Authority's base_url is invalid - InvalidAuthorityUrl(BridgeAuthorityPublicKeyBytes), - // Invalid Bridge Client request - InvalidBridgeClientRequest(String), - // Invalid ChainId - InvalidChainId, - // Message is signed by mismatched authority - MismatchedAuthoritySigner, - // Signature is over a mismatched action - MismatchedAction, - // Action is not a governance action - ActionIsNotGovernanceAction(BridgeAction), - // Client requested an non-approved governance action - GovernanceActionIsNotApproved, - // Authority has invalid url - AuthorityUrlInvalid, - // Action is not token transfer - ActionIsNotTokenTransferAction, - // IOTA transaction failure due to generic error - IotaTxFailureGeneric(String), - // Zero value bridge transfer should not be allowed - ZeroValueBridgeTransfer(String), - // Storage Error - Storage(String), - // REST API Error - RestAPI(String), - // Uncategorized error - Generic(String), -} - -pub type BridgeResult = Result; diff --git a/crates/iota-bridge/src/eth_client.rs b/crates/iota-bridge/src/eth_client.rs deleted file mode 100644 index f70d5d138f0..00000000000 --- a/crates/iota-bridge/src/eth_client.rs +++ /dev/null @@ -1,436 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{collections::HashSet, sync::Arc}; - -use ethers::{ - providers::{JsonRpcClient, Middleware, Provider}, - types::{Address as EthAddress, Block, Filter, TxHash}, -}; -use tap::TapFallible; - -#[cfg(test)] -use crate::eth_mock_provider::EthMockProvider; -use crate::{ - abi::EthBridgeEvent, - error::{BridgeError, BridgeResult}, - metered_eth_provider::{MeteredEthHttpProvider, new_metered_eth_provider}, - metrics::BridgeMetrics, - types::{BridgeAction, EthLog, RawEthLog}, -}; -pub struct EthClient

{ - provider: Provider

, - contract_addresses: HashSet, -} - -impl EthClient { - pub async fn new( - provider_url: &str, - contract_addresses: HashSet, - metrics: Arc, - ) -> anyhow::Result { - let provider = new_metered_eth_provider(provider_url, metrics)?; - let self_ = Self { - provider, - contract_addresses, - }; - self_.describe().await?; - Ok(self_) - } -} - -#[cfg(test)] -impl EthClient { - pub fn new_mocked(provider: EthMockProvider, contract_addresses: HashSet) -> Self { - let provider = Provider::new(provider); - Self { - provider, - contract_addresses, - } - } -} - -impl

EthClient

-where - P: JsonRpcClient, -{ - // TODO assert chain identifier - async fn describe(&self) -> anyhow::Result<()> { - let chain_id = self.provider.get_chainid().await?; - let block_number = self.provider.get_block_number().await?; - tracing::info!( - "EthClient is connected to chain {chain_id}, current block number: {block_number}" - ); - Ok(()) - } - - /// Returns BridgeAction from an Eth Transaction with transaction hash - /// and the event index. If event is declared in an unrecognized - /// contract, return error. - pub async fn get_finalized_bridge_action_maybe( - &self, - tx_hash: TxHash, - event_idx: u16, - ) -> BridgeResult { - let receipt = self - .provider - .get_transaction_receipt(tx_hash) - .await - .map_err(BridgeError::from)? - .ok_or(BridgeError::TxNotFound)?; - let receipt_block_num = receipt.block_number.ok_or(BridgeError::Provider( - "Provider returns log without block_number".into(), - ))?; - // TODO: save the latest finalized block id so we don't have to query it every - // time - let last_finalized_block_id = self.get_last_finalized_block_id().await?; - if receipt_block_num.as_u64() > last_finalized_block_id { - return Err(BridgeError::TxNotFinalized); - } - let log = receipt - .logs - .get(event_idx as usize) - .ok_or(BridgeError::NoBridgeEventsInTxPosition)?; - - // Ignore events emitted from unrecognized contracts - if !self.contract_addresses.contains(&log.address) { - return Err(BridgeError::BridgeEventInUnrecognizedEthContract); - } - - let eth_log = EthLog { - block_number: receipt_block_num.as_u64(), - tx_hash, - log_index_in_tx: event_idx, - log: log.clone(), - }; - let bridge_event = EthBridgeEvent::try_from_eth_log(ð_log) - .ok_or(BridgeError::NoBridgeEventsInTxPosition)?; - bridge_event - .try_into_bridge_action(tx_hash, event_idx)? - .ok_or(BridgeError::BridgeEventNotActionable) - } - - pub async fn get_last_finalized_block_id(&self) -> BridgeResult { - let block: Result>, ethers::prelude::ProviderError> = - self.provider - .request("eth_getBlockByNumber", ("finalized", false)) - .await; - let block = block?.ok_or(BridgeError::TransientProvider( - "Provider fails to return last finalized block".into(), - ))?; - let number = block.number.ok_or(BridgeError::TransientProvider( - "Provider returns block without number".into(), - ))?; - Ok(number.as_u64()) - } - - // Note: query may fail if range is too big. Callsite is responsible - // for chunking the query. - pub async fn get_events_in_range( - &self, - address: ethers::types::Address, - start_block: u64, - end_block: u64, - ) -> BridgeResult> { - let filter = Filter::new() - .from_block(start_block) - .to_block(end_block) - .address(address); - let logs = self - .provider - // TODO use get_logs_paginated? - .get_logs(&filter) - .await - .map_err(BridgeError::from) - .tap_err(|e| { - tracing::error!( - "get_events_in_range failed. Filter: {:?}. Error {:?}", - filter, - e - ) - })?; - - // Safeguard check that all events are emitted from requested contract address - if logs.iter().any(|log| log.address != address) { - return Err(BridgeError::Provider(format!( - "Provider returns logs from different contract address (expected: {:?}): {:?}", - address, logs - ))); - } - if logs.is_empty() { - return Ok(vec![]); - } - - let tasks = logs.into_iter().map(|log| self.get_log_tx_details(log)); - futures::future::join_all(tasks) - .await - .into_iter() - .collect::, _>>() - .tap_err(|e| { - tracing::error!( - "get_log_tx_details failed. Filter: {:?}. Error {:?}", - filter, - e - ) - }) - } - - // Note: query may fail if range is too big. Callsite is responsible - // for chunking the query. - pub async fn get_raw_events_in_range( - &self, - address: ethers::types::Address, - start_block: u64, - end_block: u64, - ) -> BridgeResult> { - let filter = Filter::new() - .from_block(start_block) - .to_block(end_block) - .address(address); - let logs = self - .provider - .get_logs(&filter) - .await - .map_err(BridgeError::from) - .tap_err(|e| { - tracing::error!( - "get_events_in_range failed. Filter: {:?}. Error {:?}", - filter, - e - ) - })?; - // Safeguard check that all events are emitted from requested contract address - logs.into_iter().map( - |log| { - if log.address != address { - return Err(BridgeError::Provider(format!("Provider returns logs from different contract address (expected: {:?}): {:?}", address, log))); - } - Ok(RawEthLog { - block_number: log.block_number.ok_or(BridgeError::Provider("Provider returns log without block_number".into()))?.as_u64(), - tx_hash: log.transaction_hash.ok_or(BridgeError::Provider("Provider returns log without transaction_hash".into()))?, - log, - })} - ).collect::, _>>() - } - - /// This function converts a `Log` to `EthLog`, to make sure the - /// `block_num`, `tx_hash` and `log_index_in_tx` are available for - /// downstream. - // It's frustratingly ugly because of the nulliability of many fields in `Log`. - async fn get_log_tx_details(&self, log: ethers::types::Log) -> BridgeResult { - let block_number = log - .block_number - .ok_or(BridgeError::Provider( - "Provider returns log without block_number".into(), - ))? - .as_u64(); - let tx_hash = log.transaction_hash.ok_or(BridgeError::Provider( - "Provider returns log without transaction_hash".into(), - ))?; - // This is the log index in the block, rather than transaction. - let log_index = log.log_index.ok_or(BridgeError::Provider( - "Provider returns log without log_index".into(), - ))?; - - // Now get the log's index in the transaction. There is `transaction_log_index` - // field in `Log`, but I never saw it populated. - - let receipt = self - .provider - .get_transaction_receipt(tx_hash) - .await - .map_err(BridgeError::from)? - .ok_or(BridgeError::Provider(format!( - "Provide cannot find eth transaction for log: {:?})", - log - )))?; - - let receipt_block_num = receipt.block_number.ok_or(BridgeError::Provider( - "Provider returns log without block_number".into(), - ))?; - if receipt_block_num.as_u64() != block_number { - return Err(BridgeError::Provider(format!( - "Provider returns receipt with different block number from log. Receipt: {:?}, Log: {:?}", - receipt, log - ))); - } - - // Find the log index in the transaction - let mut log_index_in_tx = None; - for (idx, receipt_log) in receipt.logs.iter().enumerate() { - // match log index (in the block) - if receipt_log.log_index == Some(log_index) { - // make sure the topics and data match - if receipt_log.topics != log.topics || receipt_log.data != log.data { - return Err(BridgeError::Provider(format!( - "Provider returns receipt with different log from log. Receipt: {:?}, Log: {:?}", - receipt, log - ))); - } - log_index_in_tx = Some(idx); - } - } - let log_index_in_tx = log_index_in_tx.ok_or(BridgeError::Provider(format!( - "Couldn't find matching log: {:?} in transaction {}", - log, tx_hash - )))?; - - Ok(EthLog { - block_number, - tx_hash, - log_index_in_tx: log_index_in_tx as u16, - log, - }) - } -} - -#[cfg(test)] -mod tests { - use ethers::types::{Address as EthAddress, Log, TransactionReceipt, U64}; - use prometheus::Registry; - - use super::*; - use crate::test_utils::{get_test_log_and_action, mock_last_finalized_block}; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_get_finalized_bridge_action_maybe() { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - let mock_provider = EthMockProvider::new(); - mock_last_finalized_block(&mock_provider, 777); - - let client = EthClient::new_mocked( - mock_provider.clone(), - HashSet::from_iter(vec![EthAddress::zero()]), - ); - let result = client.get_last_finalized_block_id().await.unwrap(); - assert_eq!(result, 777); - - let eth_tx_hash = TxHash::random(); - let log = Log { - transaction_hash: Some(eth_tx_hash), - block_number: Some(U64::from(778)), - ..Default::default() - }; - let (good_log, bridge_action) = get_test_log_and_action(EthAddress::zero(), eth_tx_hash, 1); - // Mocks `eth_getTransactionReceipt` to return `log` and `good_log` in order - mock_provider - .add_response::<[TxHash; 1], TransactionReceipt, TransactionReceipt>( - "eth_getTransactionReceipt", - [log.transaction_hash.unwrap()], - TransactionReceipt { - block_number: log.block_number, - logs: vec![log, good_log], - ..Default::default() - }, - ) - .unwrap(); - - let error = client - .get_finalized_bridge_action_maybe(eth_tx_hash, 0) - .await - .unwrap_err(); - match error { - BridgeError::TxNotFinalized => {} - _ => panic!("expected TxNotFinalized"), - }; - - // 778 is now finalized - mock_last_finalized_block(&mock_provider, 778); - - let error = client - .get_finalized_bridge_action_maybe(eth_tx_hash, 2) - .await - .unwrap_err(); - // Receipt only has 2 logs - match error { - BridgeError::NoBridgeEventsInTxPosition => {} - _ => panic!("expected NoBridgeEventsInTxPosition"), - }; - - let error = client - .get_finalized_bridge_action_maybe(eth_tx_hash, 0) - .await - .unwrap_err(); - // Same, `log` is not a BridgeEvent - match error { - BridgeError::NoBridgeEventsInTxPosition => {} - _ => panic!("expected NoBridgeEventsInTxPosition"), - }; - - let action = client - .get_finalized_bridge_action_maybe(eth_tx_hash, 1) - .await - .unwrap(); - assert_eq!(action, bridge_action); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_get_finalized_bridge_action_maybe_unrecognized_contract() { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - let mock_provider = EthMockProvider::new(); - mock_last_finalized_block(&mock_provider, 777); - - let client = EthClient::new_mocked( - mock_provider.clone(), - HashSet::from_iter(vec![ - EthAddress::repeat_byte(5), - EthAddress::repeat_byte(6), - EthAddress::repeat_byte(7), - ]), - ); - let result = client.get_last_finalized_block_id().await.unwrap(); - assert_eq!(result, 777); - - let eth_tx_hash = TxHash::random(); - // Event emitted from a different contract address - let (log, _bridge_action) = - get_test_log_and_action(EthAddress::repeat_byte(4), eth_tx_hash, 0); - mock_provider - .add_response::<[TxHash; 1], TransactionReceipt, TransactionReceipt>( - "eth_getTransactionReceipt", - [log.transaction_hash.unwrap()], - TransactionReceipt { - block_number: log.block_number, - logs: vec![log], - ..Default::default() - }, - ) - .unwrap(); - - let error = client - .get_finalized_bridge_action_maybe(eth_tx_hash, 0) - .await - .unwrap_err(); - match error { - BridgeError::BridgeEventInUnrecognizedEthContract => {} - _ => panic!("expected TxNotFinalized"), - }; - - // Ok if emitted from the right contract - let (log, bridge_action) = - get_test_log_and_action(EthAddress::repeat_byte(6), eth_tx_hash, 0); - mock_provider - .add_response::<[TxHash; 1], TransactionReceipt, TransactionReceipt>( - "eth_getTransactionReceipt", - [log.transaction_hash.unwrap()], - TransactionReceipt { - block_number: log.block_number, - logs: vec![log], - ..Default::default() - }, - ) - .unwrap(); - let action = client - .get_finalized_bridge_action_maybe(eth_tx_hash, 0) - .await - .unwrap(); - assert_eq!(action, bridge_action); - } -} diff --git a/crates/iota-bridge/src/eth_mock_provider.rs b/crates/iota-bridge/src/eth_mock_provider.rs deleted file mode 100644 index bbae63b56db..00000000000 --- a/crates/iota-bridge/src/eth_mock_provider.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! A mock implementation of Ethereum JSON-RPC client, based on `MockProvider` -//! from `ethers-rs`. - -use std::{ - borrow::Borrow, - collections::HashMap, - fmt::Debug, - sync::{Arc, Mutex}, -}; - -use async_trait::async_trait; -use ethers::providers::{JsonRpcClient, MockError}; -use serde::{Serialize, de::DeserializeOwned}; -use serde_json::Value; - -/// Helper type that can be used to pass through the `params` value. -/// This is necessary because the wrapper provider is supposed to skip the -/// `params` if it's of size 0, see `crate::transports::common::Request` -#[derive(Debug, Eq, PartialEq, Clone, Hash)] -enum MockParams { - Value(String), - Zst, -} - -/// Mock transport used in test environments. -#[derive(Clone, Debug)] -pub struct EthMockProvider { - responses: Arc>>, -} - -impl Default for EthMockProvider { - fn default() -> Self { - Self::new() - } -} - -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl JsonRpcClient for EthMockProvider { - type Error = MockError; - - /// If `method` and `params` match previously set response by - /// `add_response`, return the response. Otherwise return - /// MockError::EmptyResponses. - async fn request( - &self, - method: &str, - params: P, - ) -> Result { - let params = if std::mem::size_of::

() == 0 { - MockParams::Zst - } else { - MockParams::Value(serde_json::to_value(params)?.to_string()) - }; - let element = self - .responses - .lock() - .unwrap() - .get(&(method.to_owned(), params)) - .ok_or(MockError::EmptyResponses)? - .clone(); - let res: R = serde_json::from_value(element)?; - - Ok(res) - } -} - -impl EthMockProvider { - pub fn new() -> Self { - Self { - responses: Arc::new(Mutex::new(HashMap::new())), - } - } - - pub fn add_response>( - &self, - method: &str, - params: P, - data: K, - ) -> Result<(), MockError> { - let params = if std::mem::size_of::

() == 0 { - MockParams::Zst - } else { - MockParams::Value(serde_json::to_value(params)?.to_string()) - }; - let value = serde_json::to_value(data.borrow())?; - self.responses - .lock() - .unwrap() - .insert((method.to_owned(), params), value); - Ok(()) - } -} - -#[cfg(test)] -#[cfg(not(target_arch = "wasm32"))] -mod tests { - use ethers::{providers::Middleware, types::U64}; - - use super::*; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_basic_responses_match() { - let mock = EthMockProvider::new(); - - mock.add_response("eth_blockNumber", (), U64::from(12)) - .unwrap(); - let block: U64 = mock.request("eth_blockNumber", ()).await.unwrap(); - - assert_eq!(block.as_u64(), 12); - let block: U64 = mock.request("eth_blockNumber", ()).await.unwrap(); - assert_eq!(block.as_u64(), 12); - - mock.add_response("eth_blockNumber", (), U64::from(13)) - .unwrap(); - let block: U64 = mock.request("eth_blockNumber", ()).await.unwrap(); - assert_eq!(block.as_u64(), 13); - - mock.add_response("eth_foo", (), U64::from(0)).unwrap(); - let block: U64 = mock.request("eth_blockNumber", ()).await.unwrap(); - assert_eq!(block.as_u64(), 13); - - let err = mock - .request::<_, ()>("eth_blockNumber", "bar") - .await - .unwrap_err(); - match err { - MockError::EmptyResponses => {} - _ => panic!("expected empty responses"), - }; - - mock.add_response("eth_blockNumber", "bar", U64::from(14)) - .unwrap(); - let block: U64 = mock.request("eth_blockNumber", "bar").await.unwrap(); - assert_eq!(block.as_u64(), 14); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_with_provider() { - let mock = EthMockProvider::new(); - let provider = ethers::providers::Provider::new(mock.clone()); - - mock.add_response("eth_blockNumber", (), U64::from(12)) - .unwrap(); - let block = provider.get_block_number().await.unwrap(); - assert_eq!(block.as_u64(), 12); - } -} diff --git a/crates/iota-bridge/src/eth_syncer.rs b/crates/iota-bridge/src/eth_syncer.rs deleted file mode 100644 index 027cf75d41a..00000000000 --- a/crates/iota-bridge/src/eth_syncer.rs +++ /dev/null @@ -1,514 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! The EthSyncer module is responsible for synchronizing Events emitted on -//! Ethereum blockchain from concerned contracts. Each contract is associated -//! with a start block number, and the syncer will only query from that block -//! number onwards. The syncer also keeps track of the last finalized -//! block on Ethereum and will only query for events up to that block number. - -use std::{collections::HashMap, sync::Arc}; - -use ethers::types::Address as EthAddress; -use iota_metrics::spawn_logged_monitored_task; -use tokio::{ - sync::watch, - task::JoinHandle, - time::{self, Duration, Instant}, -}; -use tracing::error; - -use crate::{ - error::BridgeResult, eth_client::EthClient, metrics::BridgeMetrics, - retry_with_max_elapsed_time, types::EthLog, -}; - -const ETH_LOG_QUERY_MAX_BLOCK_RANGE: u64 = 1000; -const ETH_EVENTS_CHANNEL_SIZE: usize = 1000; -const FINALIZED_BLOCK_QUERY_INTERVAL: Duration = Duration::from_secs(5); - -pub struct EthSyncer

{ - eth_client: Arc>, - contract_addresses: EthTargetAddresses, -} - -/// Map from contract address to their start block. -pub type EthTargetAddresses = HashMap; - -impl

EthSyncer

-where - P: ethers::providers::JsonRpcClient + 'static, -{ - pub fn new(eth_client: Arc>, contract_addresses: EthTargetAddresses) -> Self { - Self { - eth_client, - contract_addresses, - } - } - - pub async fn run( - self, - metrics: Arc, - ) -> BridgeResult<( - Vec>, - iota_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, - watch::Receiver, - )> { - let (eth_evnets_tx, eth_events_rx) = iota_metrics::metered_channel::channel( - ETH_EVENTS_CHANNEL_SIZE, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["eth_events_queue"]), - ); - let last_finalized_block = self.eth_client.get_last_finalized_block_id().await?; - let (last_finalized_block_tx, last_finalized_block_rx) = - watch::channel(last_finalized_block); - let mut task_handles = vec![]; - let eth_client_clone = self.eth_client.clone(); - let metrics_clone = metrics.clone(); - task_handles.push(spawn_logged_monitored_task!( - Self::run_finalized_block_refresh_task( - last_finalized_block_tx, - eth_client_clone, - metrics_clone - ) - )); - for (contract_address, start_block) in self.contract_addresses { - let eth_evnets_tx_clone = eth_evnets_tx.clone(); - let last_finalized_block_rx_clone = last_finalized_block_rx.clone(); - let eth_client_clone = self.eth_client.clone(); - let metrics_clone = metrics.clone(); - task_handles.push(spawn_logged_monitored_task!( - Self::run_event_listening_task( - contract_address, - start_block, - last_finalized_block_rx_clone, - eth_evnets_tx_clone, - eth_client_clone, - metrics_clone, - ) - )); - } - Ok((task_handles, eth_events_rx, last_finalized_block_rx)) - } - - async fn run_finalized_block_refresh_task( - last_finalized_block_sender: watch::Sender, - eth_client: Arc>, - metrics: Arc, - ) { - tracing::info!("Starting finalized block refresh task."); - let mut last_block_number = 0; - let mut interval = time::interval(FINALIZED_BLOCK_QUERY_INTERVAL); - interval.set_missed_tick_behavior(time::MissedTickBehavior::Skip); - loop { - interval.tick().await; - // TODO: allow to pass custom initial interval - let Ok(Ok(new_value)) = retry_with_max_elapsed_time!( - eth_client.get_last_finalized_block_id(), - time::Duration::from_secs(600) - ) else { - error!("Failed to get last finalized block from eth client after retry"); - continue; - }; - tracing::debug!("Last finalized block: {}", new_value); - metrics.last_finalized_eth_block.set(new_value as i64); - - // TODO add a metrics for the last finalized block - - if new_value > last_block_number { - last_finalized_block_sender - .send(new_value) - .expect("last_finalized_block channel receiver is closed"); - tracing::info!("Observed new finalized eth block: {}", new_value); - last_block_number = new_value; - } - } - } - - // TODO: define a type for block number for readability - // TODO: add a metrics for current start block - async fn run_event_listening_task( - contract_address: EthAddress, - mut start_block: u64, - mut last_finalized_block_receiver: watch::Receiver, - events_sender: iota_metrics::metered_channel::Sender<(EthAddress, u64, Vec)>, - eth_client: Arc>, - metrics: Arc, - ) { - tracing::info!(contract_address=?contract_address, "Starting eth events listening task from block {start_block}"); - let mut more_blocks = false; - loop { - // If no more known blocks, wait for the next finalized block. - if !more_blocks { - last_finalized_block_receiver - .changed() - .await - .expect("last_finalized_block channel sender is closed"); - } - let new_finalized_block = *last_finalized_block_receiver.borrow(); - if new_finalized_block < start_block { - tracing::info!( - contract_address=?contract_address, - "New finalized block {} is smaller than start block {}, ignore", - new_finalized_block, - start_block, - ); - continue; - } - // Each query does at most ETH_LOG_QUERY_MAX_BLOCK_RANGE blocks. - let end_block = std::cmp::min( - start_block + ETH_LOG_QUERY_MAX_BLOCK_RANGE - 1, - new_finalized_block, - ); - more_blocks = end_block < new_finalized_block; - let timer = Instant::now(); - let Ok(Ok(events)) = retry_with_max_elapsed_time!( - eth_client.get_events_in_range(contract_address, start_block, end_block), - Duration::from_secs(600) - ) else { - error!("Failed to get events from eth client after retry"); - continue; - }; - tracing::debug!( - ?contract_address, - start_block, - end_block, - "Querying eth events took {:?}", - timer.elapsed() - ); - let len = events.len(); - let last_block = events.last().map(|e| e.block_number); - - // Note 1: we always events to the channel even when it is empty. This is - // because of how `eth_getLogs` api is designed - we want cursor to - // move forward continuously. - - // Note 2: it's extremely critical to make sure the Logs we send via this - // channel are complete per block height. Namely, we should never - // send a partial list of events for a block. Otherwise, we may end - // up missing events. - events_sender - .send((contract_address, end_block, events)) - .await - .expect("All Eth event channel receivers are closed"); - if len != 0 { - tracing::info!( - ?contract_address, - start_block, - end_block, - "Observed {len} new Eth events", - ); - } - if let Some(last_block) = last_block { - metrics.last_synced_eth_block.set(last_block as i64); - } - start_block = end_block + 1; - } - } -} - -#[cfg(test)] -mod tests { - use std::{collections::HashSet, str::FromStr}; - - use ethers::types::{Log, TxHash, U64, U256}; - use prometheus::Registry; - use tokio::sync::mpsc::error::TryRecvError; - - use super::*; - use crate::{ - eth_mock_provider::EthMockProvider, - test_utils::{mock_get_logs, mock_last_finalized_block}, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_last_finalized_block() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - let mock_provider = EthMockProvider::new(); - mock_last_finalized_block(&mock_provider, 777); - let client = EthClient::new_mocked( - mock_provider.clone(), - HashSet::from_iter(vec![EthAddress::zero()]), - ); - let result = client.get_last_finalized_block_id().await.unwrap(); - assert_eq!(result, 777); - - let addresses = HashMap::from_iter(vec![(EthAddress::zero(), 100)]); - let log = Log { - address: EthAddress::zero(), - transaction_hash: Some(TxHash::random()), - block_number: Some(U64::from(777)), - log_index: Some(U256::from(3)), - ..Default::default() - }; - let eth_log = EthLog { - block_number: 777, - tx_hash: log.transaction_hash.unwrap(), - log_index_in_tx: 0, - log: log.clone(), - }; - mock_get_logs( - &mock_provider, - EthAddress::zero(), - 100, - 777, - vec![log.clone()], - ); - let (_handles, mut logs_rx, mut finalized_block_rx) = - EthSyncer::new(Arc::new(client), addresses) - .run(Arc::new(BridgeMetrics::new_for_testing())) - .await - .unwrap(); - - // The latest finalized block stays at 777, event listener should not query - // again. - finalized_block_rx.changed().await.unwrap(); - assert_eq!(*finalized_block_rx.borrow(), 777); - let (contract_address, end_block, received_logs) = logs_rx.recv().await.unwrap(); - assert_eq!(contract_address, EthAddress::zero()); - assert_eq!(end_block, 777); - assert_eq!(received_logs, vec![eth_log.clone()]); - assert_eq!(logs_rx.try_recv().unwrap_err(), TryRecvError::Empty); - - mock_get_logs( - &mock_provider, - EthAddress::zero(), - 778, - 888, - vec![log.clone()], - ); - // The latest finalized block is updated to 888, event listener should query - // again. - mock_last_finalized_block(&mock_provider, 888); - finalized_block_rx.changed().await.unwrap(); - assert_eq!(*finalized_block_rx.borrow(), 888); - let (contract_address, end_block, received_logs) = logs_rx.recv().await.unwrap(); - assert_eq!(contract_address, EthAddress::zero()); - assert_eq!(end_block, 888); - assert_eq!(received_logs, vec![eth_log]); - assert_eq!(logs_rx.try_recv().unwrap_err(), TryRecvError::Empty); - - Ok(()) - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_multiple_addresses() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - - let mock_provider = EthMockProvider::new(); - mock_last_finalized_block(&mock_provider, 198); - - let another_address = - EthAddress::from_str("0x00000000219ab540356cbb839cbe05303d7705fa").unwrap(); - let client = EthClient::new_mocked( - mock_provider.clone(), - HashSet::from_iter(vec![another_address]), - ); - - let addresses = HashMap::from_iter(vec![(EthAddress::zero(), 100), (another_address, 200)]); - - let log1 = Log { - address: EthAddress::zero(), - transaction_hash: Some(TxHash::random()), - block_number: Some(U64::from(101)), - log_index: Some(U256::from(5)), - ..Default::default() - }; - let eth_log1 = EthLog { - block_number: log1.block_number.unwrap().as_u64(), - tx_hash: log1.transaction_hash.unwrap(), - log_index_in_tx: 0, - log: log1.clone(), - }; - mock_get_logs( - &mock_provider, - EthAddress::zero(), - 100, - 198, - vec![log1.clone()], - ); - let log2 = Log { - address: another_address, - transaction_hash: Some(TxHash::random()), - block_number: Some(U64::from(201)), - log_index: Some(U256::from(6)), - ..Default::default() - }; - // Mock logs for another_address although it shouldn't be queried. We don't - // expect to see log2 in the logs channel later on. - mock_get_logs( - &mock_provider, - another_address, - 200, - 198, - vec![log2.clone()], - ); - - let (_handles, mut logs_rx, mut finalized_block_rx) = - EthSyncer::new(Arc::new(client), addresses) - .run(Arc::new(BridgeMetrics::new_for_testing())) - .await - .unwrap(); - - // The latest finalized block stays at 198. - finalized_block_rx.changed().await.unwrap(); - assert_eq!(*finalized_block_rx.borrow(), 198); - let (_contract_address, end_block, received_logs) = logs_rx.recv().await.unwrap(); - assert_eq!(end_block, 198); - assert_eq!(received_logs, vec![eth_log1.clone()]); - // log2 should not be received as another_address's start block is 200. - assert_eq!(logs_rx.try_recv().unwrap_err(), TryRecvError::Empty); - - let log1 = Log { - address: EthAddress::zero(), - block_number: Some(U64::from(200)), - transaction_hash: Some(TxHash::random()), - log_index: Some(U256::from(7)), - ..Default::default() - }; - let eth_log1 = EthLog { - block_number: log1.block_number.unwrap().as_u64(), - tx_hash: log1.transaction_hash.unwrap(), - log_index_in_tx: 0, - log: log1.clone(), - }; - mock_get_logs( - &mock_provider, - EthAddress::zero(), - 199, - 400, - vec![log1.clone()], - ); - let log2 = Log { - address: another_address, - transaction_hash: Some(TxHash::random()), - block_number: Some(U64::from(201)), - log_index: Some(U256::from(9)), - ..Default::default() - }; - let eth_log2 = EthLog { - block_number: log2.block_number.unwrap().as_u64(), - tx_hash: log2.transaction_hash.unwrap(), - log_index_in_tx: 0, - log: log2.clone(), - }; - mock_get_logs( - &mock_provider, - another_address, - 200, - 400, - vec![log2.clone()], - ); - mock_last_finalized_block(&mock_provider, 400); - - finalized_block_rx.changed().await.unwrap(); - assert_eq!(*finalized_block_rx.borrow(), 400); - let mut logs_set = HashSet::new(); - logs_rx.recv().await.unwrap().2.into_iter().for_each(|log| { - logs_set.insert(format!("{:?}", log)); - }); - logs_rx.recv().await.unwrap().2.into_iter().for_each(|log| { - logs_set.insert(format!("{:?}", log)); - }); - assert_eq!( - logs_set, - HashSet::from_iter(vec![format!("{:?}", eth_log1), format!("{:?}", eth_log2)]) - ); - // No more finalized block change, no more logs. - assert_eq!(logs_rx.try_recv().unwrap_err(), TryRecvError::Empty); - Ok(()) - } - - /// Test that the syncer will query for logs in multiple queries if the - /// range is too big. - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_paginated_eth_log_query() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - let mock_provider = EthMockProvider::new(); - let start_block = 100; - // range too big, we need two queries - let last_finalized_block = start_block + ETH_LOG_QUERY_MAX_BLOCK_RANGE + 1; - mock_last_finalized_block(&mock_provider, last_finalized_block); - let client = EthClient::new_mocked( - mock_provider.clone(), - HashSet::from_iter(vec![EthAddress::zero()]), - ); - let result = client.get_last_finalized_block_id().await.unwrap(); - assert_eq!(result, last_finalized_block); - - let addresses = HashMap::from_iter(vec![(EthAddress::zero(), start_block)]); - let log = Log { - address: EthAddress::zero(), - transaction_hash: Some(TxHash::random()), - block_number: Some(U64::from(start_block)), - log_index: Some(U256::from(3)), - ..Default::default() - }; - let log2 = Log { - address: EthAddress::zero(), - transaction_hash: Some(TxHash::random()), - block_number: Some(U64::from(last_finalized_block)), - log_index: Some(U256::from(3)), - ..Default::default() - }; - let eth_log = EthLog { - block_number: start_block, - tx_hash: log.transaction_hash.unwrap(), - log_index_in_tx: 0, - log: log.clone(), - }; - let eth_log2 = EthLog { - block_number: last_finalized_block, - tx_hash: log2.transaction_hash.unwrap(), - log_index_in_tx: 0, - log: log2.clone(), - }; - // First query handles [start, start + ETH_LOG_QUERY_MAX_BLOCK_RANGE - 1] - mock_get_logs( - &mock_provider, - EthAddress::zero(), - start_block, - start_block + ETH_LOG_QUERY_MAX_BLOCK_RANGE - 1, - vec![log.clone()], - ); - // Second query handles [start + ETH_LOG_QUERY_MAX_BLOCK_RANGE, - // last_finalized_block] - mock_get_logs( - &mock_provider, - EthAddress::zero(), - start_block + ETH_LOG_QUERY_MAX_BLOCK_RANGE, - last_finalized_block, - vec![log2.clone()], - ); - - let (_handles, mut logs_rx, mut finalized_block_rx) = - EthSyncer::new(Arc::new(client), addresses) - .run(Arc::new(BridgeMetrics::new_for_testing())) - .await - .unwrap(); - - finalized_block_rx.changed().await.unwrap(); - assert_eq!(*finalized_block_rx.borrow(), last_finalized_block); - let (contract_address, end_block, received_logs) = logs_rx.recv().await.unwrap(); - assert_eq!(contract_address, EthAddress::zero()); - assert_eq!(end_block, start_block + ETH_LOG_QUERY_MAX_BLOCK_RANGE - 1); - assert_eq!(received_logs, vec![eth_log.clone()]); - let (contract_address, end_block, received_logs) = logs_rx.recv().await.unwrap(); - assert_eq!(contract_address, EthAddress::zero()); - assert_eq!(end_block, last_finalized_block); - assert_eq!(received_logs, vec![eth_log2.clone()]); - Ok(()) - } -} diff --git a/crates/iota-bridge/src/eth_transaction_builder.rs b/crates/iota-bridge/src/eth_transaction_builder.rs deleted file mode 100644 index c50b27a7605..00000000000 --- a/crates/iota-bridge/src/eth_transaction_builder.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use ethers::{prelude::*, types::Address as EthAddress}; - -use crate::{ - abi::{ - EthBridgeCommittee, EthBridgeConfig, EthBridgeLimiter, EthCommitteeUpgradeableContract, - EthIotaBridge, eth_bridge_committee, eth_bridge_config, eth_bridge_limiter, - eth_committee_upgradeable_contract, eth_iota_bridge, - }, - error::{BridgeError, BridgeResult}, - types::{ - AddTokensOnEvmAction, AssetPriceUpdateAction, BlocklistCommitteeAction, BridgeAction, - BridgeCommitteeValiditySignInfo, EmergencyAction, EvmContractUpgradeAction, - LimitUpdateAction, VerifiedCertifiedBridgeAction, - }, - utils::EthSigner, -}; - -pub async fn build_eth_transaction( - contract_address: EthAddress, - signer: EthSigner, - action: VerifiedCertifiedBridgeAction, -) -> BridgeResult> { - if !action.is_governace_action() { - return Err(BridgeError::ActionIsNotGovernanceAction( - action.data().clone(), - )); - } - // TODO: Check chain id? - let sigs = action.auth_sig(); - match action.data() { - BridgeAction::IotaToEthBridgeAction(_) => { - unreachable!() - } - BridgeAction::EthToIotaBridgeAction(_) => { - unreachable!() - } - BridgeAction::EmergencyAction(action) => { - build_emergency_op_approve_transaction(contract_address, signer, action.clone(), sigs) - .await - } - BridgeAction::BlocklistCommitteeAction(action) => { - build_committee_blocklist_approve_transaction( - contract_address, - signer, - action.clone(), - sigs, - ) - .await - } - BridgeAction::LimitUpdateAction(action) => { - build_limit_update_approve_transaction(contract_address, signer, action.clone(), sigs) - .await - } - BridgeAction::AssetPriceUpdateAction(action) => { - build_asset_price_update_approve_transaction( - contract_address, - signer, - action.clone(), - sigs, - ) - .await - } - BridgeAction::EvmContractUpgradeAction(action) => { - build_evm_upgrade_transaction(signer, action.clone(), sigs).await - } - BridgeAction::AddTokensOnIotaAction(_) => { - unreachable!(); - } - BridgeAction::AddTokensOnEvmAction(action) => { - build_add_tokens_on_evm_transaction(contract_address, signer, action.clone(), sigs) - .await - } - } -} - -pub async fn build_emergency_op_approve_transaction( - contract_address: EthAddress, - signer: EthSigner, - action: EmergencyAction, - sigs: &BridgeCommitteeValiditySignInfo, -) -> BridgeResult> { - let contract = EthIotaBridge::new(contract_address, signer.into()); - - let message: eth_iota_bridge::Message = action.clone().into(); - let signatures = sigs - .signatures - .values() - .map(|sig| Bytes::from(sig.as_ref().to_vec())) - .collect::>(); - Ok(contract.execute_emergency_op_with_signatures(signatures, message)) -} - -pub async fn build_committee_blocklist_approve_transaction( - contract_address: EthAddress, - signer: EthSigner, - action: BlocklistCommitteeAction, - sigs: &BridgeCommitteeValiditySignInfo, -) -> BridgeResult> { - let contract = EthBridgeCommittee::new(contract_address, signer.into()); - - let message: eth_bridge_committee::Message = action.clone().into(); - let signatures = sigs - .signatures - .values() - .map(|sig| Bytes::from(sig.as_ref().to_vec())) - .collect::>(); - Ok(contract.update_blocklist_with_signatures(signatures, message)) -} - -pub async fn build_limit_update_approve_transaction( - contract_address: EthAddress, - signer: EthSigner, - action: LimitUpdateAction, - sigs: &BridgeCommitteeValiditySignInfo, -) -> BridgeResult> { - let contract = EthBridgeLimiter::new(contract_address, signer.into()); - - let message: eth_bridge_limiter::Message = action.clone().into(); - let signatures = sigs - .signatures - .values() - .map(|sig| Bytes::from(sig.as_ref().to_vec())) - .collect::>(); - Ok(contract.update_limit_with_signatures(signatures, message)) -} - -pub async fn build_asset_price_update_approve_transaction( - contract_address: EthAddress, - signer: EthSigner, - action: AssetPriceUpdateAction, - sigs: &BridgeCommitteeValiditySignInfo, -) -> BridgeResult> { - let contract = EthBridgeConfig::new(contract_address, signer.into()); - let message: eth_bridge_config::Message = action.clone().into(); - let signatures = sigs - .signatures - .values() - .map(|sig| Bytes::from(sig.as_ref().to_vec())) - .collect::>(); - Ok(contract.update_token_price_with_signatures(signatures, message)) -} - -pub async fn build_add_tokens_on_evm_transaction( - contract_address: EthAddress, - signer: EthSigner, - action: AddTokensOnEvmAction, - sigs: &BridgeCommitteeValiditySignInfo, -) -> BridgeResult> { - let contract = EthBridgeConfig::new(contract_address, signer.into()); - let message: eth_bridge_config::Message = action.clone().into(); - let signatures = sigs - .signatures - .values() - .map(|sig| Bytes::from(sig.as_ref().to_vec())) - .collect::>(); - Ok(contract.add_tokens_with_signatures(signatures, message)) -} - -pub async fn build_evm_upgrade_transaction( - signer: EthSigner, - action: EvmContractUpgradeAction, - sigs: &BridgeCommitteeValiditySignInfo, -) -> BridgeResult> { - let contract_address = action.proxy_address; - let contract = EthCommitteeUpgradeableContract::new(contract_address, signer.into()); - let message: eth_committee_upgradeable_contract::Message = action.clone().into(); - let signatures = sigs - .signatures - .values() - .map(|sig| Bytes::from(sig.as_ref().to_vec())) - .collect::>(); - Ok(contract.upgrade_with_signatures(signatures, message)) -} - -// TODO: add tests for eth transaction building diff --git a/crates/iota-bridge/src/events.rs b/crates/iota-bridge/src/events.rs deleted file mode 100644 index a1335db0cc3..00000000000 --- a/crates/iota-bridge/src/events.rs +++ /dev/null @@ -1,595 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This file contains the definition of the IotaBridgeEvent enum, of -//! which each variant is an emitted Event struct defined in the Move -//! Bridge module. We rely on structures in this file to decode -//! the bcs content of the emitted events. - -#![allow(non_upper_case_globals)] - -use std::str::FromStr; - -use ethers::types::Address as EthAddress; -use fastcrypto::encoding::{Encoding, Hex}; -use iota_json_rpc_types::IotaEvent; -use iota_types::{ - BRIDGE_PACKAGE_ID, TypeTag, - base_types::IotaAddress, - bridge::{ - BridgeChainId, MoveTypeBridgeMessageKey, MoveTypeCommitteeMember, - MoveTypeCommitteeMemberRegistration, - }, - collection_types::VecMap, - crypto::ToFromBytes, - digests::TransactionDigest, - parse_iota_type_tag, -}; -use move_core_types::language_storage::StructTag; -use once_cell::sync::OnceCell; -use serde::{Deserialize, Serialize}; - -use crate::{ - crypto::BridgeAuthorityPublicKey, - error::{BridgeError, BridgeResult}, - types::{BridgeAction, IotaToEthBridgeAction}, -}; - -// `TokendDepositedEvent` emitted in bridge.move -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct MoveTokenDepositedEvent { - pub seq_num: u64, - pub source_chain: u8, - pub sender_address: Vec, - pub target_chain: u8, - pub target_address: Vec, - pub token_type: u8, - pub amount_iota_adjusted: u64, -} - -macro_rules! new_move_event { - ($struct_name:ident, $move_struct_name:ident) => { - - // `$move_struct_name` emitted in bridge.move - #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] - pub struct $move_struct_name { - pub message_key: MoveTypeBridgeMessageKey, - } - - // Sanitized version of the given `move_struct_name` - #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Hash)] - pub struct $struct_name { - pub nonce: u64, - pub source_chain: BridgeChainId, - } - - impl TryFrom<$move_struct_name> for $struct_name { - type Error = BridgeError; - - fn try_from(event: $move_struct_name) -> BridgeResult { - let source_chain = BridgeChainId::try_from(event.message_key.source_chain).map_err(|_e| { - BridgeError::Generic(format!( - "Failed to convert {} to {}. Failed to convert source chain {} to BridgeChainId", - stringify!($move_struct_name), - stringify!($struct_name), - event.message_key.source_chain, - )) - })?; - Ok(Self { - nonce: event.message_key.bridge_seq_num, - source_chain, - }) - } - } - }; -} - -new_move_event!(TokenTransferClaimed, MoveTokenTransferClaimed); -new_move_event!(TokenTransferApproved, MoveTokenTransferApproved); -new_move_event!( - TokenTransferAlreadyApproved, - MoveTokenTransferAlreadyApproved -); -new_move_event!(TokenTransferAlreadyClaimed, MoveTokenTransferAlreadyClaimed); -new_move_event!(TokenTransferLimitExceed, MoveTokenTransferLimitExceed); - -// `EmergencyOpEvent` emitted in bridge.move -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub struct EmergencyOpEvent { - pub frozen: bool, -} - -// `CommitteeUpdateEvent` emitted in committee.move -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveCommitteeUpdateEvent { - pub members: VecMap, MoveTypeCommitteeMember>, - pub stake_participation_percentage: u64, -} - -// `CommitteeMemberUrlUpdateEvent` emitted in committee.move -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveCommitteeMemberUrlUpdateEvent { - pub member: Vec, - pub new_url: Vec, -} - -// `BlocklistValidatorEvent` emitted in committee.move -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveBlocklistValidatorEvent { - pub blocklisted: bool, - pub public_keys: Vec>, -} - -// `UpdateRouteLimitEvent` emitted in limiter.move -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveUpdateRouteLimitEvent { - pub sending_chain: u8, - pub receiving_chain: u8, - pub new_limit: u64, -} - -// `TokenRegistrationEvent` emitted in treasury.move -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveTokenRegistrationEvent { - pub type_name: String, - pub decimal: u8, - pub native_token: bool, -} - -// Sanitized version of MoveTokenRegistrationEvent -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct TokenRegistrationEvent { - pub type_name: TypeTag, - pub decimal: u8, - pub native_token: bool, -} - -impl TryFrom for TokenRegistrationEvent { - type Error = BridgeError; - - fn try_from(event: MoveTokenRegistrationEvent) -> BridgeResult { - let type_name = parse_iota_type_tag(&format!("0x{}", event.type_name)).map_err(|e| { - BridgeError::Internal(format!( - "Failed to parse TypeTag: {e}, type name: {}", - event.type_name - )) - })?; - - Ok(Self { - type_name, - decimal: event.decimal, - native_token: event.native_token, - }) - } -} - -// `NewTokenEvent` emitted in treasury.move -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveNewTokenEvent { - pub token_id: u8, - pub type_name: String, - pub native_token: bool, - pub decimal_multiplier: u64, - pub notional_value: u64, -} - -// Sanitized version of MoveNewTokenEvent -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct NewTokenEvent { - pub token_id: u8, - pub type_name: TypeTag, - pub native_token: bool, - pub decimal_multiplier: u64, - pub notional_value: u64, -} - -impl TryFrom for NewTokenEvent { - type Error = BridgeError; - - fn try_from(event: MoveNewTokenEvent) -> BridgeResult { - let type_name = parse_iota_type_tag(&format!("0x{}", event.type_name)).map_err(|e| { - BridgeError::Internal(format!( - "Failed to parse TypeTag: {e}, type name: {}", - event.type_name - )) - })?; - - Ok(Self { - token_id: event.token_id, - type_name, - native_token: event.native_token, - decimal_multiplier: event.decimal_multiplier, - notional_value: event.notional_value, - }) - } -} - -// `UpdateTokenPriceEvent` emitted in treasury.move -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub struct UpdateTokenPriceEvent { - pub token_id: u8, - pub new_price: u64, -} - -// Sanitized version of MoveTokenDepositedEvent -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Hash)] -pub struct EmittedIotaToEthTokenBridgeV1 { - pub nonce: u64, - pub iota_chain_id: BridgeChainId, - pub eth_chain_id: BridgeChainId, - pub iota_address: IotaAddress, - pub eth_address: EthAddress, - pub token_id: u8, - // The amount of tokens deposited with decimal points on IOTA side - pub amount_iota_adjusted: u64, -} - -// Sanitized version of MoveCommitteeUpdateEvent -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct CommitteeUpdate { - pub members: Vec, - pub stake_participation_percentage: u64, -} - -impl TryFrom for CommitteeUpdate { - type Error = BridgeError; - - fn try_from(event: MoveCommitteeUpdateEvent) -> BridgeResult { - let members = event - .members - .contents - .into_iter() - .map(|v| v.value) - .collect(); - Ok(Self { - members, - stake_participation_percentage: event.stake_participation_percentage, - }) - } -} - -// Sanitized version of MoveBlocklistValidatorEvent -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct BlocklistValidatorEvent { - pub blocklisted: bool, - pub public_keys: Vec, -} - -impl TryFrom for BlocklistValidatorEvent { - type Error = BridgeError; - - fn try_from(event: MoveBlocklistValidatorEvent) -> BridgeResult { - let public_keys = event.public_keys.into_iter().map(|bytes| - BridgeAuthorityPublicKey::from_bytes(&bytes).map_err(|e| - BridgeError::Generic(format!("Failed to convert MoveBlocklistValidatorEvent to BlocklistValidatorEvent. Failed to convert public key to BridgeAuthorityPublicKey: {:?}", e)) - ) - ).collect::>>()?; - Ok(Self { - blocklisted: event.blocklisted, - public_keys, - }) - } -} - -// Sanitized version of MoveCommitteeMemberUrlUpdateEvent -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct CommitteeMemberUrlUpdateEvent { - pub member: BridgeAuthorityPublicKey, - pub new_url: String, -} - -impl TryFrom for CommitteeMemberUrlUpdateEvent { - type Error = BridgeError; - - fn try_from(event: MoveCommitteeMemberUrlUpdateEvent) -> BridgeResult { - let member = BridgeAuthorityPublicKey::from_bytes(&event.member).map_err(|e| - BridgeError::Generic(format!("Failed to convert MoveBlocklistValidatorEvent to BlocklistValidatorEvent. Failed to convert public key to BridgeAuthorityPublicKey: {:?}", e)) - )?; - let new_url = String::from_utf8(event.new_url).map_err(|e| - BridgeError::Generic(format!("Failed to convert MoveBlocklistValidatorEvent to BlocklistValidatorEvent. Failed to convert new_url to String: {:?}", e)) - )?; - Ok(Self { member, new_url }) - } -} - -impl TryFrom for EmittedIotaToEthTokenBridgeV1 { - type Error = BridgeError; - - fn try_from(event: MoveTokenDepositedEvent) -> BridgeResult { - if event.amount_iota_adjusted == 0 { - return Err(BridgeError::ZeroValueBridgeTransfer(format!( - "Failed to convert MoveTokenDepositedEvent to EmittedIotaToEthTokenBridgeV1. Manual intervention is required. 0 value transfer should not be allowed in Move: {:?}", - event, - ))); - } - - let token_id = event.token_type; - let iota_chain_id = BridgeChainId::try_from(event.source_chain).map_err(|_e| { - BridgeError::Generic(format!( - "Failed to convert MoveTokenDepositedEvent to EmittedIotaToEthTokenBridgeV1. Failed to convert source chain {} to BridgeChainId", - event.token_type, - )) - })?; - let eth_chain_id = BridgeChainId::try_from(event.target_chain).map_err(|_e| { - BridgeError::Generic(format!( - "Failed to convert MoveTokenDepositedEvent to EmittedIotaToEthTokenBridgeV1. Failed to convert target chain {} to BridgeChainId", - event.token_type, - )) - })?; - if !iota_chain_id.is_iota_chain() { - return Err(BridgeError::Generic(format!( - "Failed to convert MoveTokenDepositedEvent to EmittedIotaToEthTokenBridgeV1. Invalid source chain {}", - event.source_chain - ))); - } - if eth_chain_id.is_iota_chain() { - return Err(BridgeError::Generic(format!( - "Failed to convert MoveTokenDepositedEvent to EmittedIotaToEthTokenBridgeV1. Invalid target chain {}", - event.target_chain - ))); - } - - let iota_address = IotaAddress::from_bytes(event.sender_address) - .map_err(|e| BridgeError::Generic(format!("Failed to convert MoveTokenDepositedEvent to EmittedIotaToEthTokenBridgeV1. Failed to convert sender_address to IotaAddress: {:?}", e)))?; - let eth_address = EthAddress::from_str(&Hex::encode(&event.target_address))?; - - Ok(Self { - nonce: event.seq_num, - iota_chain_id, - eth_chain_id, - iota_address, - eth_address, - token_id, - amount_iota_adjusted: event.amount_iota_adjusted, - }) - } -} - -crate::declare_events!( - IotaToEthTokenBridgeV1(EmittedIotaToEthTokenBridgeV1) => ("bridge::TokenDepositedEvent", MoveTokenDepositedEvent), - TokenTransferApproved(TokenTransferApproved) => ("bridge::TokenTransferApproved", MoveTokenTransferApproved), - TokenTransferClaimed(TokenTransferClaimed) => ("bridge::TokenTransferClaimed", MoveTokenTransferClaimed), - TokenTransferAlreadyApproved(TokenTransferAlreadyApproved) => ("bridge::TokenTransferAlreadyApproved", MoveTokenTransferAlreadyApproved), - TokenTransferAlreadyClaimed(TokenTransferAlreadyClaimed) => ("bridge::TokenTransferAlreadyClaimed", MoveTokenTransferAlreadyClaimed), - TokenTransferLimitExceed(TokenTransferLimitExceed) => ("bridge::TokenTransferLimitExceed", MoveTokenTransferLimitExceed), - EmergencyOpEvent(EmergencyOpEvent) => ("bridge::EmergencyOpEvent", EmergencyOpEvent), - // No need to define a sanitized event struct for MoveTypeCommitteeMemberRegistration - // because the info provided by validators could be invalid - CommitteeMemberRegistration(MoveTypeCommitteeMemberRegistration) => ("committee::CommitteeMemberRegistration", MoveTypeCommitteeMemberRegistration), - CommitteeUpdateEvent(CommitteeUpdate) => ("committee::CommitteeUpdateEvent", MoveCommitteeUpdateEvent), - CommitteeMemberUrlUpdateEvent(CommitteeMemberUrlUpdateEvent) => ("committee::CommitteeMemberUrlUpdateEvent", MoveCommitteeMemberUrlUpdateEvent), - BlocklistValidatorEvent(BlocklistValidatorEvent) => ("committee::BlocklistValidatorEvent", MoveBlocklistValidatorEvent), - TokenRegistrationEvent(TokenRegistrationEvent) => ("treasury::TokenRegistrationEvent", MoveTokenRegistrationEvent), - NewTokenEvent(NewTokenEvent) => ("treasury::NewTokenEvent", MoveNewTokenEvent), - UpdateTokenPriceEvent(UpdateTokenPriceEvent) => ("treasury::UpdateTokenPriceEvent", UpdateTokenPriceEvent), - - // Add new event types here. Format: - // EnumVariantName(Struct) => ("{module}::{event_struct}", CorrespondingMoveStruct) -); - -#[macro_export] -macro_rules! declare_events { - ($($variant:ident($type:path) => ($event_tag:expr, $event_struct:path)),* $(,)?) => { - - #[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] - pub enum IotaBridgeEvent { - $($variant($type),)* - } - - $(pub static $variant: OnceCell = OnceCell::new();)* - - pub(crate) fn init_all_struct_tags() { - $($variant.get_or_init(|| { - StructTag::from_str(&format!("0x{}::{}", BRIDGE_PACKAGE_ID.to_hex(), $event_tag)).unwrap() - });)* - } - - // Try to convert a IotaEvent into IotaBridgeEvent - impl IotaBridgeEvent { - pub fn try_from_iota_event(event: &IotaEvent) -> BridgeResult> { - init_all_struct_tags(); // Ensure all tags are initialized - - // Unwrap safe: we inited above - $( - if &event.type_ == $variant.get().unwrap() { - let event_struct: $event_struct = bcs::from_bytes(event.bcs.bytes()).map_err(|e| BridgeError::Internal(format!("Failed to deserialize event to {}: {:?}", stringify!($event_struct), e)))?; - return Ok(Some(IotaBridgeEvent::$variant(event_struct.try_into()?))); - } - )* - Ok(None) - } - } - }; -} - -impl IotaBridgeEvent { - pub fn try_into_bridge_action( - self, - iota_tx_digest: TransactionDigest, - iota_tx_event_index: u16, - ) -> Option { - match self { - IotaBridgeEvent::IotaToEthTokenBridgeV1(event) => { - Some(BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction { - iota_tx_digest, - iota_tx_event_index, - iota_bridge_event: event.clone(), - })) - } - IotaBridgeEvent::TokenTransferApproved(_event) => None, - IotaBridgeEvent::TokenTransferClaimed(_event) => None, - IotaBridgeEvent::TokenTransferAlreadyApproved(_event) => None, - IotaBridgeEvent::TokenTransferAlreadyClaimed(_event) => None, - IotaBridgeEvent::TokenTransferLimitExceed(_event) => None, - IotaBridgeEvent::EmergencyOpEvent(_event) => None, - IotaBridgeEvent::CommitteeMemberRegistration(_event) => None, - IotaBridgeEvent::CommitteeUpdateEvent(_event) => None, - IotaBridgeEvent::CommitteeMemberUrlUpdateEvent(_event) => None, - IotaBridgeEvent::BlocklistValidatorEvent(_event) => None, - IotaBridgeEvent::TokenRegistrationEvent(_event) => None, - IotaBridgeEvent::NewTokenEvent(_event) => None, - IotaBridgeEvent::UpdateTokenPriceEvent(_event) => None, - } - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::HashSet; - - use ethers::types::Address as EthAddress; - use iota_json_rpc_types::{BcsEvent, IotaEvent}; - use iota_types::{ - Identifier, - base_types::{IotaAddress, ObjectID}, - bridge::{BridgeChainId, TOKEN_ID_IOTA}, - crypto::get_key_pair, - digests::TransactionDigest, - event::EventID, - }; - - use super::*; - use crate::{ - crypto::BridgeAuthorityKeyPair, - e2e_tests::test_utils::BridgeTestClusterBuilder, - types::{BridgeAction, IotaToEthBridgeAction}, - }; - - /// Returns a test IotaEvent and corresponding BridgeAction - pub fn get_test_iota_event_and_action(identifier: Identifier) -> (IotaEvent, BridgeAction) { - init_all_struct_tags(); // Ensure all tags are initialized - let sanitized_event = EmittedIotaToEthTokenBridgeV1 { - nonce: 1, - iota_chain_id: BridgeChainId::IotaTestnet, - iota_address: IotaAddress::random_for_testing_only(), - eth_chain_id: BridgeChainId::EthSepolia, - eth_address: EthAddress::random(), - token_id: TOKEN_ID_IOTA, - amount_iota_adjusted: 100, - }; - let emitted_event = MoveTokenDepositedEvent { - seq_num: sanitized_event.nonce, - source_chain: sanitized_event.iota_chain_id as u8, - sender_address: sanitized_event.iota_address.to_vec(), - target_chain: sanitized_event.eth_chain_id as u8, - target_address: sanitized_event.eth_address.as_bytes().to_vec(), - token_type: sanitized_event.token_id, - amount_iota_adjusted: sanitized_event.amount_iota_adjusted, - }; - - let tx_digest = TransactionDigest::random(); - let event_idx = 10u16; - let bridge_action = BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction { - iota_tx_digest: tx_digest, - iota_tx_event_index: event_idx, - iota_bridge_event: sanitized_event.clone(), - }); - let event = IotaEvent { - type_: IotaToEthTokenBridgeV1.get().unwrap().clone(), - bcs: BcsEvent::new(bcs::to_bytes(&emitted_event).unwrap()), - id: EventID { - tx_digest, - event_seq: event_idx as u64, - }, - - // The following fields do not matter as of writing, - // but if tests start to fail, it's worth checking these fields. - package_id: ObjectID::ZERO, - transaction_module: identifier.clone(), - sender: IotaAddress::random_for_testing_only(), - parsed_json: serde_json::json!({"test": "test"}), - timestamp_ms: None, - }; - (event, bridge_action) - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_events_when_init() { - telemetry_subscribers::init_for_testing(); - init_all_struct_tags(); - let mut bridge_test_cluster = BridgeTestClusterBuilder::new() - .with_eth_env(false) - .with_bridge_cluster(false) - .with_num_validators(2) - .build() - .await; - - let events = bridge_test_cluster - .new_bridge_events( - HashSet::from_iter([ - CommitteeMemberRegistration.get().unwrap().clone(), - CommitteeUpdateEvent.get().unwrap().clone(), - TokenRegistrationEvent.get().unwrap().clone(), - NewTokenEvent.get().unwrap().clone(), - ]), - false, - ) - .await; - let mut mask = 0u8; - for event in events.iter() { - match IotaBridgeEvent::try_from_iota_event(event) - .unwrap() - .unwrap() - { - IotaBridgeEvent::CommitteeMemberRegistration(_event) => mask |= 0x1, - IotaBridgeEvent::CommitteeUpdateEvent(_event) => mask |= 0x2, - IotaBridgeEvent::TokenRegistrationEvent(_event) => mask |= 0x4, - IotaBridgeEvent::NewTokenEvent(_event) => mask |= 0x8, - _ => panic!("Got unexpected event: {:?}", event), - } - } - // assert all the above events are emitted - assert_eq!(mask, 0xF); - - // TODO: trigger other events and make sure they are converted correctly - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_conversion_for_committee_member_url_update_event() { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - let new_url = "https://example.com:443"; - let event: CommitteeMemberUrlUpdateEvent = MoveCommitteeMemberUrlUpdateEvent { - member: kp.public.as_bytes().to_vec(), - new_url: new_url.as_bytes().to_vec(), - } - .try_into() - .unwrap(); - assert_eq!(event.member, kp.public); - assert_eq!(event.new_url, new_url); - - CommitteeMemberUrlUpdateEvent::try_from(MoveCommitteeMemberUrlUpdateEvent { - member: vec![1, 2, 3], - new_url: new_url.as_bytes().to_vec(), - }) - .unwrap_err(); - - CommitteeMemberUrlUpdateEvent::try_from(MoveCommitteeMemberUrlUpdateEvent { - member: kp.public.as_bytes().to_vec(), - new_url: [240, 130, 130, 172].into(), - }) - .unwrap_err(); - } - - // TODO: add conversion tests for other events - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_0_iota_amount_conversion_for_iota_event() { - let emitted_event = MoveTokenDepositedEvent { - seq_num: 1, - source_chain: BridgeChainId::IotaTestnet as u8, - sender_address: IotaAddress::random_for_testing_only().to_vec(), - target_chain: BridgeChainId::EthSepolia as u8, - target_address: EthAddress::random().as_bytes().to_vec(), - token_type: TOKEN_ID_IOTA, - amount_iota_adjusted: 0, - }; - match EmittedIotaToEthTokenBridgeV1::try_from(emitted_event).unwrap_err() { - BridgeError::ZeroValueBridgeTransfer(_) => (), - other => panic!("Expected Generic error, got: {:?}", other), - } - } -} diff --git a/crates/iota-bridge/src/iota_client.rs b/crates/iota-bridge/src/iota_client.rs deleted file mode 100644 index 02e1562d02a..00000000000 --- a/crates/iota-bridge/src/iota_client.rs +++ /dev/null @@ -1,902 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::panic; -use std::{collections::HashMap, str::from_utf8, time::Duration}; - -use anyhow::anyhow; -use async_trait::async_trait; -use fastcrypto::traits::ToFromBytes; -use iota_json_rpc_api::BridgeReadApiClient; -use iota_json_rpc_types::{ - DevInspectResults, EventFilter, EventPage, IotaEvent, IotaObjectDataOptions, - IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, Page, -}; -use iota_sdk::{IotaClient as IotaSdkClient, IotaClientBuilder}; -use iota_types::{ - BRIDGE_PACKAGE_ID, IOTA_BRIDGE_OBJECT_ID, Identifier, TypeTag, - base_types::{IotaAddress, ObjectID, ObjectRef, SequenceNumber}, - bridge::{ - BridgeSummary, BridgeTreasurySummary, MoveTypeCommitteeMember, - MoveTypeParsedTokenTransferMessage, - }, - digests::TransactionDigest, - event::EventID, - gas_coin::GasCoin, - object::Owner, - parse_iota_type_tag, - transaction::{ - Argument, CallArg, Command, ObjectArg, ProgrammableMoveCall, ProgrammableTransaction, - Transaction, TransactionKind, - }, -}; -use serde::de::DeserializeOwned; -use tokio::sync::OnceCell; -use tracing::{error, warn}; - -use crate::{ - crypto::BridgeAuthorityPublicKey, - error::{BridgeError, BridgeResult}, - events::IotaBridgeEvent, - retry_with_max_elapsed_time, - types::{ - BridgeAction, BridgeActionStatus, BridgeAuthority, BridgeCommittee, - ParsedTokenTransferMessage, - }, -}; - -pub struct IotaClient

{ - inner: P, -} - -pub type IotaBridgeClient = IotaClient; - -impl IotaBridgeClient { - pub async fn new(rpc_url: &str) -> anyhow::Result { - let inner = IotaClientBuilder::default() - .build(rpc_url) - .await - .map_err(|e| { - anyhow!("Can't establish connection with IOTA Rpc {rpc_url}. Error: {e}") - })?; - let self_ = Self { inner }; - self_.describe().await?; - Ok(self_) - } - - pub fn iota_client(&self) -> &IotaSdkClient { - &self.inner - } -} - -impl

IotaClient

-where - P: IotaClientInner, -{ - pub fn new_for_testing(inner: P) -> Self { - Self { inner } - } - - // TODO assert chain identifier - async fn describe(&self) -> anyhow::Result<()> { - let chain_id = self.inner.get_chain_identifier().await?; - let block_number = self.inner.get_latest_checkpoint_sequence_number().await?; - tracing::info!( - "IotaClient is connected to chain {chain_id}, current block number: {block_number}" - ); - Ok(()) - } - - /// Get the mutable bridge object arg on chain. - // We retry a few times in case of errors. If it fails eventually, we panic. - // In general it's safe to call in the beginning of the program. - // After the first call, the result is cached since the value should never - // change. - pub async fn get_mutable_bridge_object_arg_must_succeed(&self) -> ObjectArg { - static ARG: OnceCell = OnceCell::const_new(); - *ARG.get_or_init(|| async move { - let Ok(Ok(bridge_object_arg)) = retry_with_max_elapsed_time!( - self.inner.get_mutable_bridge_object_arg(), - Duration::from_secs(30) - ) else { - panic!("Failed to get bridge object arg after retries"); - }; - bridge_object_arg - }) - .await - } - - /// Query emitted Events that are defined in the given Move Module. - pub async fn query_events_by_module( - &self, - package: ObjectID, - module: Identifier, - // cursor is exclusive - cursor: Option, - ) -> BridgeResult> { - let filter = EventFilter::MoveEventModule { - package, - module: module.clone(), - }; - let events = self.inner.query_events(filter.clone(), cursor).await?; - - // Safeguard check that all events are emitted from requested package and module - assert!( - events - .data - .iter() - .all(|event| event.type_.address.as_ref() == package.as_ref() - && event.type_.module == module) - ); - Ok(events) - } - - /// Returns BridgeAction from an IOTA Transaction with transaction hash - /// and the event index. If event is declared in an unrecognized - /// package, return error. - pub async fn get_bridge_action_by_tx_digest_and_event_idx_maybe( - &self, - tx_digest: &TransactionDigest, - event_idx: u16, - ) -> BridgeResult { - let events = self.inner.get_events_by_tx_digest(*tx_digest).await?; - let event = events - .get(event_idx as usize) - .ok_or(BridgeError::NoBridgeEventsInTxPosition)?; - if event.type_.address.as_ref() != BRIDGE_PACKAGE_ID.as_ref() { - return Err(BridgeError::BridgeEventInUnrecognizedIotaPackage); - } - let bridge_event = IotaBridgeEvent::try_from_iota_event(event)? - .ok_or(BridgeError::NoBridgeEventsInTxPosition)?; - - bridge_event - .try_into_bridge_action(*tx_digest, event_idx) - .ok_or(BridgeError::BridgeEventNotActionable) - } - - pub async fn get_bridge_summary(&self) -> BridgeResult { - self.inner - .get_bridge_summary() - .await - .map_err(|e| BridgeError::Internal(format!("Can't get bridge committee: {e}"))) - } - - pub async fn is_bridge_paused(&self) -> BridgeResult { - self.get_bridge_summary() - .await - .map(|summary| summary.is_frozen) - } - - pub async fn get_treasury_summary(&self) -> BridgeResult { - Ok(self.get_bridge_summary().await?.treasury) - } - - pub async fn get_token_id_map(&self) -> BridgeResult> { - self.get_bridge_summary() - .await? - .treasury - .id_token_type_map - .into_iter() - .map(|(id, name)| { - parse_iota_type_tag(&format!("0x{name}")) - .map(|name| (id, name)) - .map_err(|e| { - BridgeError::Internal(format!( - "Failed to retrieve token id mapping: {e}, type name: {name}" - )) - }) - }) - .collect() - } - - pub async fn get_notional_values(&self) -> BridgeResult> { - let bridge_summary = self.get_bridge_summary().await?; - bridge_summary - .treasury - .id_token_type_map - .iter() - .map(|(id, type_name)| { - bridge_summary - .treasury - .supported_tokens - .iter() - .find_map(|(tn, metadata)| { - if type_name == tn { - Some((*id, metadata.notional_value)) - } else { - None - } - }) - .ok_or(BridgeError::Internal( - "Error encountered when retrieving token notional values.".into(), - )) - }) - .collect() - } - - pub async fn get_bridge_committee(&self) -> BridgeResult { - let bridge_summary = self - .inner - .get_bridge_summary() - .await - .map_err(|e| BridgeError::Internal(format!("Can't get bridge committee: {e}")))?; - let move_type_bridge_committee = bridge_summary.committee; - - let mut authorities = vec![]; - // TODO: move this to MoveTypeBridgeCommittee - for (_, member) in move_type_bridge_committee.members { - let MoveTypeCommitteeMember { - iota_address, - bridge_pubkey_bytes, - voting_power, - http_rest_url, - blocklisted, - } = member; - let pubkey = BridgeAuthorityPublicKey::from_bytes(&bridge_pubkey_bytes)?; - let base_url = from_utf8(&http_rest_url).unwrap_or_else(|_e| { - warn!( - "Bridge authority address: {}, pubkey: {:?} has invalid http url: {:?}", - iota_address, bridge_pubkey_bytes, http_rest_url - ); - "" - }); - authorities.push(BridgeAuthority { - pubkey, - voting_power, - base_url: base_url.into(), - is_blocklisted: blocklisted, - }); - } - BridgeCommittee::new(authorities) - } - - pub async fn get_chain_identifier(&self) -> BridgeResult { - Ok(self.inner.get_chain_identifier().await?) - } - - pub async fn get_reference_gas_price_until_success(&self) -> u64 { - loop { - let Ok(Ok(rgp)) = retry_with_max_elapsed_time!( - self.inner.get_reference_gas_price(), - Duration::from_secs(30) - ) else { - // TODO: add metrics and fire alert - error!("Failed to get reference gas price"); - continue; - }; - return rgp; - } - } - - pub async fn execute_transaction_block_with_effects( - &self, - tx: iota_types::transaction::Transaction, - ) -> BridgeResult { - self.inner.execute_transaction_block_with_effects(tx).await - } - - // TODO: this function is very slow (seconds) in tests, we need to optimize it - pub async fn get_token_transfer_action_onchain_status_until_success( - &self, - source_chain_id: u8, - seq_number: u64, - ) -> BridgeActionStatus { - loop { - let bridge_object_arg = self.get_mutable_bridge_object_arg_must_succeed().await; - let Ok(Ok(status)) = retry_with_max_elapsed_time!( - self.inner.get_token_transfer_action_onchain_status( - bridge_object_arg, - source_chain_id, - seq_number - ), - Duration::from_secs(30) - ) else { - // TODO: add metrics and fire alert - error!( - source_chain_id, - seq_number, "Failed to get token transfer action onchain status" - ); - continue; - }; - return status; - } - } - - pub async fn get_token_transfer_action_onchain_signatures_until_success( - &self, - source_chain_id: u8, - seq_number: u64, - ) -> Option>> { - loop { - let bridge_object_arg = self.get_mutable_bridge_object_arg_must_succeed().await; - let Ok(Ok(sigs)) = retry_with_max_elapsed_time!( - self.inner.get_token_transfer_action_onchain_signatures( - bridge_object_arg, - source_chain_id, - seq_number - ), - Duration::from_secs(30) - ) else { - // TODO: add metrics and fire alert - error!( - source_chain_id, - seq_number, "Failed to get token transfer action onchain signatures" - ); - continue; - }; - return sigs; - } - } - - pub async fn get_parsed_token_transfer_message( - &self, - source_chain_id: u8, - seq_number: u64, - ) -> BridgeResult> { - let bridge_object_arg = self.get_mutable_bridge_object_arg_must_succeed().await; - let message = self - .inner - .get_parsed_token_transfer_message(bridge_object_arg, source_chain_id, seq_number) - .await?; - Ok(match message { - Some(payload) => Some(ParsedTokenTransferMessage::try_from(payload)?), - None => None, - }) - } - - pub async fn get_gas_data_panic_if_not_gas( - &self, - gas_object_id: ObjectID, - ) -> (GasCoin, ObjectRef, Owner) { - self.inner - .get_gas_data_panic_if_not_gas(gas_object_id) - .await - } -} - -/// Use a trait to abstract over the IotaSDKClient and IotaMockClient for -/// testing. -#[async_trait] -pub trait IotaClientInner: Send + Sync { - type Error: Into + Send + Sync + std::error::Error + 'static; - async fn query_events( - &self, - query: EventFilter, - cursor: Option, - ) -> Result; - - async fn get_events_by_tx_digest( - &self, - tx_digest: TransactionDigest, - ) -> Result, Self::Error>; - - async fn get_chain_identifier(&self) -> Result; - - async fn get_reference_gas_price(&self) -> Result; - - async fn get_latest_checkpoint_sequence_number(&self) -> Result; - - async fn get_mutable_bridge_object_arg(&self) -> Result; - - async fn get_bridge_summary(&self) -> Result; - - async fn execute_transaction_block_with_effects( - &self, - tx: Transaction, - ) -> Result; - - async fn get_token_transfer_action_onchain_status( - &self, - bridge_object_arg: ObjectArg, - source_chain_id: u8, - seq_number: u64, - ) -> Result; - - async fn get_token_transfer_action_onchain_signatures( - &self, - bridge_object_arg: ObjectArg, - source_chain_id: u8, - seq_number: u64, - ) -> Result>>, BridgeError>; - - async fn get_parsed_token_transfer_message( - &self, - bridge_object_arg: ObjectArg, - source_chain_id: u8, - seq_number: u64, - ) -> Result, BridgeError>; - - async fn get_gas_data_panic_if_not_gas( - &self, - gas_object_id: ObjectID, - ) -> (GasCoin, ObjectRef, Owner); -} - -#[async_trait] -impl IotaClientInner for IotaSdkClient { - type Error = iota_sdk::error::Error; - - async fn query_events( - &self, - query: EventFilter, - cursor: Option, - ) -> Result { - self.event_api() - .query_events(query, cursor, None, false) - .await - } - - async fn get_events_by_tx_digest( - &self, - tx_digest: TransactionDigest, - ) -> Result, Self::Error> { - self.event_api().get_events(tx_digest).await - } - - async fn get_chain_identifier(&self) -> Result { - self.read_api().get_chain_identifier().await - } - - async fn get_reference_gas_price(&self) -> Result { - self.governance_api().get_reference_gas_price().await - } - - async fn get_latest_checkpoint_sequence_number(&self) -> Result { - self.read_api() - .get_latest_checkpoint_sequence_number() - .await - } - - async fn get_mutable_bridge_object_arg(&self) -> Result { - let initial_shared_version = self - .http() - .get_bridge_object_initial_shared_version() - .await?; - Ok(ObjectArg::SharedObject { - id: IOTA_BRIDGE_OBJECT_ID, - initial_shared_version: SequenceNumber::from_u64(initial_shared_version), - mutable: true, - }) - } - - async fn get_bridge_summary(&self) -> Result { - self.http().get_latest_bridge().await.map_err(|e| e.into()) - } - - async fn get_token_transfer_action_onchain_status( - &self, - bridge_object_arg: ObjectArg, - source_chain_id: u8, - seq_number: u64, - ) -> Result { - dev_inspect_bridge::( - self, - bridge_object_arg, - source_chain_id, - seq_number, - "get_token_transfer_action_status", - ) - .await - .and_then(|status_byte| BridgeActionStatus::try_from(status_byte).map_err(Into::into)) - } - - async fn get_token_transfer_action_onchain_signatures( - &self, - bridge_object_arg: ObjectArg, - source_chain_id: u8, - seq_number: u64, - ) -> Result>>, BridgeError> { - dev_inspect_bridge::>>>( - self, - bridge_object_arg, - source_chain_id, - seq_number, - "get_token_transfer_action_signatures", - ) - .await - } - - async fn execute_transaction_block_with_effects( - &self, - tx: Transaction, - ) -> Result { - match self.quorum_driver_api().execute_transaction_block( - tx, - IotaTransactionBlockResponseOptions::new().with_effects().with_events(), - Some(iota_types::quorum_driver_types::ExecuteTransactionRequestType::WaitForEffectsCert), - ).await { - Ok(response) => Ok(response), - Err(e) => return Err(BridgeError::IotaTxFailureGeneric(e.to_string())), - } - } - - async fn get_parsed_token_transfer_message( - &self, - bridge_object_arg: ObjectArg, - source_chain_id: u8, - seq_number: u64, - ) -> Result, BridgeError> { - dev_inspect_bridge::>( - self, - bridge_object_arg, - source_chain_id, - seq_number, - "get_parsed_token_transfer_message", - ) - .await - } - - async fn get_gas_data_panic_if_not_gas( - &self, - gas_object_id: ObjectID, - ) -> (GasCoin, ObjectRef, Owner) { - loop { - match self - .read_api() - .get_object_with_options( - gas_object_id, - IotaObjectDataOptions::default().with_owner().with_content(), - ) - .await - .map(|resp| resp.data) - { - Ok(Some(gas_obj)) => { - let owner = gas_obj.owner.expect("Owner is requested"); - let gas_coin = GasCoin::try_from(&gas_obj) - .unwrap_or_else(|err| panic!("{} is not a gas coin: {err}", gas_object_id)); - return (gas_coin, gas_obj.object_ref(), owner); - } - other => { - warn!("Can't get gas object: {:?}: {:?}", gas_object_id, other); - tokio::time::sleep(Duration::from_secs(5)).await; - } - } - } - } -} - -/// Helper function to dev-inspect `bridge::{function_name}` function -/// with bridge object arg, source chain id, seq number as param -/// and parse the return value as `T`. -async fn dev_inspect_bridge( - iota_client: &IotaSdkClient, - bridge_object_arg: ObjectArg, - source_chain_id: u8, - seq_number: u64, - function_name: &str, -) -> Result -where - T: DeserializeOwned, -{ - let pt = ProgrammableTransaction { - inputs: vec![ - CallArg::Object(bridge_object_arg), - CallArg::Pure(bcs::to_bytes(&source_chain_id).unwrap()), - CallArg::Pure(bcs::to_bytes(&seq_number).unwrap()), - ], - commands: vec![Command::MoveCall(Box::new(ProgrammableMoveCall { - package: BRIDGE_PACKAGE_ID, - module: Identifier::new("bridge").unwrap(), - function: Identifier::new(function_name).unwrap(), - type_arguments: vec![], - arguments: vec![Argument::Input(0), Argument::Input(1), Argument::Input(2)], - }))], - }; - let kind = TransactionKind::programmable(pt); - let resp = iota_client - .read_api() - .dev_inspect_transaction_block(IotaAddress::ZERO, kind, None, None, None) - .await?; - let DevInspectResults { - results, effects, .. - } = resp; - let Some(results) = results else { - return Err(BridgeError::Generic(format!( - "No results returned for '{}', effects: {:?}", - function_name, effects - ))); - }; - let return_values = &results - .first() - .ok_or(BridgeError::Generic(format!( - "No return values for '{}', results: {:?}", - function_name, results - )))? - .return_values; - let (value_bytes, _type_tag) = return_values.first().ok_or(BridgeError::Generic(format!( - "No first return value for '{}', results: {:?}", - function_name, results - )))?; - bcs::from_bytes::(value_bytes).map_err(|e| { - BridgeError::Generic(format!( - "Failed to parse return value for '{}', error: {:?}, results: {:?}", - function_name, e, results - )) - }) -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use ethers::types::Address as EthAddress; - use iota_json_rpc_types::BcsEvent; - use iota_types::{ - bridge::{BridgeChainId, TOKEN_ID_IOTA, TOKEN_ID_USDC}, - crypto::get_key_pair, - }; - use move_core_types::account_address::AccountAddress; - use serde::{Deserialize, Serialize}; - use test_cluster::TestClusterBuilder; - - use super::*; - use crate::{ - BRIDGE_ENABLE_PROTOCOL_VERSION, - crypto::BridgeAuthorityKeyPair, - events::{ - EmittedIotaToEthTokenBridgeV1, IotaToEthTokenBridgeV1, MoveTokenDepositedEvent, - init_all_struct_tags, - }, - iota_mock_client::IotaMockClient, - test_utils::{ - approve_action_with_validator_secrets, bridge_token, - get_test_eth_to_iota_bridge_action, get_test_iota_to_eth_bridge_action, - }, - types::IotaToEthBridgeAction, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn get_bridge_action_by_tx_digest_and_event_idx_maybe() { - // Note: for random events generated in this test, we only care about - // tx_digest and event_seq, so it's ok that package and module does - // not match the query parameters. - telemetry_subscribers::init_for_testing(); - let mock_client = IotaMockClient::default(); - let iota_client = IotaClient::new_for_testing(mock_client.clone()); - let tx_digest = TransactionDigest::random(); - - // Ensure all struct tags are inited - init_all_struct_tags(); - - let sanitized_event_1 = EmittedIotaToEthTokenBridgeV1 { - nonce: 1, - iota_chain_id: BridgeChainId::IotaTestnet, - iota_address: IotaAddress::random_for_testing_only(), - eth_chain_id: BridgeChainId::EthSepolia, - eth_address: EthAddress::random(), - token_id: TOKEN_ID_IOTA, - amount_iota_adjusted: 100, - }; - let emitted_event_1 = MoveTokenDepositedEvent { - seq_num: sanitized_event_1.nonce, - source_chain: sanitized_event_1.iota_chain_id as u8, - sender_address: sanitized_event_1.iota_address.to_vec(), - target_chain: sanitized_event_1.eth_chain_id as u8, - target_address: sanitized_event_1.eth_address.as_bytes().to_vec(), - token_type: sanitized_event_1.token_id, - amount_iota_adjusted: sanitized_event_1.amount_iota_adjusted, - }; - - let mut iota_event_1 = IotaEvent::random_for_testing(); - iota_event_1.type_ = IotaToEthTokenBridgeV1.get().unwrap().clone(); - iota_event_1.bcs = BcsEvent::new(bcs::to_bytes(&emitted_event_1).unwrap()); - - #[derive(Serialize, Deserialize)] - struct RandomStruct {} - - let event_2: RandomStruct = RandomStruct {}; - // undeclared struct tag - let mut iota_event_2 = IotaEvent::random_for_testing(); - iota_event_2.type_ = IotaToEthTokenBridgeV1.get().unwrap().clone(); - iota_event_2.type_.module = Identifier::from_str("unrecognized_module").unwrap(); - iota_event_2.bcs = BcsEvent::new(bcs::to_bytes(&event_2).unwrap()); - - // Event 3 is defined in non-bridge package - let mut iota_event_3 = iota_event_1.clone(); - iota_event_3.type_.address = AccountAddress::random(); - - mock_client.add_events_by_tx_digest( - tx_digest, - vec![ - iota_event_1.clone(), - iota_event_2.clone(), - iota_event_1.clone(), - iota_event_3.clone(), - ], - ); - let expected_action_1 = BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction { - iota_tx_digest: tx_digest, - iota_tx_event_index: 0, - iota_bridge_event: sanitized_event_1.clone(), - }); - assert_eq!( - iota_client - .get_bridge_action_by_tx_digest_and_event_idx_maybe(&tx_digest, 0) - .await - .unwrap(), - expected_action_1, - ); - let expected_action_2 = BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction { - iota_tx_digest: tx_digest, - iota_tx_event_index: 2, - iota_bridge_event: sanitized_event_1.clone(), - }); - assert_eq!( - iota_client - .get_bridge_action_by_tx_digest_and_event_idx_maybe(&tx_digest, 2) - .await - .unwrap(), - expected_action_2, - ); - assert!(matches!( - iota_client - .get_bridge_action_by_tx_digest_and_event_idx_maybe(&tx_digest, 1) - .await - .unwrap_err(), - BridgeError::NoBridgeEventsInTxPosition - ),); - assert!(matches!( - iota_client - .get_bridge_action_by_tx_digest_and_event_idx_maybe(&tx_digest, 3) - .await - .unwrap_err(), - BridgeError::BridgeEventInUnrecognizedIotaPackage - ),); - assert!(matches!( - iota_client - .get_bridge_action_by_tx_digest_and_event_idx_maybe(&tx_digest, 4) - .await - .unwrap_err(), - BridgeError::NoBridgeEventsInTxPosition - ),); - - // if the StructTag matches with unparsable bcs, it returns an error - iota_event_2.type_ = IotaToEthTokenBridgeV1.get().unwrap().clone(); - mock_client.add_events_by_tx_digest(tx_digest, vec![iota_event_2]); - iota_client - .get_bridge_action_by_tx_digest_and_event_idx_maybe(&tx_digest, 2) - .await - .unwrap_err(); - } - - // Test get_action_onchain_status. - // Use validator secrets to bridge USDC from Ethereum initially. - // TODO: we need an e2e test for this with published solidity contract and - // committee with BridgeNodes - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_get_action_onchain_status_for_iota_to_eth_transfer() { - telemetry_subscribers::init_for_testing(); - let mut bridge_keys = vec![]; - for _ in 0..=3 { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - bridge_keys.push(kp); - } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) - .await; - - let iota_client = IotaClient::new(&test_cluster.fullnode_handle.rpc_url) - .await - .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); - - // Wait until committee is set up - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; - let context = &mut test_cluster.wallet; - let sender = context.active_address().unwrap(); - let usdc_amount = 5000000; - let bridge_object_arg = iota_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - let id_token_map = iota_client.get_token_id_map().await.unwrap(); - - // 1. Create a Eth -> IOTA Transfer (recipient is sender address), approve with - // validator secrets and assert its status to be Claimed - let action = - get_test_eth_to_iota_bridge_action(None, Some(usdc_amount), Some(sender), None); - let usdc_object_ref = approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - Some(sender), - &id_token_map, - ) - .await - .unwrap(); - - let status = iota_client - .inner - .get_token_transfer_action_onchain_status( - bridge_object_arg, - action.chain_id() as u8, - action.seq_number(), - ) - .await - .unwrap(); - assert_eq!(status, BridgeActionStatus::Claimed); - - // 2. Create an IOTA -> Eth Transfer, approve with validator secrets and assert - // its status to be Approved - // We need to actually send tokens to bridge to initialize the record. - let eth_recv_address = EthAddress::random(); - let bridge_event = bridge_token( - context, - eth_recv_address, - usdc_object_ref, - id_token_map.get(&TOKEN_ID_USDC).unwrap().clone(), - bridge_object_arg, - ) - .await; - assert_eq!(bridge_event.nonce, 0); - assert_eq!(bridge_event.iota_chain_id, BridgeChainId::IotaCustom); - assert_eq!(bridge_event.eth_chain_id, BridgeChainId::EthCustom); - assert_eq!(bridge_event.eth_address, eth_recv_address); - assert_eq!(bridge_event.iota_address, sender); - assert_eq!(bridge_event.token_id, TOKEN_ID_USDC); - assert_eq!(bridge_event.amount_iota_adjusted, usdc_amount); - - let action = get_test_iota_to_eth_bridge_action( - None, - None, - Some(bridge_event.nonce), - Some(bridge_event.amount_iota_adjusted), - Some(bridge_event.iota_address), - Some(bridge_event.eth_address), - Some(TOKEN_ID_USDC), - ); - let status = iota_client - .inner - .get_token_transfer_action_onchain_status( - bridge_object_arg, - action.chain_id() as u8, - action.seq_number(), - ) - .await - .unwrap(); - // At this point, the record is created and the status is Pending - assert_eq!(status, BridgeActionStatus::Pending); - - // Approve it and assert its status to be Approved - approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - None, - &id_token_map, - ) - .await; - - let status = iota_client - .inner - .get_token_transfer_action_onchain_status( - bridge_object_arg, - action.chain_id() as u8, - action.seq_number(), - ) - .await - .unwrap(); - assert_eq!(status, BridgeActionStatus::Approved); - - // 3. Create a random action and assert its status as NotFound - let action = - get_test_iota_to_eth_bridge_action(None, None, Some(100), None, None, None, None); - let status = iota_client - .inner - .get_token_transfer_action_onchain_status( - bridge_object_arg, - action.chain_id() as u8, - action.seq_number(), - ) - .await - .unwrap(); - assert_eq!(status, BridgeActionStatus::NotFound); - } -} diff --git a/crates/iota-bridge/src/iota_mock_client.rs b/crates/iota-bridge/src/iota_mock_client.rs deleted file mode 100644 index c7738276cda..00000000000 --- a/crates/iota-bridge/src/iota_mock_client.rs +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! A mock implementation of IOTA JSON-RPC client. - -use std::{ - collections::{HashMap, VecDeque}, - sync::{Arc, Mutex}, -}; - -use async_trait::async_trait; -use iota_json_rpc_types::{EventFilter, EventPage, IotaEvent, IotaTransactionBlockResponse}; -use iota_types::{ - Identifier, - base_types::{ObjectID, ObjectRef}, - bridge::{BridgeCommitteeSummary, BridgeSummary, MoveTypeParsedTokenTransferMessage}, - digests::TransactionDigest, - event::EventID, - gas_coin::GasCoin, - object::Owner, - transaction::{ObjectArg, Transaction}, -}; - -use crate::{ - error::{BridgeError, BridgeResult}, - iota_client::IotaClientInner, - test_utils::DUMMY_MUTABLE_BRIDGE_OBJECT_ARG, - types::{BridgeAction, BridgeActionStatus, IsBridgePaused}, -}; - -/// Mock client used in test environments. -#[expect(clippy::type_complexity)] -#[derive(Clone, Debug)] -pub struct IotaMockClient { - // the top two fields do not change during tests so we don't need them to be Arc> - chain_identifier: String, - latest_checkpoint_sequence_number: u64, - events: Arc), EventPage>>>, - past_event_query_params: Arc)>>>, - events_by_tx_digest: - Arc, iota_sdk::error::Error>>>>, - transaction_responses: - Arc>>>, - wildcard_transaction_response: Arc>>>, - get_object_info: Arc>>, - onchain_status: Arc>>, - bridge_committee_summary: Arc>>, - is_paused: Arc>>, - requested_transactions_tx: tokio::sync::broadcast::Sender, -} - -impl IotaMockClient { - pub fn default() -> Self { - Self { - chain_identifier: "".to_string(), - latest_checkpoint_sequence_number: 0, - events: Default::default(), - past_event_query_params: Default::default(), - events_by_tx_digest: Default::default(), - transaction_responses: Default::default(), - wildcard_transaction_response: Default::default(), - get_object_info: Default::default(), - onchain_status: Default::default(), - bridge_committee_summary: Default::default(), - is_paused: Default::default(), - requested_transactions_tx: tokio::sync::broadcast::channel(10000).0, - } - } - - pub fn add_event_response( - &self, - package: ObjectID, - module: Identifier, - cursor: EventID, - events: EventPage, - ) { - self.events - .lock() - .unwrap() - .insert((package, module, Some(cursor)), events); - } - - pub fn add_events_by_tx_digest(&self, tx_digest: TransactionDigest, events: Vec) { - self.events_by_tx_digest - .lock() - .unwrap() - .insert(tx_digest, Ok(events)); - } - - pub fn add_events_by_tx_digest_error(&self, tx_digest: TransactionDigest) { - self.events_by_tx_digest - .lock() - .unwrap() - .insert(tx_digest, Err(iota_sdk::error::Error::Data("".to_string()))); - } - - pub fn add_transaction_response( - &self, - tx_digest: TransactionDigest, - response: BridgeResult, - ) { - self.transaction_responses - .lock() - .unwrap() - .insert(tx_digest, response); - } - - pub fn set_action_onchain_status(&self, action: &BridgeAction, status: BridgeActionStatus) { - self.onchain_status - .lock() - .unwrap() - .insert((action.chain_id() as u8, action.seq_number()), status); - } - - pub fn set_bridge_committee(&self, committee: BridgeCommitteeSummary) { - self.bridge_committee_summary - .lock() - .unwrap() - .replace(committee); - } - - pub fn set_is_bridge_paused(&self, value: IsBridgePaused) { - self.is_paused.lock().unwrap().replace(value); - } - - pub fn set_wildcard_transaction_response( - &self, - response: BridgeResult, - ) { - *self.wildcard_transaction_response.lock().unwrap() = Some(response); - } - - pub fn add_gas_object_info(&self, gas_coin: GasCoin, object_ref: ObjectRef, owner: Owner) { - self.get_object_info - .lock() - .unwrap() - .insert(object_ref.0, (gas_coin, object_ref, owner)); - } - - pub fn subscribe_to_requested_transactions( - &self, - ) -> tokio::sync::broadcast::Receiver { - self.requested_transactions_tx.subscribe() - } -} - -#[async_trait] -impl IotaClientInner for IotaMockClient { - type Error = iota_sdk::error::Error; - - // Unwraps in this function: We assume the responses are pre-populated - // by the test before calling into this function. - async fn query_events( - &self, - query: EventFilter, - cursor: Option, - ) -> Result { - let events = self.events.lock().unwrap(); - match query { - EventFilter::MoveEventModule { package, module } => { - self.past_event_query_params.lock().unwrap().push_back(( - package, - module.clone(), - cursor, - )); - Ok(events - .get(&(package, module.clone(), cursor)) - .cloned() - .unwrap_or_else(|| { - panic!( - "No preset events found for package: {:?}, module: {:?}, cursor: {:?}", - package, module, cursor - ) - })) - } - _ => unimplemented!(), - } - } - - async fn get_events_by_tx_digest( - &self, - tx_digest: TransactionDigest, - ) -> Result, Self::Error> { - let events = self.events_by_tx_digest.lock().unwrap(); - - match events - .get(&tx_digest) - .unwrap_or_else(|| panic!("No preset events found for tx_digest: {:?}", tx_digest)) - { - Ok(events) => Ok(events.clone()), - // iota_sdk::error::Error is not Clone - Err(_) => Err(iota_sdk::error::Error::Data("".to_string())), - } - } - - async fn get_chain_identifier(&self) -> Result { - Ok(self.chain_identifier.clone()) - } - - async fn get_latest_checkpoint_sequence_number(&self) -> Result { - Ok(self.latest_checkpoint_sequence_number) - } - - async fn get_mutable_bridge_object_arg(&self) -> Result { - Ok(DUMMY_MUTABLE_BRIDGE_OBJECT_ARG) - } - - async fn get_reference_gas_price(&self) -> Result { - Ok(1000) - } - - async fn get_bridge_summary(&self) -> Result { - Ok(BridgeSummary { - bridge_version: 0, - message_version: 0, - chain_id: 0, - sequence_nums: vec![], - bridge_records_id: ObjectID::random(), - is_frozen: self.is_paused.lock().unwrap().unwrap_or_default(), - limiter: Default::default(), - committee: self - .bridge_committee_summary - .lock() - .unwrap() - .clone() - .unwrap_or_default(), - treasury: Default::default(), - }) - } - - async fn get_token_transfer_action_onchain_status( - &self, - _bridge_object_arg: ObjectArg, - source_chain_id: u8, - seq_number: u64, - ) -> Result { - Ok(self - .onchain_status - .lock() - .unwrap() - .get(&(source_chain_id, seq_number)) - .cloned() - .unwrap_or(BridgeActionStatus::Pending)) - } - - async fn get_token_transfer_action_onchain_signatures( - &self, - _bridge_object_arg: ObjectArg, - _source_chain_id: u8, - _seq_number: u64, - ) -> Result>>, BridgeError> { - unimplemented!() - } - - async fn get_parsed_token_transfer_message( - &self, - _bridge_object_arg: ObjectArg, - _source_chain_id: u8, - _seq_number: u64, - ) -> Result, BridgeError> { - unimplemented!() - } - - async fn execute_transaction_block_with_effects( - &self, - tx: Transaction, - ) -> Result { - self.requested_transactions_tx.send(*tx.digest()).unwrap(); - match self.transaction_responses.lock().unwrap().get(tx.digest()) { - Some(response) => response.clone(), - None => self - .wildcard_transaction_response - .lock() - .unwrap() - .clone() - .unwrap_or_else(|| panic!("No preset transaction response found for tx: {:?}", tx)), - } - } - - async fn get_gas_data_panic_if_not_gas( - &self, - gas_object_id: ObjectID, - ) -> (GasCoin, ObjectRef, Owner) { - self.get_object_info - .lock() - .unwrap() - .get(&gas_object_id) - .cloned() - .unwrap_or_else(|| { - panic!( - "No preset gas object info found for gas_object_id: {:?}", - gas_object_id - ) - }) - } -} diff --git a/crates/iota-bridge/src/iota_syncer.rs b/crates/iota-bridge/src/iota_syncer.rs deleted file mode 100644 index fbfaa506cdf..00000000000 --- a/crates/iota-bridge/src/iota_syncer.rs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! The IotaSyncer module is responsible for synchronizing Events emitted -//! on IOTA blockchain from concerned modules of bridge package 0x9. - -use std::{collections::HashMap, sync::Arc}; - -use iota_json_rpc_types::IotaEvent; -use iota_metrics::spawn_logged_monitored_task; -use iota_types::{BRIDGE_PACKAGE_ID, Identifier, event::EventID}; -use tokio::{ - task::JoinHandle, - time::{self, Duration}, -}; - -use crate::{ - error::BridgeResult, - iota_client::{IotaClient, IotaClientInner}, - retry_with_max_elapsed_time, -}; - -const IOTA_EVENTS_CHANNEL_SIZE: usize = 1000; - -/// Map from contract address to their start cursor (exclusive) -pub type IotaTargetModules = HashMap>; - -pub struct IotaSyncer { - iota_client: Arc>, - // The last transaction that the syncer has fully processed. - // Syncer will resume post this transaction (i.e. exclusive), when it starts. - cursors: IotaTargetModules, -} - -impl IotaSyncer -where - C: IotaClientInner + 'static, -{ - pub fn new(iota_client: Arc>, cursors: IotaTargetModules) -> Self { - Self { - iota_client, - cursors, - } - } - - pub async fn run( - self, - query_interval: Duration, - ) -> BridgeResult<( - Vec>, - iota_metrics::metered_channel::Receiver<(Identifier, Vec)>, - )> { - let (events_tx, events_rx) = iota_metrics::metered_channel::channel( - IOTA_EVENTS_CHANNEL_SIZE, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["iota_events_queue"]), - ); - - let mut task_handles = vec![]; - for (module, cursor) in self.cursors { - let events_rx_clone: iota_metrics::metered_channel::Sender<( - Identifier, - Vec, - )> = events_tx.clone(); - let iota_client_clone = self.iota_client.clone(); - task_handles.push(spawn_logged_monitored_task!( - Self::run_event_listening_task( - module, - cursor, - events_rx_clone, - iota_client_clone, - query_interval - ) - )); - } - Ok((task_handles, events_rx)) - } - - async fn run_event_listening_task( - // The module where interested events are defined. - // Module is always of bridge package 0x9. - module: Identifier, - mut cursor: Option, - events_sender: iota_metrics::metered_channel::Sender<(Identifier, Vec)>, - iota_client: Arc>, - query_interval: Duration, - ) { - tracing::info!(?module, ?cursor, "Starting iota events listening task"); - let mut interval = time::interval(query_interval); - interval.set_missed_tick_behavior(time::MissedTickBehavior::Skip); - loop { - interval.tick().await; - let Ok(Ok(events)) = retry_with_max_elapsed_time!( - iota_client.query_events_by_module(BRIDGE_PACKAGE_ID, module.clone(), cursor), - Duration::from_secs(120) - ) else { - tracing::error!("Failed to query events from iota client after retry"); - continue; - }; - - let len = events.data.len(); - if len != 0 { - events_sender - .send((module.clone(), events.data)) - .await - .expect("All IOTA event channel receivers are closed"); - if let Some(next) = events.next_cursor { - cursor = Some(next); - } - tracing::info!(?module, ?cursor, "Observed {len} new IOTA events"); - } - } - } -} - -#[cfg(test)] -mod tests { - use iota_json_rpc_types::EventPage; - use iota_types::{Identifier, digests::TransactionDigest, event::EventID}; - use prometheus::Registry; - use tokio::time::timeout; - - use super::*; - use crate::{iota_client::IotaClient, iota_mock_client::IotaMockClient}; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_iota_syncer_basic() -> anyhow::Result<()> { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - - let mock = IotaMockClient::default(); - let client = Arc::new(IotaClient::new_for_testing(mock.clone())); - let module_foo = Identifier::new("Foo").unwrap(); - let module_bar = Identifier::new("Bar").unwrap(); - let empty_events = EventPage::empty(); - let cursor = EventID { - tx_digest: TransactionDigest::random(), - event_seq: 0, - }; - add_event_response(&mock, module_foo.clone(), cursor, empty_events.clone()); - add_event_response(&mock, module_bar.clone(), cursor, empty_events.clone()); - - let target_modules = HashMap::from_iter(vec![ - (module_foo.clone(), Some(cursor)), - (module_bar.clone(), Some(cursor)), - ]); - let interval = Duration::from_millis(200); - let (_handles, mut events_rx) = IotaSyncer::new(client, target_modules) - .run(interval) - .await - .unwrap(); - - // Initially there are no events - assert_no_more_events(interval, &mut events_rx).await; - - // Module Foo has new events - let mut event_1: IotaEvent = IotaEvent::random_for_testing(); - let package_id = BRIDGE_PACKAGE_ID; - event_1.type_.address = package_id.into(); - event_1.type_.module = module_foo.clone(); - let module_foo_events_1: iota_json_rpc_types::Page = EventPage { - data: vec![event_1.clone(), event_1.clone()], - next_cursor: Some(event_1.id), - has_next_page: false, - }; - add_event_response(&mock, module_foo.clone(), event_1.id, empty_events.clone()); - add_event_response( - &mock, - module_foo.clone(), - cursor, - module_foo_events_1.clone(), - ); - - let (identifier, received_events) = events_rx.recv().await.unwrap(); - assert_eq!(identifier, module_foo); - assert_eq!(received_events.len(), 2); - assert_eq!(received_events[0].id, event_1.id); - assert_eq!(received_events[1].id, event_1.id); - // No more - assert_no_more_events(interval, &mut events_rx).await; - - // Module Bar has new events - let mut event_2: IotaEvent = IotaEvent::random_for_testing(); - event_2.type_.address = package_id.into(); - event_2.type_.module = module_bar.clone(); - let module_bar_events_1 = EventPage { - data: vec![event_2.clone()], - next_cursor: Some(event_2.id), - has_next_page: false, - }; - add_event_response(&mock, module_bar.clone(), event_2.id, empty_events.clone()); - - add_event_response(&mock, module_bar.clone(), cursor, module_bar_events_1); - - let (identifier, received_events) = events_rx.recv().await.unwrap(); - assert_eq!(identifier, module_bar); - assert_eq!(received_events.len(), 1); - assert_eq!(received_events[0].id, event_2.id); - // No more - assert_no_more_events(interval, &mut events_rx).await; - - Ok(()) - } - - async fn assert_no_more_events( - interval: Duration, - events_rx: &mut iota_metrics::metered_channel::Receiver<(Identifier, Vec)>, - ) { - match timeout(interval * 2, events_rx.recv()).await { - Err(_e) => (), - other => panic!("Should have timed out, but got: {:?}", other), - }; - } - - fn add_event_response( - mock: &IotaMockClient, - module: Identifier, - cursor: EventID, - events: EventPage, - ) { - mock.add_event_response(BRIDGE_PACKAGE_ID, module.clone(), cursor, events.clone()); - } -} diff --git a/crates/iota-bridge/src/iota_transaction_builder.rs b/crates/iota-bridge/src/iota_transaction_builder.rs deleted file mode 100644 index 396f1d1bd0f..00000000000 --- a/crates/iota-bridge/src/iota_transaction_builder.rs +++ /dev/null @@ -1,1007 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{collections::HashMap, str::FromStr}; - -use fastcrypto::traits::ToFromBytes; -use iota_types::{ - BRIDGE_PACKAGE_ID, Identifier, TypeTag, - base_types::{IotaAddress, ObjectRef}, - bridge::{ - BRIDGE_CREATE_ADD_TOKEN_ON_IOTA_MESSAGE_FUNCTION_NAME, - BRIDGE_EXECUTE_SYSTEM_MESSAGE_FUNCTION_NAME, BRIDGE_MESSAGE_MODULE_NAME, - BRIDGE_MODULE_NAME, - }, - programmable_transaction_builder::ProgrammableTransactionBuilder, - transaction::{CallArg, ObjectArg, TransactionData}, -}; -use move_core_types::ident_str; - -use crate::{ - error::{BridgeError, BridgeResult}, - types::{BridgeAction, VerifiedCertifiedBridgeAction}, -}; - -pub fn build_iota_transaction( - client_address: IotaAddress, - gas_object_ref: &ObjectRef, - action: VerifiedCertifiedBridgeAction, - bridge_object_arg: ObjectArg, - iota_token_type_tags: &HashMap, - rgp: u64, -) -> BridgeResult { - // TODO: Check chain id? - match action.data() { - BridgeAction::EthToIotaBridgeAction(_) => build_token_bridge_approve_transaction( - client_address, - gas_object_ref, - action, - true, - bridge_object_arg, - iota_token_type_tags, - rgp, - ), - BridgeAction::IotaToEthBridgeAction(_) => build_token_bridge_approve_transaction( - client_address, - gas_object_ref, - action, - false, - bridge_object_arg, - iota_token_type_tags, - rgp, - ), - BridgeAction::BlocklistCommitteeAction(_) => build_committee_blocklist_approve_transaction( - client_address, - gas_object_ref, - action, - bridge_object_arg, - rgp, - ), - BridgeAction::EmergencyAction(_) => build_emergency_op_approve_transaction( - client_address, - gas_object_ref, - action, - bridge_object_arg, - rgp, - ), - BridgeAction::LimitUpdateAction(_) => build_limit_update_approve_transaction( - client_address, - gas_object_ref, - action, - bridge_object_arg, - rgp, - ), - BridgeAction::AssetPriceUpdateAction(_) => build_asset_price_update_approve_transaction( - client_address, - gas_object_ref, - action, - bridge_object_arg, - rgp, - ), - BridgeAction::EvmContractUpgradeAction(_) => { - // It does not need an IOTA transaction to execute EVM contract upgrade - unreachable!() - } - BridgeAction::AddTokensOnIotaAction(_) => build_add_tokens_on_iota_transaction( - client_address, - gas_object_ref, - action, - bridge_object_arg, - rgp, - ), - BridgeAction::AddTokensOnEvmAction(_) => { - // It does not need an IOTA transaction to add tokens on EVM - unreachable!() - } - } -} - -fn build_token_bridge_approve_transaction( - client_address: IotaAddress, - gas_object_ref: &ObjectRef, - action: VerifiedCertifiedBridgeAction, - claim: bool, - bridge_object_arg: ObjectArg, - iota_token_type_tags: &HashMap, - rgp: u64, -) -> BridgeResult { - let (bridge_action, sigs) = action.into_inner().into_data_and_sig(); - let mut builder = ProgrammableTransactionBuilder::new(); - - let (source_chain, seq_num, sender, target_chain, target, token_type, amount) = - match bridge_action { - BridgeAction::IotaToEthBridgeAction(a) => { - let bridge_event = a.iota_bridge_event; - ( - bridge_event.iota_chain_id, - bridge_event.nonce, - bridge_event.iota_address.to_vec(), - bridge_event.eth_chain_id, - bridge_event.eth_address.to_fixed_bytes().to_vec(), - bridge_event.token_id, - bridge_event.amount_iota_adjusted, - ) - } - BridgeAction::EthToIotaBridgeAction(a) => { - let bridge_event = a.eth_bridge_event; - ( - bridge_event.eth_chain_id, - bridge_event.nonce, - bridge_event.eth_address.to_fixed_bytes().to_vec(), - bridge_event.iota_chain_id, - bridge_event.iota_address.to_vec(), - bridge_event.token_id, - bridge_event.iota_adjusted_amount, - ) - } - _ => unreachable!(), - }; - - let source_chain = builder.pure(source_chain as u8).unwrap(); - let seq_num = builder.pure(seq_num).unwrap(); - let sender = builder.pure(sender.clone()).map_err(|e| { - BridgeError::BridgeSerialization(format!( - "Failed to serialize sender: {:?}. Err: {:?}", - sender, e - )) - })?; - let target_chain = builder.pure(target_chain as u8).unwrap(); - let target = builder.pure(target.clone()).map_err(|e| { - BridgeError::BridgeSerialization(format!( - "Failed to serialize target: {:?}. Err: {:?}", - target, e - )) - })?; - let arg_token_type = builder.pure(token_type).unwrap(); - let amount = builder.pure(amount).unwrap(); - - let arg_msg = builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - ident_str!("message").to_owned(), - ident_str!("create_token_bridge_message").to_owned(), - vec![], - vec![ - source_chain, - seq_num, - sender, - target_chain, - target, - arg_token_type, - amount, - ], - ); - - // Unwrap: these should not fail - let arg_bridge = builder.obj(bridge_object_arg).unwrap(); - let arg_clock = builder.input(CallArg::CLOCK_IMM).unwrap(); - - let mut sig_bytes = vec![]; - for (_, sig) in sigs.signatures { - sig_bytes.push(sig.as_bytes().to_vec()); - } - let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| { - BridgeError::BridgeSerialization(format!( - "Failed to serialize signatures: {:?}. Err: {:?}", - sig_bytes, e - )) - })?; - - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - iota_types::bridge::BRIDGE_MODULE_NAME.to_owned(), - ident_str!("approve_token_transfer").to_owned(), - vec![], - vec![arg_bridge, arg_msg, arg_signatures], - ); - - if claim { - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - iota_types::bridge::BRIDGE_MODULE_NAME.to_owned(), - ident_str!("claim_and_transfer_token").to_owned(), - vec![ - iota_token_type_tags - .get(&token_type) - .ok_or(BridgeError::UnknownTokenId(token_type))? - .clone(), - ], - vec![arg_bridge, arg_clock, source_chain, seq_num], - ); - } - - let pt = builder.finish(); - - Ok(TransactionData::new_programmable( - client_address, - vec![*gas_object_ref], - pt, - 100_000_000, - rgp, - )) -} - -fn build_emergency_op_approve_transaction( - client_address: IotaAddress, - gas_object_ref: &ObjectRef, - action: VerifiedCertifiedBridgeAction, - bridge_object_arg: ObjectArg, - rgp: u64, -) -> BridgeResult { - let (bridge_action, sigs) = action.into_inner().into_data_and_sig(); - - let mut builder = ProgrammableTransactionBuilder::new(); - - let (source_chain, seq_num, action_type) = match bridge_action { - BridgeAction::EmergencyAction(a) => (a.chain_id, a.nonce, a.action_type), - _ => unreachable!(), - }; - - // Unwrap: these should not fail - let source_chain = builder.pure(source_chain as u8).unwrap(); - let seq_num = builder.pure(seq_num).unwrap(); - let action_type = builder.pure(action_type as u8).unwrap(); - let arg_bridge = builder.obj(bridge_object_arg).unwrap(); - - let arg_msg = builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - ident_str!("message").to_owned(), - ident_str!("create_emergency_op_message").to_owned(), - vec![], - vec![source_chain, seq_num, action_type], - ); - - let mut sig_bytes = vec![]; - for (_, sig) in sigs.signatures { - sig_bytes.push(sig.as_bytes().to_vec()); - } - let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| { - BridgeError::BridgeSerialization(format!( - "Failed to serialize signatures: {:?}. Err: {:?}", - sig_bytes, e - )) - })?; - - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - ident_str!("bridge").to_owned(), - ident_str!("execute_system_message").to_owned(), - vec![], - vec![arg_bridge, arg_msg, arg_signatures], - ); - - let pt = builder.finish(); - - Ok(TransactionData::new_programmable( - client_address, - vec![*gas_object_ref], - pt, - 100_000_000, - rgp, - )) -} - -fn build_committee_blocklist_approve_transaction( - client_address: IotaAddress, - gas_object_ref: &ObjectRef, - action: VerifiedCertifiedBridgeAction, - bridge_object_arg: ObjectArg, - rgp: u64, -) -> BridgeResult { - let (bridge_action, sigs) = action.into_inner().into_data_and_sig(); - - let mut builder = ProgrammableTransactionBuilder::new(); - - let (source_chain, seq_num, blocklist_type, members_to_update) = match bridge_action { - BridgeAction::BlocklistCommitteeAction(a) => { - (a.chain_id, a.nonce, a.blocklist_type, a.members_to_update) - } - _ => unreachable!(), - }; - - // Unwrap: these should not fail - let source_chain = builder.pure(source_chain as u8).unwrap(); - let seq_num = builder.pure(seq_num).unwrap(); - let blocklist_type = builder.pure(blocklist_type as u8).unwrap(); - let members_to_update = members_to_update - .into_iter() - .map(|m| m.to_eth_address().as_bytes().to_vec()) - .collect::>(); - let members_to_update = builder.pure(members_to_update).unwrap(); - let arg_bridge = builder.obj(bridge_object_arg).unwrap(); - - let arg_msg = builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - ident_str!("message").to_owned(), - ident_str!("create_blocklist_message").to_owned(), - vec![], - vec![source_chain, seq_num, blocklist_type, members_to_update], - ); - - let mut sig_bytes = vec![]; - for (_, sig) in sigs.signatures { - sig_bytes.push(sig.as_bytes().to_vec()); - } - let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| { - BridgeError::BridgeSerialization(format!( - "Failed to serialize signatures: {:?}. Err: {:?}", - sig_bytes, e - )) - })?; - - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - ident_str!("bridge").to_owned(), - ident_str!("execute_system_message").to_owned(), - vec![], - vec![arg_bridge, arg_msg, arg_signatures], - ); - - let pt = builder.finish(); - - Ok(TransactionData::new_programmable( - client_address, - vec![*gas_object_ref], - pt, - 100_000_000, - rgp, - )) -} - -fn build_limit_update_approve_transaction( - client_address: IotaAddress, - gas_object_ref: &ObjectRef, - action: VerifiedCertifiedBridgeAction, - bridge_object_arg: ObjectArg, - rgp: u64, -) -> BridgeResult { - let (bridge_action, sigs) = action.into_inner().into_data_and_sig(); - - let mut builder = ProgrammableTransactionBuilder::new(); - - let (receiving_chain_id, seq_num, sending_chain_id, new_usd_limit) = match bridge_action { - BridgeAction::LimitUpdateAction(a) => { - (a.chain_id, a.nonce, a.sending_chain_id, a.new_usd_limit) - } - _ => unreachable!(), - }; - - // Unwrap: these should not fail - let receiving_chain_id = builder.pure(receiving_chain_id as u8).unwrap(); - let seq_num = builder.pure(seq_num).unwrap(); - let sending_chain_id = builder.pure(sending_chain_id as u8).unwrap(); - let new_usd_limit = builder.pure(new_usd_limit).unwrap(); - let arg_bridge = builder.obj(bridge_object_arg).unwrap(); - - let arg_msg = builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - ident_str!("message").to_owned(), - ident_str!("create_update_bridge_limit_message").to_owned(), - vec![], - vec![receiving_chain_id, seq_num, sending_chain_id, new_usd_limit], - ); - - let mut sig_bytes = vec![]; - for (_, sig) in sigs.signatures { - sig_bytes.push(sig.as_bytes().to_vec()); - } - let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| { - BridgeError::BridgeSerialization(format!( - "Failed to serialize signatures: {:?}. Err: {:?}", - sig_bytes, e - )) - })?; - - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - ident_str!("bridge").to_owned(), - ident_str!("execute_system_message").to_owned(), - vec![], - vec![arg_bridge, arg_msg, arg_signatures], - ); - - let pt = builder.finish(); - - Ok(TransactionData::new_programmable( - client_address, - vec![*gas_object_ref], - pt, - 100_000_000, - rgp, - )) -} - -fn build_asset_price_update_approve_transaction( - client_address: IotaAddress, - gas_object_ref: &ObjectRef, - action: VerifiedCertifiedBridgeAction, - bridge_object_arg: ObjectArg, - rgp: u64, -) -> BridgeResult { - let (bridge_action, sigs) = action.into_inner().into_data_and_sig(); - - let mut builder = ProgrammableTransactionBuilder::new(); - - let (source_chain, seq_num, token_id, new_usd_price) = match bridge_action { - BridgeAction::AssetPriceUpdateAction(a) => { - (a.chain_id, a.nonce, a.token_id, a.new_usd_price) - } - _ => unreachable!(), - }; - - // Unwrap: these should not fail - let source_chain = builder.pure(source_chain as u8).unwrap(); - let token_id = builder.pure(token_id).unwrap(); - let seq_num = builder.pure(seq_num).unwrap(); - let new_price = builder.pure(new_usd_price).unwrap(); - let arg_bridge = builder.obj(bridge_object_arg).unwrap(); - - let arg_msg = builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - ident_str!("message").to_owned(), - ident_str!("create_update_asset_price_message").to_owned(), - vec![], - vec![token_id, source_chain, seq_num, new_price], - ); - - let mut sig_bytes = vec![]; - for (_, sig) in sigs.signatures { - sig_bytes.push(sig.as_bytes().to_vec()); - } - let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| { - BridgeError::BridgeSerialization(format!( - "Failed to serialize signatures: {:?}. Err: {:?}", - sig_bytes, e - )) - })?; - - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - ident_str!("bridge").to_owned(), - ident_str!("execute_system_message").to_owned(), - vec![], - vec![arg_bridge, arg_msg, arg_signatures], - ); - - let pt = builder.finish(); - - Ok(TransactionData::new_programmable( - client_address, - vec![*gas_object_ref], - pt, - 100_000_000, - rgp, - )) -} - -pub fn build_add_tokens_on_iota_transaction( - client_address: IotaAddress, - gas_object_ref: &ObjectRef, - action: VerifiedCertifiedBridgeAction, - bridge_object_arg: ObjectArg, - rgp: u64, -) -> BridgeResult { - let (bridge_action, sigs) = action.into_inner().into_data_and_sig(); - - let mut builder = ProgrammableTransactionBuilder::new(); - - let (source_chain, seq_num, native, token_ids, token_type_names, token_prices) = - match bridge_action { - BridgeAction::AddTokensOnIotaAction(a) => ( - a.chain_id, - a.nonce, - a.native, - a.token_ids, - a.token_type_names, - a.token_prices, - ), - _ => unreachable!(), - }; - let token_type_names = token_type_names - .iter() - .map(|type_name| type_name.to_canonical_string(false)) - .collect::>(); - let source_chain = builder.pure(source_chain as u8).unwrap(); - let seq_num = builder.pure(seq_num).unwrap(); - let native_token = builder.pure(native).unwrap(); - let token_ids = builder.pure(token_ids).unwrap(); - let token_type_names = builder.pure(token_type_names).unwrap(); - let token_prices = builder.pure(token_prices).unwrap(); - - let message_arg = builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - BRIDGE_MESSAGE_MODULE_NAME.into(), - BRIDGE_CREATE_ADD_TOKEN_ON_IOTA_MESSAGE_FUNCTION_NAME.into(), - vec![], - vec![ - source_chain, - seq_num, - native_token, - token_ids, - token_type_names, - token_prices, - ], - ); - - let bridge_arg = builder.obj(bridge_object_arg).unwrap(); - - let mut sig_bytes = vec![]; - for (_, sig) in sigs.signatures { - sig_bytes.push(sig.as_bytes().to_vec()); - } - let sigs_arg = builder.pure(sig_bytes.clone()).unwrap(); - - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - BRIDGE_MODULE_NAME.into(), - BRIDGE_EXECUTE_SYSTEM_MESSAGE_FUNCTION_NAME.into(), - vec![], - vec![bridge_arg, message_arg, sigs_arg], - ); - - let pt = builder.finish(); - - Ok(TransactionData::new_programmable( - client_address, - vec![*gas_object_ref], - pt, - 100_000_000, - rgp, - )) -} - -pub fn build_committee_register_transaction( - validator_address: IotaAddress, - gas_object_ref: &ObjectRef, - bridge_object_arg: ObjectArg, - bridge_authority_pub_key_bytes: Vec, - bridge_url: &str, - ref_gas_price: u64, - gas_budget: u64, -) -> BridgeResult { - let mut builder = ProgrammableTransactionBuilder::new(); - let system_state = builder.obj(ObjectArg::IOTA_SYSTEM_MUT).unwrap(); - let bridge = builder.obj(bridge_object_arg).unwrap(); - let bridge_pubkey = builder - .input(CallArg::Pure( - bcs::to_bytes(&bridge_authority_pub_key_bytes).unwrap(), - )) - .unwrap(); - let url = builder - .input(CallArg::Pure(bcs::to_bytes(bridge_url.as_bytes()).unwrap())) - .unwrap(); - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - BRIDGE_MODULE_NAME.into(), - Identifier::from_str("committee_registration").unwrap(), - vec![], - vec![bridge, system_state, bridge_pubkey, url], - ); - let data = TransactionData::new_programmable( - validator_address, - vec![*gas_object_ref], - builder.finish(), - gas_budget, - ref_gas_price, - ); - Ok(data) -} - -pub fn build_committee_update_url_transaction( - validator_address: IotaAddress, - gas_object_ref: &ObjectRef, - bridge_object_arg: ObjectArg, - bridge_url: &str, - ref_gas_price: u64, - gas_budget: u64, -) -> BridgeResult { - let mut builder = ProgrammableTransactionBuilder::new(); - let bridge = builder.obj(bridge_object_arg).unwrap(); - let url = builder - .input(CallArg::Pure(bcs::to_bytes(bridge_url.as_bytes()).unwrap())) - .unwrap(); - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - BRIDGE_MODULE_NAME.into(), - Identifier::from_str("update_node_url").unwrap(), - vec![], - vec![bridge, url], - ); - let data = TransactionData::new_programmable( - validator_address, - vec![*gas_object_ref], - builder.finish(), - gas_budget, - ref_gas_price, - ); - Ok(data) -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use ethers::types::Address as EthAddress; - use iota_types::{ - bridge::{BridgeChainId, TOKEN_ID_BTC, TOKEN_ID_USDC}, - crypto::{ToFromBytes, get_key_pair}, - }; - use test_cluster::TestClusterBuilder; - - use crate::{ - BRIDGE_ENABLE_PROTOCOL_VERSION, - crypto::{BridgeAuthorityKeyPair, BridgeAuthorityPublicKeyBytes}, - iota_client::IotaClient, - test_utils::{ - approve_action_with_validator_secrets, bridge_token, - get_test_eth_to_iota_bridge_action, get_test_iota_to_eth_bridge_action, - }, - types::{BridgeAction, EmergencyAction, EmergencyActionType, *}, - }; - - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_build_iota_transaction_for_token_transfer() { - telemetry_subscribers::init_for_testing(); - let mut bridge_keys = vec![]; - for _ in 0..=3 { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - bridge_keys.push(kp); - } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) - .await; - - let iota_client = IotaClient::new(&test_cluster.fullnode_handle.rpc_url) - .await - .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); - - // Note: We don't call `iota_client.get_bridge_committee` here because it will - // err if the committee is not initialized during the construction of - // `BridgeCommittee`. - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; - let context = &mut test_cluster.wallet; - let sender = context.active_address().unwrap(); - let usdc_amount = 5000000; - let bridge_object_arg = iota_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - let id_token_map = iota_client.get_token_id_map().await.unwrap(); - - // 1. Test Eth -> IOTA Transfer approval - let action = - get_test_eth_to_iota_bridge_action(None, Some(usdc_amount), Some(sender), None); - // `approve_action_with_validator_secrets` covers transaction building - let usdc_object_ref = approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - Some(sender), - &id_token_map, - ) - .await - .unwrap(); - - // 2. Test IOTA -> Eth Transfer approval - let bridge_event = bridge_token( - context, - EthAddress::random(), - usdc_object_ref, - id_token_map.get(&TOKEN_ID_USDC).unwrap().clone(), - bridge_object_arg, - ) - .await; - - let action = get_test_iota_to_eth_bridge_action( - None, - None, - Some(bridge_event.nonce), - Some(bridge_event.amount_iota_adjusted), - Some(bridge_event.iota_address), - Some(bridge_event.eth_address), - Some(TOKEN_ID_USDC), - ); - // `approve_action_with_validator_secrets` covers transaction building - approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - None, - &id_token_map, - ) - .await; - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_build_iota_transaction_for_emergency_op() { - telemetry_subscribers::init_for_testing(); - let mut bridge_keys = vec![]; - for _ in 0..=3 { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - bridge_keys.push(kp); - } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) - .await; - let iota_client = IotaClient::new(&test_cluster.fullnode_handle.rpc_url) - .await - .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); - - // Wait until committee is set up - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; - let summary = iota_client.get_bridge_summary().await.unwrap(); - assert!(!summary.is_frozen); - - let context = &mut test_cluster.wallet; - let bridge_object_arg = iota_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - let id_token_map = iota_client.get_token_id_map().await.unwrap(); - - // 1. Pause - let action = BridgeAction::EmergencyAction(EmergencyAction { - nonce: 0, - chain_id: BridgeChainId::IotaCustom, - action_type: EmergencyActionType::Pause, - }); - // `approve_action_with_validator_secrets` covers transaction building - approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - None, - &id_token_map, - ) - .await; - let summary = iota_client.get_bridge_summary().await.unwrap(); - assert!(summary.is_frozen); - - // 2. Unpause - let action = BridgeAction::EmergencyAction(EmergencyAction { - nonce: 1, - chain_id: BridgeChainId::IotaCustom, - action_type: EmergencyActionType::Unpause, - }); - // `approve_action_with_validator_secrets` covers transaction building - approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - None, - &id_token_map, - ) - .await; - let summary = iota_client.get_bridge_summary().await.unwrap(); - assert!(!summary.is_frozen); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_build_iota_transaction_for_committee_blocklist() { - telemetry_subscribers::init_for_testing(); - let mut bridge_keys = vec![]; - for _ in 0..=3 { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - bridge_keys.push(kp); - } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) - .await; - let iota_client = IotaClient::new(&test_cluster.fullnode_handle.rpc_url) - .await - .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); - - // Wait until committee is set up - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; - let committee = iota_client.get_bridge_summary().await.unwrap().committee; - let victim = committee.members.first().unwrap().clone().1; - for member in committee.members { - assert!(!member.1.blocklisted); - } - - let context = &mut test_cluster.wallet; - let bridge_object_arg = iota_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - let id_token_map = iota_client.get_token_id_map().await.unwrap(); - - // 1. blocklist The victim - let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - nonce: 0, - chain_id: BridgeChainId::IotaCustom, - blocklist_type: BlocklistType::Blocklist, - members_to_update: vec![ - BridgeAuthorityPublicKeyBytes::from_bytes(&victim.bridge_pubkey_bytes).unwrap(), - ], - }); - // `approve_action_with_validator_secrets` covers transaction building - approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - None, - &id_token_map, - ) - .await; - let committee = iota_client.get_bridge_summary().await.unwrap().committee; - for member in committee.members { - if member.1.bridge_pubkey_bytes == victim.bridge_pubkey_bytes { - assert!(member.1.blocklisted); - } else { - assert!(!member.1.blocklisted); - } - } - - // 2. unblocklist the victim - let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - nonce: 1, - chain_id: BridgeChainId::IotaCustom, - blocklist_type: BlocklistType::Unblocklist, - members_to_update: vec![ - BridgeAuthorityPublicKeyBytes::from_bytes(&victim.bridge_pubkey_bytes).unwrap(), - ], - }); - // `approve_action_with_validator_secrets` covers transaction building - approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - None, - &id_token_map, - ) - .await; - let committee = iota_client.get_bridge_summary().await.unwrap().committee; - for member in committee.members { - assert!(!member.1.blocklisted); - } - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_build_iota_transaction_for_limit_update() { - telemetry_subscribers::init_for_testing(); - let mut bridge_keys = vec![]; - for _ in 0..=3 { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - bridge_keys.push(kp); - } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) - .await; - let iota_client = IotaClient::new(&test_cluster.fullnode_handle.rpc_url) - .await - .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); - - // Wait until committee is set up - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; - let transfer_limit = iota_client - .get_bridge_summary() - .await - .unwrap() - .limiter - .transfer_limit - .into_iter() - .map(|(s, d, l)| ((s, d), l)) - .collect::>(); - - let context = &mut test_cluster.wallet; - let bridge_object_arg = iota_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - let id_token_map = iota_client.get_token_id_map().await.unwrap(); - - // update limit - let action = BridgeAction::LimitUpdateAction(LimitUpdateAction { - nonce: 0, - chain_id: BridgeChainId::IotaCustom, - sending_chain_id: BridgeChainId::EthCustom, - new_usd_limit: 6_666_666 * USD_MULTIPLIER, // $1M USD - }); - // `approve_action_with_validator_secrets` covers transaction building - approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - None, - &id_token_map, - ) - .await; - let new_transfer_limit = iota_client - .get_bridge_summary() - .await - .unwrap() - .limiter - .transfer_limit; - for limit in new_transfer_limit { - if limit.0 == BridgeChainId::EthCustom && limit.1 == BridgeChainId::IotaCustom { - assert_eq!(limit.2, 6_666_666 * USD_MULTIPLIER); - } else { - assert_eq!(limit.2, *transfer_limit.get(&(limit.0, limit.1)).unwrap()); - } - } - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_build_iota_transaction_for_price_update() { - telemetry_subscribers::init_for_testing(); - let mut bridge_keys = vec![]; - for _ in 0..=3 { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - bridge_keys.push(kp); - } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) - .await; - let iota_client = IotaClient::new(&test_cluster.fullnode_handle.rpc_url) - .await - .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); - - // Note: We don't call `iota_client.get_bridge_committee` here because it will - // err if the committee is not initialized during the construction of - // `BridgeCommittee`. - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; - let notional_values = iota_client.get_notional_values().await.unwrap(); - assert_ne!(notional_values[&TOKEN_ID_USDC], 69_000 * USD_MULTIPLIER); - - let context = &mut test_cluster.wallet; - let bridge_object_arg = iota_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - let id_token_map = iota_client.get_token_id_map().await.unwrap(); - - // update price - let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction { - nonce: 0, - chain_id: BridgeChainId::IotaCustom, - token_id: TOKEN_ID_BTC, - new_usd_price: 69_000 * USD_MULTIPLIER, // $69k USD - }); - // `approve_action_with_validator_secrets` covers transaction building - approve_action_with_validator_secrets( - context, - bridge_object_arg, - action.clone(), - &bridge_authority_keys, - None, - &id_token_map, - ) - .await; - let new_notional_values = iota_client.get_notional_values().await.unwrap(); - for (token_id, price) in new_notional_values { - if token_id == TOKEN_ID_BTC { - assert_eq!(price, 69_000 * USD_MULTIPLIER); - } else { - assert_eq!(price, *notional_values.get(&token_id).unwrap()); - } - } - } -} diff --git a/crates/iota-bridge/src/lib.rs b/crates/iota-bridge/src/lib.rs deleted file mode 100644 index e62c722a293..00000000000 --- a/crates/iota-bridge/src/lib.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub mod abi; -pub mod action_executor; -pub mod client; -pub mod config; -pub mod crypto; -pub mod encoding; -pub mod error; -pub mod eth_client; -pub mod eth_syncer; -pub mod eth_transaction_builder; -pub mod events; -pub mod iota_client; -pub mod iota_syncer; -pub mod iota_transaction_builder; -pub mod metered_eth_provider; -pub mod metrics; -pub mod monitor; -pub mod node; -pub mod orchestrator; -pub mod server; -pub mod storage; -pub mod types; -pub mod utils; - -#[cfg(test)] -pub(crate) mod eth_mock_provider; - -#[cfg(test)] -pub(crate) mod iota_mock_client; - -#[cfg(test)] -pub mod test_utils; - -pub const BRIDGE_ENABLE_PROTOCOL_VERSION: u64 = 1; - -#[cfg(test)] -pub mod e2e_tests; - -#[macro_export] -macro_rules! retry_with_max_elapsed_time { - ($func:expr, $max_elapsed_time:expr) => {{ - // The following delay sequence (in secs) will be used, applied with jitter - // 0.4, 0.8, 1.6, 3.2, 6.4, 12.8, 25.6, 30, 60, 120, 120 ... - let backoff = backoff::ExponentialBackoff { - initial_interval: Duration::from_millis(400), - randomization_factor: 0.1, - multiplier: 2.0, - max_interval: Duration::from_secs(120), - max_elapsed_time: Some($max_elapsed_time), - ..Default::default() - }; - backoff::future::retry(backoff, || { - let fut = async { - let result = $func.await; - match result { - Ok(_) => { - return Ok(result); - } - Err(e) => { - // For simplicity we treat every error as transient so we can retry until - // max_elapsed_time - tracing::debug!("Retrying due to error: {:?}", e); - return Err(backoff::Error::transient(e)); - } - } - }; - std::boxed::Box::pin(fut) - }) - .await - }}; -} - -#[cfg(test)] -mod tests { - use std::time::Duration; - - use super::*; - - async fn example_func_ok() -> anyhow::Result<()> { - Ok(()) - } - - async fn example_func_err() -> anyhow::Result<()> { - tracing::info!("example_func_err"); - Err(anyhow::anyhow!("")) - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_retry_with_max_elapsed_time() { - telemetry_subscribers::init_for_testing(); - // no retry is needed, should return immediately. We give it a very small - // max_elapsed_time and it should still finish in time. - let max_elapsed_time = Duration::from_millis(20); - retry_with_max_elapsed_time!(example_func_ok(), max_elapsed_time) - .unwrap() - .unwrap(); - - // now call a function that always errors and expect it to return before - // max_elapsed_time runs out - let max_elapsed_time = Duration::from_secs(10); - let instant = std::time::Instant::now(); - retry_with_max_elapsed_time!(example_func_err(), max_elapsed_time).unwrap_err(); - assert!(instant.elapsed() < max_elapsed_time); - } -} diff --git a/crates/iota-bridge/src/main.rs b/crates/iota-bridge/src/main.rs deleted file mode 100644 index 0a4d6066f29..00000000000 --- a/crates/iota-bridge/src/main.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - net::{IpAddr, Ipv4Addr, SocketAddr}, - path::PathBuf, -}; - -use clap::Parser; -use iota_bridge::{ - config::BridgeNodeConfig, node::run_bridge_node, server::BridgeNodePublicMetadata, -}; -use iota_config::Config; -use iota_metrics::start_prometheus_server; -use tracing::info; - -// Define the `GIT_REVISION` and `VERSION` consts -bin_version::bin_version!(); - -#[derive(Parser)] -#[command(name = env!("CARGO_BIN_NAME"), version = VERSION)] -struct Args { - #[arg(long)] - pub config_path: PathBuf, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let args = Args::parse(); - let config = BridgeNodeConfig::load(&args.config_path).unwrap(); - - // Init metrics server - let metrics_address = - SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), config.metrics_port); - let registry_service = start_prometheus_server(metrics_address); - let prometheus_registry = registry_service.default_registry(); - iota_metrics::init_metrics(&prometheus_registry); - info!("Metrics server started at port {}", config.metrics_port); - - // Init logging - let (_guard, _filter_handle) = telemetry_subscribers::TelemetryConfig::new() - .with_env() - .with_prom_registry(&prometheus_registry) - .init(); - let metadata = BridgeNodePublicMetadata::new(VERSION.into()); - Ok(run_bridge_node(config, metadata, prometheus_registry) - .await? - .await?) -} diff --git a/crates/iota-bridge/src/metered_eth_provider.rs b/crates/iota-bridge/src/metered_eth_provider.rs deleted file mode 100644 index 71524e4fe47..00000000000 --- a/crates/iota-bridge/src/metered_eth_provider.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{fmt::Debug, sync::Arc}; - -use ethers::providers::{Http, HttpClientError, JsonRpcClient, Provider}; -use serde::{Serialize, de::DeserializeOwned}; -use url::{ParseError, Url}; - -use crate::metrics::BridgeMetrics; - -#[derive(Debug, Clone)] -pub struct MeteredEthHttpProvider { - inner: Http, - metrics: Arc, -} - -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -impl JsonRpcClient for MeteredEthHttpProvider { - type Error = HttpClientError; - - async fn request( - &self, - method: &str, - params: T, - ) -> Result { - self.metrics - .eth_rpc_queries - .with_label_values(&[method]) - .inc(); - let _guard = self - .metrics - .eth_rpc_queries_latency - .with_label_values(&[method]) - .start_timer(); - self.inner.request(method, params).await - } -} - -impl MeteredEthHttpProvider { - pub fn new(url: impl Into, metrics: Arc) -> Self { - let inner = Http::new(url); - Self { inner, metrics } - } -} - -pub fn new_metered_eth_provider( - url: &str, - metrics: Arc, -) -> Result, ParseError> { - let http_provider = MeteredEthHttpProvider::new(Url::parse(url)?, metrics); - Ok(Provider::new(http_provider)) -} - -#[cfg(test)] -mod tests { - use ethers::providers::Middleware; - use prometheus::Registry; - - use super::*; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_metered_eth_provider() { - let metrics = Arc::new(BridgeMetrics::new(&Registry::new())); - let provider = new_metered_eth_provider("http://localhost:9876", metrics.clone()).unwrap(); - - assert_eq!( - metrics - .eth_rpc_queries - .get_metric_with_label_values(&["eth_blockNumber"]) - .unwrap() - .get(), - 0 - ); - assert_eq!( - metrics - .eth_rpc_queries_latency - .get_metric_with_label_values(&["eth_blockNumber"]) - .unwrap() - .get_sample_count(), - 0 - ); - - provider.get_block_number().await.unwrap_err(); // the rpc cal will fail but we don't care - - assert_eq!( - metrics - .eth_rpc_queries - .get_metric_with_label_values(&["eth_blockNumber"]) - .unwrap() - .get(), - 1 - ); - assert_eq!( - metrics - .eth_rpc_queries_latency - .get_metric_with_label_values(&["eth_blockNumber"]) - .unwrap() - .get_sample_count(), - 1 - ); - } -} diff --git a/crates/iota-bridge/src/metrics.rs b/crates/iota-bridge/src/metrics.rs deleted file mode 100644 index ba9a652b8dd..00000000000 --- a/crates/iota-bridge/src/metrics.rs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use prometheus::{ - HistogramVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec, Registry, - register_histogram_vec_with_registry, register_int_counter_vec_with_registry, - register_int_counter_with_registry, register_int_gauge_vec_with_registry, - register_int_gauge_with_registry, -}; - -const FINE_GRAINED_LATENCY_SEC_BUCKETS: &[f64] = &[ - 0.001, 0.005, 0.01, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, - 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, - 10., 15., 20., 25., 30., 35., 40., 45., 50., 60., 70., 80., 90., 100., 120., 140., 160., 180., - 200., 250., 300., 350., 400., -]; - -#[derive(Clone, Debug)] -pub struct BridgeMetrics { - pub(crate) err_build_iota_transaction: IntCounter, - pub(crate) err_signature_aggregation: IntCounter, - pub(crate) err_iota_transaction_submission: IntCounter, - pub(crate) err_iota_transaction_submission_too_many_failures: IntCounter, - pub(crate) err_iota_transaction_execution: IntCounter, - pub(crate) requests_received: IntCounterVec, - pub(crate) requests_ok: IntCounterVec, - pub(crate) err_requests: IntCounterVec, - pub(crate) requests_inflight: IntGaugeVec, - - pub last_synced_iota_checkpoint: IntGauge, - pub(crate) last_finalized_eth_block: IntGauge, - pub(crate) last_synced_eth_block: IntGauge, - - pub(crate) iota_watcher_received_events: IntCounter, - pub(crate) iota_watcher_received_actions: IntCounter, - pub(crate) iota_watcher_unrecognized_events: IntCounter, - pub(crate) eth_watcher_received_events: IntCounter, - pub(crate) eth_watcher_received_actions: IntCounter, - pub(crate) eth_watcher_unrecognized_events: IntCounter, - pub(crate) action_executor_already_processed_actions: IntCounter, - pub(crate) action_executor_signing_queue_received_actions: IntCounter, - pub(crate) action_executor_signing_queue_skipped_actions: IntCounter, - pub(crate) action_executor_execution_queue_received_actions: IntCounter, - pub(crate) action_executor_execution_queue_skipped_actions_due_to_pausing: IntCounter, - - pub(crate) signer_with_cache_hit: IntCounterVec, - pub(crate) signer_with_cache_miss: IntCounterVec, - - pub(crate) eth_rpc_queries: IntCounterVec, - pub(crate) eth_rpc_queries_latency: HistogramVec, - - pub(crate) gas_coin_balance: IntGauge, -} - -impl BridgeMetrics { - pub fn new(registry: &Registry) -> Self { - Self { - err_build_iota_transaction: register_int_counter_with_registry!( - "bridge_err_build_iota_transaction", - "Total number of errors of building iota transactions", - registry, - ) - .unwrap(), - err_signature_aggregation: register_int_counter_with_registry!( - "bridge_err_signature_aggregation", - "Total number of errors of aggregating validators signatures", - registry, - ) - .unwrap(), - err_iota_transaction_submission: register_int_counter_with_registry!( - "bridge_err_iota_transaction_submission", - "Total number of errors of submitting iota transactions", - registry, - ) - .unwrap(), - err_iota_transaction_submission_too_many_failures: register_int_counter_with_registry!( - "bridge_err_iota_transaction_submission_too_many_failures", - "Total number of continuous failures to submitting iota transactions", - registry, - ) - .unwrap(), - err_iota_transaction_execution: register_int_counter_with_registry!( - "bridge_err_iota_transaction_execution", - "Total number of failures of iota transaction execution", - registry, - ) - .unwrap(), - requests_received: register_int_counter_vec_with_registry!( - "bridge_requests_received", - "Total number of requests received in Server, by request type", - &["type"], - registry, - ) - .unwrap(), - requests_ok: register_int_counter_vec_with_registry!( - "bridge_requests_ok", - "Total number of ok requests, by request type", - &["type"], - registry, - ) - .unwrap(), - err_requests: register_int_counter_vec_with_registry!( - "bridge_err_requests", - "Total number of erred requests, by request type", - &["type"], - registry, - ) - .unwrap(), - requests_inflight: register_int_gauge_vec_with_registry!( - "bridge_requests_inflight", - "Total number of inflight requests, by request type", - &["type"], - registry, - ) - .unwrap(), - iota_watcher_received_events: register_int_counter_with_registry!( - "bridge_iota_watcher_received_events", - "Total number of received events in iota watcher", - registry, - ) - .unwrap(), - eth_watcher_received_events: register_int_counter_with_registry!( - "bridge_eth_watcher_received_events", - "Total number of received events in eth watcher", - registry, - ) - .unwrap(), - iota_watcher_received_actions: register_int_counter_with_registry!( - "bridge_iota_watcher_received_actions", - "Total number of received actions in iota watcher", - registry, - ) - .unwrap(), - eth_watcher_received_actions: register_int_counter_with_registry!( - "bridge_eth_watcher_received_actions", - "Total number of received actions in eth watcher", - registry, - ) - .unwrap(), - iota_watcher_unrecognized_events: register_int_counter_with_registry!( - "bridge_iota_watcher_unrecognized_events", - "Total number of unrecognized events in iota watcher", - registry, - ) - .unwrap(), - eth_watcher_unrecognized_events: register_int_counter_with_registry!( - "bridge_eth_watcher_unrecognized_events", - "Total number of unrecognized events in eth watcher", - registry, - ) - .unwrap(), - action_executor_already_processed_actions: register_int_counter_with_registry!( - "bridge_action_executor_already_processed_actions", - "Total number of already processed actions action executor", - registry, - ) - .unwrap(), - action_executor_signing_queue_received_actions: register_int_counter_with_registry!( - "bridge_action_executor_signing_queue_received_actions", - "Total number of received actions in action executor signing queue", - registry, - ) - .unwrap(), - action_executor_signing_queue_skipped_actions: register_int_counter_with_registry!( - "bridge_action_executor_signing_queue_skipped_actions", - "Total number of skipped actions in action executor signing queue", - registry, - ) - .unwrap(), - action_executor_execution_queue_received_actions: register_int_counter_with_registry!( - "bridge_action_executor_execution_queue_received_actions", - "Total number of received actions in action executor execution queue", - registry, - ) - .unwrap(), - action_executor_execution_queue_skipped_actions_due_to_pausing: register_int_counter_with_registry!( - "bridge_action_executor_execution_queue_skipped_actions_due_to_pausing", - "Total number of skipped actions in action executor execution queue because of pausing", - registry, - ) - .unwrap(), - gas_coin_balance: register_int_gauge_with_registry!( - "bridge_gas_coin_balance", - "Current balance of gas coin, in nanos", - registry, - ) - .unwrap(), - eth_rpc_queries: register_int_counter_vec_with_registry!( - "bridge_eth_rpc_queries", - "Total number of queries issued to eth provider, by request type", - &["type"], - registry, - ) - .unwrap(), - eth_rpc_queries_latency: register_histogram_vec_with_registry!( - "bridge_eth_rpc_queries_latency", - "Latency of queries issued to eth provider, by request type", - &["type"], - FINE_GRAINED_LATENCY_SEC_BUCKETS.to_vec(), - registry, - ) - .unwrap(), - last_synced_iota_checkpoint: register_int_gauge_with_registry!( - "last_synced_iota_checkpoint", - "The latest iota checkpoint that indexer synced", - registry, - ) - .unwrap(), - last_synced_eth_block: register_int_gauge_with_registry!( - "bridge_last_synced_eth_block", - "The latest finalized eth block that indexer synced", - registry, - ) - .unwrap(), - last_finalized_eth_block: register_int_gauge_with_registry!( - "bridge_last_finalized_eth_block", - "The latest finalized eth block that indexer observed", - registry, - ) - .unwrap(), - signer_with_cache_hit: register_int_counter_vec_with_registry!( - "bridge_signer_with_cache_hit", - "Total number of hit in signer's cache, by verifier type", - &["type"], - registry, - ) - .unwrap(), - signer_with_cache_miss: register_int_counter_vec_with_registry!( - "bridge_signer_with_cache_miss", - "Total number of miss in signer's cache, by verifier type", - &["type"], - registry, - ) - .unwrap(), - } - } - - pub fn new_for_testing() -> Self { - let registry = Registry::new(); - Self::new(®istry) - } -} diff --git a/crates/iota-bridge/src/monitor.rs b/crates/iota-bridge/src/monitor.rs deleted file mode 100644 index ef7d7f3b726..00000000000 --- a/crates/iota-bridge/src/monitor.rs +++ /dev/null @@ -1,983 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! `BridgeMonitor` receives all `IotaBridgeEvent` and handles them accordingly. - -use std::{collections::HashMap, sync::Arc}; - -use arc_swap::ArcSwap; -use iota_types::TypeTag; -use tokio::time::Duration; -use tracing::{error, info, warn}; - -use crate::{ - client::bridge_authority_aggregator::BridgeAuthorityAggregator, - crypto::BridgeAuthorityPublicKeyBytes, - events::{ - BlocklistValidatorEvent, CommitteeMemberUrlUpdateEvent, EmergencyOpEvent, IotaBridgeEvent, - }, - iota_client::{IotaClient, IotaClientInner}, - retry_with_max_elapsed_time, - types::{BridgeCommittee, IsBridgePaused}, -}; - -const REFRESH_BRIDGE_RETRY_TIMES: u64 = 3; - -pub struct BridgeMonitor { - iota_client: Arc>, - monitor_rx: iota_metrics::metered_channel::Receiver, - bridge_auth_agg: Arc>, - bridge_paused_watch_tx: tokio::sync::watch::Sender, - iota_token_type_tags: Arc>>, -} - -impl BridgeMonitor -where - C: IotaClientInner + 'static, -{ - pub fn new( - iota_client: Arc>, - monitor_rx: iota_metrics::metered_channel::Receiver, - bridge_auth_agg: Arc>, - bridge_paused_watch_tx: tokio::sync::watch::Sender, - iota_token_type_tags: Arc>>, - ) -> Self { - Self { - iota_client, - monitor_rx, - bridge_auth_agg, - bridge_paused_watch_tx, - iota_token_type_tags, - } - } - - pub async fn run(self) { - tracing::info!("Starting BridgeMonitor"); - let Self { - iota_client, - mut monitor_rx, - bridge_auth_agg, - bridge_paused_watch_tx, - iota_token_type_tags, - } = self; - let mut latest_token_config = (*iota_token_type_tags.load().clone()).clone(); - - while let Some(events) = monitor_rx.recv().await { - match events { - IotaBridgeEvent::IotaToEthTokenBridgeV1(_) => (), - IotaBridgeEvent::TokenTransferApproved(_) => (), - IotaBridgeEvent::TokenTransferClaimed(_) => (), - IotaBridgeEvent::TokenTransferAlreadyApproved(_) => (), - IotaBridgeEvent::TokenTransferAlreadyClaimed(_) => (), - IotaBridgeEvent::TokenTransferLimitExceed(_) => { - // TODO - } - - IotaBridgeEvent::EmergencyOpEvent(event) => { - info!("Received EmergencyOpEvent: {:?}", event); - let is_paused = get_latest_bridge_pause_status_with_emergency_event( - iota_client.clone(), - event, - Duration::from_secs(10), - ) - .await; - bridge_paused_watch_tx - .send(is_paused) - .expect("Bridge pause status watch channel should not be closed"); - } - - IotaBridgeEvent::CommitteeMemberRegistration(_) => (), - IotaBridgeEvent::CommitteeUpdateEvent(_) => (), - - IotaBridgeEvent::CommitteeMemberUrlUpdateEvent(event) => { - info!("Received CommitteeMemberUrlUpdateEvent: {:?}", event); - let new_committee = get_latest_bridge_committee_with_url_update_event( - iota_client.clone(), - event, - Duration::from_secs(10), - ) - .await; - bridge_auth_agg.store(Arc::new(BridgeAuthorityAggregator::new(Arc::new( - new_committee, - )))); - info!("Committee updated with CommitteeMemberUrlUpdateEvent"); - } - - IotaBridgeEvent::BlocklistValidatorEvent(event) => { - info!("Received BlocklistValidatorEvent: {:?}", event); - let new_committee = get_latest_bridge_committee_with_blocklist_event( - iota_client.clone(), - event, - Duration::from_secs(10), - ) - .await; - bridge_auth_agg.store(Arc::new(BridgeAuthorityAggregator::new(Arc::new( - new_committee, - )))); - info!("Committee updated with BlocklistValidatorEvent"); - } - - IotaBridgeEvent::TokenRegistrationEvent(_) => (), - - IotaBridgeEvent::NewTokenEvent(event) => { - if let std::collections::hash_map::Entry::Vacant(entry) = - // We only add new tokens but not remove so it's ok to just insert - latest_token_config.entry(event.token_id) - { - entry.insert(event.type_name.clone()); - iota_token_type_tags.store(Arc::new(latest_token_config.clone())); - } else { - // invariant - assert_eq!(event.type_name, latest_token_config[&event.token_id]); - } - } - - IotaBridgeEvent::UpdateTokenPriceEvent(_) => (), - } - } - - panic!("BridgeMonitor channel was closed unexpectedly"); - } -} - -async fn get_latest_bridge_committee_with_url_update_event( - iota_client: Arc>, - event: CommitteeMemberUrlUpdateEvent, - staleness_retry_interval: Duration, -) -> BridgeCommittee { - let mut remaining_retry_times = REFRESH_BRIDGE_RETRY_TIMES; - loop { - let Ok(Ok(committee)) = retry_with_max_elapsed_time!( - iota_client.get_bridge_committee(), - Duration::from_secs(600) - ) else { - error!("Failed to get bridge committee after retry"); - continue; - }; - let member = committee.member(&BridgeAuthorityPublicKeyBytes::from(&event.member)); - let Some(member) = member else { - // This is possible when a node is processing an older event while the member - // quit at a later point, which is fine. Or fullnode returns a stale - // committee that the member hasn't joined, which is rare and tricy to handle so - // we just log it. - warn!( - "Committee member not found in the committee: {:?}", - event.member - ); - return committee; - }; - if member.base_url == event.new_url { - return committee; - } - // If url does not match, it could be: - // 1. the query is sent to a stale fullnode that does not have the latest data - // yet - // 2. the node is processing an older message, and the latest url has changed - // again - // In either case, we retry a few times. If it still fails to match, we assume - // it's the latter case. - tokio::time::sleep(staleness_retry_interval).await; - remaining_retry_times -= 1; - if remaining_retry_times == 0 { - warn!( - "Committee member url {:?} does not match onchain record {:?} after retry", - event.member, member - ); - return committee; - } - } -} - -async fn get_latest_bridge_committee_with_blocklist_event( - iota_client: Arc>, - event: BlocklistValidatorEvent, - staleness_retry_interval: Duration, -) -> BridgeCommittee { - let mut remaining_retry_times = REFRESH_BRIDGE_RETRY_TIMES; - loop { - let Ok(Ok(committee)) = retry_with_max_elapsed_time!( - iota_client.get_bridge_committee(), - Duration::from_secs(600) - ) else { - error!("Failed to get bridge committee after retry"); - continue; - }; - let mut any_mismatch = false; - for pk in &event.public_keys { - let member = committee.member(&BridgeAuthorityPublicKeyBytes::from(pk)); - let Some(member) = member else { - // This is possible when a node is processing an older event while the member - // quit at a later point. Or fullnode returns a stale committee that - // the member hasn't joined. - warn!("Committee member not found in the committee: {:?}", pk); - any_mismatch = true; - break; - }; - if member.is_blocklisted != event.blocklisted { - warn!( - "Committee member blocklist status does not match onchain record: {:?}", - member - ); - any_mismatch = true; - break; - } - } - if !any_mismatch { - return committee; - } - // If there is any match, it could be: - // 1. the query is sent to a stale fullnode that does not have the latest data - // yet - // 2. the node is processing an older message, and the latest blocklist status - // has changed again - // In either case, we retry a few times. If it still fails to match, we assume - // it's the latter case. - tokio::time::sleep(staleness_retry_interval).await; - remaining_retry_times -= 1; - if remaining_retry_times == 0 { - warn!( - "Committee member blocklist status {:?} does not match onchain record after retry", - event - ); - return committee; - } - } -} - -async fn get_latest_bridge_pause_status_with_emergency_event( - iota_client: Arc>, - event: EmergencyOpEvent, - staleness_retry_interval: Duration, -) -> IsBridgePaused { - let mut remaining_retry_times = REFRESH_BRIDGE_RETRY_TIMES; - loop { - let Ok(Ok(summary)) = retry_with_max_elapsed_time!( - iota_client.get_bridge_summary(), - Duration::from_secs(600) - ) else { - error!("Failed to get bridge summary after retry"); - continue; - }; - if summary.is_frozen == event.frozen { - return summary.is_frozen; - } - // If the onchain status does not match, it could be: - // 1. the query is sent to a stale fullnode that does not have the latest data - // yet - // 2. the node is processing an older message, and the latest status has changed - // again - // In either case, we retry a few times. If it still fails to match, we assume - // it's the latter case. - tokio::time::sleep(staleness_retry_interval).await; - remaining_retry_times -= 1; - if remaining_retry_times == 0 { - warn!( - "Bridge pause status {:?} does not match onchain record {:?} after retry", - event, summary.is_frozen - ); - return summary.is_frozen; - } - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use fastcrypto::traits::KeyPair; - use iota_types::{ - base_types::IotaAddress, - bridge::{BridgeCommitteeSummary, MoveTypeCommitteeMember}, - crypto::{ToFromBytes, get_key_pair}, - }; - use prometheus::Registry; - - use super::*; - use crate::{ - events::{NewTokenEvent, init_all_struct_tags}, - iota_mock_client::IotaMockClient, - test_utils::{bridge_committee_to_bridge_committee_summary, get_test_authority_and_key}, - types::{BRIDGE_PAUSED, BRIDGE_UNPAUSED, BridgeAuthority, BridgeCommittee}, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_get_latest_bridge_committee_with_url_update_event() { - telemetry_subscribers::init_for_testing(); - let iota_client_mock = IotaMockClient::default(); - let iota_client = Arc::new(IotaClient::new_for_testing(iota_client_mock.clone())); - let (_, kp): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let pk = kp.public().clone(); - let pk_as_bytes = BridgeAuthorityPublicKeyBytes::from(&pk); - let pk_bytes = pk_as_bytes.as_bytes().to_vec(); - let event = CommitteeMemberUrlUpdateEvent { - member: pk, - new_url: "http://new.url".to_string(), - }; - let summary = BridgeCommitteeSummary { - members: vec![( - pk_bytes.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes.clone(), - voting_power: 10000, - http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), - blocklisted: false, - }, - )], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - - // Test the regular case, the onchain url matches - iota_client_mock.set_bridge_committee(summary.clone()); - let timer = std::time::Instant::now(); - let committee = get_latest_bridge_committee_with_url_update_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(2), - ) - .await; - assert_eq!( - committee.member(&pk_as_bytes).unwrap().base_url, - "http://new.url" - ); - assert!(timer.elapsed().as_millis() < 500); - - // Test the case where the onchain url is older. Then update onchain url in 1 - // second. Since the retry interval is 2 seconds, it should return the - // next retry. - let old_summary = BridgeCommitteeSummary { - members: vec![( - pk_bytes.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes.clone(), - voting_power: 10000, - http_rest_url: "http://old.url".to_string().as_bytes().to_vec(), - blocklisted: false, - }, - )], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - iota_client_mock.set_bridge_committee(old_summary.clone()); - let timer = std::time::Instant::now(); - // update the url to "http://new.url" in 1 second - let iota_client_mock_clone = iota_client_mock.clone(); - tokio::spawn(async move { - tokio::time::sleep(Duration::from_secs(1)).await; - iota_client_mock_clone.set_bridge_committee(summary.clone()); - }); - let committee = get_latest_bridge_committee_with_url_update_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(2), - ) - .await; - assert_eq!( - committee.member(&pk_as_bytes).unwrap().base_url, - "http://new.url" - ); - let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 1000 && elapsed < 3000); - - // Test the case where the onchain url is newer. It should retry up to - // REFRESH_BRIDGE_RETRY_TIMES time then return the onchain record. - let newer_summary = BridgeCommitteeSummary { - members: vec![( - pk_bytes.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes.clone(), - voting_power: 10000, - http_rest_url: "http://newer.url".to_string().as_bytes().to_vec(), - blocklisted: false, - }, - )], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - iota_client_mock.set_bridge_committee(newer_summary.clone()); - let timer = std::time::Instant::now(); - let committee = get_latest_bridge_committee_with_url_update_event( - iota_client.clone(), - event.clone(), - Duration::from_millis(500), - ) - .await; - assert_eq!( - committee.member(&pk_as_bytes).unwrap().base_url, - "http://newer.url" - ); - let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); - - // Test the case where the member is not found in the committee - // It should return the onchain record. - let (_, kp2): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let pk2 = kp2.public().clone(); - let pk_as_bytes2 = BridgeAuthorityPublicKeyBytes::from(&pk2); - let pk_bytes2 = pk_as_bytes2.as_bytes().to_vec(); - let newer_summary = BridgeCommitteeSummary { - members: vec![( - pk_bytes2.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes2.clone(), - voting_power: 10000, - http_rest_url: "http://newer.url".to_string().as_bytes().to_vec(), - blocklisted: false, - }, - )], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - iota_client_mock.set_bridge_committee(newer_summary.clone()); - let timer = std::time::Instant::now(); - let committee = get_latest_bridge_committee_with_url_update_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(1), - ) - .await; - assert_eq!( - committee.member(&pk_as_bytes2).unwrap().base_url, - "http://newer.url" - ); - assert!(committee.member(&pk_as_bytes).is_none()); - let elapsed = timer.elapsed().as_millis(); - assert!(elapsed < 1000); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_get_latest_bridge_committee_with_blocklist_event() { - telemetry_subscribers::init_for_testing(); - let iota_client_mock = IotaMockClient::default(); - let iota_client = Arc::new(IotaClient::new_for_testing(iota_client_mock.clone())); - let (_, kp): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let pk = kp.public().clone(); - let pk_as_bytes = BridgeAuthorityPublicKeyBytes::from(&pk); - let pk_bytes = pk_as_bytes.as_bytes().to_vec(); - - // Test the case where the onchain status is the same as the event (blocklisted) - let event = BlocklistValidatorEvent { - blocklisted: true, - public_keys: vec![pk.clone()], - }; - let summary = BridgeCommitteeSummary { - members: vec![( - pk_bytes.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes.clone(), - voting_power: 10000, - http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), - blocklisted: true, - }, - )], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - iota_client_mock.set_bridge_committee(summary.clone()); - let timer = std::time::Instant::now(); - let committee = get_latest_bridge_committee_with_blocklist_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(2), - ) - .await; - assert!(committee.member(&pk_as_bytes).unwrap().is_blocklisted); - assert!(timer.elapsed().as_millis() < 500); - - // Test the case where the onchain status is the same as the event - // (unblocklisted) - let event = BlocklistValidatorEvent { - blocklisted: false, - public_keys: vec![pk.clone()], - }; - let summary = BridgeCommitteeSummary { - members: vec![( - pk_bytes.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes.clone(), - voting_power: 10000, - http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), - blocklisted: false, - }, - )], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - iota_client_mock.set_bridge_committee(summary.clone()); - let timer = std::time::Instant::now(); - let committee = get_latest_bridge_committee_with_blocklist_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(2), - ) - .await; - assert!(!committee.member(&pk_as_bytes).unwrap().is_blocklisted); - assert!(timer.elapsed().as_millis() < 500); - - // Test the case where the onchain status is older. Then update onchain status - // in 1 second. Since the retry interval is 2 seconds, it should return - // the next retry. - let old_summary = BridgeCommitteeSummary { - members: vec![( - pk_bytes.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes.clone(), - voting_power: 10000, - http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), - blocklisted: true, - }, - )], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - iota_client_mock.set_bridge_committee(old_summary.clone()); - let timer = std::time::Instant::now(); - // update unblocklisted in 1 second - let iota_client_mock_clone = iota_client_mock.clone(); - tokio::spawn(async move { - tokio::time::sleep(Duration::from_secs(1)).await; - iota_client_mock_clone.set_bridge_committee(summary.clone()); - }); - let committee = get_latest_bridge_committee_with_blocklist_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(2), - ) - .await; - assert!(!committee.member(&pk_as_bytes).unwrap().is_blocklisted); - let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 1000 && elapsed < 3000); - - // Test the case where the onchain url is newer. It should retry up to - // REFRESH_BRIDGE_RETRY_TIMES time then return the onchain record. - let newer_summary = BridgeCommitteeSummary { - members: vec![( - pk_bytes.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes.clone(), - voting_power: 10000, - http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), - blocklisted: true, - }, - )], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - iota_client_mock.set_bridge_committee(newer_summary.clone()); - let timer = std::time::Instant::now(); - let committee = get_latest_bridge_committee_with_blocklist_event( - iota_client.clone(), - event.clone(), - Duration::from_millis(500), - ) - .await; - assert!(committee.member(&pk_as_bytes).unwrap().is_blocklisted); - let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); - - // Test the case where the member onchain url is not found in the committee - // It should return the onchain record after retrying a few times. - let (_, kp2): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let pk2 = kp2.public().clone(); - let pk_as_bytes2 = BridgeAuthorityPublicKeyBytes::from(&pk2); - let pk_bytes2 = pk_as_bytes2.as_bytes().to_vec(); - let summary = BridgeCommitteeSummary { - members: vec![( - pk_bytes2.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes2.clone(), - voting_power: 10000, - http_rest_url: "http://newer.url".to_string().as_bytes().to_vec(), - blocklisted: false, - }, - )], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - iota_client_mock.set_bridge_committee(summary.clone()); - let timer = std::time::Instant::now(); - let committee = get_latest_bridge_committee_with_blocklist_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(1), - ) - .await; - assert_eq!( - committee.member(&pk_as_bytes2).unwrap().base_url, - "http://newer.url" - ); - assert!(committee.member(&pk_as_bytes).is_none()); - let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); - - // Test any mismtach in the blocklist status should retry a few times - let event = BlocklistValidatorEvent { - blocklisted: true, - public_keys: vec![pk, pk2], - }; - let summary = BridgeCommitteeSummary { - members: vec![ - ( - pk_bytes.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes.clone(), - voting_power: 5000, - http_rest_url: "http://pk.url".to_string().as_bytes().to_vec(), - blocklisted: true, - }, - ), - ( - pk_bytes2.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: pk_bytes2.clone(), - voting_power: 5000, - http_rest_url: "http://pk2.url".to_string().as_bytes().to_vec(), - blocklisted: false, - }, - ), - ], - member_registration: vec![], - last_committee_update_epoch: 0, - }; - iota_client_mock.set_bridge_committee(summary.clone()); - let timer = std::time::Instant::now(); - let committee = get_latest_bridge_committee_with_blocklist_event( - iota_client.clone(), - event.clone(), - Duration::from_millis(500), - ) - .await; - assert!(committee.member(&pk_as_bytes).unwrap().is_blocklisted); - assert!(!committee.member(&pk_as_bytes2).unwrap().is_blocklisted); - let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_get_bridge_pause_status_with_emergency_event() { - telemetry_subscribers::init_for_testing(); - let iota_client_mock = IotaMockClient::default(); - let iota_client = Arc::new(IotaClient::new_for_testing(iota_client_mock.clone())); - - // Test event and onchain status match - let event = EmergencyOpEvent { frozen: true }; - iota_client_mock.set_is_bridge_paused(BRIDGE_PAUSED); - let timer = std::time::Instant::now(); - assert!( - get_latest_bridge_pause_status_with_emergency_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(2), - ) - .await - ); - assert!(timer.elapsed().as_millis() < 500); - - let event = EmergencyOpEvent { frozen: false }; - iota_client_mock.set_is_bridge_paused(BRIDGE_UNPAUSED); - let timer = std::time::Instant::now(); - assert!( - !get_latest_bridge_pause_status_with_emergency_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(2), - ) - .await - ); - assert!(timer.elapsed().as_millis() < 500); - - // Test the case where the onchain status (paused) is older. Then update onchain - // status in 1 second. Since the retry interval is 2 seconds, it should - // return the next retry. - iota_client_mock.set_is_bridge_paused(BRIDGE_PAUSED); - let timer = std::time::Instant::now(); - // update the bridge to unpaused in 1 second - let iota_client_mock_clone = iota_client_mock.clone(); - tokio::spawn(async move { - tokio::time::sleep(Duration::from_secs(1)).await; - iota_client_mock_clone.set_is_bridge_paused(BRIDGE_UNPAUSED); - }); - assert!( - !get_latest_bridge_pause_status_with_emergency_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(2), - ) - .await - ); - let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 1000 && elapsed < 3000, "{}", elapsed); - - // Test the case where the onchain status (paused) is newer. It should retry up - // to REFRESH_BRIDGE_RETRY_TIMES time then return the onchain record. - iota_client_mock.set_is_bridge_paused(BRIDGE_PAUSED); - let timer = std::time::Instant::now(); - assert!( - get_latest_bridge_pause_status_with_emergency_event( - iota_client.clone(), - event.clone(), - Duration::from_secs(2), - ) - .await - ); - let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_update_bridge_authority_aggregation_with_url_change_event() { - let ( - monitor_tx, - monitor_rx, - iota_client_mock, - iota_client, - bridge_pause_tx, - _bridge_pause_rx, - mut authorities, - ) = setup(); - let old_committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( - Arc::new(old_committee), - )))); - let iota_token_type_tags = Arc::new(ArcSwap::from(Arc::new(HashMap::new()))); - let _handle = tokio::task::spawn( - BridgeMonitor::new( - iota_client.clone(), - monitor_rx, - agg.clone(), - bridge_pause_tx, - iota_token_type_tags, - ) - .run(), - ); - let new_url = "http://new.url".to_string(); - authorities[0].base_url = new_url.clone(); - let new_committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let new_committee_summary = - bridge_committee_to_bridge_committee_summary(new_committee.clone()); - iota_client_mock.set_bridge_committee(new_committee_summary.clone()); - monitor_tx - .send(IotaBridgeEvent::CommitteeMemberUrlUpdateEvent( - CommitteeMemberUrlUpdateEvent { - member: authorities[0].pubkey.clone(), - new_url: new_url.clone(), - }, - )) - .await - .unwrap(); - // Wait for the monitor to process the event - tokio::time::sleep(Duration::from_secs(1)).await; - // Now expect the committee to be updated - assert_eq!( - agg.load() - .committee - .member(&BridgeAuthorityPublicKeyBytes::from(&authorities[0].pubkey)) - .unwrap() - .base_url, - new_url - ); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_update_bridge_authority_aggregation_with_blocklist_event() { - let ( - monitor_tx, - monitor_rx, - iota_client_mock, - iota_client, - bridge_pause_tx, - _bridge_pause_rx, - mut authorities, - ) = setup(); - let old_committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( - Arc::new(old_committee), - )))); - let iota_token_type_tags = Arc::new(ArcSwap::from(Arc::new(HashMap::new()))); - let _handle = tokio::task::spawn( - BridgeMonitor::new( - iota_client.clone(), - monitor_rx, - agg.clone(), - bridge_pause_tx, - iota_token_type_tags, - ) - .run(), - ); - authorities[0].is_blocklisted = true; - let to_blocklist = &authorities[0]; - let new_committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let new_committee_summary = - bridge_committee_to_bridge_committee_summary(new_committee.clone()); - iota_client_mock.set_bridge_committee(new_committee_summary.clone()); - monitor_tx - .send(IotaBridgeEvent::BlocklistValidatorEvent( - BlocklistValidatorEvent { - public_keys: vec![to_blocklist.pubkey.clone()], - blocklisted: true, - }, - )) - .await - .unwrap(); - // Wait for the monitor to process the event - tokio::time::sleep(Duration::from_secs(1)).await; - assert!( - agg.load() - .committee - .member(&BridgeAuthorityPublicKeyBytes::from(&to_blocklist.pubkey)) - .unwrap() - .is_blocklisted, - ); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_update_bridge_pause_status_with_emergency_event() { - let ( - monitor_tx, - monitor_rx, - iota_client_mock, - iota_client, - bridge_pause_tx, - bridge_pause_rx, - authorities, - ) = setup(); - let event = EmergencyOpEvent { - frozen: !*bridge_pause_tx.borrow(), // toggle the bridge pause status - }; - let committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( - Arc::new(committee), - )))); - let iota_token_type_tags = Arc::new(ArcSwap::from(Arc::new(HashMap::new()))); - let _handle = tokio::task::spawn( - BridgeMonitor::new( - iota_client.clone(), - monitor_rx, - agg.clone(), - bridge_pause_tx, - iota_token_type_tags, - ) - .run(), - ); - - iota_client_mock.set_is_bridge_paused(event.frozen); - monitor_tx - .send(IotaBridgeEvent::EmergencyOpEvent(event.clone())) - .await - .unwrap(); - // Wait for the monitor to process the event - tokio::time::sleep(Duration::from_secs(1)).await; - // Now expect the committee to be updated - assert!(*bridge_pause_rx.borrow() == event.frozen); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_update_iota_token_type_tags() { - let ( - monitor_tx, - monitor_rx, - _iota_client_mock, - iota_client, - bridge_pause_tx, - _bridge_pause_rx, - authorities, - ) = setup(); - let event = NewTokenEvent { - token_id: 255, - type_name: TypeTag::from_str("0xbeef::beef::BEEF").unwrap(), - native_token: false, - decimal_multiplier: 1000000, - notional_value: 100000000, - }; - let committee = BridgeCommittee::new(authorities.clone()).unwrap(); - let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( - Arc::new(committee), - )))); - let iota_token_type_tags = Arc::new(ArcSwap::from(Arc::new(HashMap::new()))); - let iota_token_type_tags_clone = iota_token_type_tags.clone(); - let _handle = tokio::task::spawn( - BridgeMonitor::new( - iota_client.clone(), - monitor_rx, - agg.clone(), - bridge_pause_tx, - iota_token_type_tags_clone, - ) - .run(), - ); - - monitor_tx - .send(IotaBridgeEvent::NewTokenEvent(event.clone())) - .await - .unwrap(); - // Wait for the monitor to process the event - tokio::time::sleep(Duration::from_secs(1)).await; - // Now expect new token type tags to appear in iota_token_type_tags - iota_token_type_tags - .load() - .clone() - .get(&event.token_id) - .unwrap(); - } - - #[expect(clippy::type_complexity)] - fn setup() -> ( - iota_metrics::metered_channel::Sender, - iota_metrics::metered_channel::Receiver, - IotaMockClient, - Arc>, - tokio::sync::watch::Sender, - tokio::sync::watch::Receiver, - Vec, - ) { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - init_all_struct_tags(); - - let iota_client_mock = IotaMockClient::default(); - let iota_client = Arc::new(IotaClient::new_for_testing(iota_client_mock.clone())); - let (monitor_tx, monitor_rx) = iota_metrics::metered_channel::channel( - 10000, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["monitor_queue"]), - ); - let (bridge_pause_tx, bridge_pause_rx) = tokio::sync::watch::channel(false); - let authorities = vec![ - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - ]; - ( - monitor_tx, - monitor_rx, - iota_client_mock, - iota_client, - bridge_pause_tx, - bridge_pause_rx, - authorities, - ) - } -} diff --git a/crates/iota-bridge/src/node.rs b/crates/iota-bridge/src/node.rs deleted file mode 100644 index d103e8c9e18..00000000000 --- a/crates/iota-bridge/src/node.rs +++ /dev/null @@ -1,606 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - collections::HashMap, - net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::Arc, - time::Duration, -}; - -use arc_swap::ArcSwap; -use ethers::types::Address as EthAddress; -use iota_metrics::spawn_logged_monitored_task; -use iota_types::{ - Identifier, - bridge::{ - BRIDGE_COMMITTEE_MODULE_NAME, BRIDGE_LIMITER_MODULE_NAME, BRIDGE_MODULE_NAME, - BRIDGE_TREASURY_MODULE_NAME, - }, - event::EventID, -}; -use tokio::task::JoinHandle; -use tracing::info; - -use crate::{ - action_executor::BridgeActionExecutor, - client::bridge_authority_aggregator::BridgeAuthorityAggregator, - config::{BridgeClientConfig, BridgeNodeConfig}, - eth_syncer::EthSyncer, - events::init_all_struct_tags, - iota_syncer::IotaSyncer, - metrics::BridgeMetrics, - monitor::BridgeMonitor, - orchestrator::BridgeOrchestrator, - server::{BridgeNodePublicMetadata, handler::BridgeRequestHandler, run_server}, - storage::BridgeOrchestratorTables, -}; - -pub async fn run_bridge_node( - config: BridgeNodeConfig, - metadata: BridgeNodePublicMetadata, - prometheus_registry: prometheus::Registry, -) -> anyhow::Result> { - init_all_struct_tags(); - let metrics = Arc::new(BridgeMetrics::new(&prometheus_registry)); - let (server_config, client_config) = config.validate(metrics.clone()).await?; - - // Start Client - let _handles = if let Some(client_config) = client_config { - start_client_components(client_config, metrics.clone()).await - } else { - Ok(vec![]) - }?; - - // Start Server - let socket_address = SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), - server_config.server_listen_port, - ); - Ok(run_server( - &socket_address, - BridgeRequestHandler::new( - server_config.key, - server_config.iota_client, - server_config.eth_client, - server_config.approved_governance_actions, - metrics.clone(), - ), - metrics, - Arc::new(metadata), - )) -} - -// TODO: is there a way to clean up the overrides after it's stored in DB? -async fn start_client_components( - client_config: BridgeClientConfig, - metrics: Arc, -) -> anyhow::Result>> { - let store: std::sync::Arc = - BridgeOrchestratorTables::new(&client_config.db_path.join("client")); - let iota_modules_to_watch = get_iota_modules_to_watch( - &store, - client_config.iota_bridge_module_last_processed_event_id_override, - ); - let eth_contracts_to_watch = get_eth_contracts_to_watch( - &store, - &client_config.eth_contracts, - client_config.eth_contracts_start_block_fallback, - client_config.eth_contracts_start_block_override, - ); - - let iota_client = client_config.iota_client.clone(); - - let mut all_handles = vec![]; - let (task_handles, eth_events_rx, _) = - EthSyncer::new(client_config.eth_client.clone(), eth_contracts_to_watch) - .run(metrics.clone()) - .await - .expect("Failed to start eth syncer"); - all_handles.extend(task_handles); - - let (task_handles, iota_events_rx) = - IotaSyncer::new(client_config.iota_client, iota_modules_to_watch) - .run(Duration::from_secs(2)) - .await - .expect("Failed to start iota syncer"); - all_handles.extend(task_handles); - - let committee = Arc::new( - iota_client - .get_bridge_committee() - .await - .expect("Failed to get committee"), - ); - let bridge_auth_agg = Arc::new(ArcSwap::from(Arc::new(BridgeAuthorityAggregator::new( - committee, - )))); - // TODO: should we use one query instead of two? - let iota_token_type_tags = iota_client.get_token_id_map().await.unwrap(); - let is_bridge_paused = iota_client.is_bridge_paused().await.unwrap(); - - let (bridge_pause_tx, bridge_pause_rx) = tokio::sync::watch::channel(is_bridge_paused); - - let (monitor_tx, monitor_rx) = iota_metrics::metered_channel::channel( - 10000, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["monitor_queue"]), - ); - let iota_token_type_tags = Arc::new(ArcSwap::from(Arc::new(iota_token_type_tags))); - let bridge_action_executor = BridgeActionExecutor::new( - iota_client.clone(), - bridge_auth_agg.clone(), - store.clone(), - client_config.key, - client_config.iota_address, - client_config.gas_object_ref.0, - iota_token_type_tags.clone(), - bridge_pause_rx, - metrics.clone(), - ) - .await; - - let monitor = BridgeMonitor::new( - iota_client.clone(), - monitor_rx, - bridge_auth_agg.clone(), - bridge_pause_tx, - iota_token_type_tags, - ); - all_handles.push(spawn_logged_monitored_task!(monitor.run())); - - let orchestrator = BridgeOrchestrator::new( - iota_client, - iota_events_rx, - eth_events_rx, - store.clone(), - monitor_tx, - metrics, - ); - - all_handles.extend(orchestrator.run(bridge_action_executor).await); - Ok(all_handles) -} - -fn get_iota_modules_to_watch( - store: &std::sync::Arc, - iota_bridge_module_last_processed_event_id_override: Option, -) -> HashMap> { - let iota_bridge_modules = vec![ - BRIDGE_MODULE_NAME.to_owned(), - BRIDGE_COMMITTEE_MODULE_NAME.to_owned(), - BRIDGE_TREASURY_MODULE_NAME.to_owned(), - BRIDGE_LIMITER_MODULE_NAME.to_owned(), - ]; - if let Some(cursor) = iota_bridge_module_last_processed_event_id_override { - info!("Overriding cursor for iota bridge modules to {:?}", cursor); - return HashMap::from_iter( - iota_bridge_modules - .iter() - .map(|module| (module.clone(), Some(cursor))), - ); - } - - let iota_bridge_module_stored_cursor = store - .get_iota_event_cursors(&iota_bridge_modules) - .expect("Failed to get eth iota event cursors from storage"); - let mut iota_modules_to_watch = HashMap::new(); - for (module_identifier, cursor) in iota_bridge_modules - .iter() - .zip(iota_bridge_module_stored_cursor) - { - if cursor.is_none() { - info!( - "No cursor found for iota bridge module {} in storage or config override, query start from the beginning.", - module_identifier - ); - } - iota_modules_to_watch.insert(module_identifier.clone(), cursor); - } - iota_modules_to_watch -} - -fn get_eth_contracts_to_watch( - store: &std::sync::Arc, - eth_contracts: &[EthAddress], - eth_contracts_start_block_fallback: u64, - eth_contracts_start_block_override: Option, -) -> HashMap { - let stored_eth_cursors = store - .get_eth_event_cursors(eth_contracts) - .expect("Failed to get eth event cursors from storage"); - let mut eth_contracts_to_watch = HashMap::new(); - for (contract, stored_cursor) in eth_contracts.iter().zip(stored_eth_cursors) { - // start block precedence: - // eth_contracts_start_block_override > stored cursor > - // eth_contracts_start_block_fallback - match (eth_contracts_start_block_override, stored_cursor) { - (Some(override_), _) => { - eth_contracts_to_watch.insert(*contract, override_); - info!( - "Overriding cursor for eth bridge contract {} to {}. Stored cursor: {:?}", - contract, override_, stored_cursor - ); - } - (None, Some(stored_cursor)) => { - // +1: The stored value is the last block that was processed, so we start from - // the next block. - eth_contracts_to_watch.insert(*contract, stored_cursor + 1); - } - (None, None) => { - // If no cursor is found, start from the fallback block. - eth_contracts_to_watch.insert(*contract, eth_contracts_start_block_fallback); - } - } - } - eth_contracts_to_watch -} - -#[cfg(test)] -mod tests { - use ethers::types::Address as EthAddress; - use fastcrypto::secp256k1::Secp256k1KeyPair; - use iota_config::local_ip_utils::get_available_port; - use iota_types::{ - base_types::IotaAddress, - bridge::BridgeChainId, - crypto::{EncodeDecodeBase64, IotaKeyPair, KeypairTraits, get_key_pair}, - digests::TransactionDigest, - event::EventID, - }; - use prometheus::Registry; - use tempfile::tempdir; - - use super::*; - use crate::{ - config::{BridgeNodeConfig, EthConfig, IotaConfig}, - e2e_tests::test_utils::{BridgeTestCluster, BridgeTestClusterBuilder}, - utils::wait_for_server_to_be_up, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_get_eth_contracts_to_watch() { - telemetry_subscribers::init_for_testing(); - let temp_dir = tempfile::tempdir().unwrap(); - let eth_contracts = vec![ - EthAddress::from_low_u64_be(1), - EthAddress::from_low_u64_be(2), - ]; - let store = BridgeOrchestratorTables::new(temp_dir.path()); - - // No override, no watermark found in DB, use fallback - let contracts = get_eth_contracts_to_watch(&store, ð_contracts, 10, None); - assert_eq!( - contracts, - vec![(eth_contracts[0], 10), (eth_contracts[1], 10)] - .into_iter() - .collect::>() - ); - - // no watermark found in DB, use override - let contracts = get_eth_contracts_to_watch(&store, ð_contracts, 10, Some(420)); - assert_eq!( - contracts, - vec![(eth_contracts[0], 420), (eth_contracts[1], 420)] - .into_iter() - .collect::>() - ); - - store - .update_eth_event_cursor(eth_contracts[0], 100) - .unwrap(); - store - .update_eth_event_cursor(eth_contracts[1], 102) - .unwrap(); - - // No override, found watermarks in DB, use +1 - let contracts = get_eth_contracts_to_watch(&store, ð_contracts, 10, None); - assert_eq!( - contracts, - vec![(eth_contracts[0], 101), (eth_contracts[1], 103)] - .into_iter() - .collect::>() - ); - - // use override - let contracts = get_eth_contracts_to_watch(&store, ð_contracts, 10, Some(200)); - assert_eq!( - contracts, - vec![(eth_contracts[0], 200), (eth_contracts[1], 200)] - .into_iter() - .collect::>() - ); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_get_iota_modules_to_watch() { - telemetry_subscribers::init_for_testing(); - let temp_dir = tempfile::tempdir().unwrap(); - - let store = BridgeOrchestratorTables::new(temp_dir.path()); - let bridge_module = BRIDGE_MODULE_NAME.to_owned(); - let committee_module = BRIDGE_COMMITTEE_MODULE_NAME.to_owned(); - let treasury_module = BRIDGE_TREASURY_MODULE_NAME.to_owned(); - let limiter_module = BRIDGE_LIMITER_MODULE_NAME.to_owned(); - // No override, no stored watermark, use None - let iota_modules_to_watch = get_iota_modules_to_watch(&store, None); - assert_eq!( - iota_modules_to_watch, - vec![ - (bridge_module.clone(), None), - (committee_module.clone(), None), - (treasury_module.clone(), None), - (limiter_module.clone(), None) - ] - .into_iter() - .collect::>() - ); - - // no stored watermark, use override - let override_cursor = EventID { - tx_digest: TransactionDigest::random(), - event_seq: 42, - }; - let iota_modules_to_watch = get_iota_modules_to_watch(&store, Some(override_cursor)); - assert_eq!( - iota_modules_to_watch, - vec![ - (bridge_module.clone(), Some(override_cursor)), - (committee_module.clone(), Some(override_cursor)), - (treasury_module.clone(), Some(override_cursor)), - (limiter_module.clone(), Some(override_cursor)) - ] - .into_iter() - .collect::>() - ); - - // No override, found stored watermark for `bridge` module, use stored watermark - // for `bridge` and None for `committee` - let stored_cursor = EventID { - tx_digest: TransactionDigest::random(), - event_seq: 100, - }; - store - .update_iota_event_cursor(bridge_module.clone(), stored_cursor) - .unwrap(); - let iota_modules_to_watch = get_iota_modules_to_watch(&store, None); - assert_eq!( - iota_modules_to_watch, - vec![ - (bridge_module.clone(), Some(stored_cursor)), - (committee_module.clone(), None), - (treasury_module.clone(), None), - (limiter_module.clone(), None) - ] - .into_iter() - .collect::>() - ); - - // found stored watermark, use override - let stored_cursor = EventID { - tx_digest: TransactionDigest::random(), - event_seq: 100, - }; - store - .update_iota_event_cursor(committee_module.clone(), stored_cursor) - .unwrap(); - let iota_modules_to_watch = get_iota_modules_to_watch(&store, Some(override_cursor)); - assert_eq!( - iota_modules_to_watch, - vec![ - (bridge_module.clone(), Some(override_cursor)), - (committee_module.clone(), Some(override_cursor)), - (treasury_module.clone(), Some(override_cursor)), - (limiter_module.clone(), Some(override_cursor)) - ] - .into_iter() - .collect::>() - ); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_starting_bridge_node() { - telemetry_subscribers::init_for_testing(); - let bridge_test_cluster = setup().await; - let kp = bridge_test_cluster.bridge_authority_key(0); - - // prepare node config (server only) - let tmp_dir = tempdir().unwrap().into_path(); - let authority_key_path = "test_starting_bridge_node_bridge_authority_key"; - let server_listen_port = get_available_port("127.0.0.1"); - let base64_encoded = kp.encode_base64(); - std::fs::write(tmp_dir.join(authority_key_path), base64_encoded).unwrap(); - - let config = BridgeNodeConfig { - server_listen_port, - metrics_port: get_available_port("127.0.0.1"), - bridge_authority_key_path: tmp_dir.join(authority_key_path), - iota: IotaConfig { - iota_rpc_url: bridge_test_cluster.iota_rpc_url(), - iota_bridge_chain_id: BridgeChainId::IotaCustom as u8, - bridge_client_key_path: None, - bridge_client_gas_object: None, - iota_bridge_module_last_processed_event_id_override: None, - }, - eth: EthConfig { - eth_rpc_url: bridge_test_cluster.eth_rpc_url(), - eth_bridge_proxy_address: bridge_test_cluster.iota_bridge_address(), - eth_bridge_chain_id: BridgeChainId::EthCustom as u8, - eth_contracts_start_block_fallback: None, - eth_contracts_start_block_override: None, - }, - approved_governance_actions: vec![], - run_client: false, - db_path: None, - }; - // Spawn bridge node in memory - let _handle = run_bridge_node( - config, - BridgeNodePublicMetadata::empty_for_testing(), - Registry::new(), - ) - .await - .unwrap(); - - let server_url = format!("http://127.0.0.1:{}", server_listen_port); - // Now we expect to see the server to be up and running. - let res = wait_for_server_to_be_up(server_url, 5).await; - res.unwrap(); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_starting_bridge_node_with_client() { - telemetry_subscribers::init_for_testing(); - let bridge_test_cluster = setup().await; - let kp = bridge_test_cluster.bridge_authority_key(0); - - // prepare node config (server + client) - let tmp_dir = tempdir().unwrap().into_path(); - let db_path = tmp_dir.join("test_starting_bridge_node_with_client_db"); - let authority_key_path = "test_starting_bridge_node_with_client_bridge_authority_key"; - let server_listen_port = get_available_port("127.0.0.1"); - - let base64_encoded = kp.encode_base64(); - std::fs::write(tmp_dir.join(authority_key_path), base64_encoded).unwrap(); - - let client_iota_address = IotaAddress::from(kp.public()); - let sender_address = bridge_test_cluster.iota_user_address(); - // send some gas to this address - bridge_test_cluster - .test_cluster - .transfer_iota_must_exceed(sender_address, client_iota_address, 1000000000) - .await; - - let config = BridgeNodeConfig { - server_listen_port, - metrics_port: get_available_port("127.0.0.1"), - bridge_authority_key_path: tmp_dir.join(authority_key_path), - iota: IotaConfig { - iota_rpc_url: bridge_test_cluster.iota_rpc_url(), - iota_bridge_chain_id: BridgeChainId::IotaCustom as u8, - bridge_client_key_path: None, - bridge_client_gas_object: None, - iota_bridge_module_last_processed_event_id_override: Some(EventID { - tx_digest: TransactionDigest::random(), - event_seq: 0, - }), - }, - eth: EthConfig { - eth_rpc_url: bridge_test_cluster.eth_rpc_url(), - eth_bridge_proxy_address: bridge_test_cluster.iota_bridge_address(), - eth_bridge_chain_id: BridgeChainId::EthCustom as u8, - eth_contracts_start_block_fallback: Some(0), - eth_contracts_start_block_override: None, - }, - approved_governance_actions: vec![], - run_client: true, - db_path: Some(db_path), - }; - // Spawn bridge node in memory - let _handle = run_bridge_node( - config, - BridgeNodePublicMetadata::empty_for_testing(), - Registry::new(), - ) - .await - .unwrap(); - - let server_url = format!("http://127.0.0.1:{}", server_listen_port); - // Now we expect to see the server to be up and running. - // client components are spawned earlier than server, so as long as the server - // is up, we know the client components are already running. - let res = wait_for_server_to_be_up(server_url, 5).await; - res.unwrap(); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 8)] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_starting_bridge_node_with_client_and_separate_client_key() { - telemetry_subscribers::init_for_testing(); - let bridge_test_cluster = setup().await; - let kp = bridge_test_cluster.bridge_authority_key(0); - - // prepare node config (server + client) - let tmp_dir = tempdir().unwrap().into_path(); - let db_path = - tmp_dir.join("test_starting_bridge_node_with_client_and_separate_client_key_db"); - let authority_key_path = - "test_starting_bridge_node_with_client_and_separate_client_key_bridge_authority_key"; - let server_listen_port = get_available_port("127.0.0.1"); - - // prepare bridge authority key - let base64_encoded = kp.encode_base64(); - std::fs::write(tmp_dir.join(authority_key_path), base64_encoded).unwrap(); - - // prepare bridge client key - let (_, kp): (_, Secp256k1KeyPair) = get_key_pair(); - let kp = IotaKeyPair::from(kp); - let client_key_path = - "test_starting_bridge_node_with_client_and_separate_client_key_bridge_client_key"; - std::fs::write(tmp_dir.join(client_key_path), kp.encode_base64()).unwrap(); - let client_iota_address = IotaAddress::from(&kp.public()); - let sender_address = bridge_test_cluster.iota_user_address(); - // send some gas to this address - let gas_obj = bridge_test_cluster - .test_cluster - .transfer_iota_must_exceed(sender_address, client_iota_address, 1000000000) - .await; - - let config = BridgeNodeConfig { - server_listen_port, - metrics_port: get_available_port("127.0.0.1"), - bridge_authority_key_path: tmp_dir.join(authority_key_path), - iota: IotaConfig { - iota_rpc_url: bridge_test_cluster.iota_rpc_url(), - iota_bridge_chain_id: BridgeChainId::IotaCustom as u8, - bridge_client_key_path: Some(tmp_dir.join(client_key_path)), - bridge_client_gas_object: Some(gas_obj), - iota_bridge_module_last_processed_event_id_override: Some(EventID { - tx_digest: TransactionDigest::random(), - event_seq: 0, - }), - }, - eth: EthConfig { - eth_rpc_url: bridge_test_cluster.eth_rpc_url(), - eth_bridge_proxy_address: bridge_test_cluster.iota_bridge_address(), - eth_bridge_chain_id: BridgeChainId::EthCustom as u8, - eth_contracts_start_block_fallback: Some(0), - eth_contracts_start_block_override: Some(0), - }, - approved_governance_actions: vec![], - run_client: true, - db_path: Some(db_path), - }; - // Spawn bridge node in memory - let _handle = run_bridge_node( - config, - BridgeNodePublicMetadata::empty_for_testing(), - Registry::new(), - ) - .await - .unwrap(); - - let server_url = format!("http://127.0.0.1:{}", server_listen_port); - // Now we expect to see the server to be up and running. - // client components are spawned earlier than server, so as long as the server - // is up, we know the client components are already running. - let res = wait_for_server_to_be_up(server_url, 5).await; - res.unwrap(); - } - - async fn setup() -> BridgeTestCluster { - BridgeTestClusterBuilder::new() - .with_eth_env(true) - .with_bridge_cluster(false) - .with_num_validators(2) - .build() - .await - } -} diff --git a/crates/iota-bridge/src/orchestrator.rs b/crates/iota-bridge/src/orchestrator.rs deleted file mode 100644 index 16b1639db62..00000000000 --- a/crates/iota-bridge/src/orchestrator.rs +++ /dev/null @@ -1,588 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! `BridgeOrchestrator` is the component that: -//! 1. monitors IOTA and Ethereum events with the help of `IotaSyncer` and -//! `EthSyncer` -//! 2. updates WAL table and cursor tables -//! 2. hands actions to `BridgeExecutor` for execution - -use std::sync::Arc; - -use ethers::types::Address as EthAddress; -use iota_json_rpc_types::IotaEvent; -use iota_metrics::spawn_logged_monitored_task; -use iota_types::Identifier; -use tokio::task::JoinHandle; -use tracing::{error, info}; - -use crate::{ - abi::EthBridgeEvent, - action_executor::{ - BridgeActionExecutionWrapper, BridgeActionExecutorTrait, submit_to_executor, - }, - error::BridgeError, - events::IotaBridgeEvent, - iota_client::{IotaClient, IotaClientInner}, - metrics::BridgeMetrics, - storage::BridgeOrchestratorTables, - types::EthLog, -}; - -pub struct BridgeOrchestrator { - _iota_client: Arc>, - iota_events_rx: iota_metrics::metered_channel::Receiver<(Identifier, Vec)>, - eth_events_rx: iota_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, - store: Arc, - monitor_tx: iota_metrics::metered_channel::Sender, - metrics: Arc, -} - -impl BridgeOrchestrator -where - C: IotaClientInner + 'static, -{ - pub fn new( - iota_client: Arc>, - iota_events_rx: iota_metrics::metered_channel::Receiver<(Identifier, Vec)>, - eth_events_rx: iota_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, - store: Arc, - monitor_tx: iota_metrics::metered_channel::Sender, - metrics: Arc, - ) -> Self { - Self { - _iota_client: iota_client, - iota_events_rx, - eth_events_rx, - store, - monitor_tx, - metrics, - } - } - - pub async fn run( - self, - bridge_action_executor: impl BridgeActionExecutorTrait, - ) -> Vec> { - tracing::info!("Starting BridgeOrchestrator"); - let mut task_handles = vec![]; - let store_clone = self.store.clone(); - - // Spawn BridgeActionExecutor - let (handles, executor_sender) = bridge_action_executor.run(); - task_handles.extend(handles); - let executor_sender_clone = executor_sender.clone(); - let metrics_clone = self.metrics.clone(); - task_handles.push(spawn_logged_monitored_task!(Self::run_iota_watcher( - store_clone, - executor_sender_clone, - self.iota_events_rx, - self.monitor_tx, - metrics_clone, - ))); - let store_clone = self.store.clone(); - - // Re-submit pending actions to executor - let actions = store_clone - .get_all_pending_actions() - .into_values() - .collect::>(); - for action in actions { - submit_to_executor(&executor_sender, action) - .await - .expect("Submit to executor should not fail"); - } - - let metrics_clone = self.metrics.clone(); - task_handles.push(spawn_logged_monitored_task!(Self::run_eth_watcher( - store_clone, - executor_sender, - self.eth_events_rx, - metrics_clone, - ))); - - task_handles - } - - async fn run_iota_watcher( - store: Arc, - executor_tx: iota_metrics::metered_channel::Sender, - mut iota_events_rx: iota_metrics::metered_channel::Receiver<(Identifier, Vec)>, - monitor_tx: iota_metrics::metered_channel::Sender, - metrics: Arc, - ) { - info!("Starting iota watcher task"); - while let Some((identifier, events)) = iota_events_rx.recv().await { - if events.is_empty() { - continue; - } - info!("Received {} IOTA events: {:?}", events.len(), events); - metrics - .iota_watcher_received_events - .inc_by(events.len() as u64); - let bridge_events = events - .iter() - .filter_map(|iota_event| { - match IotaBridgeEvent::try_from_iota_event(iota_event) { - Ok(bridge_event) => Some(bridge_event), - // On testnet some early bridge transactions could have zero value (before - // we disallow it in Move) - Err(BridgeError::ZeroValueBridgeTransfer(_)) => { - error!("Zero value bridge transfer: {:?}", iota_event); - None - } - Err(e) => { - panic!( - "IOTA Event could not be deserialized to IotaBridgeEvent: {:?}", - e - ); - } - } - }) - .collect::>(); - - let mut actions = vec![]; - for (iota_event, opt_bridge_event) in events.iter().zip(bridge_events) { - if opt_bridge_event.is_none() { - // TODO: we probably should not miss any events, log for now. - metrics.iota_watcher_unrecognized_events.inc(); - error!("IOTA event not recognized: {:?}", iota_event); - continue; - } - // Unwrap safe: checked above - let bridge_event: IotaBridgeEvent = opt_bridge_event.unwrap(); - info!("Observed IOTA bridge event: {:?}", bridge_event); - - // Send event to monitor - monitor_tx - .send(bridge_event.clone()) - .await - .expect("Sending event to monitor channel should not fail"); - - if let Some(action) = bridge_event - .try_into_bridge_action(iota_event.id.tx_digest, iota_event.id.event_seq as u16) - { - actions.push(action); - } - } - - if !actions.is_empty() { - info!( - "Received {} actions from IOTA: {:?}", - actions.len(), - actions - ); - metrics - .iota_watcher_received_actions - .inc_by(actions.len() as u64); - // Write action to pending WAL - store - .insert_pending_actions(&actions) - .expect("Store operation should not fail"); - for action in actions { - submit_to_executor(&executor_tx, action) - .await - .expect("Submit to executor should not fail"); - } - } - - // Unwrap safe: in the beginning of the loop we checked that events is not empty - let cursor = events.last().unwrap().id; - store - .update_iota_event_cursor(identifier, cursor) - .expect("Store operation should not fail"); - } - panic!("IOTA event channel was closed unexpectedly"); - } - - async fn run_eth_watcher( - store: Arc, - executor_tx: iota_metrics::metered_channel::Sender, - mut eth_events_rx: iota_metrics::metered_channel::Receiver<( - ethers::types::Address, - u64, - Vec, - )>, - metrics: Arc, - ) { - info!("Starting eth watcher task"); - while let Some((contract, end_block, logs)) = eth_events_rx.recv().await { - if logs.is_empty() { - store - .update_eth_event_cursor(contract, end_block) - .expect("Store operation should not fail"); - continue; - } - - info!("Received {} Eth events", logs.len()); - metrics - .eth_watcher_received_events - .inc_by(logs.len() as u64); - - let bridge_events = logs - .iter() - .map(EthBridgeEvent::try_from_eth_log) - .collect::>(); - - let mut actions = vec![]; - for (log, opt_bridge_event) in logs.iter().zip(bridge_events) { - if opt_bridge_event.is_none() { - // TODO: we probably should not miss any events, log for now. - metrics.eth_watcher_unrecognized_events.inc(); - error!("Eth event not recognized: {:?}", log); - continue; - } - // Unwrap safe: checked above - let bridge_event = opt_bridge_event.unwrap(); - info!("Observed Eth bridge event: {:?}", bridge_event); - - match bridge_event.try_into_bridge_action(log.tx_hash, log.log_index_in_tx) { - Ok(Some(action)) => actions.push(action), - Ok(None) => {} - Err(e) => { - error!(eth_tx_hash=?log.tx_hash, eth_event_index=?log.log_index_in_tx, "Error converting EthBridgeEvent to BridgeAction: {:?}", e); - } - } - // TODO: handle non Action events - } - if !actions.is_empty() { - info!("Received {} actions from Eth: {:?}", actions.len(), actions); - metrics - .eth_watcher_received_actions - .inc_by(actions.len() as u64); - // Write action to pending WAL - store - .insert_pending_actions(&actions) - .expect("Store operation should not fail"); - // Execution will remove the pending actions from DB when the action is - // completed. - for action in actions { - submit_to_executor(&executor_tx, action) - .await - .expect("Submit to executor should not fail"); - } - } - - store - .update_eth_event_cursor(contract, end_block) - .expect("Store operation should not fail"); - } - panic!("Eth event channel was closed"); - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use ethers::types::{Address as EthAddress, TxHash}; - use prometheus::Registry; - - use super::*; - use crate::{ - events::{init_all_struct_tags, tests::get_test_iota_event_and_action}, - iota_mock_client::IotaMockClient, - test_utils::{ - get_test_eth_to_iota_bridge_action, get_test_iota_to_eth_bridge_action, - get_test_log_and_action, - }, - types::BridgeActionDigest, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_iota_watcher_task() { - // Note: this test may fail because of the following reasons: - // the IotaEvent's struct tag does not match the ones in events.rs - - let ( - iota_events_tx, - iota_events_rx, - _eth_events_tx, - eth_events_rx, - monitor_tx, - _monitor_rx, - iota_client, - store, - ) = setup(); - let (executor, mut executor_requested_action_rx) = MockExecutor::new(); - // start orchestrator - let registry = Registry::new(); - let metrics = Arc::new(BridgeMetrics::new(®istry)); - let _handles = BridgeOrchestrator::new( - Arc::new(iota_client), - iota_events_rx, - eth_events_rx, - store.clone(), - monitor_tx, - metrics, - ) - .run(executor) - .await; - - let identifier = Identifier::from_str("test_iota_watcher_task").unwrap(); - let (iota_event, bridge_action) = get_test_iota_event_and_action(identifier.clone()); - iota_events_tx - .send((identifier.clone(), vec![iota_event.clone()])) - .await - .unwrap(); - - let start = std::time::Instant::now(); - // Executor should have received the action - assert_eq!( - executor_requested_action_rx.recv().await.unwrap(), - bridge_action.digest() - ); - loop { - let actions = store.get_all_pending_actions(); - if actions.is_empty() { - if start.elapsed().as_secs() > 5 { - panic!("Timed out waiting for action to be written to WAL"); - } - tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; - continue; - } - assert_eq!(actions.len(), 1); - let action = actions.get(&bridge_action.digest()).unwrap(); - assert_eq!(action, &bridge_action); - assert_eq!( - store.get_iota_event_cursors(&[identifier]).unwrap()[0].unwrap(), - iota_event.id, - ); - break; - } - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_eth_watcher_task() { - // Note: this test may fail because of the following reasons: - // 1. Log and BridgeAction returned from `get_test_log_and_action` are not in - // sync - // 2. Log returned from `get_test_log_and_action` is not parseable log (not - // abigen!, check abi.rs) - - let ( - _iota_events_tx, - iota_events_rx, - eth_events_tx, - eth_events_rx, - monitor_tx, - _monitor_rx, - iota_client, - store, - ) = setup(); - let (executor, mut executor_requested_action_rx) = MockExecutor::new(); - // start orchestrator - let registry = Registry::new(); - let metrics = Arc::new(BridgeMetrics::new(®istry)); - let _handles = BridgeOrchestrator::new( - Arc::new(iota_client), - iota_events_rx, - eth_events_rx, - store.clone(), - monitor_tx, - metrics, - ) - .run(executor) - .await; - let address = EthAddress::random(); - let (log, bridge_action) = get_test_log_and_action(address, TxHash::random(), 10); - let log_index_in_tx = 10; - let log_block_num = log.block_number.unwrap().as_u64(); - let eth_log = EthLog { - log: log.clone(), - tx_hash: log.transaction_hash.unwrap(), - block_number: log_block_num, - log_index_in_tx, - }; - let end_block_num = log_block_num + 15; - - eth_events_tx - .send((address, end_block_num, vec![eth_log.clone()])) - .await - .unwrap(); - - // Executor should have received the action - assert_eq!( - executor_requested_action_rx.recv().await.unwrap(), - bridge_action.digest() - ); - let start = std::time::Instant::now(); - loop { - let actions = store.get_all_pending_actions(); - if actions.is_empty() { - if start.elapsed().as_secs() > 5 { - panic!("Timed out waiting for action to be written to WAL"); - } - tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; - continue; - } - assert_eq!(actions.len(), 1); - let action = actions.get(&bridge_action.digest()).unwrap(); - assert_eq!(action, &bridge_action); - assert_eq!( - store.get_eth_event_cursors(&[address]).unwrap()[0].unwrap(), - end_block_num, - ); - break; - } - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - /// Test that when orchestrator starts, all pending actions are sent to - /// executor - async fn test_resume_actions_in_pending_logs() { - let ( - _iota_events_tx, - iota_events_rx, - _eth_events_tx, - eth_events_rx, - monitor_tx, - _monitor_rx, - iota_client, - store, - ) = setup(); - let (executor, mut executor_requested_action_rx) = MockExecutor::new(); - - let action1 = get_test_iota_to_eth_bridge_action( - None, - Some(0), - Some(99), - Some(10000), - None, - None, - None, - ); - - let action2 = get_test_eth_to_iota_bridge_action(None, None, None, None); - store - .insert_pending_actions(&vec![action1.clone(), action2.clone()]) - .unwrap(); - - // start orchestrator - let registry = Registry::new(); - let metrics = Arc::new(BridgeMetrics::new(®istry)); - let _handles = BridgeOrchestrator::new( - Arc::new(iota_client), - iota_events_rx, - eth_events_rx, - store.clone(), - monitor_tx, - metrics, - ) - .run(executor) - .await; - - // Executor should have received the action - let mut digests = std::collections::HashSet::new(); - digests.insert(executor_requested_action_rx.recv().await.unwrap()); - digests.insert(executor_requested_action_rx.recv().await.unwrap()); - assert!(digests.contains(&action1.digest())); - assert!(digests.contains(&action2.digest())); - assert_eq!(digests.len(), 2); - } - - #[expect(clippy::type_complexity)] - fn setup() -> ( - iota_metrics::metered_channel::Sender<(Identifier, Vec)>, - iota_metrics::metered_channel::Receiver<(Identifier, Vec)>, - iota_metrics::metered_channel::Sender<(EthAddress, u64, Vec)>, - iota_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, - iota_metrics::metered_channel::Sender, - iota_metrics::metered_channel::Receiver, - IotaClient, - Arc, - ) { - telemetry_subscribers::init_for_testing(); - let registry = Registry::new(); - iota_metrics::init_metrics(®istry); - - init_all_struct_tags(); - - let temp_dir = tempfile::tempdir().unwrap(); - let store = BridgeOrchestratorTables::new(temp_dir.path()); - - let mock_client = IotaMockClient::default(); - let iota_client = IotaClient::new_for_testing(mock_client.clone()); - - let (eth_events_tx, eth_events_rx) = iota_metrics::metered_channel::channel( - 100, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["unit_test_eth_events_queue"]), - ); - - let (iota_events_tx, iota_events_rx) = iota_metrics::metered_channel::channel( - 100, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["unit_test_iota_events_queue"]), - ); - let (monitor_tx, monitor_rx) = iota_metrics::metered_channel::channel( - 10000, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["monitor_queue"]), - ); - ( - iota_events_tx, - iota_events_rx, - eth_events_tx, - eth_events_rx, - monitor_tx, - monitor_rx, - iota_client, - store, - ) - } - - /// A `BridgeActionExecutorTrait` implementation that only tracks the - /// submitted actions. - struct MockExecutor { - requested_transactions_tx: tokio::sync::broadcast::Sender, - } - - impl MockExecutor { - fn new() -> (Self, tokio::sync::broadcast::Receiver) { - let (tx, rx) = tokio::sync::broadcast::channel(100); - ( - Self { - requested_transactions_tx: tx, - }, - rx, - ) - } - } - - impl BridgeActionExecutorTrait for MockExecutor { - fn run( - self, - ) -> ( - Vec>, - iota_metrics::metered_channel::Sender, - ) { - let (tx, mut rx) = iota_metrics::metered_channel::channel::( - 100, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["unit_test_mock_executor"]), - ); - - let handles = tokio::spawn(async move { - while let Some(action) = rx.recv().await { - self.requested_transactions_tx - .send(action.0.digest()) - .unwrap(); - } - }); - (vec![handles], tx) - } - } -} diff --git a/crates/iota-bridge/src/server/governance_verifier.rs b/crates/iota-bridge/src/server/governance_verifier.rs deleted file mode 100644 index e9f277f2db4..00000000000 --- a/crates/iota-bridge/src/server/governance_verifier.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::HashMap; - -use crate::{ - error::{BridgeError, BridgeResult}, - server::handler::ActionVerifier, - types::{BridgeAction, BridgeActionDigest}, -}; - -#[derive(Debug)] -pub struct GovernanceVerifier { - approved_governance_actions: HashMap, -} - -impl GovernanceVerifier { - pub fn new(approved_actions: Vec) -> BridgeResult { - // TODO(audit-blocking): verify chain ids - let mut approved_governance_actions = HashMap::new(); - for action in approved_actions { - if !action.is_governace_action() { - return Err(BridgeError::ActionIsNotGovernanceAction(action)); - } - approved_governance_actions.insert(action.digest(), action); - } - Ok(Self { - approved_governance_actions, - }) - } -} - -#[async_trait::async_trait] -impl ActionVerifier for GovernanceVerifier { - fn name(&self) -> &'static str { - "GovernanceVerifier" - } - - async fn verify(&self, key: BridgeAction) -> BridgeResult { - // TODO: an optimization would be to check the current nonce on chain and err - // for older ones - if !key.is_governace_action() { - return Err(BridgeError::ActionIsNotGovernanceAction(key)); - } - if let Some(approved_action) = self.approved_governance_actions.get(&key.digest()) { - assert_eq!( - &key, approved_action, - "Mismatched action found in approved_actions" - ); - return Ok(key); - } - return Err(BridgeError::GovernanceActionIsNotApproved); - } -} - -#[cfg(test)] -mod tests { - use iota_types::bridge::BridgeChainId; - - use super::*; - use crate::{ - test_utils::get_test_iota_to_eth_bridge_action, - types::{BridgeAction, EmergencyAction, EmergencyActionType, LimitUpdateAction}, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_governance_verifier() { - let action_1 = BridgeAction::EmergencyAction(EmergencyAction { - chain_id: BridgeChainId::EthCustom, - nonce: 1, - action_type: EmergencyActionType::Pause, - }); - let action_2 = BridgeAction::LimitUpdateAction(LimitUpdateAction { - chain_id: BridgeChainId::EthCustom, - sending_chain_id: BridgeChainId::IotaCustom, - nonce: 1, - new_usd_limit: 10000, - }); - - let verifier = GovernanceVerifier::new(vec![action_1.clone(), action_2.clone()]).unwrap(); - assert_eq!( - verifier.verify(action_1.clone()).await.unwrap(), - action_1.clone() - ); - assert_eq!( - verifier.verify(action_2.clone()).await.unwrap(), - action_2.clone() - ); - - let action_3 = BridgeAction::LimitUpdateAction(LimitUpdateAction { - chain_id: BridgeChainId::EthCustom, - sending_chain_id: BridgeChainId::IotaCustom, - nonce: 2, - new_usd_limit: 10000, - }); - assert_eq!( - verifier.verify(action_3).await.unwrap_err(), - BridgeError::GovernanceActionIsNotApproved - ); - - // Token transfer action is not allowed - let action_4 = get_test_iota_to_eth_bridge_action(None, None, None, None, None, None, None); - assert!(matches!( - GovernanceVerifier::new(vec![action_1, action_2, action_4.clone()]).unwrap_err(), - BridgeError::ActionIsNotGovernanceAction(..) - )); - - // Token transfer action will be rejected - assert!(matches!( - verifier.verify(action_4).await.unwrap_err(), - BridgeError::ActionIsNotGovernanceAction(..) - )); - } -} diff --git a/crates/iota-bridge/src/server/handler.rs b/crates/iota-bridge/src/server/handler.rs deleted file mode 100644 index e7c81d952b0..00000000000 --- a/crates/iota-bridge/src/server/handler.rs +++ /dev/null @@ -1,667 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![allow(clippy::type_complexity)] - -use std::{num::NonZeroUsize, str::FromStr, sync::Arc}; - -use async_trait::async_trait; -use axum::Json; -use ethers::{providers::JsonRpcClient, types::TxHash}; -use iota_types::digests::TransactionDigest; -use lru::LruCache; -use tap::TapFallible; -use tokio::sync::{Mutex, oneshot}; -use tracing::info; - -use super::governance_verifier::GovernanceVerifier; -use crate::{ - crypto::{BridgeAuthorityKeyPair, BridgeAuthoritySignInfo}, - error::{BridgeError, BridgeResult}, - eth_client::EthClient, - iota_client::{IotaClient, IotaClientInner}, - metrics::BridgeMetrics, - types::{BridgeAction, SignedBridgeAction}, -}; - -#[async_trait] -pub trait BridgeRequestHandlerTrait { - /// Handles a request to sign a BridgeAction that bridges assets - /// from Ethereum to IOTA. The inputs are a transaction hash on Ethereum - /// that emitted the bridge event and the Event index in that transaction - async fn handle_eth_tx_hash( - &self, - tx_hash_hex: String, - event_idx: u16, - ) -> Result, BridgeError>; - /// Handles a request to sign a BridgeAction that bridges assets - /// from IOTA to Ethereum. The inputs are a transaction digest on IOTA - /// that emitted the bridge event and the Event index in that transaction - async fn handle_iota_tx_digest( - &self, - tx_digest_base58: String, - event_idx: u16, - ) -> Result, BridgeError>; - - /// Handles a request to sign a governance action. - async fn handle_governance_action( - &self, - action: BridgeAction, - ) -> Result, BridgeError>; -} - -#[async_trait::async_trait] -pub trait ActionVerifier: Send + Sync { - // Name of the verifier, used for metrics - fn name(&self) -> &'static str; - async fn verify(&self, key: K) -> BridgeResult; -} - -struct IotaActionVerifier { - iota_client: Arc>, -} - -struct EthActionVerifier

{ - eth_client: Arc>, -} - -#[async_trait::async_trait] -impl ActionVerifier<(TransactionDigest, u16)> for IotaActionVerifier -where - C: IotaClientInner + Send + Sync + 'static, -{ - fn name(&self) -> &'static str { - "IotaActionVerifier" - } - - async fn verify(&self, key: (TransactionDigest, u16)) -> BridgeResult { - let (tx_digest, event_idx) = key; - self.iota_client - .get_bridge_action_by_tx_digest_and_event_idx_maybe(&tx_digest, event_idx) - .await - .tap_ok(|action| info!("IOTA action found: {:?}", action)) - } -} - -#[async_trait::async_trait] -impl ActionVerifier<(TxHash, u16)> for EthActionVerifier -where - C: JsonRpcClient + Send + Sync + 'static, -{ - fn name(&self) -> &'static str { - "EthActionVerifier" - } - - async fn verify(&self, key: (TxHash, u16)) -> BridgeResult { - let (tx_hash, event_idx) = key; - self.eth_client - .get_finalized_bridge_action_maybe(tx_hash, event_idx) - .await - .tap_ok(|action| info!("Eth action found: {:?}", action)) - } -} - -struct SignerWithCache { - signer: Arc, - verifier: Arc>, - mutex: Arc>, - cache: LruCache>>>>, - metrics: Arc, -} - -impl SignerWithCache -where - K: std::hash::Hash + Eq + Clone + Send + Sync + 'static, -{ - fn new( - signer: Arc, - verifier: impl ActionVerifier + 'static, - metrics: Arc, - ) -> Self { - Self { - signer, - verifier: Arc::new(verifier), - mutex: Arc::new(Mutex::new(())), - cache: LruCache::new(NonZeroUsize::new(1000).unwrap()), - metrics, - } - } - - fn spawn( - mut self, - mut rx: iota_metrics::metered_channel::Receiver<( - K, - oneshot::Sender>, - )>, - ) -> tokio::task::JoinHandle<()> { - tokio::spawn(async move { - loop { - let (key, tx) = rx - .recv() - .await - .unwrap_or_else(|| panic!("Server signer's channel is closed")); - let result = self.sign(key).await; - // The receiver may be dropped before the sender (client connection was dropped - // for example), we ignore the error in that case. - let _ = tx.send(result); - } - }) - } - - async fn get_cache_entry( - &mut self, - key: K, - ) -> Arc>>> { - // This mutex exists to make sure everyone gets the same entry, namely no double - // insert - let _ = self.mutex.lock().await; - self.cache - .get_or_insert(key, || Arc::new(Mutex::new(None))) - .clone() - } - - async fn sign(&mut self, key: K) -> BridgeResult { - let signer = self.signer.clone(); - let verifier = self.verifier.clone(); - let verifier_name = verifier.name(); - let entry = self.get_cache_entry(key.clone()).await; - let mut guard = entry.lock().await; - if let Some(result) = &*guard { - self.metrics - .signer_with_cache_hit - .with_label_values(&[verifier_name]) - .inc(); - return result.clone(); - } - self.metrics - .signer_with_cache_miss - .with_label_values(&[verifier_name]) - .inc(); - match verifier.verify(key.clone()).await { - Ok(bridge_action) => { - let sig = BridgeAuthoritySignInfo::new(&bridge_action, &signer); - let result = SignedBridgeAction::new_from_data_and_sig(bridge_action, sig); - // Cache result if Ok - *guard = Some(Ok(result.clone())); - Ok(result) - } - Err(e) => { - match e { - // Only cache non-transient errors - BridgeError::GovernanceActionIsNotApproved - | BridgeError::ActionIsNotGovernanceAction(..) - | BridgeError::BridgeEventInUnrecognizedIotaPackage - | BridgeError::BridgeEventInUnrecognizedEthContract - | BridgeError::BridgeEventNotActionable - | BridgeError::NoBridgeEventsInTxPosition => { - *guard = Some(Err(e.clone())); - } - _ => (), - } - Err(e) - } - } - } - - #[cfg(test)] - async fn get_testing_only( - &mut self, - key: K, - ) -> Option<&Arc>>>> { - let _ = self.mutex.lock().await; - self.cache.get(&key) - } -} - -pub struct BridgeRequestHandler { - iota_signer_tx: iota_metrics::metered_channel::Sender<( - (TransactionDigest, u16), - oneshot::Sender>, - )>, - eth_signer_tx: iota_metrics::metered_channel::Sender<( - (TxHash, u16), - oneshot::Sender>, - )>, - governance_signer_tx: iota_metrics::metered_channel::Sender<( - BridgeAction, - oneshot::Sender>, - )>, -} - -impl BridgeRequestHandler { - pub fn new< - SC: IotaClientInner + Send + Sync + 'static, - EP: JsonRpcClient + Send + Sync + 'static, - >( - signer: BridgeAuthorityKeyPair, - iota_client: Arc>, - eth_client: Arc>, - approved_governance_actions: Vec, - metrics: Arc, - ) -> Self { - let (iota_signer_tx, iota_rx) = iota_metrics::metered_channel::channel( - 1000, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["server_iota_action_signing_queue"]), - ); - let (eth_signer_tx, eth_rx) = iota_metrics::metered_channel::channel( - 1000, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["server_eth_action_signing_queue"]), - ); - let (governance_signer_tx, governance_rx) = iota_metrics::metered_channel::channel( - 1000, - &iota_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&["server_governance_action_signing_queue"]), - ); - let signer = Arc::new(signer); - - SignerWithCache::new( - signer.clone(), - IotaActionVerifier { iota_client }, - metrics.clone(), - ) - .spawn(iota_rx); - SignerWithCache::new( - signer.clone(), - EthActionVerifier { eth_client }, - metrics.clone(), - ) - .spawn(eth_rx); - SignerWithCache::new( - signer.clone(), - GovernanceVerifier::new(approved_governance_actions).unwrap(), - metrics.clone(), - ) - .spawn(governance_rx); - - Self { - iota_signer_tx, - eth_signer_tx, - governance_signer_tx, - } - } -} - -#[async_trait] -impl BridgeRequestHandlerTrait for BridgeRequestHandler { - async fn handle_eth_tx_hash( - &self, - tx_hash_hex: String, - event_idx: u16, - ) -> Result, BridgeError> { - let tx_hash = TxHash::from_str(&tx_hash_hex).map_err(|_| BridgeError::InvalidTxHash)?; - - let (tx, rx) = oneshot::channel(); - self.eth_signer_tx - .send(((tx_hash, event_idx), tx)) - .await - .unwrap_or_else(|_| panic!("Server eth signing channel is closed")); - let signed_action = rx - .await - .unwrap_or_else(|_| panic!("Server signing task's oneshot channel is dropped"))?; - Ok(Json(signed_action)) - } - - async fn handle_iota_tx_digest( - &self, - tx_digest_base58: String, - event_idx: u16, - ) -> Result, BridgeError> { - let tx_digest = TransactionDigest::from_str(&tx_digest_base58) - .map_err(|_e| BridgeError::InvalidTxHash)?; - let (tx, rx) = oneshot::channel(); - self.iota_signer_tx - .send(((tx_digest, event_idx), tx)) - .await - .unwrap_or_else(|_| panic!("Server iota signing channel is closed")); - let signed_action = rx - .await - .unwrap_or_else(|_| panic!("Server signing task's oneshot channel is dropped"))?; - Ok(Json(signed_action)) - } - - async fn handle_governance_action( - &self, - action: BridgeAction, - ) -> Result, BridgeError> { - if !action.is_governace_action() { - return Err(BridgeError::ActionIsNotGovernanceAction(action)); - } - let (tx, rx) = oneshot::channel(); - self.governance_signer_tx - .send((action, tx)) - .await - .unwrap_or_else(|_| panic!("Server governance action signing channel is closed")); - let signed_action = rx.await.unwrap_or_else(|_| { - panic!("Server governance action task's oneshot channel is dropped") - })?; - Ok(Json(signed_action)) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashSet; - - use ethers::types::{Address as EthAddress, TransactionReceipt}; - use iota_json_rpc_types::{BcsEvent, IotaEvent}; - use iota_types::{ - base_types::IotaAddress, - bridge::{BridgeChainId, TOKEN_ID_USDC}, - crypto::get_key_pair, - }; - - use super::*; - use crate::{ - eth_mock_provider::EthMockProvider, - events::{IotaToEthTokenBridgeV1, MoveTokenDepositedEvent, init_all_struct_tags}, - iota_mock_client::IotaMockClient, - test_utils::{ - get_test_iota_to_eth_bridge_action, get_test_log_and_action, mock_last_finalized_block, - }, - types::{EmergencyAction, EmergencyActionType, LimitUpdateAction}, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_iota_signer_with_cache() { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - let signer = Arc::new(kp); - let iota_client_mock = IotaMockClient::default(); - let iota_verifier = IotaActionVerifier { - iota_client: Arc::new(IotaClient::new_for_testing(iota_client_mock.clone())), - }; - let metrics = Arc::new(BridgeMetrics::new_for_testing()); - let mut iota_signer_with_cache = - SignerWithCache::new(signer.clone(), iota_verifier, metrics); - - // Test `get_cache_entry` creates a new entry if not exist - let iota_tx_digest = TransactionDigest::random(); - let iota_event_idx = 42; - assert!( - iota_signer_with_cache - .get_testing_only((iota_tx_digest, iota_event_idx)) - .await - .is_none() - ); - let entry = iota_signer_with_cache - .get_cache_entry((iota_tx_digest, iota_event_idx)) - .await; - let entry_ = iota_signer_with_cache - .get_testing_only((iota_tx_digest, iota_event_idx)) - .await; - assert!(entry_.unwrap().lock().await.is_none()); - - let action = get_test_iota_to_eth_bridge_action( - Some(iota_tx_digest), - Some(iota_event_idx), - None, - None, - None, - None, - None, - ); - let sig = BridgeAuthoritySignInfo::new(&action, &signer); - let signed_action = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig); - entry.lock().await.replace(Ok(signed_action)); - let entry_ = iota_signer_with_cache - .get_testing_only((iota_tx_digest, iota_event_idx)) - .await; - assert!(entry_.unwrap().lock().await.is_some()); - - // Test `sign` caches Err result - let iota_tx_digest = TransactionDigest::random(); - let iota_event_idx = 0; - - // Mock an non-cacheable error such as rpc error - iota_client_mock.add_events_by_tx_digest_error(iota_tx_digest); - iota_signer_with_cache - .sign((iota_tx_digest, iota_event_idx)) - .await - .unwrap_err(); - let entry_ = iota_signer_with_cache - .get_testing_only((iota_tx_digest, iota_event_idx)) - .await; - assert!(entry_.unwrap().lock().await.is_none()); - - // Mock a cacheable error such as no bridge events in tx position (empty event - // list) - iota_client_mock.add_events_by_tx_digest(iota_tx_digest, vec![]); - assert!(matches!( - iota_signer_with_cache - .sign((iota_tx_digest, iota_event_idx)) - .await, - Err(BridgeError::NoBridgeEventsInTxPosition) - )); - let entry_ = iota_signer_with_cache - .get_testing_only((iota_tx_digest, iota_event_idx)) - .await; - assert_eq!( - entry_.unwrap().lock().await.clone().unwrap().unwrap_err(), - BridgeError::NoBridgeEventsInTxPosition, - ); - - // TODO: test BridgeEventInUnrecognizedIotaPackage, - // IotaBridgeEvent::try_from_iota_event and BridgeEventNotActionable to - // be cached - - // Test `sign` caches Ok result - let emitted_event_1 = MoveTokenDepositedEvent { - seq_num: 1, - source_chain: BridgeChainId::IotaCustom as u8, - sender_address: IotaAddress::random_for_testing_only().to_vec(), - target_chain: BridgeChainId::EthCustom as u8, - target_address: EthAddress::random().as_bytes().to_vec(), - token_type: TOKEN_ID_USDC, - amount_iota_adjusted: 12345, - }; - - init_all_struct_tags(); - - let mut iota_event_1 = IotaEvent::random_for_testing(); - iota_event_1.type_ = IotaToEthTokenBridgeV1.get().unwrap().clone(); - iota_event_1.bcs = BcsEvent::new(bcs::to_bytes(&emitted_event_1).unwrap()); - let iota_tx_digest = iota_event_1.id.tx_digest; - - let mut iota_event_2 = IotaEvent::random_for_testing(); - iota_event_2.type_ = IotaToEthTokenBridgeV1.get().unwrap().clone(); - iota_event_2.bcs = BcsEvent::new(bcs::to_bytes(&emitted_event_1).unwrap()); - let iota_event_idx_2 = 1; - iota_client_mock.add_events_by_tx_digest(iota_tx_digest, vec![iota_event_2.clone()]); - - iota_client_mock.add_events_by_tx_digest( - iota_tx_digest, - vec![iota_event_1.clone(), iota_event_2.clone()], - ); - let signed_1 = iota_signer_with_cache - .sign((iota_tx_digest, iota_event_idx)) - .await - .unwrap(); - let signed_2 = iota_signer_with_cache - .sign((iota_tx_digest, iota_event_idx_2)) - .await - .unwrap(); - - // Because the result is cached now, the verifier should not be called again. - // Even though we remove the `add_events_by_tx_digest` mock, we will still get - // the same result. - iota_client_mock.add_events_by_tx_digest(iota_tx_digest, vec![]); - assert_eq!( - iota_signer_with_cache - .sign((iota_tx_digest, iota_event_idx)) - .await - .unwrap(), - signed_1 - ); - assert_eq!( - iota_signer_with_cache - .sign((iota_tx_digest, iota_event_idx_2)) - .await - .unwrap(), - signed_2 - ); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_eth_signer_with_cache() { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - let signer = Arc::new(kp); - let eth_mock_provider = EthMockProvider::default(); - let contract_address = EthAddress::random(); - let eth_client = EthClient::new_mocked( - eth_mock_provider.clone(), - HashSet::from_iter(vec![contract_address]), - ); - let eth_verifier = EthActionVerifier { - eth_client: Arc::new(eth_client), - }; - let metrics = Arc::new(BridgeMetrics::new_for_testing()); - let mut eth_signer_with_cache = - SignerWithCache::new(signer.clone(), eth_verifier, metrics.clone()); - - // Test `get_cache_entry` creates a new entry if not exist - let eth_tx_hash = TxHash::random(); - let eth_event_idx = 42; - assert!( - eth_signer_with_cache - .get_testing_only((eth_tx_hash, eth_event_idx)) - .await - .is_none() - ); - let entry = eth_signer_with_cache - .get_cache_entry((eth_tx_hash, eth_event_idx)) - .await; - let entry_ = eth_signer_with_cache - .get_testing_only((eth_tx_hash, eth_event_idx)) - .await; - // first unwrap should not pacic because the entry should have been inserted by - // `get_cache_entry` - assert!(entry_.unwrap().lock().await.is_none()); - - let (_, action) = get_test_log_and_action(contract_address, eth_tx_hash, eth_event_idx); - let sig = BridgeAuthoritySignInfo::new(&action, &signer); - let signed_action = SignedBridgeAction::new_from_data_and_sig(action.clone(), sig); - entry.lock().await.replace(Ok(signed_action.clone())); - let entry_ = eth_signer_with_cache - .get_testing_only((eth_tx_hash, eth_event_idx)) - .await; - assert_eq!( - entry_.unwrap().lock().await.clone().unwrap().unwrap(), - signed_action - ); - - // Test `sign` caches Ok result - let eth_tx_hash = TxHash::random(); - let eth_event_idx = 0; - let (log, _action) = get_test_log_and_action(contract_address, eth_tx_hash, eth_event_idx); - eth_mock_provider - .add_response::<[TxHash; 1], TransactionReceipt, TransactionReceipt>( - "eth_getTransactionReceipt", - [log.transaction_hash.unwrap()], - TransactionReceipt { - block_number: log.block_number, - logs: vec![log.clone()], - ..Default::default() - }, - ) - .unwrap(); - mock_last_finalized_block(ð_mock_provider, log.block_number.unwrap().as_u64()); - - eth_signer_with_cache - .sign((eth_tx_hash, eth_event_idx)) - .await - .unwrap(); - let entry_ = eth_signer_with_cache - .get_testing_only((eth_tx_hash, eth_event_idx)) - .await; - entry_.unwrap().lock().await.clone().unwrap().unwrap(); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_signer_with_governace_verifier() { - let action_1 = BridgeAction::EmergencyAction(EmergencyAction { - chain_id: BridgeChainId::EthCustom, - nonce: 1, - action_type: EmergencyActionType::Pause, - }); - let action_2 = BridgeAction::LimitUpdateAction(LimitUpdateAction { - chain_id: BridgeChainId::EthCustom, - sending_chain_id: BridgeChainId::IotaCustom, - nonce: 1, - new_usd_limit: 10000, - }); - - let verifier = GovernanceVerifier::new(vec![action_1.clone(), action_2.clone()]).unwrap(); - assert_eq!( - verifier.verify(action_1.clone()).await.unwrap(), - action_1.clone() - ); - assert_eq!( - verifier.verify(action_2.clone()).await.unwrap(), - action_2.clone() - ); - - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - let signer = Arc::new(kp); - let metrics = Arc::new(BridgeMetrics::new_for_testing()); - let mut signer_with_cache = SignerWithCache::new(signer.clone(), verifier, metrics.clone()); - - // action_1 is signable - signer_with_cache.sign(action_1.clone()).await.unwrap(); - // signed action is cached - let entry_ = signer_with_cache.get_testing_only(action_1.clone()).await; - assert_eq!( - entry_ - .unwrap() - .lock() - .await - .clone() - .unwrap() - .unwrap() - .data(), - &action_1 - ); - - // alter action_1 to action_3 - let action_3 = BridgeAction::EmergencyAction(EmergencyAction { - chain_id: BridgeChainId::EthCustom, - nonce: 1, - action_type: EmergencyActionType::Unpause, - }); - // action_3 is not signable - assert!(matches!( - signer_with_cache.sign(action_3.clone()).await.unwrap_err(), - BridgeError::GovernanceActionIsNotApproved - )); - // error is cached - let entry_ = signer_with_cache.get_testing_only(action_3.clone()).await; - assert!(matches!( - entry_.unwrap().lock().await.clone().unwrap().unwrap_err(), - BridgeError::GovernanceActionIsNotApproved - )); - - // Non governance action is not signable - let action_4 = get_test_iota_to_eth_bridge_action(None, None, None, None, None, None, None); - assert!(matches!( - signer_with_cache.sign(action_4.clone()).await.unwrap_err(), - BridgeError::ActionIsNotGovernanceAction(..) - )); - // error is cached - let entry_ = signer_with_cache.get_testing_only(action_4.clone()).await; - assert!(matches!( - entry_.unwrap().lock().await.clone().unwrap().unwrap_err(), - BridgeError::ActionIsNotGovernanceAction { .. } - )); - } - // TODO: add tests for BridgeRequestHandler (need to hook up local eth node) -} diff --git a/crates/iota-bridge/src/server/mock_handler.rs b/crates/iota-bridge/src/server/mock_handler.rs deleted file mode 100644 index 39505eba88f..00000000000 --- a/crates/iota-bridge/src/server/mock_handler.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! A mock implementation for `BridgeRequestHandlerTrait` -//! that handles requests according to preset behaviors. - -use std::{ - collections::HashMap, - net::SocketAddr, - str::FromStr, - sync::{Arc, Mutex}, -}; - -use arc_swap::ArcSwap; -use async_trait::async_trait; -use axum::Json; -use iota_types::digests::TransactionDigest; - -use super::{handler::BridgeRequestHandlerTrait, make_router}; -use crate::{ - crypto::{BridgeAuthorityKeyPair, BridgeAuthoritySignInfo}, - error::{BridgeError, BridgeResult}, - metrics::BridgeMetrics, - server::BridgeNodePublicMetadata, - types::SignedBridgeAction, -}; - -#[expect(clippy::type_complexity)] -#[derive(Clone)] -pub struct BridgeRequestMockHandler { - signer: Arc>>, - iota_token_events: - Arc>>>, - iota_token_events_requested: Arc>>, -} - -impl BridgeRequestMockHandler { - pub fn new() -> Self { - Self { - signer: Arc::new(ArcSwap::new(Arc::new(None))), - iota_token_events: Arc::new(Mutex::new(HashMap::new())), - iota_token_events_requested: Arc::new(Mutex::new(HashMap::new())), - } - } - - pub fn add_iota_event_response( - &self, - tx_digest: TransactionDigest, - idx: u16, - response: BridgeResult, - ) { - self.iota_token_events - .lock() - .unwrap() - .insert((tx_digest, idx), response); - } - - pub fn get_iota_token_events_requested( - &self, - tx_digest: TransactionDigest, - event_index: u16, - ) -> u64 { - *self - .iota_token_events_requested - .lock() - .unwrap() - .get(&(tx_digest, event_index)) - .unwrap_or(&0) - } - - pub fn set_signer(&self, signer: BridgeAuthorityKeyPair) { - self.signer.store(Arc::new(Some(signer))); - } -} - -impl Default for BridgeRequestMockHandler { - fn default() -> Self { - Self::new() - } -} - -#[async_trait] -impl BridgeRequestHandlerTrait for BridgeRequestMockHandler { - async fn handle_eth_tx_hash( - &self, - _tx_hash_hex: String, - _event_idx: u16, - ) -> Result, BridgeError> { - unimplemented!() - } - - async fn handle_iota_tx_digest( - &self, - tx_digest_base58: String, - event_idx: u16, - ) -> Result, BridgeError> { - let tx_digest = TransactionDigest::from_str(&tx_digest_base58) - .map_err(|_e| BridgeError::InvalidTxHash)?; - let preset = self.iota_token_events.lock().unwrap(); - if !preset.contains_key(&(tx_digest, event_idx)) { - // Ok to panic in test - panic!( - "No preset handle_iota_tx_digest result for tx_digest: {}, event_idx: {}", - tx_digest, event_idx - ); - } - let mut requested = self.iota_token_events_requested.lock().unwrap(); - let entry = requested.entry((tx_digest, event_idx)).or_default(); - *entry += 1; - let result = preset.get(&(tx_digest, event_idx)).unwrap(); - if let Err(e) = result { - return Err(e.clone()); - } - let signed_action: &iota_types::message_envelope::Envelope< - crate::types::BridgeAction, - crate::crypto::BridgeAuthoritySignInfo, - > = result.as_ref().unwrap(); - Ok(Json(signed_action.clone())) - } - - async fn handle_governance_action( - &self, - action: crate::types::BridgeAction, - ) -> Result, BridgeError> { - let sig = - BridgeAuthoritySignInfo::new(&action, self.signer.load().as_ref().as_ref().unwrap()); - let signed_action = SignedBridgeAction::new_from_data_and_sig(action, sig); - Ok(Json(signed_action)) - } -} - -pub fn run_mock_server( - socket_address: SocketAddr, - mock_handler: BridgeRequestMockHandler, -) -> tokio::task::JoinHandle<()> { - tracing::info!("Starting mock server at {}", socket_address); - let listener = std::net::TcpListener::bind(socket_address).unwrap(); - listener.set_nonblocking(true).unwrap(); - let listener = tokio::net::TcpListener::from_std(listener).unwrap(); - tokio::spawn(async move { - let router = make_router( - Arc::new(mock_handler), - Arc::new(BridgeMetrics::new_for_testing()), - Arc::new(BridgeNodePublicMetadata::empty_for_testing()), - ); - axum::serve(listener, router).await.unwrap() - }) -} diff --git a/crates/iota-bridge/src/server/mod.rs b/crates/iota-bridge/src/server/mod.rs deleted file mode 100644 index a6affca3caf..00000000000 --- a/crates/iota-bridge/src/server/mod.rs +++ /dev/null @@ -1,758 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![allow(clippy::inconsistent_digit_grouping)] -use std::{net::SocketAddr, str::FromStr, sync::Arc}; - -use axum::{ - Json, Router, - extract::{Path, State}, - http::StatusCode, - routing::get, -}; -use ethers::types::Address as EthAddress; -use fastcrypto::{ - encoding::{Encoding, Hex}, - traits::ToFromBytes, -}; -use iota_types::{TypeTag, bridge::BridgeChainId}; -use tracing::{info, instrument}; - -use crate::{ - crypto::BridgeAuthorityPublicKeyBytes, - error::BridgeError, - metrics::BridgeMetrics, - server::handler::{BridgeRequestHandler, BridgeRequestHandlerTrait}, - types::{ - AddTokensOnEvmAction, AddTokensOnIotaAction, AssetPriceUpdateAction, - BlocklistCommitteeAction, BlocklistType, BridgeAction, EmergencyAction, - EmergencyActionType, EvmContractUpgradeAction, LimitUpdateAction, SignedBridgeAction, - }, - with_metrics, -}; - -pub mod governance_verifier; -pub mod handler; - -#[cfg(test)] -pub(crate) mod mock_handler; - -pub const APPLICATION_JSON: &str = "application/json"; - -pub const PING_PATH: &str = "/ping"; - -// Important: for BridgeActions, the paths need to match the ones in -// bridge_client.rs -pub const ETH_TO_IOTA_TX_PATH: &str = "/sign/bridge_tx/eth/iota/:tx_hash/:event_index"; -pub const IOTA_TO_ETH_TX_PATH: &str = "/sign/bridge_tx/iota/eth/:tx_digest/:event_index"; -pub const COMMITTEE_BLOCKLIST_UPDATE_PATH: &str = - "/sign/update_committee_blocklist/:chain_id/:nonce/:type/:keys"; -pub const EMERGENCY_BUTTON_PATH: &str = "/sign/emergency_button/:chain_id/:nonce/:type"; -pub const LIMIT_UPDATE_PATH: &str = - "/sign/update_limit/:chain_id/:nonce/:sending_chain_id/:new_usd_limit"; -pub const ASSET_PRICE_UPDATE_PATH: &str = - "/sign/update_asset_price/:chain_id/:nonce/:token_id/:new_usd_price"; -pub const EVM_CONTRACT_UPGRADE_PATH_WITH_CALLDATA: &str = - "/sign/upgrade_evm_contract/:chain_id/:nonce/:proxy_address/:new_impl_address/:calldata"; -pub const EVM_CONTRACT_UPGRADE_PATH: &str = - "/sign/upgrade_evm_contract/:chain_id/:nonce/:proxy_address/:new_impl_address"; -pub const ADD_TOKENS_ON_IOTA_PATH: &str = - "/sign/add_tokens_on_iota/:chain_id/:nonce/:native/:token_ids/:token_type_names/:token_prices"; -pub const ADD_TOKENS_ON_EVM_PATH: &str = "/sign/add_tokens_on_evm/:chain_id/:nonce/:native/:token_ids/:token_addresses/:token_iota_decimals/:token_prices"; - -/// BridgeNode's public metadata that is acceesible via the `/ping` endpoint. -// Be careful with what to put here, as it is public. -#[derive(serde::Serialize, Clone)] -pub struct BridgeNodePublicMetadata { - pub version: Option, -} - -impl BridgeNodePublicMetadata { - pub fn new(version: String) -> Self { - Self { - version: Some(version), - } - } - - pub fn empty_for_testing() -> Self { - Self { version: None } - } -} - -pub fn run_server( - socket_address: &SocketAddr, - handler: BridgeRequestHandler, - metrics: Arc, - metadata: Arc, -) -> tokio::task::JoinHandle<()> { - let socket_address = *socket_address; - tokio::spawn(async move { - let listener = tokio::net::TcpListener::bind(socket_address).await.unwrap(); - axum::serve( - listener, - make_router(Arc::new(handler), metrics, metadata).into_make_service(), - ) - .await - .unwrap(); - }) -} - -pub(crate) fn make_router( - handler: Arc, - metrics: Arc, - metadata: Arc, -) -> Router { - Router::new() - .route("/", get(health_check)) - .route(PING_PATH, get(ping)) - .route(ETH_TO_IOTA_TX_PATH, get(handle_eth_tx_hash)) - .route(IOTA_TO_ETH_TX_PATH, get(handle_iota_tx_digest)) - .route( - COMMITTEE_BLOCKLIST_UPDATE_PATH, - get(handle_update_committee_blocklist_action), - ) - .route(EMERGENCY_BUTTON_PATH, get(handle_emergency_action)) - .route(LIMIT_UPDATE_PATH, get(handle_limit_update_action)) - .route( - ASSET_PRICE_UPDATE_PATH, - get(handle_asset_price_update_action), - ) - .route(EVM_CONTRACT_UPGRADE_PATH, get(handle_evm_contract_upgrade)) - .route( - EVM_CONTRACT_UPGRADE_PATH_WITH_CALLDATA, - get(handle_evm_contract_upgrade_with_calldata), - ) - .route(ADD_TOKENS_ON_IOTA_PATH, get(handle_add_tokens_on_iota)) - .route(ADD_TOKENS_ON_EVM_PATH, get(handle_add_tokens_on_evm)) - .with_state((handler, metrics, metadata)) -} - -impl axum::response::IntoResponse for BridgeError { - // TODO: distinguish client error. - fn into_response(self) -> axum::response::Response { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Something went wrong: {:?}", self), - ) - .into_response() - } -} - -impl From for BridgeError -where - E: Into, -{ - fn from(err: E) -> Self { - Self::Generic(err.into().to_string()) - } -} - -async fn health_check() -> StatusCode { - StatusCode::OK -} - -async fn ping( - State((_handler, _metrics, metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - Ok(Json(metadata.as_ref().clone())) -} - -#[instrument(level = "error", skip_all, fields(tx_hash_hex=tx_hash_hex, event_idx=event_idx))] -async fn handle_eth_tx_hash( - Path((tx_hash_hex, event_idx)): Path<(String, u16)>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let sig = handler.handle_eth_tx_hash(tx_hash_hex, event_idx).await?; - Ok(sig) - }; - with_metrics!(metrics.clone(), "handle_eth_tx_hash", future).await -} - -#[instrument(level = "error", skip_all, fields(tx_digest_base58=tx_digest_base58, event_idx=event_idx))] -async fn handle_iota_tx_digest( - Path((tx_digest_base58, event_idx)): Path<(String, u16)>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let sig: Json = handler - .handle_iota_tx_digest(tx_digest_base58, event_idx) - .await?; - Ok(sig) - }; - with_metrics!(metrics.clone(), "handle_iota_tx_digest", future).await -} - -#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, blocklist_type=blocklist_type, keys=keys))] -async fn handle_update_committee_blocklist_action( - Path((chain_id, nonce, blocklist_type, keys)): Path<(u8, u64, u8, String)>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err)) - })?; - let blocklist_type = BlocklistType::try_from(blocklist_type).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!( - "Invalid blocklist action type: {:?}", - err - )) - })?; - let members_to_update = keys - .split(',') - .map(|s| { - let bytes = Hex::decode(s).map_err(|e| anyhow::anyhow!("{:?}", e))?; - BridgeAuthorityPublicKeyBytes::from_bytes(&bytes) - .map_err(|e| anyhow::anyhow!("{:?}", e)) - }) - .collect::, _>>() - .map_err(|e| BridgeError::InvalidBridgeClientRequest(format!("{:?}", e)))?; - let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - chain_id, - nonce, - blocklist_type, - members_to_update, - }); - - let sig: Json = handler.handle_governance_action(action).await?; - Ok(sig) - }; - with_metrics!( - metrics.clone(), - "handle_update_committee_blocklist_action", - future - ) - .await -} - -#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, action_type=action_type))] -async fn handle_emergency_action( - Path((chain_id, nonce, action_type)): Path<(u8, u64, u8)>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err)) - })?; - let action_type = EmergencyActionType::try_from(action_type).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!( - "Invalid emergency action type: {:?}", - err - )) - })?; - let action = BridgeAction::EmergencyAction(EmergencyAction { - chain_id, - nonce, - action_type, - }); - let sig: Json = handler.handle_governance_action(action).await?; - Ok(sig) - }; - with_metrics!(metrics.clone(), "handle_emergency_action", future).await -} - -#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, sending_chain_id=sending_chain_id, new_usd_limit=new_usd_limit))] -async fn handle_limit_update_action( - Path((chain_id, nonce, sending_chain_id, new_usd_limit)): Path<(u8, u64, u8, u64)>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err)) - })?; - let sending_chain_id = BridgeChainId::try_from(sending_chain_id).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err)) - })?; - let action = BridgeAction::LimitUpdateAction(LimitUpdateAction { - chain_id, - nonce, - sending_chain_id, - new_usd_limit, - }); - let sig: Json = handler.handle_governance_action(action).await?; - Ok(sig) - }; - with_metrics!(metrics.clone(), "handle_limit_update_action", future).await -} - -#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, token_id=token_id, new_usd_price=new_usd_price))] -async fn handle_asset_price_update_action( - Path((chain_id, nonce, token_id, new_usd_price)): Path<(u8, u64, u8, u64)>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err)) - })?; - let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction { - chain_id, - nonce, - token_id, - new_usd_price, - }); - let sig: Json = handler.handle_governance_action(action).await?; - Ok(sig) - }; - with_metrics!(metrics.clone(), "handle_asset_price_update_action", future).await -} - -#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, proxy_address=format!("{:x}", proxy_address), new_impl_address=format!("{:x}", new_impl_address)))] -async fn handle_evm_contract_upgrade_with_calldata( - Path((chain_id, nonce, proxy_address, new_impl_address, calldata)): Path<( - u8, - u64, - EthAddress, - EthAddress, - String, - )>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err)) - })?; - let call_data = Hex::decode(&calldata).map_err(|e| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid call data: {:?}", e)) - })?; - let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - chain_id, - nonce, - proxy_address, - new_impl_address, - call_data, - }); - let sig: Json = handler.handle_governance_action(action).await?; - Ok(sig) - }; - with_metrics!( - metrics.clone(), - "handle_evm_contract_upgrade_with_calldata", - future - ) - .await -} - -#[instrument( - level = "error", - skip_all, - fields(chain_id, nonce, proxy_address, new_impl_address) -)] -async fn handle_evm_contract_upgrade( - Path((chain_id, nonce, proxy_address, new_impl_address)): Path<( - u8, - u64, - EthAddress, - EthAddress, - )>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err)) - })?; - let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - chain_id, - nonce, - proxy_address, - new_impl_address, - call_data: vec![], - }); - let sig: Json = handler.handle_governance_action(action).await?; - - Ok(sig) - }; - with_metrics!(metrics.clone(), "handle_evm_contract_upgrade", future).await -} - -#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, native=native, token_ids=token_ids, token_type_names=token_type_names, token_prices=token_prices))] -async fn handle_add_tokens_on_iota( - Path((chain_id, nonce, native, token_ids, token_type_names, token_prices)): Path<( - u8, - u64, - u8, - String, - String, - String, - )>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err)) - })?; - - if !chain_id.is_iota_chain() { - return Err(BridgeError::InvalidBridgeClientRequest( - "handle_add_tokens_on_iota only expects IOTA chain id".to_string(), - )); - } - - let native = match native { - 1 => true, - 0 => false, - _ => { - return Err(BridgeError::InvalidBridgeClientRequest(format!( - "Invalid native flag: {}", - native - ))); - } - }; - let token_ids = token_ids - .split(',') - .map(|s| { - s.parse::().map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid token id: {:?}", err)) - }) - }) - .collect::, _>>()?; - let token_type_names = token_type_names - .split(',') - .map(|s| { - TypeTag::from_str(s).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!( - "Invalid token type name: {:?}", - err - )) - }) - }) - .collect::, _>>()?; - let token_prices = token_prices - .split(',') - .map(|s| { - s.parse::().map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!( - "Invalid token price: {:?}", - err - )) - }) - }) - .collect::, _>>()?; - let action = BridgeAction::AddTokensOnIotaAction(AddTokensOnIotaAction { - chain_id, - nonce, - native, - token_ids, - token_type_names, - token_prices, - }); - let sig: Json = handler.handle_governance_action(action).await?; - Ok(sig) - }; - with_metrics!(metrics.clone(), "handle_add_tokens_on_iota", future).await -} - -#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, native=native, token_ids=token_ids, token_addresses=token_addresses, token_iota_decimals=token_iota_decimals, token_prices=token_prices))] -async fn handle_add_tokens_on_evm( - Path((chain_id, nonce, native, token_ids, token_addresses, token_iota_decimals, token_prices)): Path<( - u8, - u64, - u8, - String, - String, - String, - String, - )>, - State((handler, metrics, _metadata)): State<( - Arc, - Arc, - Arc, - )>, -) -> Result, BridgeError> { - let future = async { - let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err)) - })?; - if chain_id.is_iota_chain() { - return Err(BridgeError::InvalidBridgeClientRequest( - "handle_add_tokens_on_evm does not expect IOTA chain id".to_string(), - )); - } - - let native = match native { - 1 => true, - 0 => false, - _ => { - return Err(BridgeError::InvalidBridgeClientRequest(format!( - "Invalid native flag: {}", - native - ))); - } - }; - let token_ids = token_ids - .split(',') - .map(|s| { - s.parse::().map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!("Invalid token id: {:?}", err)) - }) - }) - .collect::, _>>()?; - let token_addresses = token_addresses - .split(',') - .map(|s| { - EthAddress::from_str(s).map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!( - "Invalid token address: {:?}", - err - )) - }) - }) - .collect::, _>>()?; - let token_iota_decimals = token_iota_decimals - .split(',') - .map(|s| { - s.parse::().map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!( - "Invalid token iota decimals: {:?}", - err - )) - }) - }) - .collect::, _>>()?; - let token_prices = token_prices - .split(',') - .map(|s| { - s.parse::().map_err(|err| { - BridgeError::InvalidBridgeClientRequest(format!( - "Invalid token price: {:?}", - err - )) - }) - }) - .collect::, _>>()?; - let action = BridgeAction::AddTokensOnEvmAction(AddTokensOnEvmAction { - chain_id, - nonce, - native, - token_ids, - token_addresses, - token_iota_decimals, - token_prices, - }); - let sig: Json = handler.handle_governance_action(action).await?; - Ok(sig) - }; - with_metrics!(metrics.clone(), "handle_add_tokens_on_evm", future).await -} - -#[macro_export] -macro_rules! with_metrics { - ($metrics:expr, $type_:expr, $func:expr) => { - async move { - info!("Received {} request", $type_); - $metrics - .requests_received - .with_label_values(&[$type_]) - .inc(); - $metrics - .requests_inflight - .with_label_values(&[$type_]) - .inc(); - - let result = $func.await; - - match &result { - Ok(_) => { - info!("{} request succeeded", $type_); - $metrics.requests_ok.with_label_values(&[$type_]).inc(); - } - Err(e) => { - info!("{} request failed: {:?}", $type_, e); - $metrics.err_requests.with_label_values(&[$type_]).inc(); - } - } - - $metrics - .requests_inflight - .with_label_values(&[$type_]) - .dec(); - result - } - }; -} - -#[cfg(test)] -mod tests { - use iota_types::bridge::TOKEN_ID_BTC; - - use super::*; - use crate::{ - client::bridge_client::BridgeClient, server::mock_handler::BridgeRequestMockHandler, - test_utils::get_test_authorities_and_run_mock_bridge_server, types::BridgeCommittee, - }; - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_server_handle_blocklist_update_action_path() { - let client = setup(); - - let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes( - &Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4") - .unwrap(), - ) - .unwrap(); - let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - nonce: 129, - chain_id: BridgeChainId::IotaCustom, - blocklist_type: BlocklistType::Blocklist, - members_to_update: vec![pub_key_bytes.clone()], - }); - client.request_sign_bridge_action(action).await.unwrap(); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_server_handle_emergency_action_path() { - let client = setup(); - - let action = BridgeAction::EmergencyAction(EmergencyAction { - nonce: 55, - chain_id: BridgeChainId::IotaCustom, - action_type: EmergencyActionType::Pause, - }); - client.request_sign_bridge_action(action).await.unwrap(); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_server_handle_limit_update_action_path() { - let client = setup(); - - let action = BridgeAction::LimitUpdateAction(LimitUpdateAction { - nonce: 15, - chain_id: BridgeChainId::IotaCustom, - sending_chain_id: BridgeChainId::EthCustom, - new_usd_limit: 1_000_000_0000, // $1M USD - }); - client.request_sign_bridge_action(action).await.unwrap(); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_server_handle_asset_price_update_action_path() { - let client = setup(); - - let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction { - nonce: 266, - chain_id: BridgeChainId::IotaCustom, - token_id: TOKEN_ID_BTC, - new_usd_price: 100_000_0000, // $100k USD - }); - client.request_sign_bridge_action(action).await.unwrap(); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_server_handle_evm_contract_upgrade_action_path() { - let client = setup(); - - let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data: vec![], - }); - client.request_sign_bridge_action(action).await.unwrap(); - - let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data: vec![12, 34, 56], - }); - client.request_sign_bridge_action(action).await.unwrap(); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_server_handle_add_tokens_on_iota_action_path() { - let client = setup(); - - let action = BridgeAction::AddTokensOnIotaAction(AddTokensOnIotaAction { - nonce: 266, - chain_id: BridgeChainId::IotaCustom, - native: false, - token_ids: vec![100, 101, 102], - token_type_names: vec![ - TypeTag::from_str("0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin1").unwrap(), - TypeTag::from_str("0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin2").unwrap(), - TypeTag::from_str("0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin3").unwrap(), - ], - token_prices: vec![100_000_0000, 200_000_0000, 300_000_0000], - }); - client.request_sign_bridge_action(action).await.unwrap(); - } - - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_server_handle_add_tokens_on_evm_action_path() { - let client = setup(); - - let action = BridgeAction::AddTokensOnEvmAction(crate::types::AddTokensOnEvmAction { - nonce: 0, - chain_id: BridgeChainId::EthCustom, - native: false, - token_ids: vec![99, 100, 101], - token_addresses: vec![ - EthAddress::repeat_byte(1), - EthAddress::repeat_byte(2), - EthAddress::repeat_byte(3), - ], - token_iota_decimals: vec![5, 6, 7], - token_prices: vec![1_000_000_000, 2_000_000_000, 3_000_000_000], - }); - client.request_sign_bridge_action(action).await.unwrap(); - } - - fn setup() -> BridgeClient { - let mock = BridgeRequestMockHandler::new(); - let (_handles, authorities, mut secrets) = - get_test_authorities_and_run_mock_bridge_server(vec![10000], vec![mock.clone()]); - mock.set_signer(secrets.swap_remove(0)); - let committee = BridgeCommittee::new(authorities).unwrap(); - let pub_key = committee.members().keys().next().unwrap(); - BridgeClient::new(pub_key.clone(), Arc::new(committee)).unwrap() - } -} diff --git a/crates/iota-bridge/src/storage.rs b/crates/iota-bridge/src/storage.rs deleted file mode 100644 index 13a4e4c9b20..00000000000 --- a/crates/iota-bridge/src/storage.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{collections::HashMap, path::Path, sync::Arc}; - -use iota_types::{Identifier, event::EventID}; -use typed_store::{ - DBMapUtils, Map, - rocks::{DBMap, MetricConf}, - traits::{TableSummary, TypedStoreDebug}, -}; - -use crate::{ - error::{BridgeError, BridgeResult}, - types::{BridgeAction, BridgeActionDigest}, -}; - -#[derive(DBMapUtils)] -pub struct BridgeOrchestratorTables { - /// pending BridgeActions that orchestrator received but not yet executed - pub(crate) pending_actions: DBMap, - /// module identifier to the last processed EventID - pub(crate) iota_syncer_cursors: DBMap, - /// contract address to the last processed block - pub(crate) eth_syncer_cursors: DBMap, -} - -impl BridgeOrchestratorTables { - pub fn new(path: &Path) -> Arc { - Arc::new(Self::open_tables_read_write( - path.to_path_buf(), - MetricConf::new("bridge"), - None, - None, - )) - } - - pub(crate) fn insert_pending_actions(&self, actions: &[BridgeAction]) -> BridgeResult<()> { - let mut batch = self.pending_actions.batch(); - batch - .insert_batch( - &self.pending_actions, - actions.iter().map(|a| (a.digest(), a)), - ) - .map_err(|e| { - BridgeError::Storage(format!("Couldn't insert into pending_actions: {:?}", e)) - })?; - batch - .write() - .map_err(|e| BridgeError::Storage(format!("Couldn't write batch: {:?}", e))) - } - - pub(crate) fn remove_pending_actions( - &self, - actions: &[BridgeActionDigest], - ) -> BridgeResult<()> { - let mut batch = self.pending_actions.batch(); - batch - .delete_batch(&self.pending_actions, actions) - .map_err(|e| { - BridgeError::Storage(format!("Couldn't delete from pending_actions: {:?}", e)) - })?; - batch - .write() - .map_err(|e| BridgeError::Storage(format!("Couldn't write batch: {:?}", e))) - } - - pub(crate) fn update_iota_event_cursor( - &self, - module: Identifier, - cursor: EventID, - ) -> BridgeResult<()> { - let mut batch = self.iota_syncer_cursors.batch(); - - batch - .insert_batch(&self.iota_syncer_cursors, [(module, cursor)]) - .map_err(|e| { - BridgeError::Storage(format!("Couldn't insert into iota_syncer_cursors: {:?}", e)) - })?; - batch - .write() - .map_err(|e| BridgeError::Storage(format!("Couldn't write batch: {:?}", e))) - } - - pub(crate) fn update_eth_event_cursor( - &self, - contract_address: ethers::types::Address, - cursor: u64, - ) -> BridgeResult<()> { - let mut batch = self.eth_syncer_cursors.batch(); - - batch - .insert_batch(&self.eth_syncer_cursors, [(contract_address, cursor)]) - .map_err(|e| { - BridgeError::Storage(format!("Couldn't insert into eth_syncer_cursors: {:?}", e)) - })?; - batch - .write() - .map_err(|e| BridgeError::Storage(format!("Couldn't write batch: {:?}", e))) - } - - pub fn get_all_pending_actions(&self) -> HashMap { - self.pending_actions.unbounded_iter().collect() - } - - pub fn get_iota_event_cursors( - &self, - identifiers: &[Identifier], - ) -> BridgeResult>> { - self.iota_syncer_cursors - .multi_get(identifiers) - .map_err(|e| BridgeError::Storage(format!("Couldn't get iota_syncer_cursors: {:?}", e))) - } - - pub fn get_eth_event_cursors( - &self, - contract_addresses: &[ethers::types::Address], - ) -> BridgeResult>> { - self.eth_syncer_cursors - .multi_get(contract_addresses) - .map_err(|e| BridgeError::Storage(format!("Couldn't get iota_syncer_cursors: {:?}", e))) - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use iota_types::digests::TransactionDigest; - - use super::*; - use crate::test_utils::get_test_iota_to_eth_bridge_action; - - // async: existing runtime is required with typed-store - #[tokio::test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - async fn test_bridge_storage_basic() { - let temp_dir = tempfile::tempdir().unwrap(); - let store = BridgeOrchestratorTables::new(temp_dir.path()); - - let action1 = get_test_iota_to_eth_bridge_action( - None, - Some(0), - Some(99), - Some(10000), - None, - None, - None, - ); - - let action2 = get_test_iota_to_eth_bridge_action( - None, - Some(2), - Some(100), - Some(10000), - None, - None, - None, - ); - - // in the beginning it's empty - let actions = store.get_all_pending_actions(); - assert!(actions.is_empty()); - - // remove non existing entry is ok - store.remove_pending_actions(&[action1.digest()]).unwrap(); - - store - .insert_pending_actions(&vec![action1.clone(), action2.clone()]) - .unwrap(); - - let actions = store.get_all_pending_actions(); - assert_eq!( - actions, - HashMap::from_iter(vec![ - (action1.digest(), action1.clone()), - (action2.digest(), action2.clone()) - ]) - ); - - // insert an existing action is ok - store.insert_pending_actions(&[action1.clone()]).unwrap(); - let actions = store.get_all_pending_actions(); - assert_eq!( - actions, - HashMap::from_iter(vec![ - (action1.digest(), action1.clone()), - (action2.digest(), action2.clone()) - ]) - ); - - // remove action 2 - store.remove_pending_actions(&[action2.digest()]).unwrap(); - let actions = store.get_all_pending_actions(); - assert_eq!( - actions, - HashMap::from_iter(vec![(action1.digest(), action1.clone())]) - ); - - // remove action 1 - store.remove_pending_actions(&[action1.digest()]).unwrap(); - let actions = store.get_all_pending_actions(); - assert!(actions.is_empty()); - - // update eth event cursor - let eth_contract_address = ethers::types::Address::random(); - let eth_block_num = 199999u64; - assert!( - store - .get_eth_event_cursors(&[eth_contract_address]) - .unwrap()[0] - .is_none() - ); - store - .update_eth_event_cursor(eth_contract_address, eth_block_num) - .unwrap(); - assert_eq!( - store - .get_eth_event_cursors(&[eth_contract_address]) - .unwrap()[0] - .unwrap(), - eth_block_num - ); - - // update iota event cursor - let iota_module = Identifier::from_str("test").unwrap(); - let iota_cursor = EventID { - tx_digest: TransactionDigest::random(), - event_seq: 1, - }; - assert!( - store - .get_iota_event_cursors(&[iota_module.clone()]) - .unwrap()[0] - .is_none() - ); - store - .update_iota_event_cursor(iota_module.clone(), iota_cursor) - .unwrap(); - assert_eq!( - store - .get_iota_event_cursors(&[iota_module.clone()]) - .unwrap()[0] - .unwrap(), - iota_cursor - ); - } -} diff --git a/crates/iota-bridge/src/test_utils.rs b/crates/iota-bridge/src/test_utils.rs deleted file mode 100644 index 145386c27ff..00000000000 --- a/crates/iota-bridge/src/test_utils.rs +++ /dev/null @@ -1,425 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - collections::{BTreeMap, HashMap}, - net::{IpAddr, Ipv4Addr, SocketAddr}, -}; - -use ethers::{ - abi::{ParamType, long_signature}, - types::{ - Address as EthAddress, Block, BlockNumber, Filter, FilterBlockOption, Log, - TransactionReceipt, TxHash, U64, ValueOrArray, - }, -}; -use fastcrypto::{ - encoding::{Encoding, Hex}, - traits::KeyPair, -}; -use hex_literal::hex; -use iota_config::local_ip_utils; -use iota_json_rpc_types::IotaTransactionBlockEffectsAPI; -use iota_sdk::wallet_context::WalletContext; -use iota_test_transaction_builder::TestTransactionBuilder; -use iota_types::{ - BRIDGE_PACKAGE_ID, IOTA_BRIDGE_OBJECT_ID, - base_types::{IotaAddress, ObjectRef, SequenceNumber}, - bridge::{BridgeChainId, BridgeCommitteeSummary, MoveTypeCommitteeMember, TOKEN_ID_USDC}, - crypto::{ToFromBytes, get_key_pair}, - digests::TransactionDigest, - object::Owner, - transaction::{CallArg, ObjectArg}, -}; -use move_core_types::language_storage::TypeTag; -use tokio::task::JoinHandle; - -use crate::{ - abi::EthToIotaTokenBridgeV1, - crypto::{BridgeAuthorityKeyPair, BridgeAuthorityPublicKey, BridgeAuthoritySignInfo}, - eth_mock_provider::EthMockProvider, - events::{EmittedIotaToEthTokenBridgeV1, IotaBridgeEvent}, - iota_transaction_builder::build_iota_transaction, - server::mock_handler::{BridgeRequestMockHandler, run_mock_server}, - types::{ - BridgeAction, BridgeAuthority, BridgeCommittee, BridgeCommitteeValiditySignInfo, - CertifiedBridgeAction, EthToIotaBridgeAction, IotaToEthBridgeAction, SignedBridgeAction, - VerifiedCertifiedBridgeAction, - }, -}; - -pub const DUMMY_MUTABLE_BRIDGE_OBJECT_ARG: ObjectArg = ObjectArg::SharedObject { - id: IOTA_BRIDGE_OBJECT_ID, - initial_shared_version: SequenceNumber::from_u64(1), - mutable: true, -}; - -pub fn get_test_authority_and_key( - voting_power: u64, - port: u16, -) -> ( - BridgeAuthority, - BridgeAuthorityPublicKey, - BridgeAuthorityKeyPair, -) { - let (_, kp): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let pubkey = kp.public().clone(); - let authority = BridgeAuthority { - pubkey: pubkey.clone(), - voting_power, - base_url: format!("http://127.0.0.1:{}", port), - is_blocklisted: false, - }; - - (authority, pubkey, kp) -} - -// TODO: make a builder for this -pub fn get_test_iota_to_eth_bridge_action( - iota_tx_digest: Option, - iota_tx_event_index: Option, - nonce: Option, - amount_iota_adjusted: Option, - sender_address: Option, - recipient_address: Option, - token_id: Option, -) -> BridgeAction { - BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction { - iota_tx_digest: iota_tx_digest.unwrap_or_else(TransactionDigest::random), - iota_tx_event_index: iota_tx_event_index.unwrap_or(0), - iota_bridge_event: EmittedIotaToEthTokenBridgeV1 { - nonce: nonce.unwrap_or_default(), - iota_chain_id: BridgeChainId::IotaCustom, - iota_address: sender_address.unwrap_or_else(IotaAddress::random_for_testing_only), - eth_chain_id: BridgeChainId::EthCustom, - eth_address: recipient_address.unwrap_or_else(EthAddress::random), - token_id: token_id.unwrap_or(TOKEN_ID_USDC), - amount_iota_adjusted: amount_iota_adjusted.unwrap_or(100_000), - }, - }) -} - -pub fn get_test_eth_to_iota_bridge_action( - nonce: Option, - amount: Option, - iota_address: Option, - token_id: Option, -) -> BridgeAction { - BridgeAction::EthToIotaBridgeAction(EthToIotaBridgeAction { - eth_tx_hash: TxHash::random(), - eth_event_index: 0, - eth_bridge_event: EthToIotaTokenBridgeV1 { - eth_chain_id: BridgeChainId::EthCustom, - nonce: nonce.unwrap_or_default(), - iota_chain_id: BridgeChainId::IotaCustom, - token_id: token_id.unwrap_or(TOKEN_ID_USDC), - iota_adjusted_amount: amount.unwrap_or(100_000), - iota_address: iota_address.unwrap_or_else(IotaAddress::random_for_testing_only), - eth_address: EthAddress::random(), - }, - }) -} - -pub fn run_mock_bridge_server( - mock_handlers: Vec, -) -> (Vec>, Vec) { - let mut handles = vec![]; - let mut ports = vec![]; - for mock_handler in mock_handlers { - let localhost = local_ip_utils::localhost_for_testing(); - let port = local_ip_utils::get_available_port(&localhost); - // start server - let server_handle = run_mock_server( - SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port), - mock_handler.clone(), - ); - ports.push(port); - handles.push(server_handle); - } - (handles, ports) -} - -pub fn get_test_authorities_and_run_mock_bridge_server( - voting_power: Vec, - mock_handlers: Vec, -) -> ( - Vec>, - Vec, - Vec, -) { - assert_eq!(voting_power.len(), mock_handlers.len()); - let (handles, ports) = run_mock_bridge_server(mock_handlers); - let mut authorities = vec![]; - let mut secrets = vec![]; - for (port, vp) in ports.iter().zip(voting_power) { - let (authority, _, secret) = get_test_authority_and_key(vp, *port); - authorities.push(authority); - secrets.push(secret); - } - - (handles, authorities, secrets) -} - -pub fn sign_action_with_key( - action: &BridgeAction, - secret: &BridgeAuthorityKeyPair, -) -> SignedBridgeAction { - let sig = BridgeAuthoritySignInfo::new(action, secret); - SignedBridgeAction::new_from_data_and_sig(action.clone(), sig) -} - -pub fn mock_last_finalized_block(mock_provider: &EthMockProvider, block_number: u64) { - let block = Block:: { - number: Some(U64::from(block_number)), - ..Default::default() - }; - mock_provider - .add_response("eth_getBlockByNumber", ("finalized", false), block) - .unwrap(); -} - -// Mocks eth_getLogs and eth_getTransactionReceipt for the given address and -// block range. The input log needs to have transaction_hash set. -pub fn mock_get_logs( - mock_provider: &EthMockProvider, - address: EthAddress, - from_block: u64, - to_block: u64, - logs: Vec, -) { - mock_provider.add_response::<[ethers::types::Filter; 1], Vec, Vec>( - "eth_getLogs", - [ - Filter { - block_option: FilterBlockOption::Range { - from_block: Some(BlockNumber::Number(U64::from(from_block))), - to_block: Some(BlockNumber::Number(U64::from(to_block))), - }, - address: Some(ValueOrArray::Value(address)), - topics: [None, None, None, None], - } - ], - logs.clone(), - ).unwrap(); - - for log in logs { - mock_provider - .add_response::<[TxHash; 1], TransactionReceipt, TransactionReceipt>( - "eth_getTransactionReceipt", - [log.transaction_hash.unwrap()], - TransactionReceipt { - block_number: log.block_number, - logs: vec![log], - ..Default::default() - }, - ) - .unwrap(); - } -} - -/// Returns a test Log and corresponding BridgeAction -// Reference: https://github.com/rust-ethereum/ethabi/blob/master/ethabi/src/event.rs#L192 -pub fn get_test_log_and_action( - contract_address: EthAddress, - tx_hash: TxHash, - event_index: u16, -) -> (Log, BridgeAction) { - let token_id = 3u8; - let iota_adjusted_amount = 10000000u64; - let source_address = EthAddress::random(); - let iota_address: IotaAddress = IotaAddress::random_for_testing_only(); - let target_address = Hex::decode(&iota_address.to_string()).unwrap(); - // Note: must use `encode` rather than `encode_packaged` - let encoded = ethers::abi::encode(&[ - // u8/u64 is encoded as u256 in abi standard - ethers::abi::Token::Uint(ethers::types::U256::from(token_id)), - ethers::abi::Token::Uint(ethers::types::U256::from(iota_adjusted_amount)), - ethers::abi::Token::Address(source_address), - ethers::abi::Token::Bytes(target_address.clone()), - ]); - let log = Log { - address: contract_address, - topics: vec![ - long_signature( - "TokensDeposited", - &[ - ParamType::Uint(8), - ParamType::Uint(64), - ParamType::Uint(8), - ParamType::Uint(8), - ParamType::Uint(64), - ParamType::Address, - ParamType::Bytes, - ], - ), - hex!("0000000000000000000000000000000000000000000000000000000000000001").into(), /* chain id: iota testnet */ - hex!("0000000000000000000000000000000000000000000000000000000000000010").into(), /* nonce: 16 */ - hex!("000000000000000000000000000000000000000000000000000000000000000b").into(), /* chain id: sepolia */ - ], - data: encoded.into(), - block_hash: Some(TxHash::random()), - block_number: Some(1.into()), - transaction_hash: Some(tx_hash), - log_index: Some(0.into()), - ..Default::default() - }; - let topic_1: [u8; 32] = log.topics[1].into(); - let topic_3: [u8; 32] = log.topics[3].into(); - - let bridge_action = BridgeAction::EthToIotaBridgeAction(EthToIotaBridgeAction { - eth_tx_hash: tx_hash, - eth_event_index: event_index, - eth_bridge_event: EthToIotaTokenBridgeV1 { - eth_chain_id: BridgeChainId::try_from(topic_1[topic_1.len() - 1]).unwrap(), - nonce: u64::from_be_bytes(log.topics[2].as_ref()[24..32].try_into().unwrap()), - iota_chain_id: BridgeChainId::try_from(topic_3[topic_3.len() - 1]).unwrap(), - token_id, - iota_adjusted_amount, - iota_address, - eth_address: source_address, - }, - }); - (log, bridge_action) -} - -pub async fn bridge_token( - context: &mut WalletContext, - recv_address: EthAddress, - token_ref: ObjectRef, - token_type: TypeTag, - bridge_object_arg: ObjectArg, -) -> EmittedIotaToEthTokenBridgeV1 { - let rgp = context.get_reference_gas_price().await.unwrap(); - let sender = context.active_address().unwrap(); - let gas_object = context.get_one_gas_object().await.unwrap().unwrap().1; - let tx = TestTransactionBuilder::new(sender, gas_object, rgp) - .move_call( - BRIDGE_PACKAGE_ID, - "bridge", - "send_token", - vec![ - CallArg::Object(bridge_object_arg), - CallArg::Pure(bcs::to_bytes(&(BridgeChainId::EthCustom as u8)).unwrap()), - CallArg::Pure(bcs::to_bytes(&recv_address.as_bytes()).unwrap()), - CallArg::Object(ObjectArg::ImmOrOwnedObject(token_ref)), - ], - ) - .with_type_args(vec![token_type]) - .build(); - let signed_tn = context.sign_transaction(&tx); - let resp = context.execute_transaction_must_succeed(signed_tn).await; - let events = resp.events.unwrap(); - let bridge_events = events - .data - .iter() - .filter_map(|event| IotaBridgeEvent::try_from_iota_event(event).unwrap()) - .collect::>(); - bridge_events - .iter() - .find_map(|e| match e { - IotaBridgeEvent::IotaToEthTokenBridgeV1(event) => Some(event.clone()), - _ => None, - }) - .unwrap() -} - -/// Returns a VerifiedCertifiedBridgeAction with signatures from the given -/// BridgeAction and BridgeAuthorityKeyPair -pub fn get_certified_action_with_validator_secrets( - action: BridgeAction, - secrets: &Vec, -) -> VerifiedCertifiedBridgeAction { - let mut sigs = BTreeMap::new(); - for secret in secrets { - let signed_action = sign_action_with_key(&action, secret); - sigs.insert(secret.public().into(), signed_action.into_sig().signature); - } - let certified_action = CertifiedBridgeAction::new_from_data_and_sig( - action, - BridgeCommitteeValiditySignInfo { signatures: sigs }, - ); - VerifiedCertifiedBridgeAction::new_from_verified(certified_action) -} - -/// Approve a bridge action with the given validator secrets. Return the -/// newly created token object reference if `expected_token_receiver` is Some -/// (only relevant when the action is eth -> IOTA transfer), -/// Otherwise return None. -/// Note: for iota -> eth transfers, the actual deposit needs to be recorded. -/// Use `bridge_token` to do it. -// TODO(bridge): It appears this function is very slow (particularly, -// `execute_transaction_must_succeed`). Investigate why. -pub async fn approve_action_with_validator_secrets( - wallet_context: &mut WalletContext, - bridge_obj_org: ObjectArg, - // TODO: add `token_recipient()` for `BridgeAction` so we don't need `expected_token_receiver` - action: BridgeAction, - validator_secrets: &Vec, - // Only relevant for eth -> iota transfers when token will be dropped to the recipient - expected_token_receiver: Option, - id_token_map: &HashMap, -) -> Option { - let action_certificate = get_certified_action_with_validator_secrets(action, validator_secrets); - let rgp = wallet_context.get_reference_gas_price().await.unwrap(); - let iota_address = wallet_context.active_address().unwrap(); - let gas_obj_ref = wallet_context - .get_one_gas_object() - .await - .unwrap() - .unwrap() - .1; - let tx_data = build_iota_transaction( - iota_address, - &gas_obj_ref, - action_certificate, - bridge_obj_org, - id_token_map, - rgp, - ) - .unwrap(); - let signed_tx = wallet_context.sign_transaction(&tx_data); - let resp = wallet_context - .execute_transaction_must_succeed(signed_tx) - .await; - - // If `expected_token_receiver` is None, return - expected_token_receiver?; - - let expected_token_receiver = expected_token_receiver.unwrap(); - for created in resp.effects.unwrap().created() { - if created.owner == Owner::AddressOwner(expected_token_receiver) { - return Some(created.reference.to_object_ref()); - } - } - panic!( - "Didn't find the created object owned by {}", - expected_token_receiver - ); -} - -pub fn bridge_committee_to_bridge_committee_summary( - committee: BridgeCommittee, -) -> BridgeCommitteeSummary { - BridgeCommitteeSummary { - members: committee - .members() - .iter() - .map(|(k, v)| { - let bytes = k.as_bytes().to_vec(); - ( - bytes.clone(), - MoveTypeCommitteeMember { - iota_address: IotaAddress::random_for_testing_only(), - bridge_pubkey_bytes: bytes, - voting_power: v.voting_power, - http_rest_url: v.base_url.as_bytes().to_vec(), - blocklisted: v.is_blocklisted, - }, - ) - }) - .collect(), - member_registration: vec![], - last_committee_update_epoch: 0, - } -} diff --git a/crates/iota-bridge/src/types.rs b/crates/iota-bridge/src/types.rs deleted file mode 100644 index 2b1e669a1b1..00000000000 --- a/crates/iota-bridge/src/types.rs +++ /dev/null @@ -1,771 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - collections::{BTreeMap, BTreeSet}, - fmt::Debug, -}; - -use enum_dispatch::enum_dispatch; -pub use ethers::types::H256 as EthTransactionHash; -use ethers::types::{Address as EthAddress, H256, Log}; -use fastcrypto::hash::{HashFunction, Keccak256}; -use iota_types::{ - TypeTag, - bridge::{ - APPROVAL_THRESHOLD_ADD_TOKENS_ON_EVM, APPROVAL_THRESHOLD_ADD_TOKENS_ON_IOTA, - APPROVAL_THRESHOLD_ASSET_PRICE_UPDATE, APPROVAL_THRESHOLD_COMMITTEE_BLOCKLIST, - APPROVAL_THRESHOLD_EMERGENCY_PAUSE, APPROVAL_THRESHOLD_EMERGENCY_UNPAUSE, - APPROVAL_THRESHOLD_EVM_CONTRACT_UPGRADE, APPROVAL_THRESHOLD_LIMIT_UPDATE, - APPROVAL_THRESHOLD_TOKEN_TRANSFER, BRIDGE_COMMITTEE_MAXIMAL_VOTING_POWER, - BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER, BridgeChainId, MoveTypeParsedTokenTransferMessage, - MoveTypeTokenTransferPayload, - }, - committee::{CommitteeTrait, StakeUnit}, - digests::{Digest, TransactionDigest}, - message_envelope::{Envelope, Message, VerifiedEnvelope}, -}; -use num_enum::TryFromPrimitive; -use rand::{Rng, seq::SliceRandom}; -use serde::{Deserialize, Serialize}; -use shared_crypto::intent::IntentScope; - -use crate::{ - abi::EthToIotaTokenBridgeV1, - crypto::{ - BridgeAuthorityPublicKey, BridgeAuthorityPublicKeyBytes, - BridgeAuthorityRecoverableSignature, BridgeAuthoritySignInfo, - }, - encoding::BridgeMessageEncoding, - error::{BridgeError, BridgeResult}, - events::EmittedIotaToEthTokenBridgeV1, -}; - -pub const BRIDGE_AUTHORITY_TOTAL_VOTING_POWER: u64 = 10000; - -pub const USD_MULTIPLIER: u64 = 10000; // decimal places = 4 - -pub type IsBridgePaused = bool; -pub const BRIDGE_PAUSED: bool = true; -pub const BRIDGE_UNPAUSED: bool = false; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct BridgeAuthority { - pub pubkey: BridgeAuthorityPublicKey, - pub voting_power: u64, - pub base_url: String, - pub is_blocklisted: bool, -} - -impl BridgeAuthority { - pub fn pubkey_bytes(&self) -> BridgeAuthorityPublicKeyBytes { - BridgeAuthorityPublicKeyBytes::from(&self.pubkey) - } -} - -#[derive(Debug, Clone)] -pub struct BridgeCommittee { - members: BTreeMap, - total_blocklisted_stake: StakeUnit, -} - -impl BridgeCommittee { - pub fn new(members: Vec) -> BridgeResult { - let mut members_map = BTreeMap::new(); - let mut total_blocklisted_stake = 0; - let mut total_stake = 0; - for member in members { - let public_key = BridgeAuthorityPublicKeyBytes::from(&member.pubkey); - if members_map.contains_key(&public_key) { - return Err(BridgeError::InvalidBridgeCommittee( - "Duplicate BridgeAuthority Public key".into(), - )); - } - // TODO: should we disallow identical network addresses? - if member.is_blocklisted { - total_blocklisted_stake += member.voting_power; - } - total_stake += member.voting_power; - members_map.insert(public_key, member); - } - if total_stake < BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER { - return Err(BridgeError::InvalidBridgeCommittee(format!( - "Total voting power is below minimal {BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER}" - ))); - } - if total_stake > BRIDGE_COMMITTEE_MAXIMAL_VOTING_POWER { - return Err(BridgeError::InvalidBridgeCommittee(format!( - "Total voting power is above maximal {BRIDGE_COMMITTEE_MAXIMAL_VOTING_POWER}" - ))); - } - Ok(Self { - members: members_map, - total_blocklisted_stake, - }) - } - - pub fn is_active_member(&self, member: &BridgeAuthorityPublicKeyBytes) -> bool { - self.members.contains_key(member) && !self.members.get(member).unwrap().is_blocklisted - } - - pub fn members(&self) -> &BTreeMap { - &self.members - } - - pub fn member(&self, member: &BridgeAuthorityPublicKeyBytes) -> Option<&BridgeAuthority> { - self.members.get(member) - } - - pub fn total_blocklisted_stake(&self) -> StakeUnit { - self.total_blocklisted_stake - } -} - -impl CommitteeTrait for BridgeCommittee { - // Note: blocklisted members are always excluded. - fn shuffle_by_stake_with_rng( - &self, - // `preferences` is used as a *flag* here to influence the order of validators to be - // requested. - // * if `Some(_)`, then we will request validators in the order of the voting power - // * if `None`, we still refer to voting power, but they are shuffled by randomness. - // to save gas cost. - preferences: Option<&BTreeSet>, - // only attempt from these authorities. - restrict_to: Option<&BTreeSet>, - rng: &mut impl Rng, - ) -> Vec { - let mut candidates = self - .members - .iter() - .filter_map(|(name, a)| { - // Remove blocklisted members - if a.is_blocklisted { - return None; - } - // exclude non-allowlisted members - if let Some(restrict_to) = restrict_to { - match restrict_to.contains(name) { - true => Some((name.clone(), a.voting_power)), - false => None, - } - } else { - Some((name.clone(), a.voting_power)) - } - }) - .collect::>(); - if preferences.is_some() { - candidates.sort_by(|(_, a), (_, b)| b.cmp(a)); - candidates.iter().map(|(name, _)| name.clone()).collect() - } else { - candidates - .choose_multiple_weighted(rng, candidates.len(), |(_, weight)| *weight as f64) - // Unwrap safe: it panics when the third parameter is larger than the size of the - // slice - .unwrap() - .map(|(name, _)| name) - .cloned() - .collect() - } - } - - fn weight(&self, author: &BridgeAuthorityPublicKeyBytes) -> StakeUnit { - self.members - .get(author) - .map(|a| a.voting_power) - .unwrap_or(0) - } -} - -#[derive(Serialize, Copy, Clone, PartialEq, Eq, TryFromPrimitive, Hash)] -#[repr(u8)] -pub enum BridgeActionType { - TokenTransfer = 0, - UpdateCommitteeBlocklist = 1, - EmergencyButton = 2, - LimitUpdate = 3, - AssetPriceUpdate = 4, - EvmContractUpgrade = 5, - AddTokensOnIota = 6, - AddTokensOnEvm = 7, -} - -#[derive(Clone, PartialEq, Eq)] -pub struct BridgeActionKey { - pub action_type: BridgeActionType, - pub chain_id: BridgeChainId, - pub seq_num: u64, -} - -impl Debug for BridgeActionKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "BridgeActionKey({},{},{})", - self.action_type as u8, self.chain_id as u8, self.seq_num - ) - } -} - -#[derive(Debug, PartialEq, Eq, Clone, TryFromPrimitive)] -#[repr(u8)] -pub enum BridgeActionStatus { - Pending = 0, - Approved = 1, - Claimed = 2, - NotFound = 3, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct IotaToEthBridgeAction { - // Digest of the transaction where the event was emitted - pub iota_tx_digest: TransactionDigest, - // The index of the event in the transaction - pub iota_tx_event_index: u16, - pub iota_bridge_event: EmittedIotaToEthTokenBridgeV1, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct EthToIotaBridgeAction { - // Digest of the transaction where the event was emitted - pub eth_tx_hash: EthTransactionHash, - // The index of the event in the transaction - pub eth_event_index: u16, - pub eth_bridge_event: EthToIotaTokenBridgeV1, -} - -#[derive( - Debug, - Serialize, - Deserialize, - PartialEq, - Eq, - Clone, - Copy, - TryFromPrimitive, - Hash, - clap::ValueEnum, -)] -#[repr(u8)] -pub enum BlocklistType { - Blocklist = 0, - Unblocklist = 1, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct BlocklistCommitteeAction { - pub nonce: u64, - pub chain_id: BridgeChainId, - pub blocklist_type: BlocklistType, - pub members_to_update: Vec, -} - -#[derive( - Debug, - Serialize, - Deserialize, - PartialEq, - Eq, - Clone, - Copy, - TryFromPrimitive, - Hash, - clap::ValueEnum, -)] -#[repr(u8)] -pub enum EmergencyActionType { - Pause = 0, - Unpause = 1, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct EmergencyAction { - pub nonce: u64, - pub chain_id: BridgeChainId, - pub action_type: EmergencyActionType, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct LimitUpdateAction { - pub nonce: u64, - // The chain id that will receive this signed action. It's also the destination chain id - // for the limit update. For example, if chain_id is EthMainnet and sending_chain_id is - // IotaMainnet, it means we want to update the limit for the IotaMainnet to EthMainnet - // route. - pub chain_id: BridgeChainId, - // The sending chain id for the limit update. - pub sending_chain_id: BridgeChainId, - // 4 decimal places, namely 1 USD = 10000 - pub new_usd_limit: u64, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct AssetPriceUpdateAction { - pub nonce: u64, - pub chain_id: BridgeChainId, - pub token_id: u8, - pub new_usd_price: u64, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct EvmContractUpgradeAction { - pub nonce: u64, - pub chain_id: BridgeChainId, - pub proxy_address: EthAddress, - pub new_impl_address: EthAddress, - pub call_data: Vec, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct AddTokensOnIotaAction { - pub nonce: u64, - pub chain_id: BridgeChainId, - pub native: bool, - pub token_ids: Vec, - pub token_type_names: Vec, - pub token_prices: Vec, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct AddTokensOnEvmAction { - pub nonce: u64, - pub chain_id: BridgeChainId, - pub native: bool, - pub token_ids: Vec, - pub token_addresses: Vec, - pub token_iota_decimals: Vec, - pub token_prices: Vec, -} - -/// The type of actions Bridge Committee verify and sign off to execution. -/// Its relationship with BridgeEvent is similar to the relationship between -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] -#[enum_dispatch(BridgeMessageEncoding)] -pub enum BridgeAction { - /// IOTA to Eth bridge action - IotaToEthBridgeAction(IotaToEthBridgeAction), - /// Eth to iota bridge action - EthToIotaBridgeAction(EthToIotaBridgeAction), - BlocklistCommitteeAction(BlocklistCommitteeAction), - EmergencyAction(EmergencyAction), - LimitUpdateAction(LimitUpdateAction), - AssetPriceUpdateAction(AssetPriceUpdateAction), - EvmContractUpgradeAction(EvmContractUpgradeAction), - AddTokensOnIotaAction(AddTokensOnIotaAction), - AddTokensOnEvmAction(AddTokensOnEvmAction), -} - -impl BridgeAction { - // Digest of BridgeAction (with Keccak256 hasher) - pub fn digest(&self) -> BridgeActionDigest { - let mut hasher = Keccak256::default(); - hasher.update(self.to_bytes()); - BridgeActionDigest::new(hasher.finalize().into()) - } - - pub fn key(&self) -> BridgeActionKey { - BridgeActionKey { - action_type: self.action_type(), - chain_id: self.chain_id(), - seq_num: self.seq_number(), - } - } - - pub fn chain_id(&self) -> BridgeChainId { - match self { - BridgeAction::IotaToEthBridgeAction(a) => a.iota_bridge_event.iota_chain_id, - BridgeAction::EthToIotaBridgeAction(a) => a.eth_bridge_event.eth_chain_id, - BridgeAction::BlocklistCommitteeAction(a) => a.chain_id, - BridgeAction::EmergencyAction(a) => a.chain_id, - BridgeAction::LimitUpdateAction(a) => a.chain_id, - BridgeAction::AssetPriceUpdateAction(a) => a.chain_id, - BridgeAction::EvmContractUpgradeAction(a) => a.chain_id, - BridgeAction::AddTokensOnIotaAction(a) => a.chain_id, - BridgeAction::AddTokensOnEvmAction(a) => a.chain_id, - } - } - - pub fn is_governace_action(&self) -> bool { - match self.action_type() { - BridgeActionType::TokenTransfer => false, - BridgeActionType::UpdateCommitteeBlocklist => true, - BridgeActionType::EmergencyButton => true, - BridgeActionType::LimitUpdate => true, - BridgeActionType::AssetPriceUpdate => true, - BridgeActionType::EvmContractUpgrade => true, - BridgeActionType::AddTokensOnIota => true, - BridgeActionType::AddTokensOnEvm => true, - } - } - - // Also called `message_type` - pub fn action_type(&self) -> BridgeActionType { - match self { - BridgeAction::IotaToEthBridgeAction(_) => BridgeActionType::TokenTransfer, - BridgeAction::EthToIotaBridgeAction(_) => BridgeActionType::TokenTransfer, - BridgeAction::BlocklistCommitteeAction(_) => BridgeActionType::UpdateCommitteeBlocklist, - BridgeAction::EmergencyAction(_) => BridgeActionType::EmergencyButton, - BridgeAction::LimitUpdateAction(_) => BridgeActionType::LimitUpdate, - BridgeAction::AssetPriceUpdateAction(_) => BridgeActionType::AssetPriceUpdate, - BridgeAction::EvmContractUpgradeAction(_) => BridgeActionType::EvmContractUpgrade, - BridgeAction::AddTokensOnIotaAction(_) => BridgeActionType::AddTokensOnIota, - BridgeAction::AddTokensOnEvmAction(_) => BridgeActionType::AddTokensOnEvm, - } - } - - // Also called `nonce` - pub fn seq_number(&self) -> u64 { - match self { - BridgeAction::IotaToEthBridgeAction(a) => a.iota_bridge_event.nonce, - BridgeAction::EthToIotaBridgeAction(a) => a.eth_bridge_event.nonce, - BridgeAction::BlocklistCommitteeAction(a) => a.nonce, - BridgeAction::EmergencyAction(a) => a.nonce, - BridgeAction::LimitUpdateAction(a) => a.nonce, - BridgeAction::AssetPriceUpdateAction(a) => a.nonce, - BridgeAction::EvmContractUpgradeAction(a) => a.nonce, - BridgeAction::AddTokensOnIotaAction(a) => a.nonce, - BridgeAction::AddTokensOnEvmAction(a) => a.nonce, - } - } - - pub fn approval_threshold(&self) -> u64 { - match self { - BridgeAction::IotaToEthBridgeAction(_) => APPROVAL_THRESHOLD_TOKEN_TRANSFER, - BridgeAction::EthToIotaBridgeAction(_) => APPROVAL_THRESHOLD_TOKEN_TRANSFER, - BridgeAction::BlocklistCommitteeAction(_) => APPROVAL_THRESHOLD_COMMITTEE_BLOCKLIST, - BridgeAction::EmergencyAction(a) => match a.action_type { - EmergencyActionType::Pause => APPROVAL_THRESHOLD_EMERGENCY_PAUSE, - EmergencyActionType::Unpause => APPROVAL_THRESHOLD_EMERGENCY_UNPAUSE, - }, - BridgeAction::LimitUpdateAction(_) => APPROVAL_THRESHOLD_LIMIT_UPDATE, - BridgeAction::AssetPriceUpdateAction(_) => APPROVAL_THRESHOLD_ASSET_PRICE_UPDATE, - BridgeAction::EvmContractUpgradeAction(_) => APPROVAL_THRESHOLD_EVM_CONTRACT_UPGRADE, - BridgeAction::AddTokensOnIotaAction(_) => APPROVAL_THRESHOLD_ADD_TOKENS_ON_IOTA, - BridgeAction::AddTokensOnEvmAction(_) => APPROVAL_THRESHOLD_ADD_TOKENS_ON_EVM, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct BridgeActionDigest(Digest); - -impl BridgeActionDigest { - pub const fn new(digest: [u8; 32]) -> Self { - Self(Digest::new(digest)) - } -} - -#[derive(Debug, Clone)] -pub struct BridgeCommitteeValiditySignInfo { - pub signatures: BTreeMap, -} - -pub type SignedBridgeAction = Envelope; -pub type VerifiedSignedBridgeAction = VerifiedEnvelope; -pub type CertifiedBridgeAction = Envelope; -pub type VerifiedCertifiedBridgeAction = - VerifiedEnvelope; - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct BridgeEventDigest(Digest); - -impl BridgeEventDigest { - pub const fn new(digest: [u8; 32]) -> Self { - Self(Digest::new(digest)) - } -} - -impl Message for BridgeAction { - type DigestType = BridgeEventDigest; - - // this is not encoded in message today - const SCOPE: IntentScope = IntentScope::BridgeEventUnused; - - // this is not used today - fn digest(&self) -> Self::DigestType { - unreachable!("BridgeEventDigest is not used today") - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EthLog { - pub block_number: u64, - pub tx_hash: H256, - pub log_index_in_tx: u16, - pub log: Log, -} - -/// The version of EthLog that does not have -/// `log_index_in_tx`. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct RawEthLog { - pub block_number: u64, - pub tx_hash: H256, - pub log: Log, -} - -pub trait EthEvent { - fn block_number(&self) -> u64; - fn tx_hash(&self) -> H256; - fn log(&self) -> &Log; -} - -impl EthEvent for EthLog { - fn block_number(&self) -> u64 { - self.block_number - } - fn tx_hash(&self) -> H256 { - self.tx_hash - } - fn log(&self) -> &Log { - &self.log - } -} - -impl EthEvent for RawEthLog { - fn block_number(&self) -> u64 { - self.block_number - } - fn tx_hash(&self) -> H256 { - self.tx_hash - } - fn log(&self) -> &Log { - &self.log - } -} - -/// Check if the bridge route is valid -/// Only mainnet can bridge to mainnet, other than that we do not care. -pub fn is_route_valid(one: BridgeChainId, other: BridgeChainId) -> bool { - if one.is_iota_chain() && other.is_iota_chain() { - return false; - } - if !one.is_iota_chain() && !other.is_iota_chain() { - return false; - } - if one == BridgeChainId::EthMainnet { - return other == BridgeChainId::IotaMainnet; - } - if one == BridgeChainId::IotaMainnet { - return other == BridgeChainId::EthMainnet; - } - if other == BridgeChainId::EthMainnet { - return one == BridgeChainId::IotaMainnet; - } - if other == BridgeChainId::IotaMainnet { - return one == BridgeChainId::EthMainnet; - } - true -} - -// Sanitized version of MoveTypeParsedTokenTransferMessage -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct ParsedTokenTransferMessage { - pub message_version: u8, - pub seq_num: u64, - pub source_chain: BridgeChainId, - pub payload: Vec, - pub parsed_payload: MoveTypeTokenTransferPayload, -} - -impl TryFrom for ParsedTokenTransferMessage { - type Error = BridgeError; - - fn try_from(message: MoveTypeParsedTokenTransferMessage) -> BridgeResult { - let source_chain = BridgeChainId::try_from(message.source_chain).map_err(|_e| { - BridgeError::Generic(format!( - "Failed to convert MoveTypeParsedTokenTransferMessage to ParsedTokenTransferMessage. Failed to convert source chain {} to BridgeChainId", - message.source_chain, - )) - })?; - Ok(Self { - message_version: message.message_version, - seq_num: message.seq_num, - source_chain, - payload: message.payload, - parsed_payload: message.parsed_payload, - }) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashSet; - - use ethers::types::Address as EthAddress; - use fastcrypto::traits::KeyPair; - use iota_types::{bridge::TOKEN_ID_BTC, crypto::get_key_pair}; - - use super::*; - use crate::test_utils::{ - get_test_authority_and_key, get_test_eth_to_iota_bridge_action, - get_test_iota_to_eth_bridge_action, - }; - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_committee_construction() -> anyhow::Result<()> { - let (mut authority, _, _) = get_test_authority_and_key(8000, 9999); - // This is ok - let _ = BridgeCommittee::new(vec![authority.clone()]).unwrap(); - - // This is not ok - total voting power < BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER - authority.voting_power = BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER - 1; - let _ = BridgeCommittee::new(vec![authority.clone()]).unwrap_err(); - - // This is not ok - total voting power > BRIDGE_COMMITTEE_MAXIMAL_VOTING_POWER - authority.voting_power = BRIDGE_COMMITTEE_MAXIMAL_VOTING_POWER + 1; - let _ = BridgeCommittee::new(vec![authority.clone()]).unwrap_err(); - - // This is ok - authority.voting_power = 5000; - let mut authority_2 = authority.clone(); - let (_, kp): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); - let pubkey = kp.public().clone(); - authority_2.pubkey = pubkey.clone(); - let _ = BridgeCommittee::new(vec![authority.clone(), authority_2.clone()]).unwrap(); - - // This is not ok - duplicate pub key - authority_2.pubkey = authority.pubkey.clone(); - let _ = BridgeCommittee::new(vec![authority.clone(), authority.clone()]).unwrap_err(); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_committee_total_blocklisted_stake() -> anyhow::Result<()> { - let (mut authority1, _, _) = get_test_authority_and_key(10000, 9999); - assert_eq!( - BridgeCommittee::new(vec![authority1.clone()]) - .unwrap() - .total_blocklisted_stake(), - 0 - ); - authority1.voting_power = 6000; - - let (mut authority2, _, _) = get_test_authority_and_key(4000, 9999); - authority2.is_blocklisted = true; - assert_eq!( - BridgeCommittee::new(vec![authority1.clone(), authority2.clone()]) - .unwrap() - .total_blocklisted_stake(), - 4000 - ); - - authority1.voting_power = 7000; - authority2.voting_power = 2000; - let (mut authority3, _, _) = get_test_authority_and_key(1000, 9999); - authority3.is_blocklisted = true; - assert_eq!( - BridgeCommittee::new(vec![authority1, authority2, authority3]) - .unwrap() - .total_blocklisted_stake(), - 3000 - ); - - Ok(()) - } - - // Regression test to avoid accidentally change to approval threshold - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_action_approval_threshold_regression_test() -> anyhow::Result<()> { - let action = get_test_iota_to_eth_bridge_action(None, None, None, None, None, None, None); - assert_eq!(action.approval_threshold(), 3334); - - let action = get_test_eth_to_iota_bridge_action(None, None, None, None); - assert_eq!(action.approval_threshold(), 3334); - - let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { - nonce: 94, - chain_id: BridgeChainId::EthSepolia, - blocklist_type: BlocklistType::Unblocklist, - members_to_update: vec![], - }); - assert_eq!(action.approval_threshold(), 5001); - - let action = BridgeAction::EmergencyAction(EmergencyAction { - nonce: 56, - chain_id: BridgeChainId::EthSepolia, - action_type: EmergencyActionType::Pause, - }); - assert_eq!(action.approval_threshold(), 450); - - let action = BridgeAction::EmergencyAction(EmergencyAction { - nonce: 56, - chain_id: BridgeChainId::EthSepolia, - action_type: EmergencyActionType::Unpause, - }); - assert_eq!(action.approval_threshold(), 5001); - - let action = BridgeAction::LimitUpdateAction(LimitUpdateAction { - nonce: 15, - chain_id: BridgeChainId::IotaCustom, - sending_chain_id: BridgeChainId::EthCustom, - new_usd_limit: 1_000_000 * USD_MULTIPLIER, - }); - assert_eq!(action.approval_threshold(), 5001); - - let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction { - nonce: 266, - chain_id: BridgeChainId::IotaCustom, - token_id: TOKEN_ID_BTC, - new_usd_price: 100_000 * USD_MULTIPLIER, - }); - assert_eq!(action.approval_threshold(), 5001); - - let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction { - nonce: 123, - chain_id: BridgeChainId::EthCustom, - proxy_address: EthAddress::repeat_byte(6), - new_impl_address: EthAddress::repeat_byte(9), - call_data: vec![], - }); - assert_eq!(action.approval_threshold(), 5001); - Ok(()) - } - - #[test] - #[ignore = "https://github.com/iotaledger/iota/issues/3224"] - fn test_bridge_committee_filter_blocklisted_authorities() -> anyhow::Result<()> { - // Note: today BridgeCommittee does not shuffle authorities - let (authority1, _, _) = get_test_authority_and_key(5000, 9999); - let (mut authority2, _, _) = get_test_authority_and_key(3000, 9999); - authority2.is_blocklisted = true; - let (authority3, _, _) = get_test_authority_and_key(2000, 9999); - let committee = BridgeCommittee::new(vec![ - authority1.clone(), - authority2.clone(), - authority3.clone(), - ]) - .unwrap(); - - // exclude authority2 - let result = committee - .shuffle_by_stake(None, None) - .into_iter() - .collect::>(); - assert_eq!( - HashSet::from_iter(vec![authority1.pubkey_bytes(), authority3.pubkey_bytes()]), - result - ); - - // exclude authority2 and authority3 - let result = committee - .shuffle_by_stake( - None, - Some( - &[authority1.pubkey_bytes(), authority2.pubkey_bytes()] - .iter() - .cloned() - .collect(), - ), - ) - .into_iter() - .collect::>(); - assert_eq!(HashSet::from_iter(vec![authority1.pubkey_bytes()]), result); - - Ok(()) - } -} diff --git a/crates/iota-bridge/src/utils.rs b/crates/iota-bridge/src/utils.rs deleted file mode 100644 index 4f342e6c4ff..00000000000 --- a/crates/iota-bridge/src/utils.rs +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{path::PathBuf, str::FromStr, sync::Arc}; - -use anyhow::anyhow; -use ethers::{ - core::k256::ecdsa::SigningKey, - middleware::SignerMiddleware, - prelude::*, - providers::{Http, Provider}, - signers::Wallet, - types::Address as EthAddress, -}; -use fastcrypto::{ - ed25519::Ed25519KeyPair, - encoding::{Encoding, Hex}, - secp256k1::Secp256k1KeyPair, - traits::{EncodeDecodeBase64, KeyPair}, -}; -use futures::future::join_all; -use iota_config::Config; -use iota_json_rpc_types::{ - IotaExecutionStatus, IotaTransactionBlockEffectsAPI, IotaTransactionBlockResponseOptions, -}; -use iota_keys::keypair_file::read_key; -use iota_sdk::wallet_context::WalletContext; -use iota_test_transaction_builder::TestTransactionBuilder; -use iota_types::{ - BRIDGE_PACKAGE_ID, - base_types::IotaAddress, - bridge::{BRIDGE_MODULE_NAME, BRIDGE_REGISTER_FOREIGN_TOKEN_FUNCTION_NAME, BridgeChainId}, - crypto::{IotaKeyPair, ToFromBytes, get_key_pair}, - programmable_transaction_builder::ProgrammableTransactionBuilder, - transaction::{ObjectArg, TransactionData}, -}; - -use crate::{ - abi::{EthBridgeCommittee, EthBridgeConfig, EthBridgeLimiter, EthBridgeVault, EthIotaBridge}, - config::{BridgeNodeConfig, EthConfig, IotaConfig}, - crypto::{BridgeAuthorityKeyPair, BridgeAuthorityPublicKeyBytes}, - server::APPLICATION_JSON, - types::{AddTokensOnIotaAction, BridgeAction}, -}; - -pub type EthSigner = SignerMiddleware, Wallet>; - -pub struct EthBridgeContracts

{ - pub bridge: EthIotaBridge>, - pub committee: EthBridgeCommittee>, - pub limiter: EthBridgeLimiter>, - pub vault: EthBridgeVault>, - pub config: EthBridgeConfig>, -} - -/// Generate Bridge Authority key (Secp256k1KeyPair) and write to a file as -/// base64 encoded `privkey`. -pub fn generate_bridge_authority_key_and_write_to_file( - path: &PathBuf, -) -> Result<(), anyhow::Error> { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - let eth_address = BridgeAuthorityPublicKeyBytes::from(&kp.public).to_eth_address(); - println!( - "Corresponding Ethereum address by this ecdsa key: {:?}", - eth_address - ); - let iota_address = IotaAddress::from(&kp.public); - println!( - "Corresponding IOTA address by this ecdsa key: {:?}", - iota_address - ); - let base64_encoded = kp.encode_base64(); - std::fs::write(path, base64_encoded) - .map_err(|err| anyhow!("Failed to write encoded key to path: {:?}", err)) -} - -/// Generate Bridge Client key (Secp256k1KeyPair or Ed25519KeyPair) and write to -/// a file as base64 encoded `flag || privkey`. -pub fn generate_bridge_client_key_and_write_to_file( - path: &PathBuf, - use_ecdsa: bool, -) -> Result<(), anyhow::Error> { - let kp = if use_ecdsa { - let (_, kp): (_, Secp256k1KeyPair) = get_key_pair(); - let eth_address = BridgeAuthorityPublicKeyBytes::from(&kp.public).to_eth_address(); - println!( - "Corresponding Ethereum address by this ecdsa key: {:?}", - eth_address - ); - IotaKeyPair::from(kp) - } else { - let (_, kp): (_, Ed25519KeyPair) = get_key_pair(); - IotaKeyPair::from(kp) - }; - let iota_address = IotaAddress::from(&kp.public()); - println!("Corresponding IOTA address by this key: {:?}", iota_address); - - let contents = kp.encode_base64(); - std::fs::write(path, contents) - .map_err(|err| anyhow!("Failed to write encoded key to path: {:?}", err)) -} - -/// Given the address of IotaBridge Proxy, return the addresses of the -/// committee, limiter, vault, and config. -pub async fn get_eth_contract_addresses( - bridge_proxy_address: EthAddress, - provider: &Arc>, -) -> anyhow::Result<(EthAddress, EthAddress, EthAddress, EthAddress)> { - let iota_bridge = EthIotaBridge::new(bridge_proxy_address, provider.clone()); - let committee_address: EthAddress = iota_bridge.committee().call().await?; - let limiter_address: EthAddress = iota_bridge.limiter().call().await?; - let vault_address: EthAddress = iota_bridge.vault().call().await?; - let committee = EthBridgeCommittee::new(committee_address, provider.clone()); - let config_address: EthAddress = committee.config().call().await?; - - Ok(( - committee_address, - limiter_address, - vault_address, - config_address, - )) -} - -/// Given the address of IotaBridge Proxy, return the contracts of the -/// committee, limiter, vault, and config. -pub async fn get_eth_contracts( - bridge_proxy_address: EthAddress, - provider: &Arc>, -) -> anyhow::Result> { - let iota_bridge = EthIotaBridge::new(bridge_proxy_address, provider.clone()); - let committee_address: EthAddress = iota_bridge.committee().call().await?; - let limiter_address: EthAddress = iota_bridge.limiter().call().await?; - let vault_address: EthAddress = iota_bridge.vault().call().await?; - let committee = EthBridgeCommittee::new(committee_address, provider.clone()); - let config_address: EthAddress = committee.config().call().await?; - - let limiter = EthBridgeLimiter::new(limiter_address, provider.clone()); - let vault = EthBridgeVault::new(vault_address, provider.clone()); - let config = EthBridgeConfig::new(config_address, provider.clone()); - Ok(EthBridgeContracts { - bridge: iota_bridge, - committee, - limiter, - vault, - config, - }) -} - -/// Read bridge key from a file and print the corresponding information. -/// If `is_validator_key` is true, the key must be a Secp256k1 key. -pub fn examine_key(path: &PathBuf, is_validator_key: bool) -> Result<(), anyhow::Error> { - let key = read_key(path, is_validator_key)?; - let iota_address = IotaAddress::from(&key.public()); - let pubkey = match key { - IotaKeyPair::Secp256k1(kp) => { - println!("Secp256k1 key:"); - let eth_address = BridgeAuthorityPublicKeyBytes::from(&kp.public).to_eth_address(); - println!("Corresponding Ethereum address: {:x}", eth_address); - kp.public.as_bytes().to_vec() - } - IotaKeyPair::Ed25519(kp) => { - println!("Ed25519 key:"); - kp.public().as_bytes().to_vec() - } - IotaKeyPair::Secp256r1(kp) => { - println!("Secp256r1 key:"); - kp.public().as_bytes().to_vec() - } - }; - println!("Corresponding IOTA address: {:?}", iota_address); - println!("Corresponding PublicKey: {:?}", Hex::encode(pubkey)); - Ok(()) -} - -/// Generate Bridge Node Config template and write to a file. -pub fn generate_bridge_node_config_and_write_to_file( - path: &PathBuf, - run_client: bool, -) -> Result<(), anyhow::Error> { - let mut config = BridgeNodeConfig { - server_listen_port: 9191, - metrics_port: 9184, - bridge_authority_key_path: PathBuf::from("/path/to/your/bridge_authority_key"), - iota: IotaConfig { - iota_rpc_url: "your_iota_rpc_url".to_string(), - iota_bridge_chain_id: BridgeChainId::IotaTestnet as u8, - bridge_client_key_path: None, - bridge_client_gas_object: None, - iota_bridge_module_last_processed_event_id_override: None, - }, - eth: EthConfig { - eth_rpc_url: "your_eth_rpc_url".to_string(), - eth_bridge_proxy_address: "0x0000000000000000000000000000000000000000".to_string(), - eth_bridge_chain_id: BridgeChainId::EthSepolia as u8, - eth_contracts_start_block_fallback: Some(0), - eth_contracts_start_block_override: None, - }, - approved_governance_actions: vec![], - run_client, - db_path: None, - }; - if run_client { - config.iota.bridge_client_key_path = Some(PathBuf::from("/path/to/your/bridge_client_key")); - config.db_path = Some(PathBuf::from("/path/to/your/client_db")); - } - config.save(path) -} - -pub async fn get_eth_signer_client(url: &str, private_key_hex: &str) -> anyhow::Result { - let provider = Provider::::try_from(url) - .unwrap() - .interval(std::time::Duration::from_millis(2000)); - let chain_id = provider.get_chainid().await?; - let wallet = Wallet::from_str(private_key_hex) - .unwrap() - .with_chain_id(chain_id.as_u64()); - Ok(SignerMiddleware::new(provider, wallet)) -} - -pub async fn publish_and_register_coins_return_add_coins_on_iota_action( - wallet_context: &WalletContext, - bridge_arg: ObjectArg, - token_packages_dir: Vec, - token_ids: Vec, - token_prices: Vec, - nonce: u64, -) -> BridgeAction { - assert!(token_ids.len() == token_packages_dir.len()); - assert!(token_prices.len() == token_packages_dir.len()); - let iota_client = wallet_context.get_client().await.unwrap(); - let quorum_driver_api = Arc::new(iota_client.quorum_driver_api().clone()); - let rgp = iota_client - .governance_api() - .get_reference_gas_price() - .await - .unwrap(); - - let senders = wallet_context.get_addresses(); - // We want each sender to deal with one coin - assert!(senders.len() >= token_packages_dir.len()); - - // publish coin packages - let mut publish_tokens_tasks = vec![]; - - for (token_package_dir, sender) in token_packages_dir.iter().zip(senders.clone()) { - let gas = wallet_context - .get_one_gas_object_owned_by_address(sender) - .await - .unwrap() - .unwrap(); - let tx = TestTransactionBuilder::new(sender, gas, rgp) - .publish(token_package_dir.to_path_buf()) - .build(); - let tx = wallet_context.sign_transaction(&tx); - let api_clone = quorum_driver_api.clone(); - publish_tokens_tasks.push(tokio::spawn(async move { - api_clone.execute_transaction_block( - tx, - IotaTransactionBlockResponseOptions::new() - .with_effects() - .with_input() - .with_events() - .with_object_changes() - .with_balance_changes(), - Some(iota_types::quorum_driver_types::ExecuteTransactionRequestType::WaitForLocalExecution), - ).await - })); - } - let publish_coin_responses = join_all(publish_tokens_tasks).await; - - let mut token_type_names = vec![]; - let mut register_tasks = vec![]; - for (response, sender) in publish_coin_responses.into_iter().zip(senders.clone()) { - let response = response.unwrap().unwrap(); - assert_eq!( - response.effects.unwrap().status(), - &IotaExecutionStatus::Success - ); - let object_changes = response.object_changes.unwrap(); - let mut tc = None; - let mut type_ = None; - let mut uc = None; - let mut metadata = None; - for object_change in &object_changes { - if let o @ iota_json_rpc_types::ObjectChange::Created { object_type, .. } = - object_change - { - if object_type.name.as_str().starts_with("TreasuryCap") { - assert!(tc.is_none() && type_.is_none()); - tc = Some(o.clone()); - type_ = Some(object_type.type_params.first().unwrap().clone()); - } else if object_type.name.as_str().starts_with("UpgradeCap") { - assert!(uc.is_none()); - uc = Some(o.clone()); - } else if object_type.name.as_str().starts_with("CoinMetadata") { - assert!(metadata.is_none()); - metadata = Some(o.clone()); - } - } - } - let (tc, type_, uc, metadata) = - (tc.unwrap(), type_.unwrap(), uc.unwrap(), metadata.unwrap()); - - // register with the bridge - let mut builder = ProgrammableTransactionBuilder::new(); - let bridge_arg = builder.obj(bridge_arg).unwrap(); - let uc_arg = builder - .obj(ObjectArg::ImmOrOwnedObject(uc.object_ref())) - .unwrap(); - let tc_arg = builder - .obj(ObjectArg::ImmOrOwnedObject(tc.object_ref())) - .unwrap(); - let metadata_arg = builder - .obj(ObjectArg::ImmOrOwnedObject(metadata.object_ref())) - .unwrap(); - builder.programmable_move_call( - BRIDGE_PACKAGE_ID, - BRIDGE_MODULE_NAME.into(), - BRIDGE_REGISTER_FOREIGN_TOKEN_FUNCTION_NAME.into(), - vec![type_.clone()], - vec![bridge_arg, tc_arg, uc_arg, metadata_arg], - ); - let pt = builder.finish(); - let gas = wallet_context - .get_one_gas_object_owned_by_address(sender) - .await - .unwrap() - .unwrap(); - let tx = TransactionData::new_programmable(sender, vec![gas], pt, 1_000_000_000, rgp); - let signed_tx = wallet_context.sign_transaction(&tx); - let api_clone = quorum_driver_api.clone(); - register_tasks.push(async move { - api_clone - .execute_transaction_block( - signed_tx, - IotaTransactionBlockResponseOptions::new().with_effects(), - None, - ) - .await - }); - token_type_names.push(type_); - } - for response in join_all(register_tasks).await { - assert_eq!( - response.unwrap().effects.unwrap().status(), - &IotaExecutionStatus::Success - ); - } - - BridgeAction::AddTokensOnIotaAction(AddTokensOnIotaAction { - nonce, - chain_id: BridgeChainId::IotaCustom, - native: false, - token_ids, - token_type_names, - token_prices, - }) -} - -pub async fn wait_for_server_to_be_up(server_url: String, timeout_sec: u64) -> anyhow::Result<()> { - let now = std::time::Instant::now(); - loop { - if let Ok(true) = reqwest::Client::new() - .get(server_url.clone()) - .header(reqwest::header::ACCEPT, APPLICATION_JSON) - .send() - .await - .map(|res| res.status().is_success()) - { - break; - } - if now.elapsed().as_secs() > timeout_sec { - anyhow::bail!("Server is not up and running after {} seconds", timeout_sec); - } - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - } - Ok(()) -} diff --git a/crates/iota-cluster-test/src/cluster.rs b/crates/iota-cluster-test/src/cluster.rs index aac92a227f4..e3abf4c9fba 100644 --- a/crates/iota-cluster-test/src/cluster.rs +++ b/crates/iota-cluster-test/src/cluster.rs @@ -158,7 +158,7 @@ impl LocalNewCluster { #[async_trait] impl Cluster for LocalNewCluster { async fn start(options: &ClusterTestOpt) -> Result { - let data_ingestion_path = tempdir()?.into_path(); + let data_ingestion_path = tempdir()?.keep(); // TODO: options should contain port instead of address let fullnode_rpc_addr = options.fullnode_address.as_ref().map(|addr| { addr.parse::() diff --git a/crates/iota-config/src/genesis.rs b/crates/iota-config/src/genesis.rs index f96aadc0f12..1beac7a5feb 100644 --- a/crates/iota-config/src/genesis.rs +++ b/crates/iota-config/src/genesis.rs @@ -15,7 +15,7 @@ use fastcrypto::{ hash::HashFunction, }; use iota_types::{ - IOTA_BRIDGE_OBJECT_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, + GENESIS_IOTA_BRIDGE_OBJECT_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, authenticator_state::{AuthenticatorStateInner, get_authenticator_state}, base_types::{IotaAddress, ObjectID}, clock::Clock, @@ -341,7 +341,7 @@ impl UnsignedGenesis { pub fn has_bridge_object(&self) -> bool { self.objects() - .get_object(&IOTA_BRIDGE_OBJECT_ID) + .get_object(&GENESIS_IOTA_BRIDGE_OBJECT_ID) .expect("read from genesis cannot fail") .is_some() } diff --git a/crates/iota-core/src/authority.rs b/crates/iota-core/src/authority.rs index 35e934ef18e..892d54ec265 100644 --- a/crates/iota-core/src/authority.rs +++ b/crates/iota-core/src/authority.rs @@ -4502,57 +4502,6 @@ impl AuthorityState { Some(tx) } - #[instrument(level = "debug", skip_all)] - fn create_bridge_tx( - &self, - epoch_store: &Arc, - ) -> Option { - if !epoch_store.protocol_config().enable_bridge() { - info!("bridge not enabled"); - return None; - } - if epoch_store.bridge_exists() { - return None; - } - let tx = EndOfEpochTransactionKind::new_bridge_create(epoch_store.get_chain_identifier()); - info!("Creating Bridge Create tx"); - Some(tx) - } - - #[instrument(level = "debug", skip_all)] - fn init_bridge_committee_tx( - &self, - epoch_store: &Arc, - ) -> Option { - if !epoch_store.protocol_config().enable_bridge() { - info!("bridge not enabled"); - return None; - } - if !epoch_store - .protocol_config() - .should_try_to_finalize_bridge_committee() - { - info!("should not try to finalize bridge committee yet"); - return None; - } - // Only create this transaction if bridge exists - if !epoch_store.bridge_exists() { - return None; - } - - if epoch_store.bridge_committee_initiated() { - return None; - } - - let bridge_initial_shared_version = epoch_store - .epoch_start_config() - .bridge_obj_initial_shared_version() - .expect("initial version must exist"); - let tx = EndOfEpochTransactionKind::init_bridge_committee(bridge_initial_shared_version); - info!("Init Bridge committee tx"); - Some(tx) - } - /// Creates and execute the advance epoch transaction to effects without /// committing it to the database. The effects of the change epoch tx /// are only written to the database after a certified checkpoint has been @@ -4582,12 +4531,6 @@ impl AuthorityState { if let Some(tx) = self.create_authenticator_state_tx(epoch_store) { txns.push(tx); } - if let Some(tx) = self.create_bridge_tx(epoch_store) { - txns.push(tx); - } - if let Some(tx) = self.init_bridge_committee_tx(epoch_store) { - txns.push(tx); - } let next_epoch = epoch_store.epoch() + 1; diff --git a/crates/iota-core/src/authority/authority_per_epoch_store.rs b/crates/iota-core/src/authority/authority_per_epoch_store.rs index 7e3ccc525d6..a62387c8cda 100644 --- a/crates/iota-core/src/authority/authority_per_epoch_store.rs +++ b/crates/iota-core/src/authority/authority_per_epoch_store.rs @@ -1003,16 +1003,6 @@ impl AuthorityPerEpochStore { result } - pub fn bridge_exists(&self) -> bool { - self.epoch_start_configuration - .bridge_obj_initial_shared_version() - .is_some() - } - - pub fn bridge_committee_initiated(&self) -> bool { - self.epoch_start_configuration.bridge_committee_initiated() - } - pub fn get_parent_path(&self) -> PathBuf { self.parent_path.clone() } diff --git a/crates/iota-core/src/authority/authority_per_epoch_store_pruner.rs b/crates/iota-core/src/authority/authority_per_epoch_store_pruner.rs index f07627d6f29..45d7f3cdf3c 100644 --- a/crates/iota-core/src/authority/authority_per_epoch_store_pruner.rs +++ b/crates/iota-core/src/authority/authority_per_epoch_store_pruner.rs @@ -94,7 +94,7 @@ mod tests { #[test] fn test_basic_epoch_pruner() { - let parent_directory = tempfile::tempdir().unwrap().into_path(); + let parent_directory = tempfile::tempdir().unwrap().keep(); let directories: Vec<_> = vec!["epoch_0", "epoch_1", "epoch_3", "epoch_4"] .into_iter() .map(|name| parent_directory.join(name)) diff --git a/crates/iota-core/src/authority/authority_store_pruner.rs b/crates/iota-core/src/authority/authority_store_pruner.rs index 03197df2390..a88803d19a3 100644 --- a/crates/iota-core/src/authority/authority_store_pruner.rs +++ b/crates/iota-core/src/authority/authority_store_pruner.rs @@ -981,24 +981,24 @@ mod tests { // Tests pruning old version of live objects. #[tokio::test] async fn test_pruning_objects() { - let path = tempfile::tempdir().unwrap().into_path(); + let path = tempfile::tempdir().unwrap().keep(); let to_keep = run_pruner(&path, 3, 2, 1000, 0).await; assert_eq!( HashSet::from_iter(to_keep), get_keys_after_pruning(&path).unwrap() ); - run_pruner(&tempfile::tempdir().unwrap().into_path(), 3, 2, 1000, 0).await; + run_pruner(&tempfile::tempdir().unwrap().keep(), 3, 2, 1000, 0).await; } // Tests pruning deleted objects (object tombstones). #[tokio::test] async fn test_pruning_tombstones() { - let path = tempfile::tempdir().unwrap().into_path(); + let path = tempfile::tempdir().unwrap().keep(); let to_keep = run_pruner(&path, 0, 0, 1000, 0).await; assert_eq!(to_keep.len(), 0); assert_eq!(get_keys_after_pruning(&path).unwrap().len(), 0); - let path = tempfile::tempdir().unwrap().into_path(); + let path = tempfile::tempdir().unwrap().keep(); let to_keep = run_pruner(&path, 3, 0, 1000, 0).await; assert_eq!(to_keep.len(), 0); assert_eq!(get_keys_after_pruning(&path).unwrap().len(), 0); @@ -1006,7 +1006,7 @@ mod tests { #[tokio::test] async fn test_ref_count_pruning() { - let path = tempfile::tempdir().unwrap().into_path(); + let path = tempfile::tempdir().unwrap().keep(); run_pruner(&path, 3, 2, 1000, 1).await; { let perpetual_db = AuthorityPerpetualTables::open(&path, None); @@ -1015,7 +1015,7 @@ mod tests { assert_eq!(count, 1000); } - let path = tempfile::tempdir().unwrap().into_path(); + let path = tempfile::tempdir().unwrap().keep(); run_pruner(&path, 3, 0, 1000, 1).await; { let perpetual_db = AuthorityPerpetualTables::open(&path, None); @@ -1036,7 +1036,7 @@ mod tests { #[cfg(not(target_env = "msvc"))] #[tokio::test] async fn test_db_size_after_compaction() -> Result<(), anyhow::Error> { - let primary_path = tempfile::tempdir()?.into_path(); + let primary_path = tempfile::tempdir()?.keep(); let perpetual_db = Arc::new(AuthorityPerpetualTables::open(&primary_path, None)); let total_unique_object_ids = 10_000; let num_versions_per_object = 10; @@ -1199,7 +1199,7 @@ mod pprof_tests { // stack frame in it. let registry = Registry::default(); let metrics = AuthorityStorePruningMetrics::new(®istry); - let primary_path = tempfile::tempdir()?.into_path(); + let primary_path = tempfile::tempdir()?.keep(); let perpetual_db = Arc::new(AuthorityPerpetualTables::open(&primary_path, None)); let effects = insert_keys(&perpetual_db.objects)?; AuthorityStorePruner::prune_objects( @@ -1234,7 +1234,7 @@ mod pprof_tests { // `ignore_range_delete` set to true (default mode). We then record a // cpu profile of the `get()` calls and do not find any range fragmentation // stack frame in it. - let primary_path = tempfile::tempdir()?.into_path(); + let primary_path = tempfile::tempdir()?.keep(); let perpetual_db = Arc::new(AuthorityPerpetualTables::open(&primary_path, None)); let effects = insert_keys(&perpetual_db.objects)?; let registry = Registry::default(); diff --git a/crates/iota-core/src/authority/epoch_start_configuration.rs b/crates/iota-core/src/authority/epoch_start_configuration.rs index 26df7d243ae..39fe560bdb1 100644 --- a/crates/iota-core/src/authority/epoch_start_configuration.rs +++ b/crates/iota-core/src/authority/epoch_start_configuration.rs @@ -9,7 +9,6 @@ use iota_config::{ExecutionCacheConfig, NodeConfig}; use iota_types::{ authenticator_state::get_authenticator_state_obj_initial_shared_version, base_types::SequenceNumber, - bridge::{get_bridge_obj_initial_shared_version, is_bridge_committee_initiated}, deny_list_v1::get_deny_list_obj_initial_shared_version, epoch_data::EpochData, error::IotaResult, @@ -32,8 +31,6 @@ pub trait EpochStartConfigTrait { fn authenticator_obj_initial_shared_version(&self) -> Option; fn randomness_obj_initial_shared_version(&self) -> SequenceNumber; fn coin_deny_list_obj_initial_shared_version(&self) -> SequenceNumber; - fn bridge_obj_initial_shared_version(&self) -> Option; - fn bridge_committee_initiated(&self) -> bool; fn execution_cache_type(&self) -> ExecutionCacheConfigType { if self.flags().contains(&EpochFlag::WritebackCacheEnabled) { @@ -112,9 +109,6 @@ impl EpochStartConfiguration { get_randomness_state_obj_initial_shared_version(object_store)?; let coin_deny_list_obj_initial_shared_version = get_deny_list_obj_initial_shared_version(object_store)?; - let bridge_obj_initial_shared_version = - get_bridge_obj_initial_shared_version(object_store)?; - let bridge_committee_initiated = is_bridge_committee_initiated(object_store)?; Ok(Self::V1(EpochStartConfigurationV1 { system_state, epoch_digest, @@ -122,8 +116,6 @@ impl EpochStartConfiguration { authenticator_obj_initial_shared_version, randomness_obj_initial_shared_version, coin_deny_list_obj_initial_shared_version, - bridge_obj_initial_shared_version, - bridge_committee_initiated, })) } @@ -141,8 +133,6 @@ impl EpochStartConfiguration { randomness_obj_initial_shared_version: config.randomness_obj_initial_shared_version, coin_deny_list_obj_initial_shared_version: config .coin_deny_list_obj_initial_shared_version, - bridge_obj_initial_shared_version: config.bridge_obj_initial_shared_version, - bridge_committee_initiated: config.bridge_committee_initiated, }), _ => panic!( "This function is only implemented for the latest version of EpochStartConfiguration" @@ -172,8 +162,6 @@ pub struct EpochStartConfigurationV1 { authenticator_obj_initial_shared_version: Option, randomness_obj_initial_shared_version: SequenceNumber, coin_deny_list_obj_initial_shared_version: SequenceNumber, - bridge_obj_initial_shared_version: Option, - bridge_committee_initiated: bool, } impl EpochStartConfigTrait for EpochStartConfigurationV1 { @@ -200,12 +188,4 @@ impl EpochStartConfigTrait for EpochStartConfigurationV1 { fn coin_deny_list_obj_initial_shared_version(&self) -> SequenceNumber { self.coin_deny_list_obj_initial_shared_version } - - fn bridge_obj_initial_shared_version(&self) -> Option { - self.bridge_obj_initial_shared_version - } - - fn bridge_committee_initiated(&self) -> bool { - self.bridge_committee_initiated - } } diff --git a/crates/iota-core/src/checkpoints/mod.rs b/crates/iota-core/src/checkpoints/mod.rs index 536dd4251ae..07025b4369c 100644 --- a/crates/iota-core/src/checkpoints/mod.rs +++ b/crates/iota-core/src/checkpoints/mod.rs @@ -2161,7 +2161,7 @@ async fn diagnose_split_brain( let fork_logs_text = format!("{header}\n\n{diff_patches}\n\n"); let path = tempfile::tempdir() .expect("Failed to create tempdir") - .into_path() + .keep() .join(Path::new("checkpoint_fork_dump.txt")); let mut file = File::create(path).unwrap(); write!(file, "{}", fork_logs_text).unwrap(); diff --git a/crates/iota-core/src/epoch/consensus_store_pruner.rs b/crates/iota-core/src/epoch/consensus_store_pruner.rs index d0959ec7a6c..9dfc226255f 100644 --- a/crates/iota-core/src/epoch/consensus_store_pruner.rs +++ b/crates/iota-core/src/epoch/consensus_store_pruner.rs @@ -240,7 +240,7 @@ mod tests { let epoch_retention = 0; let current_epoch = 0; - let base_directory = tempfile::tempdir().unwrap().into_path(); + let base_directory = tempfile::tempdir().unwrap().keep(); create_epoch_directories(&base_directory, vec!["0", "other"]); @@ -264,7 +264,7 @@ mod tests { let epoch_retention = 1; let current_epoch = 100; - let base_directory = tempfile::tempdir().unwrap().into_path(); + let base_directory = tempfile::tempdir().unwrap().keep(); create_epoch_directories(&base_directory, vec!["97", "98", "99", "100", "other"]); @@ -290,7 +290,7 @@ mod tests { let epoch_retention = 0; let current_epoch = 100; - let base_directory = tempfile::tempdir().unwrap().into_path(); + let base_directory = tempfile::tempdir().unwrap().keep(); create_epoch_directories(&base_directory, vec!["97", "98", "99", "100", "other"]); @@ -314,7 +314,7 @@ mod tests { let epoch_retention = 1; let epoch_prune_period = std::time::Duration::from_millis(500); - let base_directory = tempfile::tempdir().unwrap().into_path(); + let base_directory = tempfile::tempdir().unwrap().keep(); // We create some directories up to epoch 100 create_epoch_directories(&base_directory, vec!["97", "98", "99", "100", "other"]); diff --git a/crates/iota-core/src/execution_cache.rs b/crates/iota-core/src/execution_cache.rs index 2ea779f3d9e..37d40d6f398 100644 --- a/crates/iota-core/src/execution_cache.rs +++ b/crates/iota-core/src/execution_cache.rs @@ -9,7 +9,6 @@ use iota_common::fatal; use iota_config::ExecutionCacheConfig; use iota_types::{ base_types::{EpochId, ObjectID, ObjectRef, SequenceNumber, VerifiedExecutionData}, - bridge::Bridge, digests::{TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest}, effects::{TransactionEffects, TransactionEvents}, error::{IotaError, IotaResult, UserInputError}, @@ -397,8 +396,6 @@ pub trait ObjectCacheRead: Send + Sync { fn get_iota_system_state_object_unsafe(&self) -> IotaResult; - fn get_bridge_object_unsafe(&self) -> IotaResult; - // Marker methods /// Get the marker at a specific version diff --git a/crates/iota-core/src/execution_cache/passthrough_cache.rs b/crates/iota-core/src/execution_cache/passthrough_cache.rs index 0261942423a..3fdc2cadcc8 100644 --- a/crates/iota-core/src/execution_cache/passthrough_cache.rs +++ b/crates/iota-core/src/execution_cache/passthrough_cache.rs @@ -10,7 +10,6 @@ use iota_storage::package_object_cache::PackageObjectCache; use iota_types::{ accumulator::Accumulator, base_types::{EpochId, ObjectID, ObjectRef, SequenceNumber, VerifiedExecutionData}, - bridge::{Bridge, get_bridge}, digests::{TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest}, effects::{TransactionEffects, TransactionEvents}, error::{IotaError, IotaResult}, @@ -162,10 +161,6 @@ impl ObjectCacheRead for PassthroughCache { get_iota_system_state(self) } - fn get_bridge_object_unsafe(&self) -> IotaResult { - get_bridge(self) - } - fn get_marker_value( &self, object_id: &ObjectID, diff --git a/crates/iota-core/src/execution_cache/proxy_cache.rs b/crates/iota-core/src/execution_cache/proxy_cache.rs index d4211acd36a..857b94c7922 100644 --- a/crates/iota-core/src/execution_cache/proxy_cache.rs +++ b/crates/iota-core/src/execution_cache/proxy_cache.rs @@ -8,7 +8,6 @@ use futures::{FutureExt, future::BoxFuture}; use iota_types::{ accumulator::Accumulator, base_types::{EpochId, ObjectID, ObjectRef, SequenceNumber, VerifiedExecutionData}, - bridge::Bridge, digests::{TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest}, effects::{TransactionEffects, TransactionEvents}, error::{IotaError, IotaResult}, @@ -178,10 +177,6 @@ impl ObjectCacheRead for ProxyCache { delegate_method!(self.get_iota_system_state_object_unsafe()) } - fn get_bridge_object_unsafe(&self) -> IotaResult { - delegate_method!(self.get_bridge_object_unsafe()) - } - fn get_marker_value( &self, object_id: &ObjectID, diff --git a/crates/iota-core/src/execution_cache/writeback_cache.rs b/crates/iota-core/src/execution_cache/writeback_cache.rs index e27a634a80e..6820865104a 100644 --- a/crates/iota-core/src/execution_cache/writeback_cache.rs +++ b/crates/iota-core/src/execution_cache/writeback_cache.rs @@ -60,7 +60,6 @@ use iota_macros::fail_point_async; use iota_types::{ accumulator::Accumulator, base_types::{EpochId, ObjectID, ObjectRef, SequenceNumber, VerifiedExecutionData}, - bridge::{Bridge, get_bridge}, digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest}, effects::{TransactionEffects, TransactionEvents}, error::{IotaError, IotaResult, UserInputError}, @@ -1523,10 +1522,6 @@ impl ObjectCacheRead for WritebackCache { get_iota_system_state(self) } - fn get_bridge_object_unsafe(&self) -> IotaResult { - get_bridge(self) - } - fn get_marker_value( &self, object_id: &ObjectID, diff --git a/crates/iota-data-ingestion-core/src/executor.rs b/crates/iota-data-ingestion-core/src/executor.rs index d13ef8c8ec1..2269b9243b6 100644 --- a/crates/iota-data-ingestion-core/src/executor.rs +++ b/crates/iota-data-ingestion-core/src/executor.rs @@ -337,7 +337,7 @@ pub async fn setup_single_workflow( executor.register(worker_pool).await?; Ok(( executor.run( - tempfile::tempdir()?.into_path(), + tempfile::tempdir()?.keep(), Some(remote_store_url), vec![], reader_options.unwrap_or_default(), diff --git a/crates/iota-data-ingestion-core/src/tests.rs b/crates/iota-data-ingestion-core/src/tests.rs index d3ddfe5eab7..f3a4fc21ec3 100644 --- a/crates/iota-data-ingestion-core/src/tests.rs +++ b/crates/iota-data-ingestion-core/src/tests.rs @@ -331,7 +331,7 @@ async fn graceful_shutdown_faulty_reducer() { fn temp_dir() -> std::path::PathBuf { tempfile::tempdir() .expect("Failed to open temporary directory") - .into_path() + .keep() } async fn create_executor_bundle() -> ExecutorBundle { diff --git a/crates/iota-data-ingestion/src/bin/archival_ingestion.rs b/crates/iota-data-ingestion/src/bin/archival_ingestion.rs index eb5fa595256..1d71a4a8085 100644 --- a/crates/iota-data-ingestion/src/bin/archival_ingestion.rs +++ b/crates/iota-data-ingestion/src/bin/archival_ingestion.rs @@ -61,7 +61,7 @@ async fn main() -> Result<()> { executor.register(worker_pool).await?; executor .run( - tempfile::tempdir()?.into_path(), + tempfile::tempdir()?.keep(), Some(config.remote_store_url), vec![], ReaderOptions::default(), diff --git a/crates/iota-e2e-tests/Cargo.toml b/crates/iota-e2e-tests/Cargo.toml index 6be3c394cff..79473133c8f 100644 --- a/crates/iota-e2e-tests/Cargo.toml +++ b/crates/iota-e2e-tests/Cargo.toml @@ -40,7 +40,6 @@ url.workspace = true # internal dependencies iota.workspace = true -iota-bridge.workspace = true iota-config.workspace = true iota-core.workspace = true iota-framework.workspace = true diff --git a/crates/iota-e2e-tests/tests/bridge_tests.rs b/crates/iota-e2e-tests/tests/bridge_tests.rs deleted file mode 100644 index eaeb6b96436..00000000000 --- a/crates/iota-e2e-tests/tests/bridge_tests.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use iota_bridge::{BRIDGE_ENABLE_PROTOCOL_VERSION, crypto::BridgeAuthorityKeyPair}; -use iota_json_rpc_api::BridgeReadApiClient; -use iota_macros::sim_test; -use iota_types::{ - IOTA_BRIDGE_OBJECT_ID, - bridge::{BridgeTrait, get_bridge}, - crypto::get_key_pair, -}; -use test_cluster::TestClusterBuilder; - -#[sim_test] -#[ignore = "https://github.com/iotaledger/iota/issues/3224"] -async fn test_create_bridge_state_object() { - let test_cluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION - 1).into()) - .with_epoch_duration_ms(20000) - .build() - .await; - - let handles = test_cluster.all_node_handles(); - - // no node has the bridge state object yet - for h in &handles { - h.with(|node| { - assert!( - node.state() - .get_object_cache_reader() - .get_latest_object_ref_or_tombstone(IOTA_BRIDGE_OBJECT_ID) - .unwrap() - .is_none() - ); - }); - } - - // wait until feature is enabled - test_cluster - .wait_for_protocol_version(BRIDGE_ENABLE_PROTOCOL_VERSION.into()) - .await; - // wait until next epoch - authenticator state object is created at the end of - // the first epoch in which it is supported. - test_cluster.wait_for_epoch_all_nodes(2).await; // protocol upgrade completes in epoch 1 - - for h in &handles { - h.with(|node| { - node.state() - .get_object_cache_reader() - .get_latest_object_ref_or_tombstone(IOTA_BRIDGE_OBJECT_ID) - .unwrap() - .expect("auth state object should exist"); - }); - } -} - -#[tokio::test] -#[ignore = "https://github.com/iotaledger/iota/issues/3224"] -async fn test_committee_registration() { - telemetry_subscribers::init_for_testing(); - let mut bridge_keys = vec![]; - for _ in 0..=3 { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - bridge_keys.push(kp); - } - let test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, false) - .await; - - let bridge = get_bridge( - test_cluster - .fullnode_handle - .iota_node - .state() - .get_object_store(), - ) - .unwrap(); - - // Member should be empty before end of epoch - assert!(bridge.committee().members.contents.is_empty()); - assert_eq!( - test_cluster.swarm.active_validators().count(), - bridge.committee().member_registrations.contents.len() - ); - - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; -} - -#[tokio::test] -#[ignore = "https://github.com/iotaledger/iota/issues/3224"] -async fn test_bridge_api_compatibility() { - let test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version(BRIDGE_ENABLE_PROTOCOL_VERSION.into()) - .build() - .await; - - test_cluster.force_new_epoch().await; - let client = test_cluster.rpc_client(); - client.get_latest_bridge().await.unwrap(); - // TODO: assert fields in summary - - client - .get_bridge_object_initial_shared_version() - .await - .unwrap(); -} diff --git a/crates/iota-e2e-tests/tests/protocol_version_tests.rs b/crates/iota-e2e-tests/tests/protocol_version_tests.rs index 9a1a1f61824..718c289d43b 100644 --- a/crates/iota-e2e-tests/tests/protocol_version_tests.rs +++ b/crates/iota-e2e-tests/tests/protocol_version_tests.rs @@ -66,9 +66,9 @@ mod sim_only_tests { use iota_macros::*; use iota_move_build::{BuildConfig, CompiledPackage}; use iota_types::{ - IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_BRIDGE_OBJECT_ID, IOTA_CLOCK_OBJECT_ID, - IOTA_FRAMEWORK_PACKAGE_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_PACKAGE_ID, - IOTA_SYSTEM_STATE_OBJECT_ID, MOVE_STDLIB_PACKAGE_ID, + IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_CLOCK_OBJECT_ID, IOTA_FRAMEWORK_PACKAGE_ID, + IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_PACKAGE_ID, IOTA_SYSTEM_STATE_OBJECT_ID, + MOVE_STDLIB_PACKAGE_ID, base_types::{ConciseableName, IotaAddress, ObjectID, ObjectRef, SequenceNumber}, digests::TransactionDigest, effects::{TransactionEffects, TransactionEffectsAPI}, @@ -427,7 +427,6 @@ mod sim_only_tests { IOTA_CLOCK_OBJECT_ID, IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, - IOTA_BRIDGE_OBJECT_ID, ] .contains(&obj.0); (!is_framework_obj).then_some(obj.0) @@ -779,11 +778,6 @@ mod sim_only_tests { #[sim_test] async fn test_safe_mode_recovery() { - let _guard = ProtocolConfig::apply_overrides_for_testing(|_, mut config| { - config.set_disable_bridge_for_testing(); - config - }); - override_iota_system_modules("mock_iota_systems/base"); let test_cluster = TestClusterBuilder::new() .with_epoch_duration_ms(20000) @@ -833,11 +827,6 @@ mod sim_only_tests { #[sim_test] async fn iota_system_mock_smoke_test() { - let _guard = ProtocolConfig::apply_overrides_for_testing(|_, mut config| { - config.set_disable_bridge_for_testing(); - config - }); - let test_cluster = TestClusterBuilder::new() .with_epoch_duration_ms(20000) .with_supported_protocol_versions(SupportedProtocolVersions::new_for_testing( @@ -852,11 +841,6 @@ mod sim_only_tests { #[sim_test] async fn iota_system_state_shallow_upgrade_test() { - let _guard = ProtocolConfig::apply_overrides_for_testing(|_, mut config| { - config.set_disable_bridge_for_testing(); - config - }); - override_iota_system_modules("mock_iota_systems/shallow_upgrade"); let test_cluster = TestClusterBuilder::new() @@ -889,11 +873,6 @@ mod sim_only_tests { #[sim_test] async fn iota_system_state_deep_upgrade_test() { - let _guard = ProtocolConfig::apply_overrides_for_testing(|_, mut config| { - config.set_disable_bridge_for_testing(); - config - }); - override_iota_system_modules("mock_iota_systems/deep_upgrade"); let test_cluster = TestClusterBuilder::new() diff --git a/crates/iota-e2e-tests/tests/traffic_control_tests.rs b/crates/iota-e2e-tests/tests/traffic_control_tests.rs index 03d13934614..65d482a29e1 100644 --- a/crates/iota-e2e-tests/tests/traffic_control_tests.rs +++ b/crates/iota-e2e-tests/tests/traffic_control_tests.rs @@ -409,7 +409,7 @@ async fn test_validator_traffic_control_error_delegated() -> Result<(), anyhow:: delegate_spam_blocking: true, delegate_error_blocking: false, destination_port: 8080, - drain_path: tempfile::tempdir().unwrap().into_path().join("drain"), + drain_path: tempfile::tempdir().unwrap().keep().join("drain"), drain_timeout_secs: 10, }; let network_config = ConfigBuilder::new_with_temp_dir() @@ -477,7 +477,7 @@ async fn test_fullnode_traffic_control_spam_delegated() -> Result<(), anyhow::Er delegate_spam_blocking: true, delegate_error_blocking: false, destination_port: 9000, - drain_path: tempfile::tempdir().unwrap().into_path().join("drain"), + drain_path: tempfile::tempdir().unwrap().keep().join("drain"), drain_timeout_secs: 10, }; let test_cluster = TestClusterBuilder::new() @@ -549,7 +549,7 @@ async fn test_traffic_control_dead_mans_switch() -> Result<(), anyhow::Error> { }; // sink all traffic to trigger dead mans switch - let drain_path = tempfile::tempdir().unwrap().into_path().join("drain"); + let drain_path = tempfile::tempdir().unwrap().keep().join("drain"); assert!(!drain_path.exists(), "Expected drain file to not yet exist",); let firewall_config = RemoteFirewallConfig { @@ -597,7 +597,7 @@ async fn test_traffic_control_dead_mans_switch() -> Result<(), anyhow::Error> { #[tokio::test] async fn test_traffic_control_manual_set_dead_mans_switch() -> Result<(), anyhow::Error> { - let drain_path = tempfile::tempdir().unwrap().into_path().join("drain"); + let drain_path = tempfile::tempdir().unwrap().keep().join("drain"); assert!(!drain_path.exists(), "Expected drain file to not yet exist",); File::create(&drain_path).expect("Failed to touch nodefw drain file"); assert!(drain_path.exists(), "Expected drain file to exist",); diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/9/0x0000000000000000000000000000000000000000000000000000000000000001 b/crates/iota-framework-snapshot/bytecode_snapshot/9/0x0000000000000000000000000000000000000000000000000000000000000001 new file mode 100644 index 00000000000..75cda69ec2f Binary files /dev/null and b/crates/iota-framework-snapshot/bytecode_snapshot/9/0x0000000000000000000000000000000000000000000000000000000000000001 differ diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/9/0x0000000000000000000000000000000000000000000000000000000000000002 b/crates/iota-framework-snapshot/bytecode_snapshot/9/0x0000000000000000000000000000000000000000000000000000000000000002 new file mode 100644 index 00000000000..f250a9d27c9 Binary files /dev/null and b/crates/iota-framework-snapshot/bytecode_snapshot/9/0x0000000000000000000000000000000000000000000000000000000000000002 differ diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/9/0x0000000000000000000000000000000000000000000000000000000000000003 b/crates/iota-framework-snapshot/bytecode_snapshot/9/0x0000000000000000000000000000000000000000000000000000000000000003 new file mode 100644 index 00000000000..c5ef9f62c62 Binary files /dev/null and b/crates/iota-framework-snapshot/bytecode_snapshot/9/0x0000000000000000000000000000000000000000000000000000000000000003 differ diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/9/0x000000000000000000000000000000000000000000000000000000000000107a b/crates/iota-framework-snapshot/bytecode_snapshot/9/0x000000000000000000000000000000000000000000000000000000000000107a new file mode 100644 index 00000000000..56d912f6c89 Binary files /dev/null and b/crates/iota-framework-snapshot/bytecode_snapshot/9/0x000000000000000000000000000000000000000000000000000000000000107a differ diff --git a/crates/iota-framework-snapshot/manifest.json b/crates/iota-framework-snapshot/manifest.json index 16ed26882c9..4195b1b0f9a 100644 --- a/crates/iota-framework-snapshot/manifest.json +++ b/crates/iota-framework-snapshot/manifest.json @@ -238,5 +238,30 @@ "id": "0x000000000000000000000000000000000000000000000000000000000000107a" } ] + }, + "9": { + "git_revision": "0303d9c9fc20", + "packages": [ + { + "name": "MoveStdlib", + "path": "crates/iota-framework/packages/move-stdlib", + "id": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "name": "Iota", + "path": "crates/iota-framework/packages/iota-framework", + "id": "0x0000000000000000000000000000000000000000000000000000000000000002" + }, + { + "name": "IotaSystem", + "path": "crates/iota-framework/packages/iota-system", + "id": "0x0000000000000000000000000000000000000000000000000000000000000003" + }, + { + "name": "Stardust", + "path": "crates/iota-framework/packages/stardust", + "id": "0x000000000000000000000000000000000000000000000000000000000000107a" + } + ] } } \ No newline at end of file diff --git a/crates/iota-framework-snapshot/src/lib.rs b/crates/iota-framework-snapshot/src/lib.rs index 3ec4aead9e4..71a3ecb5ab3 100644 --- a/crates/iota-framework-snapshot/src/lib.rs +++ b/crates/iota-framework-snapshot/src/lib.rs @@ -11,8 +11,8 @@ use std::{ use iota_framework::{SystemPackage, SystemPackageMetadata}; use iota_types::{ - BRIDGE_PACKAGE_ID, IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_PACKAGE_ID, MOVE_STDLIB_PACKAGE_ID, - STARDUST_PACKAGE_ID, base_types::ObjectID, + IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_PACKAGE_ID, MOVE_STDLIB_PACKAGE_ID, STARDUST_PACKAGE_ID, + base_types::ObjectID, }; use serde::{Deserialize, Serialize}; @@ -73,10 +73,19 @@ const SYSTEM_PACKAGE_PUBLISH_ORDER: &[ObjectID] = &[ MOVE_STDLIB_PACKAGE_ID, IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_PACKAGE_ID, - BRIDGE_PACKAGE_ID, STARDUST_PACKAGE_ID, ]; +/// Returns the list of system packages in the order they should be published. +/// If the protocol version is < 9 then include also the bridge package. +pub fn get_system_package_publish_order(protocol_version: u64) -> Vec { + let mut publish_order = SYSTEM_PACKAGE_PUBLISH_ORDER.to_vec(); + if protocol_version < 9 { + publish_order.insert(3, iota_types::GENESIS_BRIDGE_PACKAGE_ID); + } + publish_order +} + pub fn load_bytecode_snapshot_manifest() -> SnapshotManifest { let Ok(bytes) = fs::read(manifest_path()) else { return SnapshotManifest::default(); @@ -120,9 +129,10 @@ pub fn load_bytecode_snapshot(protocol_version: u64) -> anyhow::Result>()?; // system packages need to be restored in a specific order - assert!(snapshots.len() <= SYSTEM_PACKAGE_PUBLISH_ORDER.len()); + let snapshots_publish_order = get_system_package_publish_order(protocol_version); + assert!(snapshots.len() <= snapshots_publish_order.len()); let mut snapshot_objects = Vec::new(); - for package_id in SYSTEM_PACKAGE_PUBLISH_ORDER { + for package_id in &snapshots_publish_order { if let Some(object) = snapshots.remove(package_id) { snapshot_objects.push(object); } diff --git a/crates/iota-framework/packages/bridge/Move.toml b/crates/iota-framework/packages/bridge/Move.toml deleted file mode 100644 index 4d277fe5cf7..00000000000 --- a/crates/iota-framework/packages/bridge/Move.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "Bridge" -version = "0.0.1" -published-at = "0xb" -edition = "2024.beta" - -[dependencies] -MoveStdlib = { local = "../move-stdlib" } -Iota = { local = "../iota-framework" } -IotaSystem = { local = "../iota-system" } - -[addresses] -bridge = "0xb" diff --git a/crates/iota-framework/packages/bridge/sources/bridge.move b/crates/iota-framework/packages/bridge/sources/bridge.move deleted file mode 100644 index bd8c906ac65..00000000000 --- a/crates/iota-framework/packages/bridge/sources/bridge.move +++ /dev/null @@ -1,904 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridge::bridge; - -use bridge::chain_ids; -use bridge::committee::{Self, BridgeCommittee}; -use bridge::limiter::{Self, TransferLimiter}; -use bridge::message::{ - Self, - BridgeMessage, - BridgeMessageKey, - EmergencyOp, - UpdateAssetPrice, - UpdateBridgeLimit, - AddTokenOnIota, - ParsedTokenTransferMessage, - to_parsed_token_transfer_message -}; -use bridge::message_types; -use bridge::treasury::{Self, BridgeTreasury}; -use iota::address; -use iota::clock::Clock; -use iota::coin::{Coin, TreasuryCap, CoinMetadata}; -use iota::event::emit; -use iota::linked_table::{Self, LinkedTable}; -use iota::package::UpgradeCap; -use iota::vec_map::{Self, VecMap}; -use iota::versioned::{Self, Versioned}; -use iota_system::iota_system::IotaSystemState; - -const MESSAGE_VERSION: u8 = 1; - -// Transfer Status -const TRANSFER_STATUS_PENDING: u8 = 0; -const TRANSFER_STATUS_APPROVED: u8 = 1; -const TRANSFER_STATUS_CLAIMED: u8 = 2; -const TRANSFER_STATUS_NOT_FOUND: u8 = 3; - -const EVM_ADDRESS_LENGTH: u64 = 20; - -////////////////////////////////////////////////////// -// Types -// - -public struct Bridge has key { - id: UID, - inner: Versioned, -} - -public struct BridgeInner has store { - bridge_version: u64, - message_version: u8, - chain_id: u8, - // nonce for replay protection - // key: message type, value: next sequence number - sequence_nums: VecMap, - // committee - committee: BridgeCommittee, - // Bridge treasury for mint/burn bridged tokens - treasury: BridgeTreasury, - token_transfer_records: LinkedTable, - limiter: TransferLimiter, - paused: bool, -} - -public struct TokenDepositedEvent has copy, drop { - seq_num: u64, - source_chain: u8, - sender_address: vector, - target_chain: u8, - target_address: vector, - token_type: u8, - amount: u64, -} - -public struct EmergencyOpEvent has copy, drop { - frozen: bool, -} - -public struct BridgeRecord has drop, store { - message: BridgeMessage, - verified_signatures: Option>>, - claimed: bool, -} - -const EUnexpectedMessageType: u64 = 0; -const EUnauthorisedClaim: u64 = 1; -const EMalformedMessageError: u64 = 2; -const EUnexpectedTokenType: u64 = 3; -const EUnexpectedChainID: u64 = 4; -const ENotSystemAddress: u64 = 5; -const EUnexpectedSeqNum: u64 = 6; -const EWrongInnerVersion: u64 = 7; -const EBridgeUnavailable: u64 = 8; -const EUnexpectedOperation: u64 = 9; -const EInvariantIotaInitializedTokenTransferShouldNotBeClaimed: u64 = 10; -const EMessageNotFoundInRecords: u64 = 11; -const EUnexpectedMessageVersion: u64 = 12; -const EBridgeAlreadyPaused: u64 = 13; -const EBridgeNotPaused: u64 = 14; -const ETokenAlreadyClaimed: u64 = 15; -const EInvalidBridgeRoute: u64 = 16; -const EMustBeTokenMessage: u64 = 17; -const EInvalidEvmAddress: u64 = 18; -const ETokenValueIsZero: u64 = 19; - -const CURRENT_VERSION: u64 = 1; - -public struct TokenTransferApproved has copy, drop { - message_key: BridgeMessageKey, -} - -public struct TokenTransferClaimed has copy, drop { - message_key: BridgeMessageKey, -} - -public struct TokenTransferAlreadyApproved has copy, drop { - message_key: BridgeMessageKey, -} - -public struct TokenTransferAlreadyClaimed has copy, drop { - message_key: BridgeMessageKey, -} - -public struct TokenTransferLimitExceed has copy, drop { - message_key: BridgeMessageKey, -} - -////////////////////////////////////////////////////// -// Internal initialization functions -// - -// this method is called once in end of epoch tx to create the bridge -#[allow(unused_function)] -fun create(id: UID, chain_id: u8, ctx: &mut TxContext) { - assert!(ctx.sender() == @0x0, ENotSystemAddress); - let bridge_inner = BridgeInner { - bridge_version: CURRENT_VERSION, - message_version: MESSAGE_VERSION, - chain_id, - sequence_nums: vec_map::empty(), - committee: committee::create(ctx), - treasury: treasury::create(ctx), - token_transfer_records: linked_table::new(ctx), - limiter: limiter::new(), - paused: false, - }; - let bridge = Bridge { - id, - inner: versioned::create(CURRENT_VERSION, bridge_inner, ctx), - }; - transfer::share_object(bridge); -} - -#[allow(unused_function)] -fun init_bridge_committee( - bridge: &mut Bridge, - active_validator_voting_power: VecMap, - min_stake_participation_percentage: u64, - ctx: &TxContext, -) { - assert!(ctx.sender() == @0x0, ENotSystemAddress); - let inner = load_inner_mut(bridge); - if (inner.committee.committee_members().is_empty()) { - inner - .committee - .try_create_next_committee( - active_validator_voting_power, - min_stake_participation_percentage, - ctx, - ) - } -} - -////////////////////////////////////////////////////// -// Public functions -// - -public fun committee_registration( - bridge: &mut Bridge, - system_state: &mut IotaSystemState, - bridge_pubkey_bytes: vector, - http_rest_url: vector, - ctx: &TxContext, -) { - load_inner_mut(bridge) - .committee - .register(system_state, bridge_pubkey_bytes, http_rest_url, ctx); -} - -public fun update_node_url(bridge: &mut Bridge, new_url: vector, ctx: &TxContext) { - load_inner_mut(bridge).committee.update_node_url(new_url, ctx); -} - -public fun register_foreign_token( - bridge: &mut Bridge, - tc: TreasuryCap, - uc: UpgradeCap, - metadata: &CoinMetadata, -) { - load_inner_mut(bridge).treasury.register_foreign_token(tc, uc, metadata) -} - -// Create bridge request to send token to other chain, the request will be in -// pending state until approved -public fun send_token( - bridge: &mut Bridge, - target_chain: u8, - target_address: vector, - token: Coin, - ctx: &mut TxContext, -) { - let inner = load_inner_mut(bridge); - assert!(!inner.paused, EBridgeUnavailable); - assert!(chain_ids::is_valid_route(inner.chain_id, target_chain), EInvalidBridgeRoute); - assert!(target_address.length() == EVM_ADDRESS_LENGTH, EInvalidEvmAddress); - - let bridge_seq_num = inner.get_current_seq_num_and_increment(message_types::token()); - let token_id = inner.treasury.token_id(); - let token_amount = token.balance().value(); - assert!(token_amount > 0, ETokenValueIsZero); - - // create bridge message - let message = message::create_token_bridge_message( - inner.chain_id, - bridge_seq_num, - address::to_bytes(ctx.sender()), - target_chain, - target_address, - token_id, - token_amount, - ); - - // burn / escrow token, unsupported coins will fail in this step - inner.treasury.burn(token); - - // Store pending bridge request - inner - .token_transfer_records - .push_back( - message.key(), - BridgeRecord { - message, - verified_signatures: option::none(), - claimed: false, - }, - ); - - // emit event - emit(TokenDepositedEvent { - seq_num: bridge_seq_num, - source_chain: inner.chain_id, - sender_address: address::to_bytes(ctx.sender()), - target_chain, - target_address, - token_type: token_id, - amount: token_amount, - }); -} - -// Record bridge message approvals in IOTA, called by the bridge client -// If already approved, return early instead of aborting. -public fun approve_token_transfer( - bridge: &mut Bridge, - message: BridgeMessage, - signatures: vector>, -) { - let inner = load_inner_mut(bridge); - assert!(!inner.paused, EBridgeUnavailable); - // verify signatures - inner.committee.verify_signatures(message, signatures); - - assert!(message.message_type() == message_types::token(), EMustBeTokenMessage); - assert!(message.message_version() == MESSAGE_VERSION, EUnexpectedMessageVersion); - let token_payload = message.extract_token_bridge_payload(); - let target_chain = token_payload.token_target_chain(); - assert!( - message.source_chain() == inner.chain_id || target_chain == inner.chain_id, - EUnexpectedChainID, - ); - - let message_key = message.key(); - // retrieve pending message if source chain is IOTA, the initial message - // must exist on chain - if (message.source_chain() == inner.chain_id) { - let record = &mut inner.token_transfer_records[message_key]; - - assert!(record.message == message, EMalformedMessageError); - assert!(!record.claimed, EInvariantIotaInitializedTokenTransferShouldNotBeClaimed); - - // If record already has verified signatures, it means the message has been approved - // Then we exit early. - if (record.verified_signatures.is_some()) { - emit(TokenTransferAlreadyApproved { message_key }); - return - }; - // Store approval - record.verified_signatures = option::some(signatures) - } else { - // At this point, if this message is in token_transfer_records, we know - // it's already approved because we only add a message to token_transfer_records - // after verifying the signatures - if (inner.token_transfer_records.contains(message_key)) { - emit(TokenTransferAlreadyApproved { message_key }); - return - }; - // Store message and approval - inner - .token_transfer_records - .push_back( - message_key, - BridgeRecord { - message, - verified_signatures: option::some(signatures), - claimed: false, - }, - ); - }; - - emit(TokenTransferApproved { message_key }); -} - -// This function can only be called by the token recipient -// Abort if the token has already been claimed. -public fun claim_token( - bridge: &mut Bridge, - clock: &Clock, - source_chain: u8, - bridge_seq_num: u64, - ctx: &mut TxContext, -): Coin { - let (maybe_token, owner) = bridge.claim_token_internal( - clock, - source_chain, - bridge_seq_num, - ctx, - ); - // Only token owner can claim the token - assert!(ctx.sender() == owner, EUnauthorisedClaim); - assert!(maybe_token.is_some(), ETokenAlreadyClaimed); - maybe_token.destroy_some() -} - -// This function can be called by anyone to claim and transfer the token to the recipient -// If the token has already been claimed, it will return instead of aborting. -public fun claim_and_transfer_token( - bridge: &mut Bridge, - clock: &Clock, - source_chain: u8, - bridge_seq_num: u64, - ctx: &mut TxContext, -) { - let (token, owner) = bridge.claim_token_internal(clock, source_chain, bridge_seq_num, ctx); - if (token.is_some()) { - transfer::public_transfer(token.destroy_some(), owner) - } else { - token.destroy_none(); - }; -} - -public fun execute_system_message( - bridge: &mut Bridge, - message: BridgeMessage, - signatures: vector>, -) { - let message_type = message.message_type(); - - // TODO: test version mismatch - assert!(message.message_version() == MESSAGE_VERSION, EUnexpectedMessageVersion); - let inner = load_inner_mut(bridge); - - assert!(message.source_chain() == inner.chain_id, EUnexpectedChainID); - - // check system ops seq number and increment it - let expected_seq_num = inner.get_current_seq_num_and_increment(message_type); - assert!(message.seq_num() == expected_seq_num, EUnexpectedSeqNum); - - inner.committee.verify_signatures(message, signatures); - - if (message_type == message_types::emergency_op()) { - let payload = message.extract_emergency_op_payload(); - inner.execute_emergency_op(payload); - } else if (message_type == message_types::committee_blocklist()) { - let payload = message.extract_blocklist_payload(); - inner.committee.execute_blocklist(payload); - } else if (message_type == message_types::update_bridge_limit()) { - let payload = message.extract_update_bridge_limit(); - inner.execute_update_bridge_limit(payload); - } else if (message_type == message_types::update_asset_price()) { - let payload = message.extract_update_asset_price(); - inner.execute_update_asset_price(payload); - } else if (message_type == message_types::add_tokens_on_iota()) { - let payload = message.extract_add_tokens_on_iota(); - inner.execute_add_tokens_on_iota(payload); - } else { - abort EUnexpectedMessageType - }; -} - -////////////////////////////////////////////////////// -// DevInspect Functions for Read -// - -#[allow(unused_function)] -fun get_token_transfer_action_status(bridge: &Bridge, source_chain: u8, bridge_seq_num: u64): u8 { - let inner = load_inner(bridge); - let key = message::create_key( - source_chain, - message_types::token(), - bridge_seq_num, - ); - - if (!inner.token_transfer_records.contains(key)) { - return TRANSFER_STATUS_NOT_FOUND - }; - - let record = &inner.token_transfer_records[key]; - if (record.claimed) { - return TRANSFER_STATUS_CLAIMED - }; - - if (record.verified_signatures.is_some()) { - return TRANSFER_STATUS_APPROVED - }; - - TRANSFER_STATUS_PENDING -} - -#[allow(unused_function)] -fun get_token_transfer_action_signatures( - bridge: &Bridge, - source_chain: u8, - bridge_seq_num: u64, -): Option>> { - let inner = load_inner(bridge); - let key = message::create_key( - source_chain, - message_types::token(), - bridge_seq_num, - ); - - if (!inner.token_transfer_records.contains(key)) { - return option::none() - }; - - let record = &inner.token_transfer_records[key]; - record.verified_signatures -} - -////////////////////////////////////////////////////// -// Internal functions -// - -fun load_inner(bridge: &Bridge): &BridgeInner { - let version = bridge.inner.version(); - - // TODO: Replace this with a lazy update function when we add a new version of the inner object. - assert!(version == CURRENT_VERSION, EWrongInnerVersion); - let inner: &BridgeInner = bridge.inner.load_value(); - assert!(inner.bridge_version == version, EWrongInnerVersion); - inner -} - -fun load_inner_mut(bridge: &mut Bridge): &mut BridgeInner { - let version = bridge.inner.version(); - // TODO: Replace this with a lazy update function when we add a new version of the inner object. - assert!(version == CURRENT_VERSION, EWrongInnerVersion); - let inner: &mut BridgeInner = bridge.inner.load_value_mut(); - assert!(inner.bridge_version == version, EWrongInnerVersion); - inner -} - -// Claim token from approved bridge message -// Returns Some(Coin) if coin can be claimed. If already claimed, return None -fun claim_token_internal( - bridge: &mut Bridge, - clock: &Clock, - source_chain: u8, - bridge_seq_num: u64, - ctx: &mut TxContext, -): (Option>, address) { - let inner = load_inner_mut(bridge); - assert!(!inner.paused, EBridgeUnavailable); - - let key = message::create_key(source_chain, message_types::token(), bridge_seq_num); - assert!(inner.token_transfer_records.contains(key), EMessageNotFoundInRecords); - - // retrieve approved bridge message - let record = &mut inner.token_transfer_records[key]; - // ensure this is a token bridge message - assert!(&record.message.message_type() == message_types::token(), EUnexpectedMessageType); - // Ensure it's signed - assert!(record.verified_signatures.is_some(), EUnauthorisedClaim); - - // extract token message - let token_payload = record.message.extract_token_bridge_payload(); - // get owner address - let owner = address::from_bytes(token_payload.token_target_address()); - - // If already claimed, exit early - if (record.claimed) { - emit(TokenTransferAlreadyClaimed { message_key: key }); - return (option::none(), owner) - }; - - let target_chain = token_payload.token_target_chain(); - // ensure target chain matches bridge.chain_id - assert!(target_chain == inner.chain_id, EUnexpectedChainID); - - // TODO: why do we check validity of the route here? what if inconsistency? - // Ensure route is valid - // TODO: add unit tests - // `get_route` abort if route is invalid - let route = chain_ids::get_route(source_chain, target_chain); - // check token type - assert!( - treasury::token_id(&inner.treasury) == token_payload.token_type(), - EUnexpectedTokenType, - ); - - let amount = token_payload.token_amount(); - // Make sure transfer is within limit. - if ( - !inner - .limiter - .check_and_record_sending_transfer( - &inner.treasury, - clock, - route, - amount, - ) - ) { - emit(TokenTransferLimitExceed { message_key: key }); - return (option::none(), owner) - }; - - // claim from treasury - let token = inner.treasury.mint(amount, ctx); - - // Record changes - record.claimed = true; - emit(TokenTransferClaimed { message_key: key }); - - (option::some(token), owner) -} - -fun execute_emergency_op(inner: &mut BridgeInner, payload: EmergencyOp) { - let op = payload.emergency_op_type(); - if (op == message::emergency_op_pause()) { - assert!(!inner.paused, EBridgeAlreadyPaused); - inner.paused = true; - emit(EmergencyOpEvent { frozen: true }); - } else if (op == message::emergency_op_unpause()) { - assert!(inner.paused, EBridgeNotPaused); - inner.paused = false; - emit(EmergencyOpEvent { frozen: false }); - } else { - abort EUnexpectedOperation - }; -} - -fun execute_update_bridge_limit(inner: &mut BridgeInner, payload: UpdateBridgeLimit) { - let receiving_chain = payload.update_bridge_limit_payload_receiving_chain(); - assert!(receiving_chain == inner.chain_id, EUnexpectedChainID); - let route = chain_ids::get_route( - payload.update_bridge_limit_payload_sending_chain(), - receiving_chain, - ); - - inner - .limiter - .update_route_limit( - &route, - payload.update_bridge_limit_payload_limit(), - ) -} - -fun execute_update_asset_price(inner: &mut BridgeInner, payload: UpdateAssetPrice) { - inner - .treasury - .update_asset_notional_price( - payload.update_asset_price_payload_token_id(), - payload.update_asset_price_payload_new_price(), - ) -} - -fun execute_add_tokens_on_iota(inner: &mut BridgeInner, payload: AddTokenOnIota) { - // FIXME: assert native_token to be false and add test - let native_token = payload.is_native(); - let mut token_ids = payload.token_ids(); - let mut token_type_names = payload.token_type_names(); - let mut token_prices = payload.token_prices(); - - // Make sure token data is consistent - assert!(token_ids.length() == token_type_names.length(), EMalformedMessageError); - assert!(token_ids.length() == token_prices.length(), EMalformedMessageError); - - while (token_ids.length() > 0) { - let token_id = token_ids.pop_back(); - let token_type_name = token_type_names.pop_back(); - let token_price = token_prices.pop_back(); - inner.treasury.add_new_token(token_type_name, token_id, native_token, token_price) - } -} - -// Verify seq number matches the next expected seq number for the message type, -// and increment it. -fun get_current_seq_num_and_increment(bridge: &mut BridgeInner, msg_type: u8): u64 { - if (!bridge.sequence_nums.contains(&msg_type)) { - bridge.sequence_nums.insert(msg_type, 1); - return 0 - }; - - let entry = &mut bridge.sequence_nums[&msg_type]; - let seq_num = *entry; - *entry = seq_num + 1; - seq_num -} - -#[allow(unused_function)] -fun get_parsed_token_transfer_message( - bridge: &Bridge, - source_chain: u8, - bridge_seq_num: u64, -): Option { - let inner = load_inner(bridge); - let key = message::create_key( - source_chain, - message_types::token(), - bridge_seq_num, - ); - - if (!inner.token_transfer_records.contains(key)) { - return option::none() - }; - - let record = &inner.token_transfer_records[key]; - let message = &record.message; - option::some(to_parsed_token_transfer_message(message)) -} - -////////////////////////////////////////////////////// -// Test functions -// - -#[test_only] -public fun create_bridge_for_testing(id: UID, chain_id: u8, ctx: &mut TxContext) { - create(id, chain_id, ctx); -} - -#[test_only] -public fun new_for_testing(chain_id: u8, ctx: &mut TxContext): Bridge { - let id = object::new(ctx); - let bridge_inner = BridgeInner { - bridge_version: CURRENT_VERSION, - message_version: MESSAGE_VERSION, - chain_id, - sequence_nums: vec_map::empty(), - committee: committee::create(ctx), - treasury: treasury::create(ctx), - token_transfer_records: linked_table::new(ctx), - limiter: limiter::new(), - paused: false, - }; - let mut bridge = Bridge { - id, - inner: versioned::create(CURRENT_VERSION, bridge_inner, ctx), - }; - bridge.setup_treasury_for_testing(); - bridge -} - -#[test_only] -public fun setup_treasury_for_testing(bridge: &mut Bridge) { - bridge.load_inner_mut().treasury.setup_for_testing(); -} - -#[test_only] -public fun test_init_bridge_committee( - bridge: &mut Bridge, - active_validator_voting_power: VecMap, - min_stake_participation_percentage: u64, - ctx: &TxContext, -) { - init_bridge_committee( - bridge, - active_validator_voting_power, - min_stake_participation_percentage, - ctx, - ); -} - -#[test_only] -public fun new_bridge_record_for_testing( - message: BridgeMessage, - verified_signatures: Option>>, - claimed: bool, -): BridgeRecord { - BridgeRecord { - message, - verified_signatures, - claimed, - } -} - -#[test_only] -public fun test_load_inner_mut(bridge: &mut Bridge): &mut BridgeInner { - bridge.load_inner_mut() -} - -#[test_only] -public fun test_load_inner(bridge: &Bridge): &BridgeInner { - bridge.load_inner() -} - -#[test_only] -public fun test_get_token_transfer_action_status( - bridge: &mut Bridge, - source_chain: u8, - bridge_seq_num: u64, -): u8 { - bridge.get_token_transfer_action_status(source_chain, bridge_seq_num) -} - -#[test_only] -public fun test_get_token_transfer_action_signatures( - bridge: &mut Bridge, - source_chain: u8, - bridge_seq_num: u64, -): Option>> { - bridge.get_token_transfer_action_signatures(source_chain, bridge_seq_num) -} - -#[test_only] -public fun test_get_parsed_token_transfer_message( - bridge: &Bridge, - source_chain: u8, - bridge_seq_num: u64, -): Option { - bridge.get_parsed_token_transfer_message(source_chain, bridge_seq_num) -} - -#[test_only] -public fun inner_limiter(bridge_inner: &BridgeInner): &TransferLimiter { - &bridge_inner.limiter -} - -#[test_only] -public fun inner_treasury(bridge_inner: &BridgeInner): &BridgeTreasury { - &bridge_inner.treasury -} - -#[test_only] -public fun inner_treasury_mut(bridge_inner: &mut BridgeInner): &mut BridgeTreasury { - &mut bridge_inner.treasury -} - -#[test_only] -public fun inner_paused(bridge_inner: &BridgeInner): bool { - bridge_inner.paused -} - -#[test_only] -public fun inner_token_transfer_records( - bridge_inner: &BridgeInner, -): &LinkedTable { - &bridge_inner.token_transfer_records -} - -#[test_only] -public fun inner_token_transfer_records_mut( - bridge_inner: &mut BridgeInner, -): &mut LinkedTable { - &mut bridge_inner.token_transfer_records -} - -#[test_only] -public fun test_execute_emergency_op(bridge_inner: &mut BridgeInner, payload: EmergencyOp) { - bridge_inner.execute_emergency_op(payload) -} - -#[test_only] -public fun sequence_nums(bridge_inner: &BridgeInner): &VecMap { - &bridge_inner.sequence_nums -} - -#[test_only] -public fun assert_paused(bridge_inner: &BridgeInner, error: u64) { - assert!(bridge_inner.paused, error); -} - -#[test_only] -public fun assert_not_paused(bridge_inner: &BridgeInner, error: u64) { - assert!(!bridge_inner.paused, error); -} - -#[test_only] -public fun test_get_current_seq_num_and_increment( - bridge_inner: &mut BridgeInner, - msg_type: u8, -): u64 { - get_current_seq_num_and_increment(bridge_inner, msg_type) -} - -#[test_only] -public fun test_execute_update_bridge_limit(inner: &mut BridgeInner, payload: UpdateBridgeLimit) { - execute_update_bridge_limit(inner, payload) -} - -#[test_only] -public fun test_execute_update_asset_price(inner: &mut BridgeInner, payload: UpdateAssetPrice) { - execute_update_asset_price(inner, payload) -} - -#[test_only] -public fun transfer_status_pending(): u8 { - TRANSFER_STATUS_PENDING -} - -#[test_only] -public fun transfer_status_approved(): u8 { - TRANSFER_STATUS_APPROVED -} - -#[test_only] -public fun transfer_status_claimed(): u8 { - TRANSFER_STATUS_CLAIMED -} - -#[test_only] -public fun transfer_status_not_found(): u8 { - TRANSFER_STATUS_NOT_FOUND -} - -#[test_only] -public fun test_execute_add_tokens_on_iota(bridge: &mut Bridge, payload: AddTokenOnIota) { - let inner = load_inner_mut(bridge); - inner.execute_add_tokens_on_iota(payload); -} - -#[test_only] -public fun get_seq_num_for(bridge: &mut Bridge, message_type: u8): u64 { - let inner = load_inner_mut(bridge); - let seq_num = if (inner.sequence_nums.contains(&message_type)) { - inner.sequence_nums[&message_type] - } else { - inner.sequence_nums.insert(message_type, 0); - 0 - }; - seq_num -} - -#[test_only] -public fun get_seq_num_inc_for(bridge: &mut Bridge, message_type: u8): u64 { - let inner = load_inner_mut(bridge); - inner.get_current_seq_num_and_increment(message_type) -} - -#[test_only] -public fun transfer_approve_key(event: TokenTransferApproved): BridgeMessageKey { - event.message_key -} - -#[test_only] -public fun transfer_claimed_key(event: TokenTransferClaimed): BridgeMessageKey { - event.message_key -} - -#[test_only] -public fun transfer_already_approved_key(event: TokenTransferAlreadyApproved): BridgeMessageKey { - event.message_key -} - -#[test_only] -public fun transfer_already_claimed_key(event: TokenTransferAlreadyClaimed): BridgeMessageKey { - event.message_key -} - -#[test_only] -public fun transfer_limit_exceed_key(event: TokenTransferLimitExceed): BridgeMessageKey { - event.message_key -} - -#[test_only] -public fun unwrap_deposited_event( - event: TokenDepositedEvent, -): (u64, u8, vector, u8, vector, u8, u64) { - ( - event.seq_num, - event.source_chain, - event.sender_address, - event.target_chain, - event.target_address, - event.token_type, - event.amount, - ) -} - -#[test_only] -public fun unwrap_emergency_op_event(event: EmergencyOpEvent): bool { - event.frozen -} diff --git a/crates/iota-framework/packages/bridge/sources/chain_ids.move b/crates/iota-framework/packages/bridge/sources/chain_ids.move deleted file mode 100644 index 8373e39d5e5..00000000000 --- a/crates/iota-framework/packages/bridge/sources/chain_ids.move +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridge::chain_ids; - -// Chain IDs -const IotaMainnet: u8 = 0; -const IotaTestnet: u8 = 1; -const IotaCustom: u8 = 2; - -const EthMainnet: u8 = 10; -const EthSepolia: u8 = 11; -const EthCustom: u8 = 12; - -const EInvalidBridgeRoute: u64 = 0; - -////////////////////////////////////////////////////// -// Types -// - -public struct BridgeRoute has copy, drop, store { - source: u8, - destination: u8, -} - -////////////////////////////////////////////////////// -// Public functions -// - -public fun iota_mainnet(): u8 { IotaMainnet } - -public fun iota_testnet(): u8 { IotaTestnet } - -public fun iota_custom(): u8 { IotaCustom } - -public fun eth_mainnet(): u8 { EthMainnet } - -public fun eth_sepolia(): u8 { EthSepolia } - -public fun eth_custom(): u8 { EthCustom } - -public use fun route_source as BridgeRoute.source; - -public fun route_source(route: &BridgeRoute): &u8 { - &route.source -} - -public use fun route_destination as BridgeRoute.destination; - -public fun route_destination(route: &BridgeRoute): &u8 { - &route.destination -} - -public fun assert_valid_chain_id(id: u8) { - assert!( - id == IotaMainnet || - id == IotaTestnet || - id == IotaCustom || - id == EthMainnet || - id == EthSepolia || - id == EthCustom, - EInvalidBridgeRoute, - ) -} - -public fun valid_routes(): vector { - vector[ - BridgeRoute { source: IotaMainnet, destination: EthMainnet }, - BridgeRoute { source: EthMainnet, destination: IotaMainnet }, - BridgeRoute { source: IotaTestnet, destination: EthSepolia }, - BridgeRoute { source: IotaTestnet, destination: EthCustom }, - BridgeRoute { source: IotaCustom, destination: EthCustom }, - BridgeRoute { source: IotaCustom, destination: EthSepolia }, - BridgeRoute { source: EthSepolia, destination: IotaTestnet }, - BridgeRoute { source: EthSepolia, destination: IotaCustom }, - BridgeRoute { source: EthCustom, destination: IotaTestnet }, - BridgeRoute { source: EthCustom, destination: IotaCustom }, - ] -} - -public fun is_valid_route(source: u8, destination: u8): bool { - let route = BridgeRoute { source, destination }; - valid_routes().contains(&route) -} - -// Checks and return BridgeRoute if the route is supported by the bridge. -public fun get_route(source: u8, destination: u8): BridgeRoute { - let route = BridgeRoute { source, destination }; - assert!(valid_routes().contains(&route), EInvalidBridgeRoute); - route -} - -////////////////////////////////////////////////////// -// Test functions -// - -#[test] -fun test_chains_ok() { - assert_valid_chain_id(IotaMainnet); - assert_valid_chain_id(IotaTestnet); - assert_valid_chain_id(IotaCustom); - assert_valid_chain_id(EthMainnet); - assert_valid_chain_id(EthSepolia); - assert_valid_chain_id(EthCustom); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_chains_error() { - assert_valid_chain_id(100); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_iota_chains_error() { - // this will break if we add one more iota chain id and should be corrected - assert_valid_chain_id(4); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_eth_chains_error() { - // this will break if we add one more eth chain id and should be corrected - assert_valid_chain_id(13); -} - -#[test] -fun test_routes() { - let valid_routes = vector[ - BridgeRoute { source: IotaMainnet, destination: EthMainnet }, - BridgeRoute { source: EthMainnet, destination: IotaMainnet }, - BridgeRoute { source: IotaTestnet, destination: EthSepolia }, - BridgeRoute { source: IotaTestnet, destination: EthCustom }, - BridgeRoute { source: IotaCustom, destination: EthCustom }, - BridgeRoute { source: IotaCustom, destination: EthSepolia }, - BridgeRoute { source: EthSepolia, destination: IotaTestnet }, - BridgeRoute { source: EthSepolia, destination: IotaCustom }, - BridgeRoute { source: EthCustom, destination: IotaTestnet }, - BridgeRoute { source: EthCustom, destination: IotaCustom }, - ]; - let mut size = valid_routes.length(); - while (size > 0) { - size = size - 1; - let route = valid_routes[size]; - assert!(is_valid_route(route.source, route.destination)); // should not assert - } -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_routes_err_iota_1() { - get_route(IotaMainnet, IotaMainnet); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_routes_err_iota_2() { - get_route(IotaMainnet, IotaTestnet); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_routes_err_iota_3() { - get_route(IotaMainnet, EthSepolia); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_routes_err_iota_4() { - get_route(IotaMainnet, EthCustom); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_routes_err_eth_1() { - get_route(EthMainnet, EthMainnet); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_routes_err_eth_2() { - get_route(EthMainnet, EthCustom); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_routes_err_eth_3() { - get_route(EthMainnet, IotaCustom); -} - -#[test] -#[expected_failure(abort_code = EInvalidBridgeRoute)] -fun test_routes_err_eth_4() { - get_route(EthMainnet, IotaTestnet); -} diff --git a/crates/iota-framework/packages/bridge/sources/committee.move b/crates/iota-framework/packages/bridge/sources/committee.move deleted file mode 100644 index b48a9b30da1..00000000000 --- a/crates/iota-framework/packages/bridge/sources/committee.move +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[allow(unused_use)] -module bridge::committee; - -use bridge::crypto; -use bridge::message::{Self, Blocklist, BridgeMessage}; -use iota::ecdsa_k1; -use iota::event::emit; -use iota::vec_map::{Self, VecMap}; -use iota::vec_set; -use iota_system::iota_system::IotaSystemState; - -const ESignatureBelowThreshold: u64 = 0; -const EDuplicatedSignature: u64 = 1; -const EInvalidSignature: u64 = 2; -const ENotSystemAddress: u64 = 3; -const EValidatorBlocklistContainsUnknownKey: u64 = 4; -const ESenderNotActiveValidator: u64 = 5; -const EInvalidPubkeyLength: u64 = 6; -const ECommitteeAlreadyInitiated: u64 = 7; -const EDuplicatePubkey: u64 = 8; -const ESenderIsNotInBridgeCommittee: u64 = 9; - -const IOTA_MESSAGE_PREFIX: vector = b"IOTA_BRIDGE_MESSAGE"; - -const ECDSA_COMPRESSED_PUBKEY_LENGTH: u64 = 33; - -////////////////////////////////////////////////////// -// Types -// - -public struct BlocklistValidatorEvent has copy, drop { - blocklisted: bool, - public_keys: vector>, -} - -public struct BridgeCommittee has store { - // committee pub key and weight - members: VecMap, CommitteeMember>, - // Committee member registrations for the next committee creation. - member_registrations: VecMap, - // Epoch when the current committee was updated, - // the voting power for each of the committee members are snapshot from this epoch. - // This is mainly for verification/auditing purposes, it might not be useful for bridge operations. - last_committee_update_epoch: u64, -} - -public struct CommitteeUpdateEvent has copy, drop { - // committee pub key and weight - members: VecMap, CommitteeMember>, - stake_participation_percentage: u64, -} - -public struct CommitteeMemberUrlUpdateEvent has copy, drop { - member: vector, - new_url: vector, -} - -public struct CommitteeMember has copy, drop, store { - /// The IOTA Address of the validator - iota_address: address, - /// The public key bytes of the bridge key - bridge_pubkey_bytes: vector, - /// Voting power, values are voting power in the scale of 10000. - voting_power: u64, - /// The HTTP REST URL the member's node listens to - /// it looks like b'https://127.0.0.1:9191' - http_rest_url: vector, - /// If this member is blocklisted - blocklisted: bool, -} - -public struct CommitteeMemberRegistration has copy, drop, store { - /// The IOTA Address of the validator - iota_address: address, - /// The public key bytes of the bridge key - bridge_pubkey_bytes: vector, - /// The HTTP REST URL the member's node listens to - /// it looks like b'https://127.0.0.1:9191' - http_rest_url: vector, -} - -////////////////////////////////////////////////////// -// Public functions -// - -public fun verify_signatures( - self: &BridgeCommittee, - message: BridgeMessage, - signatures: vector>, -) { - let (mut i, signature_counts) = (0, vector::length(&signatures)); - let mut seen_pub_key = vec_set::empty>(); - let required_voting_power = message.required_voting_power(); - // add prefix to the message bytes - let mut message_bytes = IOTA_MESSAGE_PREFIX; - message_bytes.append(message.serialize_message()); - - let mut threshold = 0; - while (i < signature_counts) { - let pubkey = ecdsa_k1::secp256k1_ecrecover(&signatures[i], &message_bytes, 0); - - // check duplicate - // and make sure pub key is part of the committee - assert!(!seen_pub_key.contains(&pubkey), EDuplicatedSignature); - assert!(self.members.contains(&pubkey), EInvalidSignature); - - // get committee signature weight and check pubkey is part of the committee - let member = &self.members[&pubkey]; - if (!member.blocklisted) { - threshold = threshold + member.voting_power; - }; - seen_pub_key.insert(pubkey); - i = i + 1; - }; - - assert!(threshold >= required_voting_power, ESignatureBelowThreshold); -} - -////////////////////////////////////////////////////// -// Internal functions -// - -public(package) fun create(ctx: &TxContext): BridgeCommittee { - assert!(tx_context::sender(ctx) == @0x0, ENotSystemAddress); - BridgeCommittee { - members: vec_map::empty(), - member_registrations: vec_map::empty(), - last_committee_update_epoch: 0, - } -} - -public(package) fun register( - self: &mut BridgeCommittee, - system_state: &mut IotaSystemState, - bridge_pubkey_bytes: vector, - http_rest_url: vector, - ctx: &TxContext, -) { - // We disallow registration after committee initiated in v1 - assert!(self.members.is_empty(), ECommitteeAlreadyInitiated); - // Ensure pubkey is valid - assert!(bridge_pubkey_bytes.length() == ECDSA_COMPRESSED_PUBKEY_LENGTH, EInvalidPubkeyLength); - // sender must be the same sender that created the validator object, this is to prevent DDoS from non-validator actor. - let sender = ctx.sender(); - let validators = system_state.active_validator_addresses(); - - assert!(validators.contains(&sender), ESenderNotActiveValidator); - // Sender is active validator, record the registration - - // In case validator need to update the info - let registration = if (self.member_registrations.contains(&sender)) { - let registration = &mut self.member_registrations[&sender]; - registration.http_rest_url = http_rest_url; - registration.bridge_pubkey_bytes = bridge_pubkey_bytes; - *registration - } else { - let registration = CommitteeMemberRegistration { - iota_address: sender, - bridge_pubkey_bytes, - http_rest_url, - }; - self.member_registrations.insert(sender, registration); - registration - }; - - // check uniqueness of the bridge pubkey. - // `try_create_next_committee` will abort if bridge_pubkey_bytes are not unique and - // that will fail the end of epoch transaction (possibly "forever", well, we - // need to deploy proper validator changes to stop end of epoch from failing). - check_uniqueness_bridge_keys(self, bridge_pubkey_bytes); - - emit(registration) -} - -// This method will try to create the next committee using the registration and system state, -// if the total stake fails to meet the minimum required percentage, it will skip the update. -// This is to ensure we don't fail the end of epoch transaction. -public(package) fun try_create_next_committee( - self: &mut BridgeCommittee, - active_validator_voting_power: VecMap, - min_stake_participation_percentage: u64, - ctx: &TxContext, -) { - let mut i = 0; - let mut new_members = vec_map::empty(); - let mut stake_participation_percentage = 0; - - while (i < self.member_registrations.size()) { - // retrieve registration - let (_, registration) = self.member_registrations.get_entry_by_idx(i); - // Find validator stake amount from system state - - // Process registration if it's active validator - let voting_power = active_validator_voting_power.try_get(®istration.iota_address); - if (voting_power.is_some()) { - let voting_power = voting_power.destroy_some(); - stake_participation_percentage = stake_participation_percentage + voting_power; - - let member = CommitteeMember { - iota_address: registration.iota_address, - bridge_pubkey_bytes: registration.bridge_pubkey_bytes, - voting_power: (voting_power as u64), - http_rest_url: registration.http_rest_url, - blocklisted: false, - }; - - new_members.insert(registration.bridge_pubkey_bytes, member) - }; - - i = i + 1; - }; - - // Make sure the new committee represent enough stakes, percentage are accurate to 2DP - if (stake_participation_percentage >= min_stake_participation_percentage) { - // Clear registrations - self.member_registrations = vec_map::empty(); - // Store new committee info - self.members = new_members; - self.last_committee_update_epoch = ctx.epoch(); - - emit(CommitteeUpdateEvent { - members: new_members, - stake_participation_percentage, - }) - } -} - -// This function applies the blocklist to the committee members, we won't need to run this very often so this is not gas optimised. -// TODO: add tests for this function -public(package) fun execute_blocklist(self: &mut BridgeCommittee, blocklist: Blocklist) { - let blocklisted = blocklist.blocklist_type() != 1; - let eth_addresses = blocklist.blocklist_validator_addresses(); - let list_len = eth_addresses.length(); - let mut list_idx = 0; - let mut member_idx = 0; - let mut pub_keys = vector[]; - - while (list_idx < list_len) { - let target_address = ð_addresses[list_idx]; - let mut found = false; - - while (member_idx < self.members.size()) { - let (pub_key, member) = self.members.get_entry_by_idx_mut(member_idx); - let eth_address = crypto::ecdsa_pub_key_to_eth_address(pub_key); - - if (*target_address == eth_address) { - member.blocklisted = blocklisted; - pub_keys.push_back(*pub_key); - found = true; - member_idx = 0; - break - }; - - member_idx = member_idx + 1; - }; - - assert!(found, EValidatorBlocklistContainsUnknownKey); - list_idx = list_idx + 1; - }; - - emit(BlocklistValidatorEvent { - blocklisted, - public_keys: pub_keys, - }) -} - -public(package) fun committee_members( - self: &BridgeCommittee, -): &VecMap, CommitteeMember> { - &self.members -} - -public(package) fun update_node_url( - self: &mut BridgeCommittee, - new_url: vector, - ctx: &TxContext, -) { - let mut idx = 0; - while (idx < self.members.size()) { - let (_, member) = self.members.get_entry_by_idx_mut(idx); - if (member.iota_address == ctx.sender()) { - member.http_rest_url = new_url; - emit(CommitteeMemberUrlUpdateEvent { - member: member.bridge_pubkey_bytes, - new_url, - }); - return - }; - idx = idx + 1; - }; - abort ESenderIsNotInBridgeCommittee -} - -// Assert if `bridge_pubkey_bytes` is duplicated in `member_registrations`. -// Duplicate keys would cause `try_create_next_committee` to fail and, -// in consequence, an end of epoch transaction to fail (safe mode run). -// This check will ensure the creation of the committee is correct. -fun check_uniqueness_bridge_keys(self: &BridgeCommittee, bridge_pubkey_bytes: vector) { - let mut count = self.member_registrations.size(); - // bridge_pubkey_bytes must be found once and once only - let mut bridge_key_found = false; - while (count > 0) { - count = count - 1; - let (_, registration) = self.member_registrations.get_entry_by_idx(count); - if (registration.bridge_pubkey_bytes == bridge_pubkey_bytes) { - assert!(!bridge_key_found, EDuplicatePubkey); - bridge_key_found = true; // bridge_pubkey_bytes found, we must not have another one - } - }; -} - -////////////////////////////////////////////////////// -// Test functions -// - -#[test_only] -public(package) fun members(self: &BridgeCommittee): &VecMap, CommitteeMember> { - &self.members -} - -#[test_only] -public(package) fun voting_power(member: &CommitteeMember): u64 { - member.voting_power -} - -#[test_only] -public(package) fun http_rest_url(member: &CommitteeMember): vector { - member.http_rest_url -} - -#[test_only] -public(package) fun member_registrations( - self: &BridgeCommittee, -): &VecMap { - &self.member_registrations -} - -#[test_only] -public(package) fun blocklisted(member: &CommitteeMember): bool { - member.blocklisted -} - -#[test_only] -public(package) fun bridge_pubkey_bytes(registration: &CommitteeMemberRegistration): &vector { - ®istration.bridge_pubkey_bytes -} - -#[test_only] -public(package) fun make_bridge_committee( - members: VecMap, CommitteeMember>, - member_registrations: VecMap, - last_committee_update_epoch: u64, -): BridgeCommittee { - BridgeCommittee { - members, - member_registrations, - last_committee_update_epoch, - } -} - -#[test_only] -public(package) fun make_committee_member( - iota_address: address, - bridge_pubkey_bytes: vector, - voting_power: u64, - http_rest_url: vector, - blocklisted: bool, -): CommitteeMember { - CommitteeMember { - iota_address, - bridge_pubkey_bytes, - voting_power, - http_rest_url, - blocklisted, - } -} diff --git a/crates/iota-framework/packages/bridge/sources/crypto.move b/crates/iota-framework/packages/bridge/sources/crypto.move deleted file mode 100644 index dbf4c58bb01..00000000000 --- a/crates/iota-framework/packages/bridge/sources/crypto.move +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridge::crypto; - -use iota::ecdsa_k1; -use iota::hash::keccak256; - -#[test_only] -use iota::hex; - -public(package) fun ecdsa_pub_key_to_eth_address(compressed_pub_key: &vector): vector { - // Decompress pub key - let decompressed = ecdsa_k1::decompress_pubkey(compressed_pub_key); - - // Skip the first byte - let (mut i, mut decompressed_64) = (1, vector[]); - while (i < 65) { - decompressed_64.push_back(decompressed[i]); - i = i + 1; - }; - - // Hash - let hash = keccak256(&decompressed_64); - - // Take last 20 bytes - let mut address = vector[]; - let mut i = 12; - while (i < 32) { - address.push_back(hash[i]); - i = i + 1; - }; - address -} - -#[test] -fun test_pub_key_to_eth_address() { - let validator_pub_key = hex::decode( - b"029bef8d556d80e43ae7e0becb3a7e6838b95defe45896ed6075bb9035d06c9964", - ); - let expected_address = hex::decode(b"b14d3c4f5fbfbcfb98af2d330000d49c95b93aa7"); - - assert!(ecdsa_pub_key_to_eth_address(&validator_pub_key) == expected_address); -} diff --git a/crates/iota-framework/packages/bridge/sources/limiter.move b/crates/iota-framework/packages/bridge/sources/limiter.move deleted file mode 100644 index 54dae4cf4f8..00000000000 --- a/crates/iota-framework/packages/bridge/sources/limiter.move +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridge::limiter; - -use bridge::chain_ids::{Self, BridgeRoute}; -use bridge::treasury::BridgeTreasury; -use iota::clock::{Self, Clock}; -use iota::event::emit; -use iota::vec_map::{Self, VecMap}; - -const ELimitNotFoundForRoute: u64 = 0; - -// TODO: U64::MAX, make this configurable? -const MAX_TRANSFER_LIMIT: u64 = 18_446_744_073_709_551_615; - -const USD_VALUE_MULTIPLIER: u64 = 100000000; // 8 DP accuracy - -////////////////////////////////////////////////////// -// Types -// - -public struct TransferLimiter has store { - transfer_limits: VecMap, - // Per hour transfer amount for each bridge route - transfer_records: VecMap, -} - -public struct TransferRecord has store { - hour_head: u64, - hour_tail: u64, - per_hour_amounts: vector, - // total amount in USD, 4 DP accuracy, so 10000 => 1USD - total_amount: u64, -} - -public struct UpdateRouteLimitEvent has copy, drop { - sending_chain: u8, - receiving_chain: u8, - new_limit: u64, -} - -////////////////////////////////////////////////////// -// Public functions -// - -// Abort if the route limit is not found -public fun get_route_limit(self: &TransferLimiter, route: &BridgeRoute): u64 { - self.transfer_limits[route] -} - -////////////////////////////////////////////////////// -// Internal functions -// - -public(package) fun new(): TransferLimiter { - // hardcoded limit for bridge genesis - TransferLimiter { - transfer_limits: initial_transfer_limits(), - transfer_records: vec_map::empty(), - } -} - -public(package) fun check_and_record_sending_transfer( - self: &mut TransferLimiter, - treasury: &BridgeTreasury, - clock: &Clock, - route: BridgeRoute, - amount: u64, -): bool { - // Create record for route if not exists - if (!self.transfer_records.contains(&route)) { - self - .transfer_records - .insert( - route, - TransferRecord { - hour_head: 0, - hour_tail: 0, - per_hour_amounts: vector[], - total_amount: 0, - }, - ) - }; - let record = self.transfer_records.get_mut(&route); - let current_hour_since_epoch = current_hour_since_epoch(clock); - - record.adjust_transfer_records(current_hour_since_epoch); - - // Get limit for the route - let route_limit = self.transfer_limits.try_get(&route); - assert!(route_limit.is_some(), ELimitNotFoundForRoute); - let route_limit = route_limit.destroy_some(); - let route_limit_adjusted = (route_limit as u128) * (treasury.decimal_multiplier() as u128); - - // Compute notional amount - // Upcast to u128 to prevent overflow, to not miss out on small amounts. - let value = (treasury.notional_value() as u128); - let notional_amount_with_token_multiplier = value * (amount as u128); - - // Check if transfer amount exceed limit - // Upscale them to the token's decimal. - if ( - (record.total_amount as u128) - * (treasury.decimal_multiplier() as u128) - + notional_amount_with_token_multiplier > route_limit_adjusted - ) { - return false - }; - - // Now scale down to notional value - let notional_amount = - notional_amount_with_token_multiplier - / (treasury.decimal_multiplier() as u128); - // Should be safe to downcast to u64 after dividing by the decimals - let notional_amount = (notional_amount as u64); - - // Record transfer value - let new_amount = record.per_hour_amounts.pop_back() + notional_amount; - record.per_hour_amounts.push_back(new_amount); - record.total_amount = record.total_amount + notional_amount; - true -} - -public(package) fun update_route_limit( - self: &mut TransferLimiter, - route: &BridgeRoute, - new_usd_limit: u64, -) { - let receiving_chain = *route.destination(); - - if (!self.transfer_limits.contains(route)) { - self.transfer_limits.insert(*route, new_usd_limit); - } else { - *&mut self.transfer_limits[route] = new_usd_limit; - }; - - emit(UpdateRouteLimitEvent { - sending_chain: *route.source(), - receiving_chain, - new_limit: new_usd_limit, - }) -} - -// Current hour since unix epoch -fun current_hour_since_epoch(clock: &Clock): u64 { - clock::timestamp_ms(clock) / 3600000 -} - -fun adjust_transfer_records(self: &mut TransferRecord, current_hour_since_epoch: u64) { - if (self.hour_head == current_hour_since_epoch) { - return // nothing to backfill - }; - - let target_tail = current_hour_since_epoch - 23; - - // If `hour_head` is even older than 24 hours ago, it means all items in - // `per_hour_amounts` are to be evicted. - if (self.hour_head < target_tail) { - self.per_hour_amounts = vector[]; - self.total_amount = 0; - self.hour_tail = target_tail; - self.hour_head = target_tail; - // Don't forget to insert this hour's record - self.per_hour_amounts.push_back(0); - } else { - // self.hour_head is within 24 hour range. - // some items in `per_hour_amounts` are still valid, we remove stale hours. - while (self.hour_tail < target_tail) { - self.total_amount = self.total_amount - self.per_hour_amounts.remove(0); - self.hour_tail = self.hour_tail + 1; - } - }; - - // Backfill from hour_head to current hour - while (self.hour_head < current_hour_since_epoch) { - self.per_hour_amounts.push_back(0); - self.hour_head = self.hour_head + 1; - } -} - -// It's tedious to list every pair, but it's safer to do so so we don't -// accidentally turn off limiter for a new production route in the future. -// Note limiter only takes effects on the receiving chain, so we only need to -// specify routes from Ethereum to IOTA. -fun initial_transfer_limits(): VecMap { - let mut transfer_limits = vec_map::empty(); - // 5M limit on IOTA -> Ethereum mainnet - transfer_limits.insert( - chain_ids::get_route(chain_ids::eth_mainnet(), chain_ids::iota_mainnet()), - 5_000_000 * USD_VALUE_MULTIPLIER, - ); - - // MAX limit for testnet and devnet - transfer_limits.insert( - chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()), - MAX_TRANSFER_LIMIT, - ); - - transfer_limits.insert( - chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_custom()), - MAX_TRANSFER_LIMIT, - ); - - transfer_limits.insert( - chain_ids::get_route(chain_ids::eth_custom(), chain_ids::iota_testnet()), - MAX_TRANSFER_LIMIT, - ); - - transfer_limits.insert( - chain_ids::get_route(chain_ids::eth_custom(), chain_ids::iota_custom()), - MAX_TRANSFER_LIMIT, - ); - - transfer_limits -} - -////////////////////////////////////////////////////// -// Test functions -// - -#[test_only] -public(package) fun transfer_limits(limiter: &TransferLimiter): &VecMap { - &limiter.transfer_limits -} - -#[test_only] -public(package) fun transfer_limits_mut( - limiter: &mut TransferLimiter, -): &mut VecMap { - &mut limiter.transfer_limits -} - -#[test_only] -public(package) fun transfer_records( - limiter: &TransferLimiter, -): &VecMap { - &limiter.transfer_records -} - -#[test_only] -public(package) fun transfer_records_mut( - limiter: &mut TransferLimiter, -): &mut VecMap { - &mut limiter.transfer_records -} - -#[test_only] -public(package) fun usd_value_multiplier(): u64 { - USD_VALUE_MULTIPLIER -} - -#[test_only] -public(package) fun max_transfer_limit(): u64 { - MAX_TRANSFER_LIMIT -} - -#[test_only] -public(package) fun make_transfer_limiter(): TransferLimiter { - TransferLimiter { - transfer_limits: vec_map::empty(), - transfer_records: vec_map::empty(), - } -} - -#[test_only] -public(package) fun total_amount(record: &TransferRecord): u64 { - record.total_amount -} - -#[test_only] -public(package) fun per_hour_amounts(record: &TransferRecord): &vector { - &record.per_hour_amounts -} - -#[test_only] -public(package) fun hour_head(record: &TransferRecord): u64 { - record.hour_head -} - -#[test_only] -public(package) fun hour_tail(record: &TransferRecord): u64 { - record.hour_tail -} - -#[test_only] -public(package) fun unpack_route_limit_event(event: UpdateRouteLimitEvent): (u8, u8, u64) { - (event.sending_chain, event.receiving_chain, event.new_limit) -} diff --git a/crates/iota-framework/packages/bridge/sources/message.move b/crates/iota-framework/packages/bridge/sources/message.move deleted file mode 100644 index 5977f86c6d4..00000000000 --- a/crates/iota-framework/packages/bridge/sources/message.move +++ /dev/null @@ -1,662 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridge::message; - -use bridge::chain_ids; -use bridge::message_types; -use iota::bcs::{Self, BCS}; -use std::ascii::{Self, String}; - -const CURRENT_MESSAGE_VERSION: u8 = 1; -const ECDSA_ADDRESS_LENGTH: u64 = 20; - -const ETrailingBytes: u64 = 0; -const EInvalidAddressLength: u64 = 1; -const EEmptyList: u64 = 2; -const EInvalidMessageType: u64 = 3; -const EInvalidEmergencyOpType: u64 = 4; -const EInvalidPayloadLength: u64 = 5; -const EMustBeTokenMessage: u64 = 6; - -// Emergency Op types -const PAUSE: u8 = 0; -const UNPAUSE: u8 = 1; - -////////////////////////////////////////////////////// -// Types -// - -public struct BridgeMessage has copy, drop, store { - message_type: u8, - message_version: u8, - seq_num: u64, - source_chain: u8, - payload: vector, -} - -public struct BridgeMessageKey has copy, drop, store { - source_chain: u8, - message_type: u8, - bridge_seq_num: u64, -} - -public struct TokenTransferPayload has drop { - sender_address: vector, - target_chain: u8, - target_address: vector, - token_type: u8, - amount: u64, -} - -public struct EmergencyOp has drop { - op_type: u8, -} - -public struct Blocklist has drop { - blocklist_type: u8, - validator_eth_addresses: vector>, -} - -// Update the limit for route from sending_chain to receiving_chain -// This message is supposed to be processed by `chain` or the receiving chain -public struct UpdateBridgeLimit has drop { - // The receiving chain, also the chain that checks and processes this message - receiving_chain: u8, - // The sending chain - sending_chain: u8, - limit: u64, -} - -public struct UpdateAssetPrice has drop { - token_id: u8, - new_price: u64, -} - -public struct AddTokenOnIota has drop { - native_token: bool, - token_ids: vector, - token_type_names: vector, - token_prices: vector, -} - -// For read -public struct ParsedTokenTransferMessage has drop { - message_version: u8, - seq_num: u64, - source_chain: u8, - payload: vector, - parsed_payload: TokenTransferPayload, -} - -////////////////////////////////////////////////////// -// Public functions -// - -// Note: `bcs::peel_vec_u8` *happens* to work here because -// `sender_address` and `target_address` are no longer than 255 bytes. -// Therefore their length can be represented by a single byte. -// See `create_token_bridge_message` for the actual encoding rule. -public fun extract_token_bridge_payload(message: &BridgeMessage): TokenTransferPayload { - let mut bcs = bcs::new(message.payload); - let sender_address = bcs.peel_vec_u8(); - let target_chain = bcs.peel_u8(); - let target_address = bcs.peel_vec_u8(); - let token_type = bcs.peel_u8(); - let amount = peel_u64_be(&mut bcs); - - chain_ids::assert_valid_chain_id(target_chain); - assert!(bcs.into_remainder_bytes().is_empty(), ETrailingBytes); - - TokenTransferPayload { - sender_address, - target_chain, - target_address, - token_type, - amount, - } -} - -/// Emergency op payload is just a single byte -public fun extract_emergency_op_payload(message: &BridgeMessage): EmergencyOp { - assert!(message.payload.length() == 1, ETrailingBytes); - EmergencyOp { op_type: message.payload[0] } -} - -public fun extract_blocklist_payload(message: &BridgeMessage): Blocklist { - // blocklist payload should consist of one byte blocklist type, and list of 20 bytes evm addresses - // derived from ECDSA public keys - let mut bcs = bcs::new(message.payload); - let blocklist_type = bcs.peel_u8(); - let mut address_count = bcs.peel_u8(); - - assert!(address_count != 0, EEmptyList); - - let mut validator_eth_addresses = vector[]; - while (address_count > 0) { - let (mut address, mut i) = (vector[], 0); - while (i < ECDSA_ADDRESS_LENGTH) { - address.push_back(bcs.peel_u8()); - i = i + 1; - }; - validator_eth_addresses.push_back(address); - address_count = address_count - 1; - }; - - assert!(bcs.into_remainder_bytes().is_empty(), ETrailingBytes); - - Blocklist { - blocklist_type, - validator_eth_addresses, - } -} - -public fun extract_update_bridge_limit(message: &BridgeMessage): UpdateBridgeLimit { - let mut bcs = bcs::new(message.payload); - let sending_chain = bcs.peel_u8(); - let limit = peel_u64_be(&mut bcs); - - chain_ids::assert_valid_chain_id(sending_chain); - assert!(bcs.into_remainder_bytes().is_empty(), ETrailingBytes); - - UpdateBridgeLimit { - receiving_chain: message.source_chain, - sending_chain, - limit, - } -} - -public fun extract_update_asset_price(message: &BridgeMessage): UpdateAssetPrice { - let mut bcs = bcs::new(message.payload); - let token_id = bcs.peel_u8(); - let new_price = peel_u64_be(&mut bcs); - - assert!(bcs.into_remainder_bytes().is_empty(), ETrailingBytes); - - UpdateAssetPrice { - token_id, - new_price, - } -} - -public fun extract_add_tokens_on_iota(message: &BridgeMessage): AddTokenOnIota { - let mut bcs = bcs::new(message.payload); - let native_token = bcs.peel_bool(); - let token_ids = bcs.peel_vec_u8(); - let token_type_names_bytes = bcs.peel_vec_vec_u8(); - let token_prices = bcs.peel_vec_u64(); - - let mut n = 0; - let mut token_type_names = vector[]; - while (n < token_type_names_bytes.length()) { - token_type_names.push_back(ascii::string(*token_type_names_bytes.borrow(n))); - n = n + 1; - }; - assert!(bcs.into_remainder_bytes().is_empty(), ETrailingBytes); - AddTokenOnIota { - native_token, - token_ids, - token_type_names, - token_prices, - } -} - -public fun serialize_message(message: BridgeMessage): vector { - let BridgeMessage { - message_type, - message_version, - seq_num, - source_chain, - payload, - } = message; - - let mut message = vector[message_type, message_version]; - - // bcs serializes u64 as 8 bytes - message.append(reverse_bytes(bcs::to_bytes(&seq_num))); - message.push_back(source_chain); - message.append(payload); - message -} - -/// Token Transfer Message Format: -/// [message_type: u8] -/// [version:u8] -/// [nonce:u64] -/// [source_chain: u8] -/// [sender_address_length:u8] -/// [sender_address: byte[]] -/// [target_chain:u8] -/// [target_address_length:u8] -/// [target_address: byte[]] -/// [token_type:u8] -/// [amount:u64] -public fun create_token_bridge_message( - source_chain: u8, - seq_num: u64, - sender_address: vector, - target_chain: u8, - target_address: vector, - token_type: u8, - amount: u64, -): BridgeMessage { - chain_ids::assert_valid_chain_id(source_chain); - chain_ids::assert_valid_chain_id(target_chain); - - let mut payload = vector[]; - - // sender address should be less than 255 bytes so can fit into u8 - payload.push_back((vector::length(&sender_address) as u8)); - payload.append(sender_address); - payload.push_back(target_chain); - // target address should be less than 255 bytes so can fit into u8 - payload.push_back((vector::length(&target_address) as u8)); - payload.append(target_address); - payload.push_back(token_type); - // bcs serializes u64 as 8 bytes - payload.append(reverse_bytes(bcs::to_bytes(&amount))); - - assert!(vector::length(&payload) == 64, EInvalidPayloadLength); - - BridgeMessage { - message_type: message_types::token(), - message_version: CURRENT_MESSAGE_VERSION, - seq_num, - source_chain, - payload, - } -} - -/// Emergency Op Message Format: -/// [message_type: u8] -/// [version:u8] -/// [nonce:u64] -/// [chain_id: u8] -/// [op_type: u8] -public fun create_emergency_op_message(source_chain: u8, seq_num: u64, op_type: u8): BridgeMessage { - chain_ids::assert_valid_chain_id(source_chain); - - BridgeMessage { - message_type: message_types::emergency_op(), - message_version: CURRENT_MESSAGE_VERSION, - seq_num, - source_chain, - payload: vector[op_type], - } -} - -/// Blocklist Message Format: -/// [message_type: u8] -/// [version:u8] -/// [nonce:u64] -/// [chain_id: u8] -/// [blocklist_type: u8] -/// [validator_length: u8] -/// [validator_ecdsa_addresses: byte[][]] -public fun create_blocklist_message( - source_chain: u8, - seq_num: u64, - // 0: block, 1: unblock - blocklist_type: u8, - validator_ecdsa_addresses: vector>, -): BridgeMessage { - chain_ids::assert_valid_chain_id(source_chain); - - let address_length = validator_ecdsa_addresses.length(); - let mut payload = vector[blocklist_type, (address_length as u8)]; - let mut i = 0; - - while (i < address_length) { - let address = validator_ecdsa_addresses[i]; - assert!(address.length() == ECDSA_ADDRESS_LENGTH, EInvalidAddressLength); - payload.append(address); - - i = i + 1; - }; - - BridgeMessage { - message_type: message_types::committee_blocklist(), - message_version: CURRENT_MESSAGE_VERSION, - seq_num, - source_chain, - payload, - } -} - -/// Update bridge limit Message Format: -/// [message_type: u8] -/// [version:u8] -/// [nonce:u64] -/// [receiving_chain_id: u8] -/// [sending_chain_id: u8] -/// [new_limit: u64] -public fun create_update_bridge_limit_message( - receiving_chain: u8, - seq_num: u64, - sending_chain: u8, - new_limit: u64, -): BridgeMessage { - chain_ids::assert_valid_chain_id(receiving_chain); - chain_ids::assert_valid_chain_id(sending_chain); - - let mut payload = vector[sending_chain]; - payload.append(reverse_bytes(bcs::to_bytes(&new_limit))); - - BridgeMessage { - message_type: message_types::update_bridge_limit(), - message_version: CURRENT_MESSAGE_VERSION, - seq_num, - source_chain: receiving_chain, - payload, - } -} - -/// Update asset price message -/// [message_type: u8] -/// [version:u8] -/// [nonce:u64] -/// [chain_id: u8] -/// [token_id: u8] -/// [new_price:u64] -public fun create_update_asset_price_message( - token_id: u8, - source_chain: u8, - seq_num: u64, - new_price: u64, -): BridgeMessage { - chain_ids::assert_valid_chain_id(source_chain); - - let mut payload = vector[token_id]; - payload.append(reverse_bytes(bcs::to_bytes(&new_price))); - BridgeMessage { - message_type: message_types::update_asset_price(), - message_version: CURRENT_MESSAGE_VERSION, - seq_num, - source_chain, - payload, - } -} - -/// Update IOTA token message -/// [message_type:u8] -/// [version:u8] -/// [nonce:u64] -/// [chain_id: u8] -/// [native_token:bool] -/// [token_ids:vector] -/// [token_type_name:vector] -/// [token_prices:vector] -public fun create_add_tokens_on_iota_message( - source_chain: u8, - seq_num: u64, - native_token: bool, - token_ids: vector, - type_names: vector, - token_prices: vector, -): BridgeMessage { - chain_ids::assert_valid_chain_id(source_chain); - let mut payload = bcs::to_bytes(&native_token); - payload.append(bcs::to_bytes(&token_ids)); - payload.append(bcs::to_bytes(&type_names)); - payload.append(bcs::to_bytes(&token_prices)); - BridgeMessage { - message_type: message_types::add_tokens_on_iota(), - message_version: CURRENT_MESSAGE_VERSION, - seq_num, - source_chain, - payload, - } -} - -public fun create_key(source_chain: u8, message_type: u8, bridge_seq_num: u64): BridgeMessageKey { - BridgeMessageKey { source_chain, message_type, bridge_seq_num } -} - -public fun key(self: &BridgeMessage): BridgeMessageKey { - create_key(self.source_chain, self.message_type, self.seq_num) -} - -// BridgeMessage getters -public fun message_version(self: &BridgeMessage): u8 { - self.message_version -} - -public fun message_type(self: &BridgeMessage): u8 { - self.message_type -} - -public fun seq_num(self: &BridgeMessage): u64 { - self.seq_num -} - -public fun source_chain(self: &BridgeMessage): u8 { - self.source_chain -} - -public fun payload(self: &BridgeMessage): vector { - self.payload -} - -public fun token_target_chain(self: &TokenTransferPayload): u8 { - self.target_chain -} - -public fun token_target_address(self: &TokenTransferPayload): vector { - self.target_address -} - -public fun token_type(self: &TokenTransferPayload): u8 { - self.token_type -} - -public fun token_amount(self: &TokenTransferPayload): u64 { - self.amount -} - -// EmergencyOpPayload getters -public fun emergency_op_type(self: &EmergencyOp): u8 { - self.op_type -} - -public fun blocklist_type(self: &Blocklist): u8 { - self.blocklist_type -} - -public fun blocklist_validator_addresses(self: &Blocklist): &vector> { - &self.validator_eth_addresses -} - -public fun update_bridge_limit_payload_sending_chain(self: &UpdateBridgeLimit): u8 { - self.sending_chain -} - -public fun update_bridge_limit_payload_receiving_chain(self: &UpdateBridgeLimit): u8 { - self.receiving_chain -} - -public fun update_bridge_limit_payload_limit(self: &UpdateBridgeLimit): u64 { - self.limit -} - -public fun update_asset_price_payload_token_id(self: &UpdateAssetPrice): u8 { - self.token_id -} - -public fun update_asset_price_payload_new_price(self: &UpdateAssetPrice): u64 { - self.new_price -} - -public fun is_native(self: &AddTokenOnIota): bool { - self.native_token -} - -public fun token_ids(self: &AddTokenOnIota): vector { - self.token_ids -} - -public fun token_type_names(self: &AddTokenOnIota): vector { - self.token_type_names -} - -public fun token_prices(self: &AddTokenOnIota): vector { - self.token_prices -} - -public fun emergency_op_pause(): u8 { - PAUSE -} - -public fun emergency_op_unpause(): u8 { - UNPAUSE -} - -/// Return the required signature threshold for the message, values are voting power in the scale of 10000 -public fun required_voting_power(self: &BridgeMessage): u64 { - let message_type = message_type(self); - - if (message_type == message_types::token()) { - 3334 - } else if (message_type == message_types::emergency_op()) { - let payload = extract_emergency_op_payload(self); - if (payload.op_type == PAUSE) { - 450 - } else if (payload.op_type == UNPAUSE) { - 5001 - } else { - abort EInvalidEmergencyOpType - } - } else if (message_type == message_types::committee_blocklist()) { - 5001 - } else if (message_type == message_types::update_asset_price()) { - 5001 - } else if (message_type == message_types::update_bridge_limit()) { - 5001 - } else if (message_type == message_types::add_tokens_on_iota()) { - 5001 - } else { - abort EInvalidMessageType - } -} - -// Convert BridgeMessage to ParsedTokenTransferMessage -public fun to_parsed_token_transfer_message(message: &BridgeMessage): ParsedTokenTransferMessage { - assert!(message.message_type() == message_types::token(), EMustBeTokenMessage); - let payload = message.extract_token_bridge_payload(); - ParsedTokenTransferMessage { - message_version: message.message_version(), - seq_num: message.seq_num(), - source_chain: message.source_chain(), - payload: message.payload(), - parsed_payload: payload, - } -} - -////////////////////////////////////////////////////// -// Internal functions -// - -fun reverse_bytes(mut bytes: vector): vector { - vector::reverse(&mut bytes); - bytes -} - -fun peel_u64_be(bcs: &mut BCS): u64 { - let (mut value, mut i) = (0u64, 64u8); - while (i > 0) { - i = i - 8; - let byte = (bcs::peel_u8(bcs) as u64); - value = value + (byte << i); - }; - value -} - -////////////////////////////////////////////////////// -// Test functions -// - -#[test_only] -public(package) fun peel_u64_be_for_testing(bcs: &mut BCS): u64 { - peel_u64_be(bcs) -} - -#[test_only] -public(package) fun make_generic_message( - message_type: u8, - message_version: u8, - seq_num: u64, - source_chain: u8, - payload: vector, -): BridgeMessage { - BridgeMessage { - message_type, - message_version, - seq_num, - source_chain, - payload, - } -} - -#[test_only] -public(package) fun make_payload( - sender_address: vector, - target_chain: u8, - target_address: vector, - token_type: u8, - amount: u64, -): TokenTransferPayload { - TokenTransferPayload { - sender_address, - target_chain, - target_address, - token_type, - amount, - } -} - -#[test_only] -public(package) fun deserialize_message_test_only(message: vector): BridgeMessage { - let mut bcs = bcs::new(message); - let message_type = bcs::peel_u8(&mut bcs); - let message_version = bcs::peel_u8(&mut bcs); - let seq_num = peel_u64_be_for_testing(&mut bcs); - let source_chain = bcs::peel_u8(&mut bcs); - let payload = bcs::into_remainder_bytes(bcs); - make_generic_message( - message_type, - message_version, - seq_num, - source_chain, - payload, - ) -} - -#[test_only] -public(package) fun reverse_bytes_test(bytes: vector): vector { - reverse_bytes(bytes) -} - -#[test_only] -public(package) fun set_payload(message: &mut BridgeMessage, bytes: vector) { - message.payload = bytes; -} - -#[test_only] -public(package) fun make_add_token_on_iota( - native_token: bool, - token_ids: vector, - token_type_names: vector, - token_prices: vector, -): AddTokenOnIota { - AddTokenOnIota { - native_token, - token_ids, - token_type_names, - token_prices, - } -} - -#[test_only] -public(package) fun unpack_message(msg: BridgeMessageKey): (u8, u8, u64) { - (msg.source_chain, msg.message_type, msg.bridge_seq_num) -} diff --git a/crates/iota-framework/packages/bridge/sources/message_types.move b/crates/iota-framework/packages/bridge/sources/message_types.move deleted file mode 100644 index 0027fd4a644..00000000000 --- a/crates/iota-framework/packages/bridge/sources/message_types.move +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridge::message_types; - -// message types -const TOKEN: u8 = 0; -const COMMITTEE_BLOCKLIST: u8 = 1; -const EMERGENCY_OP: u8 = 2; -const UPDATE_BRIDGE_LIMIT: u8 = 3; -const UPDATE_ASSET_PRICE: u8 = 4; -const ADD_TOKENS_ON_IOTA: u8 = 6; - -public fun token(): u8 { TOKEN } - -public fun committee_blocklist(): u8 { COMMITTEE_BLOCKLIST } - -public fun emergency_op(): u8 { EMERGENCY_OP } - -public fun update_bridge_limit(): u8 { UPDATE_BRIDGE_LIMIT } - -public fun update_asset_price(): u8 { UPDATE_ASSET_PRICE } - -public fun add_tokens_on_iota(): u8 { ADD_TOKENS_ON_IOTA } diff --git a/crates/iota-framework/packages/bridge/sources/treasury.move b/crates/iota-framework/packages/bridge/sources/treasury.move deleted file mode 100644 index 0b58423fc6b..00000000000 --- a/crates/iota-framework/packages/bridge/sources/treasury.move +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -module bridge::treasury; - -use iota::address; -use iota::bag::{Self, Bag}; -use iota::coin::{Self, Coin, TreasuryCap, CoinMetadata}; -use iota::event::emit; -use iota::hex; -use iota::object_bag::{Self, ObjectBag}; -use iota::package::{Self, UpgradeCap}; -use iota::vec_map::{Self, VecMap}; -use std::ascii::{Self, String}; -use std::type_name::{Self, TypeName}; - -const EUnsupportedTokenType: u64 = 1; -const EInvalidUpgradeCap: u64 = 2; -const ETokenSupplyNonZero: u64 = 3; -const EInvalidNotionalValue: u64 = 4; - -#[test_only] -const USD_VALUE_MULTIPLIER: u64 = 100000000; // 8 DP accuracy - -////////////////////////////////////////////////////// -// Types -// - -public struct BridgeTreasury has store { - // token treasuries, values are TreasuryCaps for native bridge V1. - treasuries: ObjectBag, - supported_tokens: VecMap, - // Mapping token id to type name - id_token_type_map: VecMap, - // Bag for storing potential new token waiting to be approved - waiting_room: Bag, -} - -public struct BridgeTokenMetadata has copy, drop, store { - id: u8, - decimal_multiplier: u64, - notional_value: u64, - native_token: bool, -} - -public struct ForeignTokenRegistration has store { - type_name: TypeName, - uc: UpgradeCap, - decimal: u8, -} - -public struct UpdateTokenPriceEvent has copy, drop { - token_id: u8, - new_price: u64, -} - -public struct NewTokenEvent has copy, drop { - token_id: u8, - type_name: TypeName, - native_token: bool, - decimal_multiplier: u64, - notional_value: u64, -} - -public struct TokenRegistrationEvent has copy, drop { - type_name: TypeName, - decimal: u8, - native_token: bool, -} - -public fun token_id(self: &BridgeTreasury): u8 { - let metadata = self.get_token_metadata(); - metadata.id -} - -public fun decimal_multiplier(self: &BridgeTreasury): u64 { - let metadata = self.get_token_metadata(); - metadata.decimal_multiplier -} - -public fun notional_value(self: &BridgeTreasury): u64 { - let metadata = self.get_token_metadata(); - metadata.notional_value -} - -////////////////////////////////////////////////////// -// Internal functions -// - -public(package) fun register_foreign_token( - self: &mut BridgeTreasury, - tc: TreasuryCap, - uc: UpgradeCap, - metadata: &CoinMetadata, -) { - // Make sure TreasuryCap has not been minted before. - assert!(coin::total_supply(&tc) == 0, ETokenSupplyNonZero); - let type_name = type_name::get(); - let address_bytes = hex::decode(ascii::into_bytes(type_name::get_address(&type_name))); - let coin_address = address::from_bytes(address_bytes); - // Make sure upgrade cap is for the Coin package - // FIXME: add test - assert!( - object::id_to_address(&package::upgrade_package(&uc)) - == coin_address, - EInvalidUpgradeCap, - ); - let registration = ForeignTokenRegistration { - type_name, - uc, - decimal: coin::get_decimals(metadata), - }; - self.waiting_room.add(type_name::into_string(type_name), registration); - self.treasuries.add(type_name, tc); - - emit(TokenRegistrationEvent { - type_name, - decimal: coin::get_decimals(metadata), - native_token: false, - }); -} - -public(package) fun add_new_token( - self: &mut BridgeTreasury, - token_name: String, - token_id: u8, - native_token: bool, - notional_value: u64, -) { - if (!native_token) { - assert!(notional_value > 0, EInvalidNotionalValue); - let ForeignTokenRegistration { - type_name, - uc, - decimal, - } = self.waiting_room.remove(token_name); - let decimal_multiplier = 10u64.pow(decimal); - self - .supported_tokens - .insert( - type_name, - BridgeTokenMetadata { - id: token_id, - decimal_multiplier, - notional_value, - native_token, - }, - ); - self.id_token_type_map.insert(token_id, type_name); - - // Freeze upgrade cap to prevent changes to the coin - transfer::public_freeze_object(uc); - - emit(NewTokenEvent { - token_id, - type_name, - native_token, - decimal_multiplier, - notional_value, - }) - } else {} -} - -public(package) fun create(ctx: &mut TxContext): BridgeTreasury { - BridgeTreasury { - treasuries: object_bag::new(ctx), - supported_tokens: vec_map::empty(), - id_token_type_map: vec_map::empty(), - waiting_room: bag::new(ctx), - } -} - -public(package) fun burn(self: &mut BridgeTreasury, token: Coin) { - let treasury = &mut self.treasuries[type_name::get()]; - coin::burn(treasury, token); -} - -public(package) fun mint(self: &mut BridgeTreasury, amount: u64, ctx: &mut TxContext): Coin { - let treasury = &mut self.treasuries[type_name::get()]; - coin::mint(treasury, amount, ctx) -} - -public(package) fun update_asset_notional_price( - self: &mut BridgeTreasury, - token_id: u8, - new_usd_price: u64, -) { - let type_name = self.id_token_type_map.try_get(&token_id); - assert!(type_name.is_some(), EUnsupportedTokenType); - assert!(new_usd_price > 0, EInvalidNotionalValue); - let type_name = type_name.destroy_some(); - let metadata = self.supported_tokens.get_mut(&type_name); - metadata.notional_value = new_usd_price; - - emit(UpdateTokenPriceEvent { - token_id, - new_price: new_usd_price, - }) -} - -fun get_token_metadata(self: &BridgeTreasury): BridgeTokenMetadata { - let coin_type = type_name::get(); - let metadata = self.supported_tokens.try_get(&coin_type); - assert!(metadata.is_some(), EUnsupportedTokenType); - metadata.destroy_some() -} - -////////////////////////////////////////////////////// -// Test functions -// - -#[test_only] -public struct ETH has drop {} -#[test_only] -public struct BTC has drop {} -#[test_only] -public struct USDT has drop {} -#[test_only] -public struct USDC has drop {} - -#[test_only] -public fun new_for_testing(ctx: &mut TxContext): BridgeTreasury { - create(ctx) -} - -#[test_only] -public fun mock_for_test(ctx: &mut TxContext): BridgeTreasury { - let mut treasury = new_for_testing(ctx); - treasury.setup_for_testing(); - treasury -} - -#[test_only] -public fun setup_for_testing(treasury: &mut BridgeTreasury) { - treasury - .supported_tokens - .insert( - type_name::get(), - BridgeTokenMetadata { - id: 1, - decimal_multiplier: 100_000_000, - notional_value: 50_000 * USD_VALUE_MULTIPLIER, - native_token: false, - }, - ); - treasury - .supported_tokens - .insert( - type_name::get(), - BridgeTokenMetadata { - id: 2, - decimal_multiplier: 100_000_000, - notional_value: 3_000 * USD_VALUE_MULTIPLIER, - native_token: false, - }, - ); - treasury - .supported_tokens - .insert( - type_name::get(), - BridgeTokenMetadata { - id: 3, - decimal_multiplier: 1_000_000, - notional_value: USD_VALUE_MULTIPLIER, - native_token: false, - }, - ); - treasury - .supported_tokens - .insert( - type_name::get(), - BridgeTokenMetadata { - id: 4, - decimal_multiplier: 1_000_000, - notional_value: USD_VALUE_MULTIPLIER, - native_token: false, - }, - ); - - treasury.id_token_type_map.insert(1, type_name::get()); - treasury.id_token_type_map.insert(2, type_name::get()); - treasury.id_token_type_map.insert(3, type_name::get()); - treasury.id_token_type_map.insert(4, type_name::get()); -} - -#[test_only] -public fun waiting_room(treasury: &BridgeTreasury): &Bag { - &treasury.waiting_room -} - -#[test_only] -public fun treasuries(treasury: &BridgeTreasury): &ObjectBag { - &treasury.treasuries -} - -#[test_only] -public fun unwrap_update_event(event: UpdateTokenPriceEvent): (u8, u64) { - (event.token_id, event.new_price) -} - -#[test_only] -public fun unwrap_new_token_event(event: NewTokenEvent): (u8, TypeName, bool, u64, u64) { - ( - event.token_id, - event.type_name, - event.native_token, - event.decimal_multiplier, - event.notional_value, - ) -} - -#[test_only] -public fun unwrap_registration_event(event: TokenRegistrationEvent): (TypeName, u8, bool) { - (event.type_name, event.decimal, event.native_token) -} diff --git a/crates/iota-framework/packages/bridge/tests/bridge_env.test.move b/crates/iota-framework/packages/bridge/tests/bridge_env.test.move deleted file mode 100644 index f3237189ba0..00000000000 --- a/crates/iota-framework/packages/bridge/tests/bridge_env.test.move +++ /dev/null @@ -1,1241 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::bridge_env { - use bridge::bridge::{ - assert_not_paused, - assert_paused, - create_bridge_for_testing, - inner_token_transfer_records, - test_init_bridge_committee, - test_load_inner_mut, - Bridge, - EmergencyOpEvent, - TokenDepositedEvent, - TokenTransferAlreadyApproved, - TokenTransferAlreadyClaimed, - TokenTransferApproved, - TokenTransferClaimed, - TokenTransferLimitExceed - }; - use bridge::btc::{Self, BTC}; - use bridge::chain_ids; - use bridge::committee::BlocklistValidatorEvent; - use bridge::eth::{Self, ETH}; - use bridge::limiter::UpdateRouteLimitEvent; - use bridge::message::{ - Self, - BridgeMessage, - create_add_tokens_on_iota_message, - create_blocklist_message, - emergency_op_pause, - emergency_op_unpause - }; - use bridge::message_types; - use bridge::test_token::{Self, TEST_TOKEN}; - use bridge::treasury::{ - TokenRegistrationEvent, - NewTokenEvent, - UpdateTokenPriceEvent - }; - use bridge::usdc::{Self, USDC}; - use bridge::usdt::{Self, USDT}; - use std::ascii::String; - use std::type_name; - use iota::address; - use iota::clock::Clock; - use iota::coin::{Self, Coin, CoinMetadata, TreasuryCap}; - use iota::ecdsa_k1::{KeyPair, secp256k1_keypair_from_seed, secp256k1_sign}; - use iota::event; - use iota::package::UpgradeCap; - use iota::test_scenario::{Self, Scenario}; - use iota::test_utils::destroy; - use iota_system::governance_test_utils::{ - advance_epoch_with_balanced_reward_amounts, - create_iota_system_state_for_testing, - create_validator_for_testing - }; - use iota_system::iota_system::{ - validator_voting_powers_for_testing, - IotaSystemState - }; - - // - // Token IDs - // - const BTC_ID: u8 = 1; - const ETH_ID: u8 = 2; - const USDC_ID: u8 = 3; - const USDT_ID: u8 = 4; - const TEST_TOKEN_ID: u8 = 5; - - public fun btc_id(): u8 { - BTC_ID - } - - public fun eth_id(): u8 { - ETH_ID - } - - public fun usdc_id(): u8 { - USDC_ID - } - - public fun usdt_id(): u8 { - USDT_ID - } - - public fun test_token_id(): u8 { - TEST_TOKEN_ID - } - - // - // Claim status - // - const CLAIMED: u8 = 1; - const ALREADY_CLAIMED: u8 = 2; - const LIMIT_EXCEEDED: u8 = 3; - - public fun claimed(): u8 { - CLAIMED - } - - public fun already_claimed(): u8 { - ALREADY_CLAIMED - } - - public fun limit_exceeded(): u8 { - LIMIT_EXCEEDED - } - - // - // Approve status - // - const APPROVED: u8 = 1; - const ALREADY_APPROVED: u8 = 2; - - public fun approved(): u8 { - APPROVED - } - - public fun already_approved(): u8 { - ALREADY_APPROVED - } - - // - // Validators setup and info - // - - // Validator info - public struct ValidatorInfo has drop { - validator: address, - key_pair: KeyPair, - stake_amount: u64, - } - - public fun addr(validator: &ValidatorInfo): address { - validator.validator - } - - public fun public_key(validator: &ValidatorInfo): &vector { - validator.key_pair.public_key() - } - - public fun create_validator( - validator: address, - stake_amount: u64, - seed: &vector, - ): ValidatorInfo { - ValidatorInfo { - validator, - key_pair: secp256k1_keypair_from_seed(seed), - stake_amount, - } - } - - // Bridge environment - public struct BridgeEnv { - scenario: Scenario, - validators: vector, - chain_id: u8, - vault: Vault, - clock: Clock, - } - - // Holds coins for different bridged tokens - public struct Vault { - btc_coins: Coin, - eth_coins: Coin, - usdc_coins: Coin, - usdt_coins: Coin, - test_coins: Coin, - } - - // HotPotato to access shared state - // TODO: if the bridge is the only shared state we could remove this - public struct BridgeWrapper { - bridge: Bridge, - } - - public fun bridge(env: &mut BridgeEnv, sender: address): BridgeWrapper { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let bridge = scenario.take_shared(); - BridgeWrapper { bridge } - } - - public fun bridge_ref(wrapper: &BridgeWrapper): &Bridge { - &wrapper.bridge - } - - public fun bridge_ref_mut(wrapper: &mut BridgeWrapper): &mut Bridge { - &mut wrapper.bridge - } - - public fun return_bridge(bridge: BridgeWrapper) { - let BridgeWrapper { bridge } = bridge; - test_scenario::return_shared(bridge); - } - - // - // Public functions - // - - // - // Environment creation and destruction - // - - public fun create_env(chain_id: u8): BridgeEnv { - let mut scenario = test_scenario::begin(@0x0); - let ctx = scenario.ctx(); - let mut clock = iota::clock::create_for_testing(ctx); - clock.set_for_testing(1_000_000_000); - let btc_coins = coin::zero(ctx); - let eth_coins = coin::zero(ctx); - let usdc_coins = coin::zero(ctx); - let usdt_coins = coin::zero(ctx); - let test_coins = coin::zero(ctx); - let vault = Vault { - btc_coins, - eth_coins, - usdc_coins, - usdt_coins, - test_coins, - }; - BridgeEnv { - scenario, - chain_id, - vault, - validators: vector::empty(), - clock, - } - } - - public fun destroy_env(env: BridgeEnv) { - let BridgeEnv { - scenario, - chain_id: _, - vault, - validators: _, - clock, - } = env; - destroy_valut(vault); - clock.destroy_for_testing(); - scenario.end(); - } - - // - // Add a set of validators to the chain. - // Call only once in a test scenario. - public fun setup_validators( - env: &mut BridgeEnv, - validators_info: vector, - ) { - let scenario = &mut env.scenario; - scenario.next_tx(@0x0); - let ctx = scenario.ctx(); - let validators = validators_info.map_ref!( - |validator| { - create_validator_for_testing( - validator.validator, - validator.stake_amount, - ctx, - ) - }, - ); - env.validators = validators_info; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, scenario); - } - - // - // Bridge creation and setup - // - - // Set up an environment with 3 validators, a bridge with - // a treasury and a committee with all 3 validators. - // The treasury will contain 4 tokens: ETH, BTC, USDT, USDC. - // Save the Bridge as a shared object. - public fun create_bridge_default(env: &mut BridgeEnv) { - let validators = vector[ - create_validator( - @0xAAAA, - 100, - &b"1234567890_1234567890_1234567890", - ), - create_validator( - @0xBBBB, - 100, - &b"234567890_1234567890_1234567890_", - ), - create_validator( - @0xCCCC, - 100, - &b"34567890_1234567890_1234567890_1", - ), - ]; - env.setup_validators(validators); - - let sender = @0x0; - env.create_bridge(sender); - env.register_committee(); - env.init_committee(sender); - env.setup_treasury(sender); - } - - // Create a bridge and set up a treasury. - // The treasury will contain 4 tokens: ETH, BTC, USDT, USDC. - // Save the Bridge as a shared object. - // No operation on the validators. - public fun create_bridge(env: &mut BridgeEnv, sender: address) { - env.scenario.next_tx(sender); - let ctx = env.scenario.ctx(); - create_bridge_for_testing(object::new(ctx), env.chain_id, ctx); - } - - // Register 3 committee members (validators `@0xA`, `@0xB`, `@0xC`) - public fun register_committee(env: &mut BridgeEnv) { - let scenario = &mut env.scenario; - scenario.next_tx(@0x0); - let mut bridge = scenario.take_shared(); - let mut system_state = test_scenario::take_shared( - scenario, - ); - - env - .validators - .do_ref!( - |validator| { - scenario.next_tx(validator.validator); - bridge.committee_registration( - &mut system_state, - *validator.key_pair.public_key(), - b"", - scenario.ctx(), - ); - }, - ); - - test_scenario::return_shared(bridge); - test_scenario::return_shared(system_state); - } - - // Init the bridge committee - public fun init_committee(env: &mut BridgeEnv, sender: address) { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let mut system_state = test_scenario::take_shared( - scenario, - ); - let voting_powers = validator_voting_powers_for_testing( - &mut system_state, - ); - bridge.test_init_bridge_committee( - voting_powers, - 50, - scenario.ctx(), - ); - test_scenario::return_shared(bridge); - test_scenario::return_shared(system_state); - } - - // Set up a treasury with 4 tokens: ETH, BTC, USDT, USDC. - public fun setup_treasury(env: &mut BridgeEnv, sender: address) { - env.register_default_tokens(sender); - env.add_default_tokens(sender); - env.load_vault(sender); - } - - // Register 4 tokens with the Bridge: ETH, BTC, USDT, USDC. - fun register_default_tokens(env: &mut BridgeEnv, sender: address) { - env.scenario.next_tx(sender); - let mut bridge = env.scenario.take_shared(); - - // BTC - let (upgrade_cap, treasury_cap, metadata) = btc::create_bridge_token(env - .scenario - .ctx()); - bridge.register_foreign_token( - treasury_cap, - upgrade_cap, - &metadata, - ); - destroy(metadata); - // ETH - let (upgrade_cap, treasury_cap, metadata) = eth::create_bridge_token(env - .scenario - .ctx()); - bridge.register_foreign_token( - treasury_cap, - upgrade_cap, - &metadata, - ); - destroy(metadata); - // USDC - let ( - upgrade_cap, - treasury_cap, - metadata, - ) = usdc::create_bridge_token(env.scenario.ctx()); - bridge.register_foreign_token( - treasury_cap, - upgrade_cap, - &metadata, - ); - destroy(metadata); - // USDT - let ( - upgrade_cap, - treasury_cap, - metadata, - ) = usdt::create_bridge_token(env.scenario.ctx()); - bridge.register_foreign_token( - treasury_cap, - upgrade_cap, - &metadata, - ); - destroy(metadata); - - test_scenario::return_shared(bridge); - } - - // Add the 4 tokens previously registered: ETH, BTC, USDT, USDC. - fun add_default_tokens(env: &mut BridgeEnv, sender: address) { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - - let add_token_message = create_add_tokens_on_iota_message( - env.chain_id, - bridge.get_seq_num_for(message_types::add_tokens_on_iota()), - false, - vector[BTC_ID, ETH_ID, USDC_ID, USDT_ID], - vector[ - type_name::get().into_string(), - type_name::get().into_string(), - type_name::get().into_string(), - type_name::get().into_string(), - ], - vector[1000, 100, 1, 1], - ); - let signatures = env.sign_message(add_token_message); - bridge.execute_system_message(add_token_message, signatures); - - test_scenario::return_shared(bridge); - } - - // - // Utility functions for custom behavior - // - - public fun token_type(env: &mut BridgeEnv): u8 { - env.scenario.next_tx(@0x0); - let bridge = env.scenario.take_shared(); - let inner = bridge.test_load_inner(); - let token_id = inner.inner_treasury().token_id(); - test_scenario::return_shared(bridge); - token_id - } - - const IOTA_MESSAGE_PREFIX: vector = b"IOTA_BRIDGE_MESSAGE"; - - fun sign_message( - env: &BridgeEnv, - message: BridgeMessage, - ): vector> { - let mut message_bytes = IOTA_MESSAGE_PREFIX; - message_bytes.append(message.serialize_message()); - let mut message_bytes = IOTA_MESSAGE_PREFIX; - message_bytes.append(message.serialize_message()); - env - .validators - .map_ref!( - |validator| { - secp256k1_sign( - validator.key_pair.private_key(), - &message_bytes, - 0, - true, - ) - }, - ) - } - - public fun sign_message_with( - env: &BridgeEnv, - message: BridgeMessage, - validator_idxs: vector, - ): vector> { - let mut message_bytes = IOTA_MESSAGE_PREFIX; - message_bytes.append(message.serialize_message()); - validator_idxs.map!( - |idx| { - secp256k1_sign( - env.validators[idx].key_pair.private_key(), - &message_bytes, - 0, - true, - ) - }, - ) - } - - public fun bridge_in_message( - env: &mut BridgeEnv, - source_chain: u8, - source_address: vector, - target_address: address, - amount: u64, - ): BridgeMessage { - let token_type = env.token_type(); - - let scenario = &mut env.scenario; - scenario.next_tx(@0x0); - let mut bridge = scenario.take_shared(); - - let message = message::create_token_bridge_message( - source_chain, - bridge.get_seq_num_inc_for(message_types::token()), - source_address, - env.chain_id, - address::to_bytes(target_address), - token_type, - amount, - ); - test_scenario::return_shared(bridge); - message - } - - public fun bridge_out_message( - env: &mut BridgeEnv, - target_chain: u8, - target_address: vector, - source_address: address, - amount: u64, - transfer_id: u64, - ): BridgeMessage { - let token_type = env.token_type(); - - let scenario = &mut env.scenario; - scenario.next_tx(@0x0); - let bridge = scenario.take_shared(); - - let message = message::create_token_bridge_message( - env.chain_id, - transfer_id, - address::to_bytes(source_address), - target_chain, - target_address, - token_type, - amount, - ); - test_scenario::return_shared(bridge); - message - } - - public fun bridge_token_signed_message( - env: &mut BridgeEnv, - source_chain: u8, - source_address: vector, - target_address: address, - amount: u64, - ): (BridgeMessage, vector>) { - let token_type = env.token_type(); - let scenario = &mut env.scenario; - scenario.next_tx(@0x0); - let mut bridge = scenario.take_shared(); - let seq_num = bridge.get_seq_num_inc_for(message_types::token()); - test_scenario::return_shared(bridge); - let message = message::create_token_bridge_message( - source_chain, - seq_num, - source_address, - env.chain_id, - address::to_bytes(target_address), - token_type, - amount, - ); - let signatures = env.sign_message(message); - (message, signatures) - } - - // Bridge the `amount` of the given `Token` from the `source_chain`. - public fun bridge_to_iota( - env: &mut BridgeEnv, - source_chain: u8, - source_address: vector, - target_address: address, - amount: u64, - ): u64 { - let token_type = env.token_type(); - - // setup - let scenario = &mut env.scenario; - scenario.next_tx(@0x0); - let mut bridge = scenario.take_shared(); - - // sign message - let seq_num = bridge.get_seq_num_inc_for(message_types::token()); - let message = message::create_token_bridge_message( - source_chain, - seq_num, - source_address, - env.chain_id, - address::to_bytes(target_address), - token_type, - amount, - ); - let signatures = env.sign_message(message); - - // run approval - bridge.approve_token_transfer(message, signatures); - - // verify approval events - let approved_events = event::events_by_type(); - let already_approved_events = event::events_by_type< - TokenTransferAlreadyApproved, - >(); - assert!( - approved_events.length() == 1 || - already_approved_events.length() == 1, - ); - let key = if (approved_events.length() == 1) { - approved_events[0].transfer_approve_key() - } else { - already_approved_events[0].transfer_already_approved_key() - }; - let (sc, mt, sn) = key.unpack_message(); - assert!(source_chain == sc); - assert!(mt == message_types::token()); - assert!(sn == seq_num); - - // tear down - test_scenario::return_shared(bridge); - seq_num - } - - // Approves a token transfer - public fun approve_token_transfer( - env: &mut BridgeEnv, - message: BridgeMessage, - signatures: vector>, - ): u8 { - let msg_key = message.key(); - - // set up - let scenario = &mut env.scenario; - scenario.next_tx(@0x0); - let mut bridge = scenario.take_shared(); - - // run approval - bridge.approve_token_transfer(message, signatures); - - // verify approval events - let approved = event::events_by_type(); - let already_approved = event::events_by_type< - TokenTransferAlreadyApproved, - >(); - assert!(approved.length() == 1 || already_approved.length() == 1); - let (key, approve_status) = if (approved.length() == 1) { - (approved[0].transfer_approve_key(), APPROVED) - } else { - ( - already_approved[0].transfer_already_approved_key(), - ALREADY_APPROVED, - ) - }; - assert!(msg_key == key); - - // tear down - test_scenario::return_shared(bridge); - approve_status - } - - // Clain a token transfer and returns the coin - public fun claim_token( - env: &mut BridgeEnv, - sender: address, - source_chain: u8, - bridge_seq_num: u64, - ): Coin { - // set up - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let clock = &env.clock; - let mut bridge = scenario.take_shared(); - let ctx = scenario.ctx(); - let total_supply_before = get_total_supply(&bridge); - - // run claim - let token = bridge.claim_token( - clock, - source_chain, - bridge_seq_num, - ctx, - ); - - // verify value change and claim events - let token_value = token.value(); - assert!( - total_supply_before + token_value == get_total_supply(&bridge), - ); - let claimed = event::events_by_type(); - let already_claimed = event::events_by_type< - TokenTransferAlreadyClaimed, - >(); - let limit_exceeded = event::events_by_type(); - assert!( - claimed.length() == 1 || already_claimed.length() == 1 || - limit_exceeded.length() == 1, - ); - let key = if (claimed.length() == 1) { - claimed[0].transfer_claimed_key() - } else if (already_claimed.length() == 1) { - already_claimed[0].transfer_already_claimed_key() - } else { - limit_exceeded[0].transfer_limit_exceed_key() - }; - let (sc, mt, sn) = key.unpack_message(); - assert!(source_chain == sc); - assert!(mt == message_types::token()); - assert!(sn == bridge_seq_num); - - // tear down - test_scenario::return_shared(bridge); - token - } - - // Claim a token and transfer to the receiver in the bridge message - public fun claim_and_transfer_token( - env: &mut BridgeEnv, - source_chain: u8, - bridge_seq_num: u64, - ): u8 { - // set up - let sender = @0xA1B2C3; // random sender - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let clock = &env.clock; - let mut bridge = scenario.take_shared(); - let ctx = scenario.ctx(); - let total_supply_before = get_total_supply(&bridge); - - // run claim and transfer - bridge.claim_and_transfer_token( - clock, - source_chain, - bridge_seq_num, - ctx, - ); - - // verify claim events - let claimed = event::events_by_type(); - let already_claimed = event::events_by_type< - TokenTransferAlreadyClaimed, - >(); - let limit_exceeded = event::events_by_type(); - assert!( - claimed.length() == 1 || already_claimed.length() == 1 || - limit_exceeded.length() == 1, - ); - let (key, claim_status) = if (claimed.length() == 1) { - (claimed[0].transfer_claimed_key(), CLAIMED) - } else if (already_claimed.length() == 1) { - (already_claimed[0].transfer_already_claimed_key(), ALREADY_CLAIMED) - } else { - (limit_exceeded[0].transfer_limit_exceed_key(), LIMIT_EXCEEDED) - }; - let (sc, mt, sn) = key.unpack_message(); - assert!(source_chain == sc); - assert!(mt == message_types::token()); - assert!(sn == bridge_seq_num); - - // verify effects - let effects = scenario.next_tx(@0xABCDEF); - let created = effects.created(); - if (!created.is_empty()) { - let token_id = effects.created()[0]; - let token = scenario.take_from_sender_by_id>(token_id); - let token_value = token.value(); - assert!( - total_supply_before + token_value == - get_total_supply(&bridge), - ); - scenario.return_to_sender(token); - }; - - // tear down - test_scenario::return_shared(bridge); - claim_status - } - - // Send a coin (token) to the target chain - public fun send_token( - env: &mut BridgeEnv, - sender: address, - target_chain_id: u8, - eth_address: vector, - coin: Coin, - ): u64 { - // set up - let chain_id = env.chain_id; - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let coin_value = coin.value(); - let total_supply_before = get_total_supply(&bridge); - let seq_num = bridge.get_seq_num_for(message_types::token()); - - // run send - bridge.send_token(target_chain_id, eth_address, coin, scenario.ctx()); - - // verify send events - assert!( - total_supply_before - coin_value == get_total_supply(&bridge), - ); - let deposited_events = event::events_by_type(); - assert!(deposited_events.length() == 1); - let ( - event_seq_num, - _event_source_chain, - _event_sender_address, - _event_target_chain, - _event_target_address, - _event_token_type, - event_amount, - ) = deposited_events[0].unwrap_deposited_event(); - assert!(event_seq_num == seq_num); - assert!(event_amount == coin_value); - assert_key(chain_id, &bridge); - - // tear down - test_scenario::return_shared(bridge); - seq_num - } - - // Update the limit for a given route - public fun update_bridge_limit( - env: &mut BridgeEnv, - sender: address, - receiving_chain: u8, - sending_chain: u8, - limit: u64, - ): u64 { - // set up - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - - // message signed - let msg = message::create_update_bridge_limit_message( - receiving_chain, - bridge.get_seq_num_for(message_types::update_bridge_limit()), - sending_chain, - limit, - ); - let signatures = env.sign_message(msg); - - // run limit update - bridge.execute_system_message(msg, signatures); - - // verify limit events - let limit_events = event::events_by_type(); - assert!(limit_events.length() == 1); - let event = limit_events[0]; - let (sc, rc, new_limit) = event.unpack_route_limit_event(); - assert!(sc == sending_chain); - assert!(rc == receiving_chain); - assert!(new_limit == limit); - - // tear down - test_scenario::return_shared(bridge); - new_limit - } - - // Update a given asset price (notional value) - public fun update_asset_price( - env: &mut BridgeEnv, - sender: address, - token_id: u8, - value: u64, - ) { - // set up - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - - // message signed - let message = message::create_update_asset_price_message( - token_id, - env.chain_id, - bridge.get_seq_num_for(message_types::update_asset_price()), - value, - ); - let signatures = env.sign_message(message); - - // run price update - bridge.execute_system_message(message, signatures); - - // verify price events - let update_events = event::events_by_type(); - assert!(update_events.length() == 1); - let (event_token_id, event_new_price) = update_events[ - 0 - ].unwrap_update_event(); - assert!(event_token_id == token_id); - assert!(event_new_price == value); - - // tear down - test_scenario::return_shared(bridge); - } - - // Register the `TEST_TOKEN` token - public fun register_test_token(env: &mut BridgeEnv) { - // set up - let scenario = &mut env.scenario; - scenario.next_tx(@0x0); - let mut bridge = scenario.take_shared(); - - // "create" the `Coin` - let ( - upgrade_cap, - treasury_cap, - metadata, - ) = test_token::create_bridge_token(scenario.ctx()); - // register the coin/token with the bridge - bridge.register_foreign_token( - treasury_cap, - upgrade_cap, - &metadata, - ); - - // verify registration events - let register_events = event::events_by_type(); - assert!(register_events.length() == 1); - let (type_name, decimal, nat) = register_events[ - 0 - ].unwrap_registration_event(); - assert!(type_name == type_name::get()); - assert!(decimal == 8); - assert!(nat == false); - - // tear down - destroy(metadata); - test_scenario::return_shared(bridge); - } - - // Add a list of tokens to the bridge. - public fun add_tokens( - env: &mut BridgeEnv, - sender: address, - native_token: bool, - token_ids: vector, - type_names: vector, - token_prices: vector, - ) { - // set up - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - - // message signed - let message = create_add_tokens_on_iota_message( - env.chain_id, - bridge.get_seq_num_for(message_types::add_tokens_on_iota()), - native_token, - token_ids, - type_names, - token_prices, - ); - let signatures = env.sign_message(message); - - // run token addition - bridge.execute_system_message(message, signatures); - - // verify token addition events - let new_tokens_events = event::events_by_type(); - assert!(new_tokens_events.length() <= token_ids.length()); - - // tear down - test_scenario::return_shared(bridge); - } - - // Blocklist a list of bridge nodes - public fun execute_blocklist( - env: &mut BridgeEnv, - sender: address, - chain_id: u8, - blocklist_type: u8, - validator_ecdsa_addresses: vector>, - ) { - // set up - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - - // message signed - let blocklist = create_blocklist_message( - chain_id, - bridge.get_seq_num_for(message_types::committee_blocklist()), - blocklist_type, - validator_ecdsa_addresses, - ); - let signatures = env.sign_message(blocklist); - - // run blocklist - bridge.execute_system_message(blocklist, signatures); - - // verify blocklist events - let block_list_events = event::events_by_type< - BlocklistValidatorEvent, - >(); - assert!( - block_list_events.length() == validator_ecdsa_addresses.length(), - ); - - // tear down - test_scenario::return_shared(bridge); - } - - // Register new token - public fun register_foreign_token( - env: &mut BridgeEnv, - treasury_cap: TreasuryCap, - upgrade_cap: UpgradeCap, - metadata: CoinMetadata, - sender: address, - ) { - // set up - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - - // run registration - bridge.register_foreign_token(treasury_cap, upgrade_cap, &metadata); - - // verify registration events - let register_events = event::events_by_type(); - assert!(register_events.length() == 1); - - // verify changes in bridge - let type_name = type_name::get(); - let inner = bridge.test_load_inner(); - let treasury = inner.inner_treasury(); - let waiting_room = treasury.waiting_room(); - assert!(waiting_room.contains(type_name::into_string(type_name))); - let treasuries = treasury.treasuries(); - assert!(treasuries.contains(type_name)); - - // tear down - test_scenario::return_shared(bridge); - destroy(metadata); - } - - // Freeze the bridge - public fun freeze_bridge(env: &mut BridgeEnv, sender: address, error: u64) { - // set up - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let seq_num = bridge.get_seq_num_for(message_types::emergency_op()); - - // message signed - let msg = message::create_emergency_op_message( - env.chain_id, - seq_num, - emergency_op_pause(), - ); - let signatures = env.sign_message(msg); - - // run freeze - bridge.execute_system_message(msg, signatures); - - // verify freeze events - let register_events = event::events_by_type(); - assert!(register_events.length() == 1); - assert!(register_events[0].unwrap_emergency_op_event() == true); - - // verify freeze - let inner = bridge.test_load_inner_mut(); - inner.assert_paused(error); - - // tear down - test_scenario::return_shared(bridge); - } - - // Unfreeze the bridge - public fun unfreeze_bridge( - env: &mut BridgeEnv, - sender: address, - error: u64, - ) { - // set up - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let seq_num = bridge.get_seq_num_for(message_types::emergency_op()); - - // message signed - let msg = message::create_emergency_op_message( - env.chain_id, - seq_num, - emergency_op_unpause(), - ); - let signatures = env.sign_message(msg); - - // run unfreeze - bridge.execute_system_message(msg, signatures); - let register_events = event::events_by_type(); - assert!(register_events.length() == 1); - assert!(register_events[0].unwrap_emergency_op_event() == false); - - // verify unfreeze events - - // verify unfreeze - let inner = bridge.test_load_inner_mut(); - inner.assert_not_paused(error); - - // tear down - test_scenario::return_shared(bridge); - } - - // - // Getters - // - - public fun ctx(env: &mut BridgeEnv): &mut TxContext { - env.scenario.ctx() - } - - public fun scenario(env: &mut BridgeEnv): &mut Scenario { - &mut env.scenario - } - - public fun chain_id(env: &mut BridgeEnv): u8 { - env.chain_id - } - - public fun validators(env: &BridgeEnv): &vector { - &env.validators - } - - public fun get_btc(env: &mut BridgeEnv, amount: u64): Coin { - let scenario = &mut env.scenario; - let ctx = scenario.ctx(); - env.vault.btc_coins.split(amount, ctx) - } - - public fun get_eth(env: &mut BridgeEnv, amount: u64): Coin { - let scenario = &mut env.scenario; - let ctx = scenario.ctx(); - env.vault.eth_coins.split(amount, ctx) - } - - public fun get_usdc(env: &mut BridgeEnv, amount: u64): Coin { - let scenario = &mut env.scenario; - let ctx = scenario.ctx(); - env.vault.usdc_coins.split(amount, ctx) - } - - public fun get_usdt(env: &mut BridgeEnv, amount: u64): Coin { - let scenario = &mut env.scenario; - let ctx = scenario.ctx(); - env.vault.usdt_coins.split(amount, ctx) - } - - public fun limits(env: &mut BridgeEnv, dest: u8): u64 { - let scenario = env.scenario(); - scenario.next_tx(@0x0); - let bridge = scenario.take_shared(); - let route = chain_ids::get_route(dest, env.chain_id); - let limits = bridge - .test_load_inner() - .inner_limiter() - .get_route_limit(&route); - test_scenario::return_shared(bridge); - limits - } - - fun assert_key(chain_id: u8, bridge: &Bridge) { - let inner = bridge.test_load_inner(); - let transfer_record = inner.inner_token_transfer_records(); - let seq_num = inner.sequence_nums()[&message_types::token()] - 1; - let key = message::create_key( - chain_id, - message_types::token(), - seq_num, - ); - assert!(transfer_record.contains(key)); - } - - // - // Internal functions - // - - // Destroy the vault - fun destroy_valut(vault: Vault) { - let Vault { - btc_coins, - eth_coins, - usdc_coins, - usdt_coins, - test_coins, - } = vault; - btc_coins.burn_for_testing(); - eth_coins.burn_for_testing(); - usdc_coins.burn_for_testing(); - usdt_coins.burn_for_testing(); - test_coins.burn_for_testing(); - } - - // Load the vault with some coins - fun load_vault(env: &mut BridgeEnv, sender: address) { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let vault = &mut env.vault; - vault.btc_coins.join(mint_some(&mut bridge, scenario.ctx())); - vault.eth_coins.join(mint_some(&mut bridge, scenario.ctx())); - vault.usdc_coins.join(mint_some(&mut bridge, scenario.ctx())); - vault.usdt_coins.join(mint_some(&mut bridge, scenario.ctx())); - test_scenario::return_shared(bridge); - } - - // Mint some coins - fun mint_some(bridge: &mut Bridge, ctx: &mut TxContext): Coin { - let treasury = bridge.test_load_inner_mut().inner_treasury_mut(); - let coin = treasury.mint(1_000_000, ctx); - coin - } - - fun get_total_supply(bridge: &Bridge): u64 { - let inner = bridge.test_load_inner(); - let treasury = inner.inner_treasury(); - let treasuries = treasury.treasuries(); - let tc: &TreasuryCap = &treasuries[type_name::get()]; - tc.total_supply() - } -} \ No newline at end of file diff --git a/crates/iota-framework/packages/bridge/tests/bridge_tests.move b/crates/iota-framework/packages/bridge/tests/bridge_tests.move deleted file mode 100644 index ca34afb5046..00000000000 --- a/crates/iota-framework/packages/bridge/tests/bridge_tests.move +++ /dev/null @@ -1,729 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::bridge_tests; - -use bridge::bridge::{ - inner_limiter, - inner_paused, - inner_treasury, - inner_token_transfer_records_mut, - new_bridge_record_for_testing, - new_for_testing, - test_get_current_seq_num_and_increment, - test_execute_update_asset_price, - test_get_token_transfer_action_signatures, - test_load_inner, - test_load_inner_mut, - test_get_token_transfer_action_status, - transfer_status_approved, - transfer_status_claimed, - transfer_status_not_found, - transfer_status_pending, - Bridge -}; -use bridge::bridge_env::{ - btc_id, - create_bridge, - create_bridge_default, - create_env, - create_validator, - eth_id, - freeze_bridge, - init_committee, - register_committee, - unfreeze_bridge, - test_token_id -}; -use bridge::btc::BTC; -use bridge::chain_ids; -use bridge::eth::ETH; -use bridge::message::{Self, to_parsed_token_transfer_message}; -use bridge::message_types; -use bridge::test_token::{TEST_TOKEN, create_bridge_token as create_test_token}; -use bridge::usdc::USDC; -use iota::address; -use iota::balance; -use iota::coin::{Self, Coin}; -use iota::hex; -use iota::package::test_publish; -use iota::test_scenario; -use iota::test_utils::destroy; -use std::type_name; - -// common error start code for unexpected errors in tests (assertions). -// If more than one assert in a test needs to use an unexpected error code, -// use this as the starting error and add 1 to subsequent errors -const UNEXPECTED_ERROR: u64 = 10293847; -// use on tests that fail to save cleanup -const TEST_DONE: u64 = 74839201; - -#[test] -fun test_bridge_create() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge(@0x0); - - let bridge = env.bridge(@0x0); - let inner = bridge.bridge_ref().test_load_inner(); - inner.assert_not_paused(UNEXPECTED_ERROR); - assert!(inner.inner_token_transfer_records().length() == 0); - bridge.return_bridge(); - - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::ENotSystemAddress)] -fun test_bridge_create_non_system_addr() { - let mut env = create_env(chain_ids::iota_mainnet()); - env.create_bridge(@0x1); - - abort TEST_DONE -} - -#[test] -fun test_create_bridge_default() { - let mut env = create_env(chain_ids::iota_custom()); - env.create_bridge_default(); - env.destroy_env(); -} - -#[test] -fun test_init_committee_twice() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - env.init_committee(@0x0); // second time is a no-op - - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::ENotSystemAddress)] -fun test_init_committee_non_system_addr() { - let mut env = create_env(chain_ids::iota_mainnet()); - env.setup_validators(vector[create_validator(@0xA, 100, &b"12345678901234567890123456789012")]); - env.create_bridge(@0x0); - env.register_committee(); - env.init_committee(@0xA); - - abort TEST_DONE -} - -#[test] -#[expected_failure(abort_code = bridge::committee::ECommitteeAlreadyInitiated)] -fun test_register_committee_after_init() { - let mut env = create_env(chain_ids::iota_custom()); - env.create_bridge_default(); - env.register_committee(); - - abort TEST_DONE -} - -#[test] -fun test_register_foreign_token() { - let addr = @0x0; - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - let (upgrade_cap, treasury_cap, metadata) = create_test_token(env.scenario().ctx()); - env.register_foreign_token( - treasury_cap, - upgrade_cap, - metadata, - addr, - ); - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::treasury::ETokenSupplyNonZero)] -fun test_register_foreign_token_non_zero_supply() { - let addr = @0x0; - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - let (upgrade_cap, mut treasury_cap, metadata) = create_test_token(env.scenario().ctx()); - let _coin = treasury_cap.mint(1, env.scenario().ctx()); - env.register_foreign_token( - treasury_cap, - upgrade_cap, - metadata, - addr, - ); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::treasury::EInvalidNotionalValue)] -fun test_add_token_price_zero_value() { - let addr = @0x0; - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - env.add_tokens( - addr, - false, - vector[test_token_id()], - vector[type_name::get().into_string()], - vector[0], - ); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EMalformedMessageError)] -fun test_add_token_malformed_1() { - let addr = @0x0; - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - env.add_tokens( - addr, - false, - vector[test_token_id(), eth_id()], - vector[type_name::get().into_string()], - vector[10], - ); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EMalformedMessageError)] -fun test_add_token_malformed_2() { - let addr = @0x0; - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - env.add_tokens( - addr, - false, - vector[test_token_id()], - vector[type_name::get().into_string(), type_name::get().into_string()], - vector[10], - ); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EMalformedMessageError)] -fun test_add_token_malformed_3() { - let addr = @0x0; - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - env.add_tokens( - addr, - false, - vector[test_token_id()], - vector[type_name::get().into_string()], - vector[10, 20], - ); - - abort 0 -} - -#[test] -fun test_add_native_token_nop() { - // adding a native token is simply a NO-OP at the moment - let addr = @0x0; - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - env.add_tokens( - addr, - true, - vector[test_token_id()], - vector[type_name::get().into_string()], - vector[100], - ); - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::treasury::EInvalidUpgradeCap)] -fun test_register_foreign_token_bad_upgrade_cap() { - let addr = @0x0; - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - let (_upgrade_cap, treasury_cap, metadata) = create_test_token(env.scenario().ctx()); - let upgrade_cap = test_publish(@0x42.to_id(), env.scenario().ctx()); - env.register_foreign_token( - treasury_cap, - upgrade_cap, - metadata, - addr, - ); - - abort 0 -} - -#[test] -fun test_execute_send_token() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - let btc: Coin = env.get_btc(1); - let eth_address = x"0000000000000000000000000000000000000000"; - env.send_token(@0xABCD, chain_ids::eth_sepolia(), eth_address, btc); - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::ETokenValueIsZero)] -fun test_execute_send_token_zero_value() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - let btc: Coin = env.get_btc(0); - let eth_address = x"0000000000000000000000000000000000000000"; - env.send_token(@0x0, chain_ids::eth_sepolia(), eth_address, btc); - - abort TEST_DONE -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EInvalidEvmAddress)] -fun test_execute_send_token_invalid_evm_address() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - let btc: Coin = env.get_btc(1); - let eth_address = x"1234"; - let val_addr = env.validators()[0].addr(); - env.send_token(val_addr, chain_ids::eth_sepolia(), eth_address, btc); - - abort TEST_DONE -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EBridgeUnavailable)] -fun test_execute_send_token_frozen() { - let chain_id = chain_ids::iota_testnet(); - let mut env = create_env(chain_id); - env.create_bridge_default(); - let eth: Coin = env.get_eth(1); - let eth_address = x"0000000000000000000000000000000000000000"; - env.freeze_bridge(@0x0, UNEXPECTED_ERROR); - env.send_token(@0xAAAA, chain_ids::eth_sepolia(), eth_address, eth); - - abort TEST_DONE -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EInvalidBridgeRoute)] -fun test_execute_send_token_invalid_route() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - let usdc: Coin = env.get_usdc(100); - let eth_address = x"0000000000000000000000000000000000000000"; - env.send_token(@0xABCDEF, chain_ids::eth_mainnet(), eth_address, usdc); - - abort TEST_DONE -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EUnexpectedChainID)] -fun test_system_msg_incorrect_chain_id() { - let sender = @0x0; - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - env.execute_blocklist(sender, chain_ids::iota_mainnet(), 0, vector[]); - - abort TEST_DONE -} - -#[test] -fun test_get_seq_num_and_increment() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = scenario.ctx(); - let chain_id = chain_ids::iota_testnet(); - let mut bridge = new_for_testing(chain_id, ctx); - - let inner = bridge.test_load_inner_mut(); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::committee_blocklist(), - ) == - 0, - ); - assert!(inner.sequence_nums()[&message_types::committee_blocklist()] == 1); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::committee_blocklist(), - ) == - 1, - ); - // other message type nonce does not change - assert!(!inner.sequence_nums().contains(&message_types::token())); - assert!(!inner.sequence_nums().contains(&message_types::emergency_op())); - assert!(!inner.sequence_nums().contains(&message_types::update_bridge_limit())); - assert!(!inner.sequence_nums().contains(&message_types::update_asset_price())); - assert!(inner.test_get_current_seq_num_and_increment(message_types::token()) == - 0); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::emergency_op(), - ) == - 0, - ); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::update_bridge_limit(), - ) == - 0, - ); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::update_asset_price(), - ) == - 0, - ); - - destroy(bridge); - scenario.end(); -} - -#[test] -fun test_update_limit() { - let chain_id = chain_ids::iota_mainnet(); - let mut env = create_env(chain_id); - env.create_bridge_default(); - - let bridge = env.bridge(@0x0); - let inner = bridge.bridge_ref().test_load_inner(); - // Assert the starting limit is a different value - assert!( - inner - .inner_limiter() - .get_route_limit( - &chain_ids::get_route( - chain_ids::eth_mainnet(), - chain_ids::iota_mainnet(), - ), - ) != - 1, - ); - bridge.return_bridge(); - - // update limit - env.update_bridge_limit( - @0x0, - chain_ids::iota_mainnet(), - chain_ids::eth_mainnet(), - 1, - ); - - let bridge = env.bridge(@0x0); - let inner = bridge.bridge_ref().test_load_inner(); - // Assert the starting limit is a different value - assert!( - inner - .inner_limiter() - .get_route_limit( - &chain_ids::get_route( - chain_ids::eth_mainnet(), - chain_ids::iota_mainnet(), - ), - ) == - 1, - ); - // other routes are not impacted - assert!( - inner - .inner_limiter() - .get_route_limit( - &chain_ids::get_route( - chain_ids::eth_sepolia(), - chain_ids::iota_testnet(), - ), - ) != - 1, - ); - bridge.return_bridge(); - - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EUnexpectedChainID)] -fun test_execute_update_bridge_limit_abort_with_unexpected_chain_id() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - - // This abort because the receiving_chain (iota_mainnet) is not the same as - // the bridge's chain_id (iota_devnet) - env.update_bridge_limit( - @0x0, - chain_ids::iota_mainnet(), - chain_ids::eth_mainnet(), - 1, - ); - - abort TEST_DONE -} - -#[test] -fun test_update_asset_price() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - let scenario = env.scenario(); - scenario.next_tx(@0x0); - let mut bridge = scenario.take_shared(); - let inner = bridge.test_load_inner_mut(); - - // Assert the starting limit is a different value - assert!(inner.inner_treasury().notional_value() != 1_001_000_000); - // now change it to 100_001_000 - let msg = message::create_update_asset_price_message( - inner.inner_treasury().token_id(), - chain_ids::iota_mainnet(), - 0, - 1_001_000_000, - ); - let payload = msg.extract_update_asset_price(); - inner.test_execute_update_asset_price(payload); - - // should be 1_001_000_000 now - assert!(inner.inner_treasury().notional_value() == 1_001_000_000); - // other assets are not impacted - assert!(inner.inner_treasury().notional_value() != 1_001_000_000); - - destroy(bridge); - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::treasury::EInvalidNotionalValue)] -fun test_invalid_price_update() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - env.update_asset_price(@0x0, btc_id(), 0); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::treasury::EUnsupportedTokenType)] -fun test_unsupported_token_type() { - let mut env = create_env(chain_ids::iota_testnet()); - env.create_bridge_default(); - env.update_asset_price(@0x0, 42, 100); - - abort 0 -} - -#[test] -fun test_execute_freeze_unfreeze() { - let chain_id = chain_ids::iota_testnet(); - let mut env = create_env(chain_id); - env.create_bridge_default(); - env.freeze_bridge(@0x0, UNEXPECTED_ERROR + 1); - let bridge = env.bridge(@0x0); - assert!(bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - env.unfreeze_bridge(@0x0, UNEXPECTED_ERROR + 2); - let bridge = env.bridge(@0x0); - assert!(!bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EBridgeNotPaused)] -fun test_execute_unfreeze_err() { - let chain_id = chain_ids::iota_testnet(); - let mut env = create_env(chain_id); - env.create_bridge_default(); - let bridge = env.bridge(@0x0); - assert!(!bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - env.unfreeze_bridge(@0x0, UNEXPECTED_ERROR + 2); - - abort TEST_DONE -} - -#[test] -#[expected_failure(abort_code = bridge::bridge::EBridgeAlreadyPaused)] -fun test_execute_emergency_op_abort_when_already_frozen() { - let chain_id = chain_ids::iota_testnet(); - let mut env = create_env(chain_id); - env.create_bridge_default(); - - // initially it's unfrozen - let bridge = env.bridge(@0x0); - assert!(!bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - // freeze it - env.freeze_bridge(@0x0, UNEXPECTED_ERROR); - let bridge = env.bridge(@0x0); - assert!(bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - // freeze it again, should abort - env.freeze_bridge(@0x0, UNEXPECTED_ERROR); - - abort TEST_DONE -} - -#[test] -fun test_get_token_transfer_action_data() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = scenario.ctx(); - let chain_id = chain_ids::iota_testnet(); - let mut bridge = new_for_testing(chain_id, ctx); - let coin = coin::mint_for_testing(12345, ctx); - - // Test when pending - let message = message::create_token_bridge_message( - chain_ids::iota_testnet(), // source chain - 10, // seq_num - address::to_bytes(ctx.sender()), // sender address - chain_ids::eth_sepolia(), // target_chain - hex::decode( - b"00000000000000000000000000000000000000c8", - ), // target_address - 1u8, // token_type - coin.balance().value(), - ); - - let key = message.key(); - bridge - .test_load_inner_mut() - .inner_token_transfer_records_mut() - .push_back( - key, - new_bridge_record_for_testing(message, option::none(), false), - ); - assert!( - bridge.test_get_token_transfer_action_status(chain_id, 10) == - transfer_status_pending(), - ); - assert!( - bridge.test_get_token_transfer_action_signatures(chain_id, 10) == - option::none(), - ); - - // Test when ready for claim - let message = message::create_token_bridge_message( - chain_ids::iota_testnet(), // source chain - 11, // seq_num - address::to_bytes(ctx.sender()), // sender address - chain_ids::eth_sepolia(), // target_chain - hex::decode( - b"00000000000000000000000000000000000000c8", - ), // target_address - 1u8, // token_type - balance::value(coin::balance(&coin)), - ); - let key = message.key(); - bridge - .test_load_inner_mut() - .inner_token_transfer_records_mut() - .push_back( - key, - new_bridge_record_for_testing( - message, - option::some(vector[]), - false, - ), - ); - assert!( - bridge.test_get_token_transfer_action_status(chain_id, 11) == - transfer_status_approved(), - ); - assert!( - bridge.test_get_token_transfer_action_signatures(chain_id, 11) == - option::some(vector[]), - ); - assert!( - bridge.test_get_parsed_token_transfer_message(chain_id, 11) == - option::some( - to_parsed_token_transfer_message(&message), - ), - ); - - // Test when already claimed - let message = message::create_token_bridge_message( - chain_ids::iota_testnet(), // source chain - 12, // seq_num - address::to_bytes(ctx.sender()), // sender address - chain_ids::eth_sepolia(), // target_chain - hex::decode( - b"00000000000000000000000000000000000000c8", - ), // target_address - 1u8, // token_type - balance::value(coin::balance(&coin)), - ); - let key = message.key(); - bridge - .test_load_inner_mut() - .inner_token_transfer_records_mut() - .push_back( - key, - new_bridge_record_for_testing( - message, - option::some(vector[b"1234"]), - true, - ), - ); - assert!( - bridge.test_get_token_transfer_action_status(chain_id, 12) == - transfer_status_claimed(), - ); - assert!( - bridge.test_get_token_transfer_action_signatures(chain_id, 12) == - option::some(vector[b"1234"]), - ); - assert!( - bridge.test_get_parsed_token_transfer_message(chain_id, 12) == - option::some( - to_parsed_token_transfer_message(&message), - ), - ); - - // Test when message not found - assert!( - bridge.test_get_token_transfer_action_status(chain_id, 13) == - transfer_status_not_found(), - ); - assert!( - bridge.test_get_token_transfer_action_signatures(chain_id, 13) == - option::none(), - ); - assert!(bridge.test_get_parsed_token_transfer_message(chain_id, 13) == - option::none()); - - destroy(bridge); - coin.burn_for_testing(); - scenario.end(); -} - -#[test] -#[expected_failure(abort_code = bridge::treasury::EUnsupportedTokenType)] -fun test_get_metadata_no_token() { - let chain_id = chain_ids::iota_testnet(); - let mut env = create_env(chain_id); - env.create_bridge_default(); - let bridge = env.bridge(@0x0); - let treasury = bridge.bridge_ref().test_load_inner().inner_treasury(); - treasury.notional_value(); - - abort 0 -} - -#[test] -fun change_url() { - let chain_id = chain_ids::iota_testnet(); - let mut env = create_env(chain_id); - env.create_bridge_default(); - let mut bridge = env.bridge(@0xAAAA); - bridge.bridge_ref_mut().update_node_url(b"", env.scenario().ctx()); - bridge.return_bridge(); - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::committee::ESenderIsNotInBridgeCommittee)] -fun change_url_bad_sender() { - let chain_id = chain_ids::iota_testnet(); - let mut env = create_env(chain_id); - env.create_bridge_default(); - let mut bridge = env.bridge(@0x0); - bridge.bridge_ref_mut().update_node_url(b"", env.scenario().ctx()); - abort 0 -} diff --git a/crates/iota-framework/packages/bridge/tests/bridge_txns.move b/crates/iota-framework/packages/bridge/tests/bridge_txns.move deleted file mode 100644 index 1ac6f7a2dcf..00000000000 --- a/crates/iota-framework/packages/bridge/tests/bridge_txns.move +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::bridge_txns; - -use bridge::bridge_env::{ - already_approved, - already_claimed, - approved, - claimed, - create_bridge_default, - create_env, - create_validator, - eth_id, - limit_exceeded, - register_test_token, - test_token_id -}; -use bridge::chain_ids; -use bridge::crypto::ecdsa_pub_key_to_eth_address; -use bridge::eth::ETH; -use bridge::test_token::TEST_TOKEN; -use std::type_name; - -#[test] -fun test_limits() { - let mut env = create_env(chain_ids::iota_custom()); - env.create_bridge_default(); - - let source_chain = chain_ids::eth_custom(); - let iota_address = @0xABCDEF; - let eth_address = x"0000000000000000000000000000000000001234"; - - // lower limits - let chain_id = env.chain_id(); - env.update_bridge_limit(@0x0, chain_id, source_chain, 3000); - let transfer_id1 = env.bridge_to_iota( - source_chain, - eth_address, - iota_address, - 4000000000, - ); - let transfer_id2 = env.bridge_to_iota( - source_chain, - eth_address, - iota_address, - 1000, - ); - assert!( - env.claim_and_transfer_token(source_chain, transfer_id1) == - limit_exceeded(), - ); - assert!(env.claim_and_transfer_token(source_chain, transfer_id2) == - claimed()); - // double claim is ok and it is a no-op - assert!( - env.claim_and_transfer_token(source_chain, transfer_id2) == - already_claimed(), - ); - - // up limits to allow claim - env.update_bridge_limit(@0x0, chain_id, source_chain, 4000); - assert!(env.claim_and_transfer_token(source_chain, transfer_id1) == - claimed()); - - env.destroy_env(); -} - -#[test] -fun test_bridge_and_claim() { - let mut env = create_env(chain_ids::iota_custom()); - env.create_bridge_default(); - - let source_chain = chain_ids::eth_custom(); - let iota_address = @0xABCDEF; - let eth_address = x"0000000000000000000000000000000000001234"; - let amount = 1000; - - // - // move from eth and transfer to iota account - let transfer_id1 = env.bridge_to_iota( - source_chain, - eth_address, - iota_address, - amount, - ); - assert!(env.claim_and_transfer_token(source_chain, transfer_id1) == - claimed()); - let transfer_id2 = env.bridge_to_iota( - source_chain, - eth_address, - iota_address, - amount, - ); - assert!(env.claim_and_transfer_token(source_chain, transfer_id2) == - claimed()); - // double claim is ok and it is a no-op - assert!( - env.claim_and_transfer_token(source_chain, transfer_id2) == - already_claimed(), - ); - - // - // change order - let transfer_id1 = env.bridge_to_iota( - source_chain, - eth_address, - iota_address, - amount, - ); - let transfer_id2 = env.bridge_to_iota( - source_chain, - eth_address, - iota_address, - amount, - ); - assert!(env.claim_and_transfer_token(source_chain, transfer_id1) == - claimed()); - assert!(env.claim_and_transfer_token(source_chain, transfer_id2) == - claimed()); - - // - // move from eth and send it back - let transfer_id = env.bridge_to_iota( - source_chain, - eth_address, - iota_address, - amount, - ); - let token = env.claim_token(iota_address, source_chain, transfer_id); - env.send_token( - iota_address, - source_chain, - eth_address, - token, - ); - - // - // approve with subset of signatures - let message = env.bridge_in_message( - source_chain, - eth_address, - iota_address, - amount, - ); - let signatures = env.sign_message_with(message, vector[0, 2]); - let transfer_id = message.seq_num(); - assert!(env.approve_token_transfer(message, signatures) == approved()); - assert!(env.claim_and_transfer_token(source_chain, transfer_id) == - claimed()); - - // - // multiple approve with subset of signatures - let message = env.bridge_in_message( - source_chain, - eth_address, - iota_address, - amount, - ); - let signatures = env.sign_message_with(message, vector[0, 2]); - let transfer_id = message.seq_num(); - assert!(env.approve_token_transfer(message, signatures) == approved()); - assert!(env.approve_token_transfer(message, signatures) == already_approved()); - assert!(env.approve_token_transfer(message, signatures) == already_approved()); - let token = env.claim_token(iota_address, source_chain, transfer_id); - let send_token_id = env.send_token( - iota_address, - source_chain, - eth_address, - token, - ); - let message = env.bridge_out_message( - source_chain, - eth_address, - iota_address, - amount, - send_token_id, - ); - let signatures = env.sign_message_with(message, vector[1, 2]); - assert!(env.approve_token_transfer(message, signatures) == approved()); - let signatures = env.sign_message_with(message, vector[0, 2]); - assert!(env.approve_token_transfer(message, signatures) == already_approved()); - - // - // multiple approve with different subset of signatures - let message = env.bridge_in_message( - source_chain, - eth_address, - iota_address, - amount, - ); - let transfer_id = message.seq_num(); - let signatures = env.sign_message_with(message, vector[0, 2]); - assert!(env.approve_token_transfer(message, signatures) == approved()); - let signatures = env.sign_message_with(message, vector[0, 1]); - assert!(env.approve_token_transfer(message, signatures) == already_approved()); - let signatures = env.sign_message_with(message, vector[1, 2]); - assert!(env.approve_token_transfer(message, signatures) == already_approved()); - let token = env.claim_token(iota_address, source_chain, transfer_id); - env.send_token( - iota_address, - source_chain, - eth_address, - token, - ); - - env.destroy_env(); -} - -#[test] -#[expected_failure(abort_code = bridge::committee::ESignatureBelowThreshold)] -fun test_blocklist() { - let mut env = create_env(chain_ids::iota_custom()); - let validators = vector[ - create_validator(@0xAAAA, 100, &b"1234567890_1234567890_1234567890"), - create_validator(@0xBBBB, 100, &b"234567890_1234567890_1234567890_"), - create_validator(@0xCCCC, 100, &b"34567890_1234567890_1234567890_1"), - create_validator(@0xDDDD, 100, &b"4567890_1234567890_1234567890_12"), - ]; - env.setup_validators(validators); - - let sender = @0x0; - env.create_bridge(sender); - env.register_committee(); - env.init_committee(sender); - env.setup_treasury(sender); - - let source_chain = chain_ids::eth_custom(); - let iota_address = @0xABCDEF; - let eth_address = x"0000000000000000000000000000000000001234"; - let amount = 1000; - - // bridging in and out works - let message = env.bridge_in_message( - source_chain, - eth_address, - iota_address, - amount, - ); - let signatures = env.sign_message_with(message, vector[0, 2]); - let transfer_id = message.seq_num(); - assert!(env.approve_token_transfer(message, signatures) == approved()); - assert!(env.claim_and_transfer_token(source_chain, transfer_id) == - claimed()); - - // block bridge node 0 - let chain_id = env.chain_id(); - let node_key = ecdsa_pub_key_to_eth_address(env.validators()[0].public_key()); - env.execute_blocklist(@0x0, chain_id, 0, vector[node_key]); - - // signing with 2 valid bridge nodes works - let message = env.bridge_in_message( - source_chain, - eth_address, - iota_address, - amount, - ); - let signatures = env.sign_message_with(message, vector[1, 2]); - assert!(env.approve_token_transfer(message, signatures) == approved()); - assert!(env.approve_token_transfer(message, signatures) == already_approved()); - - // signing with blocked node fails - let message = env.bridge_in_message( - source_chain, - eth_address, - iota_address, - amount, - ); - let signatures = env.sign_message_with(message, vector[0, 2]); - env.approve_token_transfer(message, signatures); - - env.destroy_env(); -} - -#[test] -fun test_system_messages() { - let addr = @0xABCDEF0123; // random address - let mut env = create_env(chain_ids::iota_custom()); - env.create_bridge_default(); - - env.update_asset_price(addr, eth_id(), 735); - - env.register_test_token(); - env.add_tokens( - addr, - false, - vector[test_token_id()], - vector[type_name::get().into_string()], - vector[333], - ); - - let chain_id = env.chain_id(); - let node_key = ecdsa_pub_key_to_eth_address(env.validators()[0].public_key()); - env.execute_blocklist(@0x0, chain_id, 0, vector[node_key]); - - env.destroy_env(); -} diff --git a/crates/iota-framework/packages/bridge/tests/btc.test.move b/crates/iota-framework/packages/bridge/tests/btc.test.move deleted file mode 100644 index 44a58c9055d..00000000000 --- a/crates/iota-framework/packages/bridge/tests/btc.test.move +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::btc; - -use iota::address; -use iota::coin::{CoinMetadata, TreasuryCap, create_currency}; -use iota::hex; -use iota::package::{UpgradeCap, test_publish}; -use iota::test_utils::create_one_time_witness; -use std::ascii; -use std::type_name; - -public struct BTC has drop {} - -public fun create_bridge_token( - ctx: &mut TxContext, -): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 8, - b"btc", - b"bitcoin", - b"bridge bitcoin token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode( - ascii::into_bytes(type_name::get_address(&type_name)), - ); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) -} diff --git a/crates/iota-framework/packages/bridge/tests/committee_test.move b/crates/iota-framework/packages/bridge/tests/committee_test.move deleted file mode 100644 index 4505671af2d..00000000000 --- a/crates/iota-framework/packages/bridge/tests/committee_test.move +++ /dev/null @@ -1,660 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::committee_test; - - use iota::vec_map; - use iota_system::iota_system; - use iota_system::iota_system::IotaSystemState; - - use bridge::committee::{ - BridgeCommittee, CommitteeMember, blocklisted, bridge_pubkey_bytes, create, - members, member_registrations, - register, try_create_next_committee, verify_signatures, voting_power, - }; - use bridge::committee::execute_blocklist; - use bridge::committee::make_committee_member; - use bridge::committee::make_bridge_committee; - use bridge::crypto; - use bridge::message; - - use iota::{hex, test_scenario, test_utils::{Self, assert_eq}}; - use bridge::chain_ids; - use iota_system::governance_test_utils::{ - advance_epoch_with_balanced_reward_amounts, - create_iota_system_state_for_testing, - create_validator_for_testing - }; - -// This is a token transfer message for testing -const TEST_MSG: vector = - b"00010a0000000000000000200000000000000000000000000000000000000000000000000000000000000064012000000000000000000000000000000000000000000000000000000000000000c8033930000000000000"; - -const VALIDATOR1_PUBKEY: vector = - b"02708023b69a1c3a460c4c16ba36976fde0333c81eec253db9ae825782210f0d66"; -const VALIDATOR2_PUBKEY: vector = - b"030573cb972a57108488f5bdf10c4cbbb2cc3e81a8ff7dd23b7761411013664788"; -const VALIDATOR3_PUBKEY: vector = - b"033e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a63"; - -#[test] -fun test_verify_signatures_good_path() { - let committee = setup_test(); - let msg = message::deserialize_message_test_only(hex::decode(TEST_MSG)); - // good path - committee.verify_signatures( - msg, - vector[ - hex::decode( - b"29f7c5cee069946d134ffc6c3a386f2e0fdb7f6ae8a2b6bfb488b55f5347ba364090a7cc01db3dcecfd61810a3eb55806b38cbb8a54df727c1f4de56b3d1806201", - ), - hex::decode( - b"9925c68918d4a29670b77470fe43b3f3da1b4aff83d55676a4b75e6b0f2477fc4e2173ac8bfdb1e3cd0951b3738729136f7703b5b4bda86fd91c5c89be1f816901", - ), - ], - ); - - // Clean up - test_utils::destroy(committee) -} - -#[test] -#[expected_failure(abort_code = bridge::committee::EDuplicatedSignature)] -fun test_verify_signatures_duplicated_sig() { - let committee = setup_test(); - let msg = message::deserialize_message_test_only(hex::decode(TEST_MSG)); - // good path - committee.verify_signatures( - msg, - vector[ - hex::decode( - b"9925c68918d4a29670b77470fe43b3f3da1b4aff83d55676a4b75e6b0f2477fc4e2173ac8bfdb1e3cd0951b3738729136f7703b5b4bda86fd91c5c89be1f816901", - ), - hex::decode( - b"9925c68918d4a29670b77470fe43b3f3da1b4aff83d55676a4b75e6b0f2477fc4e2173ac8bfdb1e3cd0951b3738729136f7703b5b4bda86fd91c5c89be1f816901", - ), - ], - ); - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::committee::EInvalidSignature)] -fun test_verify_signatures_invalid_signature() { - let committee = setup_test(); - let msg = message::deserialize_message_test_only(hex::decode(TEST_MSG)); - // good path - committee.verify_signatures( - msg, - vector[ - hex::decode( - b"6ffb3e5ce04dd138611c49520fddfbd6778879c2db4696139f53a487043409536c369c6ffaca165ce3886723cfa8b74f3e043e226e206ea25e313ea2215e6caf01", - ), - ], - ); - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::committee::ESignatureBelowThreshold)] -fun test_verify_signatures_below_threshold() { - let committee = setup_test(); - let msg = message::deserialize_message_test_only(hex::decode(TEST_MSG)); - // good path - committee.verify_signatures( - msg, - vector[ - hex::decode( - b"9925c68918d4a29670b77470fe43b3f3da1b4aff83d55676a4b75e6b0f2477fc4e2173ac8bfdb1e3cd0951b3738729136f7703b5b4bda86fd91c5c89be1f816901", - ), - ], - ); - abort 0 -} - -#[test] -fun test_init_committee() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - create_validator_for_testing(@0xC, 100, ctx) - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - test_scenario::next_tx(&mut scenario, @0x0); - - let mut system_state = test_scenario::take_shared(&scenario); - - // validator registration - committee.register( - &mut system_state, - hex::decode(VALIDATOR1_PUBKEY), - b"", - &tx(@0xA, 0), - ); - committee.register( - &mut system_state, - hex::decode(VALIDATOR2_PUBKEY), - b"", - &tx(@0xC, 0), - ); - - // Check committee before creation - assert!(committee.members().is_empty()); - - let ctx = test_scenario::ctx(&mut scenario); - let voting_powers = system_state.validator_voting_powers_for_testing(); - committee.try_create_next_committee(voting_powers, 6000, ctx); - - assert_eq(2, committee.members().size()); - let (_, member0) = committee.members().get_entry_by_idx(0); - let (_, member1) = committee.members().get_entry_by_idx(1); - assert_eq(5000, member0.voting_power()); - assert_eq(5000, member1.voting_power()); - - let members = committee.members(); - assert!(members.size() == 2); // must succeed - - test_utils::destroy(committee); - test_scenario::return_shared(system_state); - test_scenario::end(scenario); -} - -#[test] -fun test_update_node_url() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - test_scenario::next_tx(&mut scenario, @0x0); - - let mut system_state = test_scenario::take_shared(&scenario); - - // validator registration - committee.register( - &mut system_state, - hex::decode(VALIDATOR1_PUBKEY), - b"test url 1", - &tx(@0xA, 0), - ); - - let ctx = test_scenario::ctx(&mut scenario); - let voting_powers = system_state.validator_voting_powers_for_testing(); - committee.try_create_next_committee(voting_powers, 6000, ctx); - - let members = committee.members(); - assert!(members.size() == 1); - let (_, member) = members.get_entry_by_idx(0); - assert_eq(member.http_rest_url(), b"test url 1"); - - // Update URL - committee.update_node_url( - b"test url 2", - &tx(@0xA, 0), - ); - - let members = committee.members(); - let (_, member) = members.get_entry_by_idx(0); - assert_eq(member.http_rest_url(), b"test url 2"); - - test_utils::destroy(committee); - test_scenario::return_shared(system_state); - test_scenario::end(scenario); -} - -#[test] -#[expected_failure(abort_code = bridge::committee::ESenderIsNotInBridgeCommittee)] -fun test_update_node_url_not_validator() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - test_scenario::next_tx(&mut scenario, @0x0); - - let mut system_state = test_scenario::take_shared(&scenario); - - // validator registration - committee.register( - &mut system_state, - hex::decode(VALIDATOR1_PUBKEY), - b"test url 1", - &tx(@0xA, 0), - ); - - let ctx = test_scenario::ctx(&mut scenario); - let voting_powers = system_state.validator_voting_powers_for_testing(); - committee.try_create_next_committee(voting_powers, 6000, ctx); - - // Update URL should fail for validator @0xB - committee.update_node_url( - b"test url", - &tx(@0xB, 0), - ); - - // test should have failed, abort - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::committee::ENotSystemAddress)] -fun test_init_non_system_sender() { - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let _committee = create(ctx); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::committee::ESenderNotActiveValidator)] -fun test_init_committee_not_validator() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - create_validator_for_testing(@0xC, 100, ctx) - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - test_scenario::next_tx(&mut scenario, @0x0); - - let mut system_state = test_scenario::take_shared(&scenario); - - // validator registration - committee.register(&mut system_state, hex::decode(VALIDATOR1_PUBKEY), b"", &tx(@0xD, 0)); - - test_utils::destroy(committee); - test_scenario::return_shared(system_state); - test_scenario::end(scenario); -} - -#[test] -#[expected_failure(abort_code = bridge::committee::EDuplicatePubkey)] -fun test_init_committee_dup_pubkey() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - create_validator_for_testing(@0xC, 100, ctx) - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - test_scenario::next_tx(&mut scenario, @0x0); - - let mut system_state = test_scenario::take_shared(&scenario); - - // validator registration - committee.register(&mut system_state, hex::decode(VALIDATOR1_PUBKEY), b"", &tx(@0xA, 0)); - committee.register(&mut system_state, hex::decode(VALIDATOR1_PUBKEY), b"", &tx(@0xC, 0)); - - test_utils::destroy(committee); - test_scenario::return_shared(system_state); - test_scenario::end(scenario); -} - -#[test] -fun test_init_committee_validator_become_inactive() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - create_validator_for_testing(@0xC, 100, ctx), - create_validator_for_testing(@0xD, 100, ctx), - create_validator_for_testing(@0xE, 100, ctx), - create_validator_for_testing(@0xF, 100, ctx) - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - test_scenario::next_tx(&mut scenario, @0x0); - - let mut system_state = test_scenario::take_shared(&scenario); - - // validator registration, 3 validators registered, should have 60% voting power in total - committee.register(&mut system_state, hex::decode(VALIDATOR1_PUBKEY), b"", &tx(@0xA, 0)); - committee.register(&mut system_state, hex::decode(VALIDATOR2_PUBKEY), b"", &tx(@0xC, 0)); - committee.register(&mut system_state, hex::decode(VALIDATOR3_PUBKEY), b"", &tx(@0xD, 0)); - - // Verify validator registration - assert_eq(3, committee.member_registrations().size()); - - // Validator 0xA become inactive, total voting power become 50% - iota_system::request_remove_validator(&mut system_state, &mut tx(@0xA, 0)); - test_scenario::return_shared(system_state); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - - let mut system_state = test_scenario::take_shared(&scenario); - - // create committee should not create a committee because of not enough stake. - let ctx = test_scenario::ctx(&mut scenario); - let voting_powers = iota_system::validator_voting_powers_for_testing(&mut system_state); - try_create_next_committee(&mut committee, voting_powers, 6000, ctx); - - assert!(committee.members().is_empty()); - - test_utils::destroy(committee); - test_scenario::return_shared(system_state); - test_scenario::end(scenario); -} - -#[test] -fun test_update_committee_registration() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - create_validator_for_testing(@0xC, 100, ctx) - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - test_scenario::next_tx(&mut scenario, @0x0); - - let mut system_state = test_scenario::take_shared(&scenario); - - // validator registration - committee.register(&mut system_state, hex::decode(VALIDATOR1_PUBKEY), b"", &tx(@0xA, 0)); - - // Verify registration info - assert_eq(1, committee.member_registrations().size()); - let (address, registration) = committee.member_registrations().get_entry_by_idx(0); - assert_eq(@0xA, *address); - assert!(&hex::decode(VALIDATOR1_PUBKEY) == registration.bridge_pubkey_bytes(), 0); - - // Register again with different pub key. - committee.register(&mut system_state, hex::decode(VALIDATOR2_PUBKEY), b"", &tx(@0xA, 0)); - - // Verify registration info, registration count should still be 1 - assert_eq(1, committee.member_registrations().size()); - let (address, registration) = committee.member_registrations().get_entry_by_idx(0); - assert_eq(@0xA, *address); - assert!(&hex::decode(VALIDATOR2_PUBKEY) == registration.bridge_pubkey_bytes(), 0); - - // teardown - test_utils::destroy(committee); - test_scenario::return_shared(system_state); - test_scenario::end(scenario); -} - -#[test] -fun test_init_committee_not_enough_stake() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - create_validator_for_testing(@0xC, 100, ctx) - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - test_scenario::next_tx(&mut scenario, @0x0); - - let mut system_state = test_scenario::take_shared(&scenario); - - // validator registration - committee.register(&mut system_state, hex::decode(VALIDATOR1_PUBKEY), b"", &tx(@0xA, 0)); - - // Check committee before creation - assert!(committee.members().is_empty()); - - let ctx = test_scenario::ctx(&mut scenario); - let voting_powers = iota_system::validator_voting_powers_for_testing(&mut system_state); - try_create_next_committee(&mut committee, voting_powers, 6000, ctx); - - // committee should be empty because registration did not reach min stake threshold. - assert!(committee.members().is_empty()); - - test_utils::destroy(committee); - test_scenario::return_shared(system_state); - test_scenario::end(scenario); -} - -#[test] -#[expected_failure(abort_code = bridge::committee::ECommitteeAlreadyInitiated)] -fun test_register_already_initialized() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - create_validator_for_testing(@0xC, 100, ctx) - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - - test_scenario::next_tx(&mut scenario, @0x0); - let mut system_state = test_scenario::take_shared(&scenario); - committee.register(&mut system_state, hex::decode(VALIDATOR1_PUBKEY), b"", &tx(@0xA, 0)); - committee.register(&mut system_state, hex::decode(VALIDATOR2_PUBKEY), b"", &tx(@0xC, 0)); - assert!(committee.members().is_empty()); - let ctx = test_scenario::ctx(&mut scenario); - let voting_powers = iota_system::validator_voting_powers_for_testing(&mut system_state); - try_create_next_committee(&mut committee, voting_powers, 6000, ctx); - - test_scenario::next_tx(&mut scenario, @0x0); - assert!(committee.members().size() == 2); // must succeed - // this fails because committee is already initiated - committee.register(&mut system_state, hex::decode(VALIDATOR1_PUBKEY), b"", &tx(@0xA, 0)); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::committee::EInvalidPubkeyLength)] -fun test_register_bad_pubkey() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = test_scenario::ctx(&mut scenario); - let mut committee = create(ctx); - - let validators = vector[ - create_validator_for_testing(@0xA, 100, ctx), - create_validator_for_testing(@0xC, 100, ctx) - ]; - create_iota_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_balanced_reward_amounts(0, 0, &mut scenario); - - test_scenario::next_tx(&mut scenario, @0x0); - let mut system_state = test_scenario::take_shared(&scenario); - committee.register(&mut system_state, hex::decode(VALIDATOR2_PUBKEY), b"", &tx(@0xC, 0)); - // this fails with invalid public key - committee.register(&mut system_state, b"029bef8", b"", &tx(@0xA, 0)); - - abort 0 -} - -fun tx(sender: address, hint: u64): TxContext { - tx_context::new_from_hint(sender, hint, 1, 0, 0) -} - -#[test] -#[expected_failure(abort_code = bridge::committee::ESignatureBelowThreshold)] -fun test_verify_signatures_with_blocked_committee_member() { - let mut committee = setup_test(); - let msg = message::deserialize_message_test_only(hex::decode(TEST_MSG)); - // good path, this test should have passed in previous test - committee.verify_signatures( - msg, - vector[ - hex::decode( - b"29f7c5cee069946d134ffc6c3a386f2e0fdb7f6ae8a2b6bfb488b55f5347ba364090a7cc01db3dcecfd61810a3eb55806b38cbb8a54df727c1f4de56b3d1806201", - ), - hex::decode( - b"9925c68918d4a29670b77470fe43b3f3da1b4aff83d55676a4b75e6b0f2477fc4e2173ac8bfdb1e3cd0951b3738729136f7703b5b4bda86fd91c5c89be1f816901", - ), - ], - ); - - let (validator1, member) = committee.members().get_entry_by_idx(0); - assert!(!member.blocklisted()); - - // Block a member - let blocklist = message::create_blocklist_message( - chain_ids::iota_testnet(), - 0, - 0, // type 0 is block - vector[crypto::ecdsa_pub_key_to_eth_address(validator1)], - ); - let blocklist = message::extract_blocklist_payload(&blocklist); - execute_blocklist(&mut committee, blocklist); - - let (_, blocked_member) = committee.members().get_entry_by_idx(0); - assert!(blocked_member.blocklisted()); - - // Verify signature should fail now - committee.verify_signatures( - msg, - vector[ - hex::decode( - b"29f7c5cee069946d134ffc6c3a386f2e0fdb7f6ae8a2b6bfb488b55f5347ba364090a7cc01db3dcecfd61810a3eb55806b38cbb8a54df727c1f4de56b3d1806201", - ), - hex::decode( - b"9925c68918d4a29670b77470fe43b3f3da1b4aff83d55676a4b75e6b0f2477fc4e2173ac8bfdb1e3cd0951b3738729136f7703b5b4bda86fd91c5c89be1f816901", - ), - ], - ); - - // Clean up - test_utils::destroy(committee); -} - -#[test] -#[expected_failure(abort_code = bridge::committee::EValidatorBlocklistContainsUnknownKey)] -fun test_execute_blocklist_abort_upon_unknown_validator() { - let mut committee = setup_test(); - - // // val0 and val1 are not blocked yet - let (validator0, _) = committee.members().get_entry_by_idx(0); - // assert!(!member0.blocklisted()); - // let (validator1, member1) = committee.members().get_entry_by_idx(1); - // assert!(!member1.blocklisted()); - - let eth_address0 = crypto::ecdsa_pub_key_to_eth_address(validator0); - let invalid_eth_address1 = x"0000000000000000000000000000000000000000"; - - // Blocklist both - let blocklist = message::create_blocklist_message( - chain_ids::iota_testnet(), - 0, // seq - 0, // type 0 is blocklist - vector[eth_address0, invalid_eth_address1], - ); - let blocklist = message::extract_blocklist_payload(&blocklist); - execute_blocklist(&mut committee, blocklist); - - // Clean up - test_utils::destroy(committee); -} - -#[test] -fun test_execute_blocklist() { - let mut committee = setup_test(); - - // val0 and val1 are not blocked yet - let (validator0, member0) = committee.members().get_entry_by_idx(0); - assert!(!member0.blocklisted()); - let (validator1, member1) = committee.members().get_entry_by_idx(1); - assert!(!member1.blocklisted()); - - let eth_address0 = crypto::ecdsa_pub_key_to_eth_address(validator0); - let eth_address1 = crypto::ecdsa_pub_key_to_eth_address(validator1); - - // Blocklist both - let blocklist = message::create_blocklist_message( - chain_ids::iota_testnet(), - 0, // seq - 0, // type 0 is blocklist - vector[eth_address0, eth_address1], - ); - let blocklist = message::extract_blocklist_payload(&blocklist); - execute_blocklist(&mut committee, blocklist); - - // Blocklist both reverse order - let blocklist = message::create_blocklist_message( - chain_ids::iota_testnet(), - 0, // seq - 0, // type 0 is blocklist - vector[eth_address1, eth_address0], - ); - let blocklist = message::extract_blocklist_payload(&blocklist); - execute_blocklist(&mut committee, blocklist); - - // val 0 is blocklisted - let (_, blocked_member) = committee.members().get_entry_by_idx(0); - assert!(blocked_member.blocklisted()); - // val 1 is too - let (_, blocked_member) = committee.members().get_entry_by_idx(1); - assert!(blocked_member.blocklisted()); - - // unblocklist val1 - let blocklist = message::create_blocklist_message( - chain_ids::iota_testnet(), - 1, // seq, this is supposed to increment, but we don't test it here - 1, // type 1 is unblocklist - vector[eth_address1], - ); - let blocklist = message::extract_blocklist_payload(&blocklist); - execute_blocklist(&mut committee, blocklist); - - // val 0 is still blocklisted - let (_, blocked_member) = committee.members().get_entry_by_idx(0); - assert!(blocked_member.blocklisted()); - // val 1 is not - let (_, blocked_member) = committee.members().get_entry_by_idx(1); - assert!(!blocked_member.blocklisted()); - - // Clean up - test_utils::destroy(committee); -} - -fun setup_test(): BridgeCommittee { - let mut members = vec_map::empty, CommitteeMember>(); - - let bridge_pubkey_bytes = hex::decode(VALIDATOR1_PUBKEY); - members.insert( - bridge_pubkey_bytes, - make_committee_member( - @0xA, - bridge_pubkey_bytes, - 3333, - b"https://127.0.0.1:9191", - false, - ), - ); - - let bridge_pubkey_bytes = hex::decode(VALIDATOR2_PUBKEY); - members.insert( - bridge_pubkey_bytes, - make_committee_member( - @0xC, - bridge_pubkey_bytes, - 3333, - b"https://127.0.0.1:9192", - false, - ), - ); - - make_bridge_committee(members, vec_map::empty(), 1) -} diff --git a/crates/iota-framework/packages/bridge/tests/eth.test.move b/crates/iota-framework/packages/bridge/tests/eth.test.move deleted file mode 100644 index f0ac46f2ac5..00000000000 --- a/crates/iota-framework/packages/bridge/tests/eth.test.move +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::eth; - -use iota::address; -use iota::coin::{CoinMetadata, TreasuryCap, create_currency}; -use iota::hex; -use iota::package::{UpgradeCap, test_publish}; -use iota::test_utils::create_one_time_witness; -use std::ascii; -use std::type_name; - -public struct ETH has drop {} - -public fun create_bridge_token( - ctx: &mut TxContext, -): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 8, - b"eth", - b"eth", - b"bridge ethereum token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode( - ascii::into_bytes(type_name::get_address(&type_name)), - ); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) -} diff --git a/crates/iota-framework/packages/bridge/tests/limiter_tests.move b/crates/iota-framework/packages/bridge/tests/limiter_tests.move deleted file mode 100644 index 9705e045f5f..00000000000 --- a/crates/iota-framework/packages/bridge/tests/limiter_tests.move +++ /dev/null @@ -1,620 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::limiter_tests; - -use bridge::chain_ids; -use bridge::limiter::{ - check_and_record_sending_transfer, - make_transfer_limiter, - max_transfer_limit, - new, - transfer_limits_mut, - total_amount, - transfer_records, - update_route_limit, - usd_value_multiplier -}; -use bridge::treasury::{Self, BTC, ETH, USDC, USDT}; -use iota::clock; -use iota::test_scenario; -use iota::test_utils::{assert_eq, destroy}; - -#[test] -fun test_24_hours_windows() { - let mut limiter = make_transfer_limiter(); - - let route = chain_ids::get_route(chain_ids::iota_custom(), chain_ids::eth_sepolia()); - - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let mut treasury = treasury::mock_for_test(ctx); - - // Global transfer limit is 100M USD - limiter.transfer_limits_mut().insert(route, 100_000_000 * usd_value_multiplier()); - // Notional price for ETH is 5 USD - let id = treasury::token_id(&treasury); - treasury.update_asset_notional_price(id, 5 * usd_value_multiplier()); - - let mut clock = clock::create_for_testing(ctx); - clock.set_for_testing(1706288001377); - - // transfer 10000 ETH every hour, the total should be 10000 * 5 - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 10_000 * treasury.decimal_multiplier(), - ), - 0, - ); - - let record = limiter.transfer_records().get(&route); - assert!(record.total_amount() == 10000 * 5 * usd_value_multiplier()); - - // transfer 1000 ETH every hour for 50 hours, the 24 hours total should be 24000 * 10 - let mut i = 0; - while (i < 50) { - clock.increment_for_testing(60 * 60 * 1000); - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 1_000 * treasury.decimal_multiplier(), - ), - 0, - ); - i = i + 1; - }; - let record = limiter.transfer_records().get(&route); - let mut expected_value = 24000 * 5 * usd_value_multiplier(); - assert_eq(record.total_amount(), expected_value); - - // transfer 1000 * i ETH every hour for 24 hours, the 24 hours - // total should be 300 * 1000 * 5 - let mut i = 0; - // At this point, every hour in past 24 hour has value $5000. - // In each iteration, the old $5000 gets replaced with (i * 5000) - while (i < 24) { - clock.increment_for_testing(60 * 60 * 1000); - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 1_000 * treasury.decimal_multiplier() * (i + 1), - ), - 0, - ); - - let record = limiter.transfer_records().get(&route); - - expected_value = expected_value + 1000 * 5 * i * usd_value_multiplier(); - assert_eq(record.total_amount(), expected_value); - i = i + 1; - }; - - let record = limiter.transfer_records().get(&route); - assert_eq(record.total_amount(), 300 * 1000 * 5 * usd_value_multiplier()); - - destroy(limiter); - destroy(treasury); - clock::destroy_for_testing(clock); - test_scenario::end(scenario); -} - -#[test] -fun test_24_hours_windows_multiple_route() { - let mut limiter = make_transfer_limiter(); - - let route = chain_ids::get_route(chain_ids::iota_custom(), chain_ids::eth_sepolia()); - let route2 = chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_custom()); - - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let mut treasury = treasury::mock_for_test(ctx); - - // Global transfer limit is 1M USD - limiter.transfer_limits_mut().insert(route, 1_000_000 * usd_value_multiplier()); - limiter.transfer_limits_mut().insert(route2, 500_000 * usd_value_multiplier()); - // Notional price for ETH is 5 USD - let id = treasury::token_id(&treasury); - treasury.update_asset_notional_price(id, 5 * usd_value_multiplier()); - - let mut clock = clock::create_for_testing(ctx); - clock.set_for_testing(1706288001377); - - // Transfer 10000 ETH on route 1 - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 10_000 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - // Transfer 50000 ETH on route 2 - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route2, - 50_000 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - - let record = limiter.transfer_records().get(&route); - assert!(record.total_amount() == 10000 * 5 * usd_value_multiplier()); - - let record = limiter.transfer_records().get(&route2); - assert!(record.total_amount() == 50000 * 5 * usd_value_multiplier()); - - destroy(limiter); - destroy(treasury); - clock::destroy_for_testing(clock); - test_scenario::end(scenario); -} - -#[test] -fun test_exceed_limit() { - let mut limiter = make_transfer_limiter(); - - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let mut treasury = treasury::mock_for_test(ctx); - - let route = chain_ids::get_route(chain_ids::iota_custom(), chain_ids::eth_sepolia()); - // Global transfer limit is 1M USD - limiter.transfer_limits_mut().insert(route, 1_000_000 * usd_value_multiplier()); - // Notional price for ETH is 10 USD - let id = treasury::token_id(&treasury); - treasury.update_asset_notional_price(id, 10 * usd_value_multiplier()); - - let mut clock = clock::create_for_testing(ctx); - clock.set_for_testing(1706288001377); - - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 90_000 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - - let record = limiter.transfer_records().get(&route); - assert_eq(record.total_amount(), 90000 * 10 * usd_value_multiplier()); - - clock.increment_for_testing(60 * 60 * 1000); - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 10_000 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - let record = limiter.transfer_records().get(&route); - assert_eq(record.total_amount(), 100000 * 10 * usd_value_multiplier()); - - // Tx should fail with a tiny amount because the limit is hit - assert!(!limiter.check_and_record_sending_transfer(&treasury, &clock, route, 1), 0); - assert!( - !limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 90_000 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - - // Fast forward 23 hours, now the first 90k should be discarded - clock.increment_for_testing(60 * 60 * 1000 * 23); - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 90_000 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - let record = limiter.transfer_records().get(&route); - assert_eq(record.total_amount(), 100000 * 10 * usd_value_multiplier()); - - // But now limit is hit again - assert!(!limiter.check_and_record_sending_transfer(&treasury, &clock, route, 1), 0); - let record = limiter.transfer_records().get(&route); - assert_eq(record.total_amount(), 100000 * 10 * usd_value_multiplier()); - - destroy(limiter); - destroy(treasury); - clock::destroy_for_testing(clock); - test_scenario::end(scenario); -} - -#[test] -#[expected_failure(abort_code = bridge::limiter::ELimitNotFoundForRoute)] -fun test_limiter_does_not_limit_receiving_transfers() { - let mut limiter = new(); - - let route = chain_ids::get_route(chain_ids::iota_mainnet(), chain_ids::eth_mainnet()); - let mut scenario = test_scenario::begin(@0x1); - let ctx = scenario.ctx(); - let treasury = treasury::mock_for_test(ctx); - let mut clock = clock::create_for_testing(ctx); - clock.set_for_testing(1706288001377); - // We don't limit iota -> eth transfers. This aborts with `ELimitNotFoundForRoute` - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 1 * treasury::decimal_multiplier(&treasury), - ); - destroy(limiter); - destroy(treasury); - clock::destroy_for_testing(clock); - test_scenario::end(scenario); -} - -#[test] -fun test_limiter_basic_op() { - // In this test we use very simple number for easier calculation. - let mut limiter = make_transfer_limiter(); - - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let mut treasury = treasury::mock_for_test(ctx); - - let route = chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()); - // Global transfer limit is 100 USD - limiter.transfer_limits_mut().insert(route, 100 * usd_value_multiplier()); - // BTC: $10, ETH: $2.5, USDC: $1, USDT: $0.5 - let id = treasury::token_id(&treasury); - treasury.update_asset_notional_price(id, 10 * usd_value_multiplier()); - let id = treasury::token_id(&treasury); - let eth_price = 250000000; - treasury.update_asset_notional_price(id, eth_price); - let id = treasury::token_id(&treasury); - treasury.update_asset_notional_price(id, 1 * usd_value_multiplier()); - let id = treasury::token_id(&treasury); - treasury.update_asset_notional_price(id, 50000000); - - let mut clock = clock::create_for_testing(ctx); - clock.set_for_testing(36082800000); // hour 10023 - - // hour 0 (10023): $15 * 2.5 = $37.5 - // 15 eth = $37.5 - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 15 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - let record = limiter.transfer_records().get(&route); - assert_eq(record.hour_head(), 10023); - assert_eq(record.hour_tail(), 10000); - assert!( - record.per_hour_amounts() == - &vector[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 15 * eth_price, - ], - 0, - ); - assert_eq(record.total_amount(), 15 * eth_price); - - // hour 0 (10023): $37.5 + $10 = $47.5 - // 10 uddc = $10 - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 10 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - let record = limiter.transfer_records().get(&route); - assert_eq(record.hour_head(), 10023); - assert_eq(record.hour_tail(), 10000); - let expected_notion_amount_10023 = 15 * eth_price + 10 * usd_value_multiplier(); - assert!( - record.per_hour_amounts() == - &vector[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - expected_notion_amount_10023, - ], - 0, - ); - assert_eq(record.total_amount(), expected_notion_amount_10023); - - // hour 1 (10024): $20 - clock.increment_for_testing(60 * 60 * 1000); - // 2 btc = $20 - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 2 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - let record = limiter.transfer_records().get(&route); - assert_eq(record.hour_head(), 10024); - assert_eq(record.hour_tail(), 10001); - let expected_notion_amount_10024 = 20 * usd_value_multiplier(); - assert!( - record.per_hour_amounts() == - &vector[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - expected_notion_amount_10023, - expected_notion_amount_10024, - ], - 0, - ); - assert_eq(record.total_amount(), expected_notion_amount_10023 + expected_notion_amount_10024); - - // Fast forward 22 hours, now hour 23 (10046): try to transfer $33 willf fail - clock.increment_for_testing(60 * 60 * 1000 * 22); - // fail - // 65 usdt = $33 - assert!( - !limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 66 * 1_000_000, - ), - 0, - ); - // but window slid - let record = limiter.transfer_records().get(&route); - assert_eq(record.hour_head(), 10046); - assert_eq(record.hour_tail(), 10023); - assert!( - record.per_hour_amounts() == - &vector[ - expected_notion_amount_10023, expected_notion_amount_10024, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], - 0, - ); - assert_eq(record.total_amount(), expected_notion_amount_10023 + expected_notion_amount_10024); - - // hour 23 (10046): $32.5 deposit will succeed - // 65 usdt = $32.5 - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 65 * 1_000_000, - ), - 0, - ); - let record = limiter.transfer_records().get(&route); - let expected_notion_amount_10046 = 325 * usd_value_multiplier() / 10; - assert_eq(record.hour_head(), 10046); - assert_eq(record.hour_tail(), 10023); - assert!( - record.per_hour_amounts() == - &vector[ - expected_notion_amount_10023, - expected_notion_amount_10024, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - expected_notion_amount_10046, - ], - 0, - ); - assert_eq( - record.total_amount(), - expected_notion_amount_10023 + expected_notion_amount_10024 + expected_notion_amount_10046, - ); - - // Hour 24 (10047), we can deposit $0.5 now - clock.increment_for_testing(60 * 60 * 1000); - // 1 usdt = $0.5 - assert!( - limiter.check_and_record_sending_transfer(&treasury, &clock, route, 1_000_000), - 0, - ); - let record = limiter.transfer_records().get(&route); - let expected_notion_amount_10047 = 5 * usd_value_multiplier() / 10; - assert_eq(record.hour_head(), 10047); - assert_eq(record.hour_tail(), 10024); - assert!( - record.per_hour_amounts() == - &vector[ - expected_notion_amount_10024, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - expected_notion_amount_10046, - expected_notion_amount_10047, - ], - 0, - ); - assert_eq( - record.total_amount(), - expected_notion_amount_10024 + expected_notion_amount_10046 + expected_notion_amount_10047, - ); - - // Fast forward to Hour 30 (10053) - clock.increment_for_testing(60 * 60 * 1000 * 6); - // 1 usdc = $1 - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - 1 * treasury::decimal_multiplier(&treasury), - ), - 0, - ); - let record = limiter.transfer_records().get(&route); - let expected_notion_amount_10053 = 1 * usd_value_multiplier(); - assert_eq(record.hour_head(), 10053); - assert_eq(record.hour_tail(), 10030); - assert!( - record.per_hour_amounts() == - &vector[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - expected_notion_amount_10046, - expected_notion_amount_10047, - 0, 0, 0, 0, 0, - expected_notion_amount_10053, - ], - 0, - ); - assert_eq( - record.total_amount(), - expected_notion_amount_10046 + expected_notion_amount_10047 + expected_notion_amount_10053, - ); - - // Fast forward to hour 130 (10153) - clock.increment_for_testing(60 * 60 * 1000 * 100); - // 1 usdc = $1 - assert!( - limiter.check_and_record_sending_transfer( - &treasury, - &clock, - route, - treasury::decimal_multiplier(&treasury), - ), - 0, - ); - let record = limiter.transfer_records().get(&route); - let expected_notion_amount_10153 = 1 * usd_value_multiplier(); - assert_eq(record.hour_head(), 10153); - assert_eq(record.hour_tail(), 10130); - assert!( - record.per_hour_amounts() == - &vector[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - expected_notion_amount_10153, - ], - 0, - ); - assert_eq(record.total_amount(), expected_notion_amount_10153); - - destroy(limiter); - destroy(treasury); - clock::destroy_for_testing(clock); - test_scenario::end(scenario); -} - -#[test] -fun test_update_route_limit() { - // default routes, default notion values - let mut limiter = new(); - assert_eq( - limiter.transfer_limits()[ - &chain_ids::get_route(chain_ids::eth_mainnet(), chain_ids::iota_mainnet()), - ], - 5_000_000 * usd_value_multiplier(), - ); - - assert_eq( - limiter.transfer_limits()[ - &chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()), - ], - max_transfer_limit(), - ); - - // shrink testnet limit - update_route_limit( - &mut limiter, - &chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()), - 1_000 * usd_value_multiplier(), - ); - assert_eq( - limiter.transfer_limits()[ - &chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()), - ], - 1_000 * usd_value_multiplier(), - ); - // mainnet route does not change - assert_eq( - limiter.transfer_limits()[ - &chain_ids::get_route(chain_ids::eth_mainnet(), chain_ids::iota_mainnet()), - ], - 5_000_000 * usd_value_multiplier(), - ); - destroy(limiter); -} - -#[test] -fun test_update_route_limit_all_paths() { - let mut limiter = new(); - // pick an existing route limit - assert_eq( - limiter.transfer_limits()[ - &chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()), - ], - max_transfer_limit(), - ); - let new_limit = 1_000 * usd_value_multiplier(); - update_route_limit( - &mut limiter, - &chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()), - new_limit, - ); - assert_eq( - limiter.transfer_limits()[ - &chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()), - ], - new_limit, - ); - - // pick a new route limit - update_route_limit( - &mut limiter, - &chain_ids::get_route(chain_ids::iota_testnet(), chain_ids::eth_sepolia()), - new_limit, - ); - assert_eq( - limiter.transfer_limits()[ - &chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()), - ], - new_limit, - ); - - destroy(limiter); -} - -#[test] -fun test_update_asset_price() { - // default routes, default notion values - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let mut treasury = treasury::mock_for_test(ctx); - - assert_eq(treasury.notional_value(), (50_000 * usd_value_multiplier())); - assert_eq(treasury.notional_value(), (3_000 * usd_value_multiplier())); - assert_eq(treasury.notional_value(), (1 * usd_value_multiplier())); - assert_eq(treasury.notional_value(), (1 * usd_value_multiplier())); - // change usdt price - let id = treasury.token_id(); - treasury.update_asset_notional_price(id, 11 * usd_value_multiplier() / 10); - assert_eq(treasury.notional_value(), (11 * usd_value_multiplier() / 10)); - // other prices do not change - assert_eq(treasury.notional_value(), (50_000 * usd_value_multiplier())); - assert_eq(treasury.notional_value(), (3_000 * usd_value_multiplier())); - assert_eq(treasury.notional_value(), (1 * usd_value_multiplier())); - scenario.end(); - destroy(treasury); -} diff --git a/crates/iota-framework/packages/bridge/tests/message_tests.move b/crates/iota-framework/packages/bridge/tests/message_tests.move deleted file mode 100644 index da72d81854b..00000000000 --- a/crates/iota-framework/packages/bridge/tests/message_tests.move +++ /dev/null @@ -1,839 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::message_tests; - -use bridge::chain_ids; -use bridge::message::{ - BridgeMessage, - blocklist_validator_addresses, - create_add_tokens_on_iota_message, - create_blocklist_message, - create_emergency_op_message, - create_token_bridge_message, - create_update_asset_price_message, - create_update_bridge_limit_message, - deserialize_message_test_only, - emergency_op_pause, - emergency_op_unpause, - extract_add_tokens_on_iota, - extract_blocklist_payload, - extract_token_bridge_payload, - extract_update_asset_price, - extract_update_bridge_limit, - make_add_token_on_iota, - make_generic_message, - make_payload, - peel_u64_be_for_testing, - reverse_bytes_test, - serialize_message, - set_payload, - update_asset_price_payload_token_id, - update_bridge_limit_payload_limit, - update_bridge_limit_payload_receiving_chain, - update_bridge_limit_payload_sending_chain -}; -use bridge::treasury::{Self, BTC, ETH, USDC}; -use iota::address; -use iota::balance; -use iota::bcs; -use iota::coin::{Self, Coin}; -use iota::hex; -use iota::test_scenario; -use iota::test_utils::{assert_eq, destroy}; -use std::ascii; - -const INVALID_CHAIN: u8 = 42; - -#[test] -fun test_message_serialization_iota_to_eth() { - let sender_address = address::from_u256(100); - let mut scenario = test_scenario::begin(sender_address); - let ctx = test_scenario::ctx(&mut scenario); - let coin = coin::mint_for_testing(12345, ctx); - - let token_bridge_message = default_token_bridge_message( - sender_address, - &coin, - chain_ids::iota_testnet(), - chain_ids::eth_sepolia(), - ); - - // Test payload extraction - let token_payload = make_payload( - address::to_bytes(sender_address), - chain_ids::eth_sepolia(), - hex::decode(b"00000000000000000000000000000000000000c8"), - 3u8, - balance::value(coin::balance(&coin)), - ); - let payload = token_bridge_message.extract_token_bridge_payload(); - assert!(payload.token_target_chain() == token_payload.token_target_chain()); - assert!(payload.token_target_address() == token_payload.token_target_address()); - assert!(payload.token_type() == token_payload.token_type()); - assert!(payload.token_amount() == token_payload.token_amount()); - assert!(payload == token_payload); - - // Test message serialization - let message = serialize_message(token_bridge_message); - let expected_msg = hex::decode( - b"0001000000000000000a012000000000000000000000000000000000000000000000000000000000000000640b1400000000000000000000000000000000000000c8030000000000003039", - ); - - assert!(message == expected_msg); - assert!(token_bridge_message == deserialize_message_test_only(message)); - - coin::burn_for_testing(coin); - test_scenario::end(scenario); -} - -#[test] -fun test_message_serialization_eth_to_iota() { - let address_1 = address::from_u256(100); - let mut scenario = test_scenario::begin(address_1); - let ctx = test_scenario::ctx(&mut scenario); - - let coin = coin::mint_for_testing(12345, ctx); - - let token_bridge_message = create_token_bridge_message( - chain_ids::eth_sepolia(), // source chain - 10, // seq_num - // Eth address is 20 bytes long - hex::decode(b"00000000000000000000000000000000000000c8"), // eth sender address - chain_ids::iota_testnet(), // target_chain - address::to_bytes(address_1), // target address - 3u8, // token_type - balance::value(coin::balance(&coin)), // amount: u64 - ); - - // Test payload extraction - let token_payload = make_payload( - hex::decode(b"00000000000000000000000000000000000000c8"), - chain_ids::iota_testnet(), - address::to_bytes(address_1), - 3u8, - balance::value(coin::balance(&coin)), - ); - assert!(token_bridge_message.extract_token_bridge_payload() == token_payload); - - // Test message serialization - let message = serialize_message(token_bridge_message); - let expected_msg = hex::decode( - b"0001000000000000000a0b1400000000000000000000000000000000000000c801200000000000000000000000000000000000000000000000000000000000000064030000000000003039", - ); - assert!(message == expected_msg); - assert!(token_bridge_message == deserialize_message_test_only(message)); - - coin::burn_for_testing(coin); - test_scenario::end(scenario); -} - -#[test] -fun test_emergency_op_message_serialization() { - let emergency_op_message = create_emergency_op_message( - chain_ids::iota_testnet(), // source chain - 10, // seq_num - emergency_op_pause(), - ); - - // Test message serialization - let message = serialize_message(emergency_op_message); - let expected_msg = hex::decode(b"0201000000000000000a0100"); - - assert!(message == expected_msg); - assert!(emergency_op_message == deserialize_message_test_only(message)); -} - -// Do not change/remove this test, it uses move bytes generated by Rust -#[test] -fun test_emergency_op_message_serialization_regression() { - let emergency_op_message = create_emergency_op_message( - chain_ids::iota_custom(), - 55, // seq_num - emergency_op_pause(), - ); - - // Test message serialization - let message = serialize_message(emergency_op_message); - let expected_msg = hex::decode(b"020100000000000000370200"); - - assert_eq(expected_msg, message); - assert!(emergency_op_message == deserialize_message_test_only(message)); -} - -#[test] -fun test_blocklist_message_serialization() { - let validator_pub_key1 = hex::decode(b"b14d3c4f5fbfbcfb98af2d330000d49c95b93aa7"); - let validator_pub_key2 = hex::decode(b"f7e93cc543d97af6632c9b8864417379dba4bf15"); - - let validator_eth_addresses = vector[validator_pub_key1, validator_pub_key2]; - let blocklist_message = create_blocklist_message( - chain_ids::iota_testnet(), // source chain - 10, // seq_num - 0, - validator_eth_addresses, - ); - // Test message serialization - let message = serialize_message(blocklist_message); - - let expected_msg = hex::decode( - b"0101000000000000000a010002b14d3c4f5fbfbcfb98af2d330000d49c95b93aa7f7e93cc543d97af6632c9b8864417379dba4bf15", - ); - - assert!(message == expected_msg); - assert!(blocklist_message == deserialize_message_test_only(message)); - - let blocklist = blocklist_message.extract_blocklist_payload(); - assert!(blocklist.blocklist_validator_addresses() == validator_eth_addresses) -} - -// Do not change/remove this test, it uses move bytes generated by Rust -#[test] -fun test_blocklist_message_serialization_regression() { - let validator_eth_addr_1 = hex::decode(b"68b43fd906c0b8f024a18c56e06744f7c6157c65"); - let validator_eth_addr_2 = hex::decode(b"acaef39832cb995c4e049437a3e2ec6a7bad1ab5"); - // Test 1 - let validator_eth_addresses = vector[validator_eth_addr_1]; - let blocklist_message = create_blocklist_message( - chain_ids::iota_custom(), // source chain - 129, // seq_num - 0, // blocklist - validator_eth_addresses, - ); - // Test message serialization - let message = serialize_message(blocklist_message); - - let expected_msg = hex::decode( - b"0101000000000000008102000168b43fd906c0b8f024a18c56e06744f7c6157c65", - ); - - assert_eq(expected_msg, message); - assert!(blocklist_message == deserialize_message_test_only(message)); - - let blocklist = blocklist_message.extract_blocklist_payload(); - assert!(blocklist.blocklist_validator_addresses() == validator_eth_addresses); - - // Test 2 - let validator_eth_addresses = vector[validator_eth_addr_1, validator_eth_addr_2]; - let blocklist_message = create_blocklist_message( - chain_ids::iota_custom(), // source chain - 68, // seq_num - 1, // unblocklist - validator_eth_addresses, - ); - // Test message serialization - let message = serialize_message(blocklist_message); - - let expected_msg = hex::decode( - b"0101000000000000004402010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5", - ); - - assert_eq(expected_msg, message); - assert!(blocklist_message == deserialize_message_test_only(message)); - - let blocklist = blocklist_message.extract_blocklist_payload(); - assert!(blocklist.blocklist_validator_addresses() == validator_eth_addresses) -} - -#[test] -fun test_update_bridge_limit_message_serialization() { - let update_bridge_limit = create_update_bridge_limit_message( - chain_ids::iota_testnet(), // source chain - 10, // seq_num - chain_ids::eth_sepolia(), - 1000000000, - ); - - // Test message serialization - let message = serialize_message(update_bridge_limit); - let expected_msg = hex::decode(b"0301000000000000000a010b000000003b9aca00"); - - assert!(message == expected_msg); - assert!(update_bridge_limit == deserialize_message_test_only(message)); - - let bridge_limit = extract_update_bridge_limit(&update_bridge_limit); - assert!( - bridge_limit.update_bridge_limit_payload_receiving_chain() - == chain_ids::iota_testnet(), - ); - assert!( - bridge_limit.update_bridge_limit_payload_sending_chain() - == chain_ids::eth_sepolia(), - ); - assert!(bridge_limit.update_bridge_limit_payload_limit() == 1000000000); -} - -// Do not change/remove this test, it uses move bytes generated by Rust -#[test] -fun test_update_bridge_limit_message_serialization_regression() { - let update_bridge_limit = create_update_bridge_limit_message( - chain_ids::iota_custom(), // source chain - 15, // seq_num - chain_ids::eth_custom(), - 10_000_000_000, // 1M USD - ); - - // Test message serialization - let message = serialize_message(update_bridge_limit); - let expected_msg = hex::decode(b"0301000000000000000f020c00000002540be400"); - - assert_eq(message, expected_msg); - assert!(update_bridge_limit == deserialize_message_test_only(message)); - - let bridge_limit = extract_update_bridge_limit(&update_bridge_limit); - assert!( - bridge_limit.update_bridge_limit_payload_receiving_chain() - == chain_ids::iota_custom(), - ); - assert!( - bridge_limit.update_bridge_limit_payload_sending_chain() - == chain_ids::eth_custom(), - ); - assert!(bridge_limit.update_bridge_limit_payload_limit() == 10_000_000_000); -} - -#[test] -fun test_update_asset_price_message_serialization() { - let asset_price_message = create_update_asset_price_message( - 2, - chain_ids::iota_testnet(), // source chain - 10, // seq_num - 12345, - ); - - // Test message serialization - let message = serialize_message(asset_price_message); - let expected_msg = hex::decode(b"0401000000000000000a01020000000000003039"); - assert!(message == expected_msg); - assert!(asset_price_message == deserialize_message_test_only(message)); - - let asset_price = extract_update_asset_price(&asset_price_message); - - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let treasury = treasury::mock_for_test(ctx); - - assert!( - asset_price.update_asset_price_payload_token_id() - == treasury::token_id(&treasury), - ); - assert!(asset_price.update_asset_price_payload_new_price() == 12345); - - destroy(treasury); - test_scenario::end(scenario); -} - -// Do not change/remove this test, it uses move bytes generated by Rust -#[test] -fun test_update_asset_price_message_serialization_regression() { - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let treasury = treasury::mock_for_test(ctx); - - let asset_price_message = create_update_asset_price_message( - treasury.token_id(), - chain_ids::iota_custom(), // source chain - 266, // seq_num - 1_000_000_000, // $100k USD - ); - - // Test message serialization - let message = serialize_message(asset_price_message); - let expected_msg = hex::decode(b"0401000000000000010a0201000000003b9aca00"); - assert_eq(expected_msg, message); - assert!(asset_price_message == deserialize_message_test_only(message)); - - let asset_price = extract_update_asset_price(&asset_price_message); - - assert!( - asset_price.update_asset_price_payload_token_id() - == treasury::token_id(&treasury), - ); - assert!(asset_price.update_asset_price_payload_new_price() == 1_000_000_000); - - destroy(treasury); - test_scenario::end(scenario); -} - -#[test] -fun test_add_tokens_on_iota_message_serialization() { - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let treasury = treasury::mock_for_test(ctx); - - let add_tokens_on_iota_message = create_add_tokens_on_iota_message( - chain_ids::iota_custom(), - 1, // seq_num - false, // native_token - vector[treasury.token_id(), treasury.token_id()], - vector[ - ascii::string( - b"28ac483b6f2b62dd58abdf0bbc3f86900d86bbdc710c704ba0b33b7f1c4b43c8::btc::BTC", - ), - ascii::string( - b"0xbd69a54e7c754a332804f325307c6627c06631dc41037239707e3242bc542e99::eth::ETH", - ), - ], - vector[100, 100], - ); - let payload = add_tokens_on_iota_message.extract_add_tokens_on_iota(); - assert!(payload.is_native() == false); - assert!(payload.token_ids() == vector[treasury.token_id(), treasury.token_id()]); - assert!( - payload.token_type_names() == - vector[ - ascii::string(b"28ac483b6f2b62dd58abdf0bbc3f86900d86bbdc710c704ba0b33b7f1c4b43c8::btc::BTC"), - ascii::string(b"0xbd69a54e7c754a332804f325307c6627c06631dc41037239707e3242bc542e99::eth::ETH"), - ], - ); - assert!(payload.token_prices() == vector[100, 100]); - assert!( - payload == make_add_token_on_iota( - false, - vector[treasury.token_id(), treasury.token_id()], - vector[ascii::string(b"28ac483b6f2b62dd58abdf0bbc3f86900d86bbdc710c704ba0b33b7f1c4b43c8::btc::BTC"), ascii::string(b"0xbd69a54e7c754a332804f325307c6627c06631dc41037239707e3242bc542e99::eth::ETH")], - vector[100, 100], - ), - ); - // Test message serialization - let message = serialize_message(add_tokens_on_iota_message); - let expected_msg = hex::decode( - b"060100000000000000010200020102024a323861633438336236663262363264643538616264663062626333663836393030643836626264633731306337303462613062333362376631633462343363383a3a6274633a3a4254434c3078626436396135346537633735346133333238303466333235333037633636323763303636333164633431303337323339373037653332343262633534326539393a3a6574683a3a4554480264000000000000006400000000000000", - ); - assert_eq(message, expected_msg); - assert!(add_tokens_on_iota_message == deserialize_message_test_only(message)); - - destroy(treasury); - test_scenario::end(scenario); -} - -#[test] -fun test_add_tokens_on_iota_message_serialization_2() { - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let treasury = treasury::mock_for_test(ctx); - - let add_tokens_on_iota_message = create_add_tokens_on_iota_message( - chain_ids::iota_custom(), - 0, // seq_num - false, // native_token - vector[1, 2, 3, 4], - vector[ - ascii::string( - b"9b5e13bcd0cb23ff25c07698e89d48056c745338d8c9dbd033a4172b87027073::btc::BTC", - ), - ascii::string( - b"7970d71c03573f540a7157f0d3970e117effa6ae16cefd50b45c749670b24e6a::eth::ETH", - ), - ascii::string( - b"500e429a24478405d5130222b20f8570a746b6bc22423f14b4d4e6a8ea580736::usdc::USDC", - ), - ascii::string( - b"46bfe51da1bd9511919a92eb1154149b36c0f4212121808e13e3e5857d607a9c::usdt::USDT", - ), - ], - vector[500_000_000, 30_000_000, 1_000, 1_000], - ); - let payload = add_tokens_on_iota_message.extract_add_tokens_on_iota(); - assert!( - payload == make_add_token_on_iota( - false, - vector[1, 2, 3, 4], - vector[ - ascii::string(b"9b5e13bcd0cb23ff25c07698e89d48056c745338d8c9dbd033a4172b87027073::btc::BTC"), - ascii::string(b"7970d71c03573f540a7157f0d3970e117effa6ae16cefd50b45c749670b24e6a::eth::ETH"), - ascii::string(b"500e429a24478405d5130222b20f8570a746b6bc22423f14b4d4e6a8ea580736::usdc::USDC"), - ascii::string(b"46bfe51da1bd9511919a92eb1154149b36c0f4212121808e13e3e5857d607a9c::usdt::USDT") - ], - vector[500_000_000, 30_000_000, 1_000, 1_000], - ), - ); - // Test message serialization - let message = serialize_message(add_tokens_on_iota_message); - let expected_msg = hex::decode( - b"0601000000000000000002000401020304044a396235653133626364306362323366663235633037363938653839643438303536633734353333386438633964626430333361343137326238373032373037333a3a6274633a3a4254434a373937306437316330333537336635343061373135376630643339373065313137656666613661653136636566643530623435633734393637306232346536613a3a6574683a3a4554484c353030653432396132343437383430356435313330323232623230663835373061373436623662633232343233663134623464346536613865613538303733363a3a757364633a3a555344434c343662666535316461316264393531313931396139326562313135343134396233366330663432313231323138303865313365336535383537643630376139633a3a757364743a3a55534454040065cd1d0000000080c3c90100000000e803000000000000e803000000000000", - ); - assert_eq(message, expected_msg); - assert!(add_tokens_on_iota_message == deserialize_message_test_only(message)); - - let mut message_bytes = b"IOTA_BRIDGE_MESSAGE"; - message_bytes.append(message); - - let pubkey = iota::ecdsa_k1::secp256k1_ecrecover( - &x"0827116f333b60598771f535a4094d7886eb372352cc7f850f06733a5852f12611096d9cb1dba69796bbb49b4e4864adeba0065ec7ad4de12f5b342d434a6d1c01", - &message_bytes, - 0, - ); - - assert_eq(pubkey, x"037c37864b65ad79b574866b631daa28d7f868b75d088a21a1a92de4497fd9f5a0"); - destroy(treasury); - test_scenario::end(scenario); -} - -#[test] -fun test_be_to_le_conversion() { - let input = hex::decode(b"78563412"); - let expected = hex::decode(b"12345678"); - assert!(reverse_bytes_test(input) == expected) -} - -#[test] -fun test_peel_u64_be() { - let input = hex::decode(b"0000000000003039"); - let expected = 12345u64; - let mut bcs = bcs::new(input); - assert!(peel_u64_be_for_testing(&mut bcs) == expected) -} - -#[test] -#[expected_failure(abort_code = bridge::message::ETrailingBytes)] -fun test_bad_payload() { - let sender_address = address::from_u256(100); - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let coin = coin::mint_for_testing(12345, ctx); - let mut token_bridge_message = create_token_bridge_message( - chain_ids::iota_testnet(), // source chain - 10, // seq_num - address::to_bytes(sender_address), // sender address - chain_ids::eth_sepolia(), // target_chain - // Eth address is 20 bytes long - hex::decode(b"00000000000000000000000000000000000000c8"), // target_address - 3u8, // token_type - balance::value(coin::balance(&coin)), // amount: u64 - ); - let mut payload = token_bridge_message.payload(); - payload.push_back(0u8); - token_bridge_message.set_payload(payload); - - token_bridge_message.extract_token_bridge_payload(); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::message::ETrailingBytes)] -fun test_bad_emergency_op() { - let mut msg = create_emergency_op_message( - chain_ids::iota_testnet(), - 0, - emergency_op_pause(), - ); - let mut payload = msg.payload(); - payload.push_back(0u8); - msg.set_payload(payload); - msg.extract_emergency_op_payload(); -} - -#[test] -#[expected_failure(abort_code = bridge::message::EEmptyList)] -fun test_bad_blocklist() { - let blocklist_message = create_blocklist_message( - chain_ids::iota_testnet(), - 10, - 0, - vector[], - ); - blocklist_message.extract_blocklist_payload(); -} - -#[test] -#[expected_failure(abort_code = bridge::message::ETrailingBytes)] -fun test_bad_blocklist_1() { - let mut blocklist_message = default_blocklist_message(); - let mut payload = blocklist_message.payload(); - payload.push_back(0u8); - blocklist_message.set_payload(payload); - blocklist_message.extract_blocklist_payload(); -} - -#[test] -#[expected_failure(abort_code = bridge::message::EInvalidAddressLength)] -fun test_bad_blocklist_2() { - let validator_pub_key1 = hex::decode(b"b14d3c4f5fbfbcfb98af2d330000d49c95b93aa7"); - // bad address - let validator_pub_key2 = hex::decode(b"f7e93cc543d97af6632c9b8864417379dba4bf150000"); - let validator_eth_addresses = vector[validator_pub_key1, validator_pub_key2]; - create_blocklist_message(chain_ids::iota_testnet(), 10, 0, validator_eth_addresses); -} - -#[test] -#[expected_failure(abort_code = bridge::message::ETrailingBytes)] -fun test_bad_bridge_limit() { - let mut update_bridge_limit = create_update_bridge_limit_message( - chain_ids::iota_testnet(), - 10, - chain_ids::eth_sepolia(), - 1000000000, - ); - let mut payload = update_bridge_limit.payload(); - payload.push_back(0u8); - update_bridge_limit.set_payload(payload); - update_bridge_limit.extract_update_bridge_limit(); -} - -#[test] -#[expected_failure(abort_code = bridge::message::ETrailingBytes)] -fun test_bad_update_price() { - let mut asset_price_message = create_update_asset_price_message( - 2, - chain_ids::iota_testnet(), // source chain - 10, // seq_num - 12345, - ); - let mut payload = asset_price_message.payload(); - payload.push_back(0u8); - asset_price_message.set_payload(payload); - asset_price_message.extract_update_asset_price(); -} - -#[test] -#[expected_failure(abort_code = bridge::message::ETrailingBytes)] -fun test_bad_add_token() { - let mut scenario = test_scenario::begin(@0x1); - let ctx = test_scenario::ctx(&mut scenario); - let treasury = treasury::mock_for_test(ctx); - - let mut add_token_message = create_add_tokens_on_iota_message( - chain_ids::iota_custom(), - 1, // seq_num - false, // native_token - vector[treasury.token_id(), treasury.token_id()], - vector[ - ascii::string( - b"28ac483b6f2b62dd58abdf0bbc3f86900d86bbdc710c704ba0b33b7f1c4b43c8::btc::BTC", - ), - ascii::string( - b"0xbd69a54e7c754a332804f325307c6627c06631dc41037239707e3242bc542e99::eth::ETH", - ), - ], - vector[100, 100], - ); - let mut payload = add_token_message.payload(); - payload.push_back(0u8); - add_token_message.set_payload(payload); - add_token_message.extract_add_tokens_on_iota(); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::message::EInvalidPayloadLength)] -fun test_bad_payload_size() { - let sender_address = address::from_u256(100); - let mut scenario = test_scenario::begin(sender_address); - let ctx = test_scenario::ctx(&mut scenario); - let coin = coin::mint_for_testing(12345, ctx); - let mut sender = address::to_bytes(sender_address); - // double sender which will make the payload different the 64 bytes - sender.append(address::to_bytes(sender_address)); - create_token_bridge_message( - chain_ids::iota_testnet(), // source chain - 10, // seq_num - sender, // sender address - chain_ids::eth_sepolia(), // target_chain - // Eth address is 20 bytes long - hex::decode(b"00000000000000000000000000000000000000c8"), // target_address - 3u8, // token_type - balance::value(coin::balance(&coin)), // amount: u64 - ); - - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::message::EMustBeTokenMessage)] -fun test_bad_token_transfer_type() { - let msg = create_update_asset_price_message(2, chain_ids::iota_testnet(), 10, 12345); - msg.to_parsed_token_transfer_message(); -} - -#[test] -fun test_voting_power() { - let sender_address = address::from_u256(100); - let mut scenario = test_scenario::begin(sender_address); - - let ctx = test_scenario::ctx(&mut scenario); - let coin = coin::mint_for_testing(12345, ctx); - let message = default_token_bridge_message( - sender_address, - &coin, - chain_ids::iota_testnet(), - chain_ids::eth_sepolia(), - ); - assert!(message.required_voting_power() == 3334); - - let treasury = treasury::mock_for_test(ctx); - let message = create_add_tokens_on_iota_message( - chain_ids::iota_custom(), - 1, // seq_num - false, // native_token - vector[treasury.token_id(), treasury.token_id()], - vector[ - ascii::string( - b"28ac483b6f2b62dd58abdf0bbc3f86900d86bbdc710c704ba0b33b7f1c4b43c8::btc::BTC", - ), - ascii::string( - b"0xbd69a54e7c754a332804f325307c6627c06631dc41037239707e3242bc542e99::eth::ETH", - ), - ], - vector[100, 100], - ); - assert!(message.required_voting_power() == 5001); - - destroy(treasury); - coin::burn_for_testing(coin); - test_scenario::end(scenario); - - let message = create_emergency_op_message( - chain_ids::iota_testnet(), - 10, - emergency_op_pause(), - ); - assert!(message.required_voting_power() == 450); - let message = create_emergency_op_message( - chain_ids::iota_testnet(), - 10, - emergency_op_unpause(), - ); - assert!(message.required_voting_power() == 5001); - - let message = default_blocklist_message(); - assert!(message.required_voting_power() == 5001); - - let message = create_update_asset_price_message(2, chain_ids::iota_testnet(), 10, 12345); - assert!(message.required_voting_power() == 5001); - - let message = create_update_bridge_limit_message( - chain_ids::iota_testnet(), // source chain - 10, // seq_num - chain_ids::eth_sepolia(), - 1000000000, - ); - assert!(message.required_voting_power() == 5001); -} - -#[test] -#[expected_failure(abort_code = bridge::message::EInvalidEmergencyOpType)] -fun test_bad_voting_power_1() { - let message = create_emergency_op_message(chain_ids::iota_testnet(), 10, 3); - message.required_voting_power(); -} - -#[test] -#[expected_failure(abort_code = bridge::message::EInvalidMessageType)] -fun test_bad_voting_power_2() { - let message = make_generic_message( - 100, // bad message type - 1, - 10, - chain_ids::iota_testnet(), - vector[], - ); - message.required_voting_power(); -} - -fun default_token_bridge_message( - sender: address, - coin: &Coin, - source_chain: u8, - target_chain: u8, -): BridgeMessage { - create_token_bridge_message( - source_chain, - 10, // seq_num - address::to_bytes(sender), - target_chain, - // Eth address is 20 bytes long - hex::decode(b"00000000000000000000000000000000000000c8"), - 3u8, // token_type - balance::value(coin::balance(coin)), // amount: u64 - ) -} - -#[test] -#[expected_failure(abort_code = bridge::chain_ids::EInvalidBridgeRoute)] -fun test_invalid_chain_id_1() { - let sender_address = address::from_u256(100); - let mut scenario = test_scenario::begin(sender_address); - let ctx = test_scenario::ctx(&mut scenario); - let coin = coin::mint_for_testing(1, ctx); - default_token_bridge_message( - sender_address, - &coin, - INVALID_CHAIN, - chain_ids::eth_sepolia(), - ); - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::chain_ids::EInvalidBridgeRoute)] -fun test_invalid_chain_id_2() { - let sender_address = address::from_u256(100); - let mut scenario = test_scenario::begin(sender_address); - let ctx = test_scenario::ctx(&mut scenario); - let coin = coin::mint_for_testing(1, ctx); - default_token_bridge_message( - sender_address, - &coin, - chain_ids::iota_testnet(), - INVALID_CHAIN, - ); - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::chain_ids::EInvalidBridgeRoute)] -fun test_invalid_chain_id_3() { - create_emergency_op_message( - INVALID_CHAIN, - 10, // seq_num - emergency_op_pause(), - ); - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::chain_ids::EInvalidBridgeRoute)] -fun test_invalid_chain_id_4() { - create_blocklist_message(INVALID_CHAIN, 10, 0, vector[]); - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::chain_ids::EInvalidBridgeRoute)] -fun test_invalid_chain_id_5() { - create_update_bridge_limit_message(INVALID_CHAIN, 1, chain_ids::eth_sepolia(), 1); - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::chain_ids::EInvalidBridgeRoute)] -fun test_invalid_chain_id_6() { - create_update_bridge_limit_message(chain_ids::iota_testnet(), 1, INVALID_CHAIN, 1); - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::chain_ids::EInvalidBridgeRoute)] -fun test_invalid_chain_id_7() { - create_update_asset_price_message(2, INVALID_CHAIN, 1, 5); - abort 0 -} - -#[test] -#[expected_failure(abort_code = bridge::chain_ids::EInvalidBridgeRoute)] -fun test_invalid_chain_id_8() { - create_add_tokens_on_iota_message(INVALID_CHAIN, 1, false, vector[], vector[], vector[]); - abort 0 -} - -fun default_blocklist_message(): BridgeMessage { - let validator_pub_key1 = hex::decode(b"b14d3c4f5fbfbcfb98af2d330000d49c95b93aa7"); - let validator_pub_key2 = hex::decode(b"f7e93cc543d97af6632c9b8864417379dba4bf15"); - let validator_eth_addresses = vector[validator_pub_key1, validator_pub_key2]; - create_blocklist_message(chain_ids::iota_testnet(), 10, 0, validator_eth_addresses) -} diff --git a/crates/iota-framework/packages/bridge/tests/test_token.test.move b/crates/iota-framework/packages/bridge/tests/test_token.test.move deleted file mode 100644 index 139a794439a..00000000000 --- a/crates/iota-framework/packages/bridge/tests/test_token.test.move +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::test_token; - -use iota::address; -use iota::coin::{CoinMetadata, TreasuryCap, create_currency}; -use iota::hex; -use iota::package::{UpgradeCap, test_publish}; -use iota::test_utils::create_one_time_witness; -use std::ascii; -use std::type_name; - -public struct TEST_TOKEN has drop {} - -public fun create_bridge_token( - ctx: &mut TxContext, -): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 8, - b"tst", - b"test", - b"bridge test token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode( - ascii::into_bytes(type_name::get_address(&type_name)), - ); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) -} diff --git a/crates/iota-framework/packages/bridge/tests/usdc.test.move b/crates/iota-framework/packages/bridge/tests/usdc.test.move deleted file mode 100644 index 41fee86323f..00000000000 --- a/crates/iota-framework/packages/bridge/tests/usdc.test.move +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::usdc; - -use iota::address; -use iota::coin::{CoinMetadata, TreasuryCap, create_currency}; -use iota::hex; -use iota::package::{UpgradeCap, test_publish}; -use iota::test_utils::create_one_time_witness; -use std::ascii; -use std::type_name; - -public struct USDC has drop {} - -public fun create_bridge_token( - ctx: &mut TxContext, -): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 6, - b"usdc", - b"usdc", - b"bridge usdc token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode( - ascii::into_bytes(type_name::get_address(&type_name)), - ); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) -} diff --git a/crates/iota-framework/packages/bridge/tests/usdt.test.move b/crates/iota-framework/packages/bridge/tests/usdt.test.move deleted file mode 100644 index e8e0ad21905..00000000000 --- a/crates/iota-framework/packages/bridge/tests/usdt.test.move +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::usdt; - -use iota::address; -use iota::coin::{CoinMetadata, TreasuryCap, create_currency}; -use iota::hex; -use iota::package::{UpgradeCap, test_publish}; -use iota::test_utils::create_one_time_witness; -use std::ascii; -use std::type_name; - -public struct USDT has drop {} - -public fun create_bridge_token( - ctx: &mut TxContext, -): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 6, - b"usdt", - b"usdt", - b"bridge usdt token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode( - ascii::into_bytes(type_name::get_address(&type_name)), - ); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) -} diff --git a/crates/iota-framework/packages/iota-framework/sources/object.move b/crates/iota-framework/packages/iota-framework/sources/object.move index 24ee2d15e46..3b73d50164e 100644 --- a/crates/iota-framework/packages/iota-framework/sources/object.move +++ b/crates/iota-framework/packages/iota-framework/sources/object.move @@ -41,9 +41,6 @@ const IOTA_RANDOM_ID: address = @0x8; /// The hardcoded ID for the singleton DenyList. const IOTA_DENY_LIST_OBJECT_ID: address = @0x403; -/// The hardcoded ID for the Bridge Object. -const IOTA_BRIDGE_ID: address = @0x9; - /// Sender is not @0x0 the system address. const ENotSystemAddress: u64 = 0; @@ -137,15 +134,6 @@ public(package) fun iota_deny_list_object_id(): UID { } } -#[allow(unused_function)] -/// Create the `UID` for the singleton `Bridge` object. -/// This should only be called once from `bridge`. -fun bridge(): UID { - UID { - id: ID { bytes: IOTA_BRIDGE_ID }, - } -} - /// Get the inner `ID` of `uid` public fun uid_as_inner(uid: &UID): &ID { &uid.id diff --git a/crates/iota-framework/packages_compiled/bridge b/crates/iota-framework/packages_compiled/bridge deleted file mode 100644 index 81161c0986f..00000000000 Binary files a/crates/iota-framework/packages_compiled/bridge and /dev/null differ diff --git a/crates/iota-framework/packages_compiled/iota-framework b/crates/iota-framework/packages_compiled/iota-framework index c7b5e044c50..2ede07ef001 100644 Binary files a/crates/iota-framework/packages_compiled/iota-framework and b/crates/iota-framework/packages_compiled/iota-framework differ diff --git a/crates/iota-framework/published_api.txt b/crates/iota-framework/published_api.txt index 10819f2de76..be1a2a6e689 100644 --- a/crates/iota-framework/published_api.txt +++ b/crates/iota-framework/published_api.txt @@ -1216,9 +1216,6 @@ randomness_state iota_deny_list_object_id public(package) fun 0x2::object -bridge - fun - 0x2::object uid_as_inner public fun 0x2::object @@ -3469,432 +3466,6 @@ check_zklogin_issuer check_zklogin_issuer_internal fun 0x2::zklogin_verified_issuer -BridgeTreasury - public struct - 0xb::treasury -BridgeTokenMetadata - public struct - 0xb::treasury -ForeignTokenRegistration - public struct - 0xb::treasury -UpdateTokenPriceEvent - public struct - 0xb::treasury -NewTokenEvent - public struct - 0xb::treasury -TokenRegistrationEvent - public struct - 0xb::treasury -token_id - public fun - 0xb::treasury -decimal_multiplier - public fun - 0xb::treasury -notional_value - public fun - 0xb::treasury -register_foreign_token - public(package) fun - 0xb::treasury -add_new_token - public(package) fun - 0xb::treasury -create - public(package) fun - 0xb::treasury -burn - public(package) fun - 0xb::treasury -mint - public(package) fun - 0xb::treasury -update_asset_notional_price - public(package) fun - 0xb::treasury -get_token_metadata - fun - 0xb::treasury -token - public fun - 0xb::message_types -committee_blocklist - public fun - 0xb::message_types -emergency_op - public fun - 0xb::message_types -update_bridge_limit - public fun - 0xb::message_types -update_asset_price - public fun - 0xb::message_types -add_tokens_on_iota - public fun - 0xb::message_types -BridgeRoute - public struct - 0xb::chain_ids -iota_mainnet - public fun - 0xb::chain_ids -iota_testnet - public fun - 0xb::chain_ids -iota_custom - public fun - 0xb::chain_ids -eth_mainnet - public fun - 0xb::chain_ids -eth_sepolia - public fun - 0xb::chain_ids -eth_custom - public fun - 0xb::chain_ids -route_source - public fun - 0xb::chain_ids -route_destination - public fun - 0xb::chain_ids -assert_valid_chain_id - public fun - 0xb::chain_ids -valid_routes - public fun - 0xb::chain_ids -is_valid_route - public fun - 0xb::chain_ids -get_route - public fun - 0xb::chain_ids -BridgeMessage - public struct - 0xb::message -BridgeMessageKey - public struct - 0xb::message -TokenTransferPayload - public struct - 0xb::message -EmergencyOp - public struct - 0xb::message -Blocklist - public struct - 0xb::message -UpdateBridgeLimit - public struct - 0xb::message -UpdateAssetPrice - public struct - 0xb::message -AddTokenOnIota - public struct - 0xb::message -ParsedTokenTransferMessage - public struct - 0xb::message -extract_token_bridge_payload - public fun - 0xb::message -extract_emergency_op_payload - public fun - 0xb::message -extract_blocklist_payload - public fun - 0xb::message -extract_update_bridge_limit - public fun - 0xb::message -extract_update_asset_price - public fun - 0xb::message -extract_add_tokens_on_iota - public fun - 0xb::message -serialize_message - public fun - 0xb::message -create_token_bridge_message - public fun - 0xb::message -create_emergency_op_message - public fun - 0xb::message -create_blocklist_message - public fun - 0xb::message -create_update_bridge_limit_message - public fun - 0xb::message -create_update_asset_price_message - public fun - 0xb::message -create_add_tokens_on_iota_message - public fun - 0xb::message -create_key - public fun - 0xb::message -key - public fun - 0xb::message -message_version - public fun - 0xb::message -message_type - public fun - 0xb::message -seq_num - public fun - 0xb::message -source_chain - public fun - 0xb::message -payload - public fun - 0xb::message -token_target_chain - public fun - 0xb::message -token_target_address - public fun - 0xb::message -token_type - public fun - 0xb::message -token_amount - public fun - 0xb::message -emergency_op_type - public fun - 0xb::message -blocklist_type - public fun - 0xb::message -blocklist_validator_addresses - public fun - 0xb::message -update_bridge_limit_payload_sending_chain - public fun - 0xb::message -update_bridge_limit_payload_receiving_chain - public fun - 0xb::message -update_bridge_limit_payload_limit - public fun - 0xb::message -update_asset_price_payload_token_id - public fun - 0xb::message -update_asset_price_payload_new_price - public fun - 0xb::message -is_native - public fun - 0xb::message -token_ids - public fun - 0xb::message -token_type_names - public fun - 0xb::message -token_prices - public fun - 0xb::message -emergency_op_pause - public fun - 0xb::message -emergency_op_unpause - public fun - 0xb::message -required_voting_power - public fun - 0xb::message -to_parsed_token_transfer_message - public fun - 0xb::message -reverse_bytes - fun - 0xb::message -peel_u64_be - fun - 0xb::message -TransferLimiter - public struct - 0xb::limiter -TransferRecord - public struct - 0xb::limiter -UpdateRouteLimitEvent - public struct - 0xb::limiter -get_route_limit - public fun - 0xb::limiter -new - public(package) fun - 0xb::limiter -check_and_record_sending_transfer - public(package) fun - 0xb::limiter -update_route_limit - public(package) fun - 0xb::limiter -current_hour_since_epoch - fun - 0xb::limiter -adjust_transfer_records - fun - 0xb::limiter -initial_transfer_limits - fun - 0xb::limiter -ecdsa_pub_key_to_eth_address - public(package) fun - 0xb::crypto -BlocklistValidatorEvent - public struct - 0xb::committee -BridgeCommittee - public struct - 0xb::committee -CommitteeUpdateEvent - public struct - 0xb::committee -CommitteeMemberUrlUpdateEvent - public struct - 0xb::committee -CommitteeMember - public struct - 0xb::committee -CommitteeMemberRegistration - public struct - 0xb::committee -verify_signatures - public fun - 0xb::committee -create - public(package) fun - 0xb::committee -register - public(package) fun - 0xb::committee -try_create_next_committee - public(package) fun - 0xb::committee -execute_blocklist - public(package) fun - 0xb::committee -committee_members - public(package) fun - 0xb::committee -update_node_url - public(package) fun - 0xb::committee -check_uniqueness_bridge_keys - fun - 0xb::committee -Bridge - public struct - 0xb::bridge -BridgeInner - public struct - 0xb::bridge -TokenDepositedEvent - public struct - 0xb::bridge -EmergencyOpEvent - public struct - 0xb::bridge -BridgeRecord - public struct - 0xb::bridge -TokenTransferApproved - public struct - 0xb::bridge -TokenTransferClaimed - public struct - 0xb::bridge -TokenTransferAlreadyApproved - public struct - 0xb::bridge -TokenTransferAlreadyClaimed - public struct - 0xb::bridge -TokenTransferLimitExceed - public struct - 0xb::bridge -create - fun - 0xb::bridge -init_bridge_committee - fun - 0xb::bridge -committee_registration - public fun - 0xb::bridge -update_node_url - public fun - 0xb::bridge -register_foreign_token - public fun - 0xb::bridge -send_token - public fun - 0xb::bridge -approve_token_transfer - public fun - 0xb::bridge -claim_token - public fun - 0xb::bridge -claim_and_transfer_token - public fun - 0xb::bridge -execute_system_message - public fun - 0xb::bridge -get_token_transfer_action_status - fun - 0xb::bridge -get_token_transfer_action_signatures - fun - 0xb::bridge -load_inner - fun - 0xb::bridge -load_inner_mut - fun - 0xb::bridge -claim_token_internal - fun - 0xb::bridge -execute_emergency_op - fun - 0xb::bridge -execute_update_bridge_limit - fun - 0xb::bridge -execute_update_asset_price - fun - 0xb::bridge -execute_add_tokens_on_iota - fun - 0xb::bridge -get_current_seq_num_and_increment - fun - 0xb::bridge -get_parsed_token_transfer_message - fun - 0xb::bridge to_bytes public fun 0x1::bcs diff --git a/crates/iota-framework/src/lib.rs b/crates/iota-framework/src/lib.rs index a8b5622dd5c..e6af8bebe6f 100644 --- a/crates/iota-framework/src/lib.rs +++ b/crates/iota-framework/src/lib.rs @@ -5,8 +5,7 @@ use std::{fmt::Formatter, sync::LazyLock}; use iota_types::{ - BRIDGE_PACKAGE_ID, IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_PACKAGE_ID, MOVE_STDLIB_PACKAGE_ID, - STARDUST_PACKAGE_ID, + IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_PACKAGE_ID, MOVE_STDLIB_PACKAGE_ID, STARDUST_PACKAGE_ID, base_types::{ObjectID, ObjectRef}, digests::TransactionDigest, move_package::MovePackage, @@ -138,16 +137,6 @@ impl BuiltInFramework { "iota-system", [MOVE_STDLIB_PACKAGE_ID, IOTA_FRAMEWORK_PACKAGE_ID] ), - ( - BRIDGE_PACKAGE_ID, - "Bridge", - "bridge", - [ - MOVE_STDLIB_PACKAGE_ID, - IOTA_FRAMEWORK_PACKAGE_ID, - IOTA_SYSTEM_PACKAGE_ID - ] - ), ( STARDUST_PACKAGE_ID, "Stardust", diff --git a/crates/iota-framework/tests/build-system-packages.rs b/crates/iota-framework/tests/build-system-packages.rs index 01dbf7bd260..842b0af330c 100644 --- a/crates/iota-framework/tests/build-system-packages.rs +++ b/crates/iota-framework/tests/build-system-packages.rs @@ -39,14 +39,12 @@ fn build_system_packages() { let packages_path = Path::new(CRATE_ROOT).join("packages"); - let bridge_path = packages_path.join("bridge"); let iota_system_path = packages_path.join("iota-system"); let iota_framework_path = packages_path.join("iota-framework"); let move_stdlib_path = packages_path.join("move-stdlib"); let stardust_path = packages_path.join("stardust"); build_packages( - &bridge_path, &iota_system_path, &iota_framework_path, &move_stdlib_path, @@ -81,7 +79,6 @@ fn check_diff(checked_in: &Path, built: &Path) { } fn build_packages( - bridge_path: &Path, iota_system_path: &Path, iota_framework_path: &Path, stdlib_path: &Path, @@ -98,13 +95,11 @@ fn build_packages( }; debug_assert!(!config.test_mode); build_packages_with_move_config( - bridge_path, iota_system_path, iota_framework_path, stdlib_path, stardust_path, out_dir, - "bridge", "iota-system", "iota-framework", "move-stdlib", @@ -114,13 +109,11 @@ fn build_packages( } fn build_packages_with_move_config( - bridge_path: &Path, iota_system_path: &Path, iota_framework_path: &Path, stdlib_path: &Path, stardust_path: &Path, out_dir: &Path, - bridge_dir: &str, system_dir: &str, framework_dir: &str, stdlib_dir: &str, @@ -151,14 +144,6 @@ fn build_packages_with_move_config( } .build(iota_system_path) .unwrap(); - let bridge_pkg = BuildConfig { - config: config.clone(), - run_bytecode_verifier: true, - print_diags_to_stderr: false, - chain_id: None, // Framework pkg addr is agnostic to chain, resolves from Move.toml - } - .build(bridge_path) - .unwrap(); let stardust_pkg = BuildConfig { config, run_bytecode_verifier: true, @@ -171,7 +156,6 @@ fn build_packages_with_move_config( let move_stdlib = stdlib_pkg.get_stdlib_modules(); let iota_system = system_pkg.get_iota_system_modules(); let iota_framework = framework_pkg.get_iota_framework_modules(); - let bridge = bridge_pkg.get_bridge_modules(); let stardust = stardust_pkg.get_stardust_modules(); let compiled_packages_dir = out_dir.join(COMPILED_PACKAGES_DIR); @@ -181,8 +165,6 @@ fn build_packages_with_move_config( let iota_framework_members = serialize_modules_to_file(iota_framework, &compiled_packages_dir.join(framework_dir)) .unwrap(); - let bridge_members = - serialize_modules_to_file(bridge, &compiled_packages_dir.join(bridge_dir)).unwrap(); let stdlib_members = serialize_modules_to_file(move_stdlib, &compiled_packages_dir.join(stdlib_dir)).unwrap(); let stardust_members = @@ -206,11 +188,6 @@ fn build_packages_with_move_config( &stdlib_pkg.package.compiled_docs.unwrap(), &mut files_to_write, ); - create_category_file("bridge"); - relocate_docs( - &bridge_pkg.package.compiled_docs.unwrap(), - &mut files_to_write, - ); create_category_file("stardust"); relocate_docs( &stardust_pkg.package.compiled_docs.unwrap(), @@ -225,7 +202,6 @@ fn build_packages_with_move_config( let published_api = [ iota_system_members.join("\n"), iota_framework_members.join("\n"), - bridge_members.join("\n"), stdlib_members.join("\n"), stardust_members.join("\n"), ] diff --git a/crates/iota-genesis-builder/src/lib.rs b/crates/iota-genesis-builder/src/lib.rs index 0d8a3d2e99f..307f21889a9 100644 --- a/crates/iota-genesis-builder/src/lib.rs +++ b/crates/iota-genesis-builder/src/lib.rs @@ -30,13 +30,12 @@ use iota_genesis_common::{execute_genesis_transaction, get_genesis_protocol_conf use iota_protocol_config::{Chain, ProtocolConfig, ProtocolVersion}; use iota_sdk::Url; use iota_types::{ - BRIDGE_ADDRESS, IOTA_BRIDGE_OBJECT_ID, IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_ADDRESS, + IOTA_FRAMEWORK_PACKAGE_ID, IOTA_SYSTEM_ADDRESS, balance::{BALANCE_MODULE_NAME, Balance}, base_types::{ ExecutionDigests, IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest, TxContext, }, - bridge::{BRIDGE_CREATE_FUNCTION_NAME, BRIDGE_MODULE_NAME, BridgeChainId}, committee::Committee, crypto::{ AuthorityKeyPair, AuthorityPublicKeyBytes, AuthoritySignInfo, AuthoritySignInfoTrait, @@ -49,7 +48,6 @@ use iota_types::{ event::Event, gas_coin::{GAS, GasCoin, STARDUST_TOTAL_SUPPLY_NANOS}, governance::StakedIota, - id::UID, in_memory_storage::InMemoryStorage, inner_temporary_store::InnerTemporaryStore, iota_system_state::{IotaSystemState, IotaSystemStateTrait, get_iota_system_state}, @@ -557,11 +555,6 @@ impl Builder { } assert!(unsigned_genesis.has_randomness_state_object()); - assert_eq!( - protocol_config.enable_bridge(), - unsigned_genesis.has_bridge_object() - ); - assert!(unsigned_genesis.has_coin_deny_list_object()); assert_eq!( @@ -1540,24 +1533,6 @@ pub fn generate_genesis_system_object( vec![], )?; - if protocol_config.enable_bridge() { - let bridge_uid = builder - .input(CallArg::Pure( - UID::new(IOTA_BRIDGE_OBJECT_ID).to_bcs_bytes(), - )) - .unwrap(); - // TODO(bridge): this needs to be passed in as a parameter for next testnet - // regenesis Hardcoding chain id to IotaCustom - let bridge_chain_id = builder.pure(BridgeChainId::IotaCustom).unwrap(); - builder.programmable_move_call( - BRIDGE_ADDRESS.into(), - BRIDGE_MODULE_NAME.to_owned(), - BRIDGE_CREATE_FUNCTION_NAME.to_owned(), - vec![], - vec![bridge_uid, bridge_chain_id], - ); - } - // Step 4: Create the IOTA Coin Treasury Cap. let iota_treasury_cap = builder.programmable_move_call( IOTA_FRAMEWORK_PACKAGE_ID, diff --git a/crates/iota-graphql-rpc/src/test_infra/cluster.rs b/crates/iota-graphql-rpc/src/test_infra/cluster.rs index 31e3639ed9b..98b6db0e5e7 100644 --- a/crates/iota-graphql-rpc/src/test_infra/cluster.rs +++ b/crates/iota-graphql-rpc/src/test_infra/cluster.rs @@ -56,7 +56,7 @@ pub async fn start_cluster( graphql_connection_config: ConnectionConfig, internal_data_source_rpc_port: Option, ) -> Cluster { - let data_ingestion_path = tempfile::tempdir().unwrap().into_path(); + let data_ingestion_path = tempfile::tempdir().unwrap().keep(); let db_url = graphql_connection_config.db_url.clone(); let cancellation_token = CancellationToken::new(); // Starts validator+fullnode diff --git a/crates/iota-graphql-rpc/src/types/transaction_block_kind/end_of_epoch.rs b/crates/iota-graphql-rpc/src/types/transaction_block_kind/end_of_epoch.rs index 00aae789045..1b3d4e15018 100644 --- a/crates/iota-graphql-rpc/src/types/transaction_block_kind/end_of_epoch.rs +++ b/crates/iota-graphql-rpc/src/types/transaction_block_kind/end_of_epoch.rs @@ -401,16 +401,6 @@ impl EndOfEpochTransactionKind { checkpoint_viewed_at, }) } - N::BridgeStateCreate(chain_id) => K::BridgeStateCreate(BridgeStateCreateTransaction { - native: chain_id, - checkpoint_viewed_at, - }), - N::BridgeCommitteeInit(bridge_shared_version) => { - K::BridgeCommitteeInit(BridgeCommitteeInitTransaction { - native: bridge_shared_version, - checkpoint_viewed_at, - }) - } } } } diff --git a/crates/iota-graphql-rpc/tests/e2e_tests.rs b/crates/iota-graphql-rpc/tests/e2e_tests.rs index 806d57b4712..79fb3e88475 100644 --- a/crates/iota-graphql-rpc/tests/e2e_tests.rs +++ b/crates/iota-graphql-rpc/tests/e2e_tests.rs @@ -27,7 +27,7 @@ mod tests { async fn prep_executor_cluster() -> (ConnectionConfig, ExecutorCluster) { let rng = StdRng::from_seed([12; 32]); - let data_ingestion_path = tempdir().unwrap().into_path(); + let data_ingestion_path = tempdir().unwrap().keep(); let mut sim = Simulacrum::new_with_rng(rng); sim.set_data_ingestion_path(data_ingestion_path.clone()); @@ -100,7 +100,7 @@ mod tests { async fn test_simple_client_simulator_cluster() { let rng = StdRng::from_seed([12; 32]); let mut sim = Simulacrum::new_with_rng(rng); - let data_ingestion_path = tempdir().unwrap().into_path(); + let data_ingestion_path = tempdir().unwrap().keep(); sim.set_data_ingestion_path(data_ingestion_path.clone()); sim.create_checkpoint(); diff --git a/crates/iota-graphql-rpc/tests/examples_validation_tests.rs b/crates/iota-graphql-rpc/tests/examples_validation_tests.rs index aadc465d949..88e88e23276 100644 --- a/crates/iota-graphql-rpc/tests/examples_validation_tests.rs +++ b/crates/iota-graphql-rpc/tests/examples_validation_tests.rs @@ -143,7 +143,7 @@ mod tests { #[serial] async fn good_examples_within_limits() { let rng = StdRng::from_seed([12; 32]); - let data_ingestion_path = tempdir().unwrap().into_path(); + let data_ingestion_path = tempdir().unwrap().keep(); let mut sim = Simulacrum::new_with_rng(rng); let (mut max_nodes, mut max_output_nodes, mut max_depth, mut max_payload) = (0, 0, 0, 0); @@ -210,7 +210,7 @@ mod tests { #[serial] async fn bad_examples_fail() { let rng = StdRng::from_seed([12; 32]); - let data_ingestion_path = tempdir().unwrap().into_path(); + let data_ingestion_path = tempdir().unwrap().keep(); let mut sim = Simulacrum::new_with_rng(rng); let (mut max_nodes, mut max_output_nodes, mut max_depth, mut max_payload) = (0, 0, 0, 0); sim.set_data_ingestion_path(data_ingestion_path.clone()); diff --git a/crates/iota-indexer/src/indexer.rs b/crates/iota-indexer/src/indexer.rs index dc14c52b720..c86369a5eca 100644 --- a/crates/iota-indexer/src/indexer.rs +++ b/crates/iota-indexer/src/indexer.rs @@ -159,7 +159,7 @@ impl Indexer { config .data_ingestion_path .clone() - .unwrap_or(tempfile::tempdir().unwrap().into_path()), + .unwrap_or(tempfile::tempdir().unwrap().keep()), config.remote_store_url.clone(), vec![], extra_reader_options, diff --git a/crates/iota-indexer/tests/common/mod.rs b/crates/iota-indexer/tests/common/mod.rs index 5bb7d609c97..0b7054d1a83 100644 --- a/crates/iota-indexer/tests/common/mod.rs +++ b/crates/iota-indexer/tests/common/mod.rs @@ -86,7 +86,7 @@ impl SimulacrumTestSetup { ) -> &'a SimulacrumTestSetup { initialized_env_container.get_or_init(|| { let runtime = tokio::runtime::Runtime::new().unwrap(); - let data_ingestion_path = tempdir().unwrap().into_path(); + let data_ingestion_path = tempdir().unwrap().keep(); let sim = env_initializer(data_ingestion_path.clone()); let sim = Arc::new(sim); @@ -115,7 +115,7 @@ pub async fn start_test_cluster_with_read_write_indexer( database_name: Option<&str>, builder_modifier: Option TestClusterBuilder>>, ) -> (TestCluster, PgIndexerStore, HttpClient) { - let temp = tempdir().unwrap().into_path(); + let temp = tempdir().unwrap().keep(); let mut builder = TestClusterBuilder::new(); if let Some(builder_modifier) = builder_modifier { diff --git a/crates/iota-indexer/tests/ingestion_tests.rs b/crates/iota-indexer/tests/ingestion_tests.rs index ef508adbba5..af0da474bb3 100644 --- a/crates/iota-indexer/tests/ingestion_tests.rs +++ b/crates/iota-indexer/tests/ingestion_tests.rs @@ -48,7 +48,7 @@ mod ingestion_tests { #[tokio::test] pub async fn test_transaction_table() -> Result<(), IndexerError> { let mut sim = Simulacrum::new(); - let data_ingestion_path = tempdir().unwrap().into_path(); + let data_ingestion_path = tempdir().unwrap().keep(); sim.set_data_ingestion_path(data_ingestion_path.clone()); // Execute a simple transaction. @@ -99,7 +99,7 @@ mod ingestion_tests { #[tokio::test] pub async fn test_object_type() -> Result<(), IndexerError> { let mut sim = Simulacrum::new(); - let data_ingestion_path = tempdir().unwrap().into_path(); + let data_ingestion_path = tempdir().unwrap().keep(); sim.set_data_ingestion_path(data_ingestion_path.clone()); // Execute a simple transaction. @@ -228,7 +228,7 @@ mod ingestion_tests { #[tokio::test] pub async fn test_tx_insertion_order_table() -> Result<(), IndexerError> { let mut sim = Simulacrum::new(); - let data_ingestion_path = tempdir().unwrap().into_path(); + let data_ingestion_path = tempdir().unwrap().keep(); sim.set_data_ingestion_path(data_ingestion_path.clone()); // Execute a simple transaction. @@ -269,7 +269,7 @@ mod ingestion_tests { #[tokio::test] pub async fn test_tx_insertion_order_table_for_existing_digest() -> Result<(), IndexerError> { let mut sim = Simulacrum::new(); - let data_ingestion_path = tempdir().unwrap().into_path(); + let data_ingestion_path = tempdir().unwrap().keep(); sim.set_data_ingestion_path(data_ingestion_path.clone()); // Execute a simple transaction. diff --git a/crates/iota-json-rpc-api/src/bridge.rs b/crates/iota-json-rpc-api/src/bridge.rs deleted file mode 100644 index 91fbf84b73e..00000000000 --- a/crates/iota-json-rpc-api/src/bridge.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use iota_open_rpc_macros::open_rpc; -use iota_types::bridge::BridgeSummary; -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; - -#[open_rpc(namespace = "iotax", tag = "Bridge Read API")] -#[rpc(server, client, namespace = "iotax")] -pub trait BridgeReadApi { - /// Returns the latest BridgeSummary - #[method(name = "getLatestBridge")] - async fn get_latest_bridge(&self) -> RpcResult; - - /// Returns the initial shared version of the bridge object, usually - /// for the purpose of constructing an ObjectArg in a transaction. - #[method(name = "getBridgeObjectInitialSharedVersion")] - async fn get_bridge_object_initial_shared_version(&self) -> RpcResult; -} diff --git a/crates/iota-json-rpc-api/src/lib.rs b/crates/iota-json-rpc-api/src/lib.rs index 62705bf2cef..bbb3420e090 100644 --- a/crates/iota-json-rpc-api/src/lib.rs +++ b/crates/iota-json-rpc-api/src/lib.rs @@ -7,7 +7,6 @@ //! managing governance-related data, and more. use anyhow::anyhow; -pub use bridge::{BridgeReadApiClient, BridgeReadApiOpenRpc, BridgeReadApiServer}; pub use coin::{CoinReadApiClient, CoinReadApiOpenRpc, CoinReadApiServer}; pub use extended::{ExtendedApiClient, ExtendedApiOpenRpc, ExtendedApiServer}; pub use governance::{GovernanceReadApiClient, GovernanceReadApiOpenRpc, GovernanceReadApiServer}; @@ -31,7 +30,6 @@ pub use transaction_builder::{ }; pub use write::{WriteApiClient, WriteApiOpenRpc, WriteApiServer}; -mod bridge; mod coin; mod extended; mod governance; diff --git a/crates/iota-json-rpc-types/src/iota_transaction.rs b/crates/iota-json-rpc-types/src/iota_transaction.rs index 254bc17dabc..a29a03c3d95 100644 --- a/crates/iota-json-rpc-types/src/iota_transaction.rs +++ b/crates/iota-json-rpc-types/src/iota_transaction.rs @@ -17,7 +17,7 @@ use iota_types::{ authenticator_state::ActiveJwk, base_types::{EpochId, IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest}, crypto::IotaSignature, - digests::{CheckpointDigest, ConsensusCommitDigest, ObjectDigest, TransactionEventsDigest}, + digests::{ConsensusCommitDigest, ObjectDigest, TransactionEventsDigest}, effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents}, error::{ExecutionError, IotaError, IotaResult}, event::EventID, @@ -531,16 +531,6 @@ impl IotaTransactionBlockKind { }, ) } - EndOfEpochTransactionKind::BridgeStateCreate(chain_id) => { - IotaEndOfEpochTransactionKind::BridgeStateCreate( - (*chain_id.as_bytes()).into(), - ) - } - EndOfEpochTransactionKind::BridgeCommitteeInit( - bridge_shared_version, - ) => IotaEndOfEpochTransactionKind::BridgeCommitteeUpdate( - bridge_shared_version, - ), }) .collect(), }) @@ -620,14 +610,6 @@ impl IotaTransactionBlockKind { }, ) } - EndOfEpochTransactionKind::BridgeStateCreate(id) => { - IotaEndOfEpochTransactionKind::BridgeStateCreate( - (*id.as_bytes()).into(), - ) - } - EndOfEpochTransactionKind::BridgeCommitteeInit(seq) => { - IotaEndOfEpochTransactionKind::BridgeCommitteeUpdate(seq) - } }) .collect(), }) @@ -1670,8 +1652,6 @@ pub enum IotaEndOfEpochTransactionKind { ChangeEpochV2(IotaChangeEpochV2), AuthenticatorStateCreate, AuthenticatorStateExpire(IotaAuthenticatorStateExpire), - BridgeStateCreate(CheckpointDigest), - BridgeCommitteeUpdate(SequenceNumber), } #[serde_as] diff --git a/crates/iota-json-rpc/src/authority_state.rs b/crates/iota-json-rpc/src/authority_state.rs index 66eeba27601..a825355592d 100644 --- a/crates/iota-json-rpc/src/authority_state.rs +++ b/crates/iota-json-rpc/src/authority_state.rs @@ -27,7 +27,6 @@ use iota_storage::{ }; use iota_types::{ base_types::{IotaAddress, MoveObjectType, ObjectID, ObjectInfo, ObjectRef, SequenceNumber}, - bridge::Bridge, committee::{Committee, EpochId}, digests::{ChainIdentifier, TransactionDigest}, dynamic_field::DynamicFieldInfo, @@ -170,9 +169,6 @@ pub trait StateRead: Send + Sync { fn get_system_state(&self) -> StateReadResult; fn get_or_latest_committee(&self, epoch: Option>) -> StateReadResult; - // bridge_api - fn get_bridge(&self) -> StateReadResult; - // coin_api fn find_publish_txn_digest(&self, package_id: ObjectID) -> StateReadResult; fn get_owned_coins( @@ -427,12 +423,6 @@ impl StateRead for AuthorityState { .get_or_latest_committee(epoch.map(|e| *e))?) } - fn get_bridge(&self) -> StateReadResult { - self.get_cache_reader() - .get_bridge_object_unsafe() - .map_err(|err| err.into()) - } - fn find_publish_txn_digest(&self, package_id: ObjectID) -> StateReadResult { Ok(self.find_publish_txn_digest(package_id)?) } diff --git a/crates/iota-json-rpc/src/bridge_api.rs b/crates/iota-json-rpc/src/bridge_api.rs deleted file mode 100644 index b951a5fb725..00000000000 --- a/crates/iota-json-rpc/src/bridge_api.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::sync::Arc; - -use async_trait::async_trait; -use iota_core::authority::AuthorityState; -use iota_json_rpc_api::{BridgeReadApiOpenRpc, BridgeReadApiServer, JsonRpcMetrics}; -use iota_open_rpc::Module; -use iota_types::bridge::{BridgeSummary, BridgeTrait, get_bridge_obj_initial_shared_version}; -use jsonrpsee::{RpcModule, core::RpcResult}; -use tracing::instrument; - -use crate::{IotaRpcModule, authority_state::StateRead, error::Error, logger::FutureWithTracing}; - -#[derive(Clone)] -pub struct BridgeReadApi { - state: Arc, - pub metrics: Arc, -} - -impl BridgeReadApi { - pub fn new(state: Arc, metrics: Arc) -> Self { - Self { state, metrics } - } -} - -#[async_trait] -impl BridgeReadApiServer for BridgeReadApi { - #[instrument(skip(self))] - async fn get_latest_bridge(&self) -> RpcResult { - async move { - self.state - .get_bridge() - .map_err(Error::from)? - .try_into_bridge_summary() - .map_err(Error::from) - } - .trace() - .await - } - - #[instrument(skip(self))] - async fn get_bridge_object_initial_shared_version(&self) -> RpcResult { - async move { - Ok( - get_bridge_obj_initial_shared_version(self.state.get_object_store())? - .ok_or(Error::Unexpected( - "Failed to find Bridge object initial version".to_string(), - ))? - .into(), - ) - } - .trace() - .await - } -} - -impl IotaRpcModule for BridgeReadApi { - fn rpc(self) -> RpcModule { - self.into_rpc() - } - - fn rpc_doc_module() -> Module { - BridgeReadApiOpenRpc::module_doc() - } -} diff --git a/crates/iota-json-rpc/src/lib.rs b/crates/iota-json-rpc/src/lib.rs index 75280868d70..2cad0e8eeb5 100644 --- a/crates/iota-json-rpc/src/lib.rs +++ b/crates/iota-json-rpc/src/lib.rs @@ -41,7 +41,6 @@ use crate::{ pub mod authority_state; pub mod axum_router; mod balance_changes; -pub mod bridge_api; pub mod coin_api; pub mod error; pub mod governance_api; diff --git a/crates/iota-move-build/src/lib.rs b/crates/iota-move-build/src/lib.rs index de9e3544e8e..77c852a0981 100644 --- a/crates/iota-move-build/src/lib.rs +++ b/crates/iota-move-build/src/lib.rs @@ -14,8 +14,7 @@ use std::{ use fastcrypto::encoding::Base64; use iota_package_management::{PublishedAtError, resolve_published_id}; use iota_types::{ - BRIDGE_ADDRESS, IOTA_FRAMEWORK_ADDRESS, IOTA_SYSTEM_ADDRESS, MOVE_STDLIB_ADDRESS, - STARDUST_ADDRESS, + IOTA_FRAMEWORK_ADDRESS, IOTA_SYSTEM_ADDRESS, MOVE_STDLIB_ADDRESS, STARDUST_ADDRESS, base_types::ObjectID, error::{IotaError, IotaResult}, is_system_package, @@ -86,7 +85,7 @@ impl BuildConfig { pub fn new_for_testing() -> Self { move_package::package_hooks::register_package_hooks(Box::new(IotaPackageHooks)); let mut build_config: Self = Default::default(); - let install_dir = tempfile::tempdir().unwrap().into_path(); + let install_dir = tempfile::tempdir().unwrap().keep(); let lock_file = install_dir.join("Move.lock"); build_config.config.install_dir = Some(install_dir); build_config.config.lock_file = Some(lock_file); @@ -430,12 +429,6 @@ impl CompiledPackage { .collect() } - /// Get bytecode modules from Bridge that are used by this package - pub fn get_bridge_modules(&self) -> impl Iterator { - self.get_modules_and_deps() - .filter(|m| *m.self_id().address() == BRIDGE_ADDRESS) - } - /// Get bytecode modules from the IOTA System that are used by this package pub fn get_iota_system_modules(&self) -> impl Iterator { self.get_modules_and_deps() diff --git a/crates/iota-network/src/state_sync/tests.rs b/crates/iota-network/src/state_sync/tests.rs index fe9f6902d13..234aa6790b5 100644 --- a/crates/iota-network/src/state_sync/tests.rs +++ b/crates/iota-network/src/state_sync/tests.rs @@ -277,7 +277,7 @@ async fn test_state_sync_using_archive() -> anyhow::Result<()> { let (ordered_checkpoints, _, sequence_number_to_digest, checkpoints) = committee.make_empty_checkpoints(100, None); // Initialize archive store with all checkpoints - let temp_dir = tempdir()?.into_path(); + let temp_dir = tempdir()?.keep(); let local_path = temp_dir.join("local_dir"); let remote_path = temp_dir.join("remote_dir"); let local_store_config = ObjectStoreConfig { diff --git a/crates/iota-node/src/lib.rs b/crates/iota-node/src/lib.rs index 12852ce8a64..ac6debfcd81 100644 --- a/crates/iota-node/src/lib.rs +++ b/crates/iota-node/src/lib.rs @@ -73,9 +73,9 @@ use iota_core::{ validator_tx_finalizer::ValidatorTxFinalizer, }; use iota_json_rpc::{ - JsonRpcServerBuilder, bridge_api::BridgeReadApi, coin_api::CoinReadApi, - governance_api::GovernanceReadApi, indexer_api::IndexerApi, move_utils::MoveUtils, - read_api::ReadApi, transaction_builder_api::TransactionBuilderApi, + JsonRpcServerBuilder, coin_api::CoinReadApi, governance_api::GovernanceReadApi, + indexer_api::IndexerApi, move_utils::MoveUtils, read_api::ReadApi, + transaction_builder_api::TransactionBuilderApi, transaction_execution_api::TransactionExecutionApi, }; use iota_json_rpc_api::JsonRpcMetrics; @@ -2046,7 +2046,6 @@ pub async fn build_http_server( server.register_module(TransactionBuilderApi::new(state.clone()))?; } server.register_module(GovernanceReadApi::new(state.clone(), metrics.clone()))?; - server.register_module(BridgeReadApi::new(state.clone(), metrics.clone()))?; if let Some(transaction_orchestrator) = transaction_orchestrator { server.register_module(TransactionExecutionApi::new( diff --git a/crates/iota-proc-macros/src/lib.rs b/crates/iota-proc-macros/src/lib.rs index 859c3a3377d..8c14b3acccd 100644 --- a/crates/iota-proc-macros/src/lib.rs +++ b/crates/iota-proc-macros/src/lib.rs @@ -61,7 +61,7 @@ pub fn init_static_initializers(_args: TokenStream, item: TokenStream) -> TokenS let mut path = PathBuf::from(env!("SIMTEST_STATIC_INIT_MOVE")); let mut build_config = BuildConfig::default(); - build_config.config.install_dir = Some(TempDir::new().unwrap().into_path()); + build_config.config.install_dir = Some(TempDir::new().unwrap().keep()); let _all_module_bytes = build_config .build(&path) .unwrap() diff --git a/crates/iota-protocol-config/src/lib.rs b/crates/iota-protocol-config/src/lib.rs index 6682972649a..309ecd47eeb 100644 --- a/crates/iota-protocol-config/src/lib.rs +++ b/crates/iota-protocol-config/src/lib.rs @@ -19,7 +19,7 @@ use tracing::{info, warn}; /// The minimum and maximum protocol versions supported by this build. const MIN_PROTOCOL_VERSION: u64 = 1; -pub const MAX_PROTOCOL_VERSION: u64 = 8; +pub const MAX_PROTOCOL_VERSION: u64 = 9; // Record history of protocol version allocations here: // @@ -51,6 +51,7 @@ pub const MAX_PROTOCOL_VERSION: u64 = 8; // Enable the new consensus commit rule for testnet. // Enable min_free_execution_slot for the shared object congestion // tracker in devnet. +// Version 9: Remove the iota-bridge from the framework. #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -162,10 +163,6 @@ struct FeatureFlags { #[serde(skip_serializing_if = "is_false")] enable_jwk_consensus_updates: bool, - // Enable bridge protocol - #[serde(skip_serializing_if = "is_false")] - bridge: bool, - // If true, multisig containing zkLogin sig is accepted. #[serde(skip_serializing_if = "is_false")] accept_zklogin_in_multisig: bool, @@ -1036,6 +1033,7 @@ pub struct ProtocolConfig { /// Bundle. max_soft_bundle_size: Option, + /// Deprecated because of bridge removal. /// Whether to try to form bridge committee // Note: this is not a feature flag because we want to distinguish between // `None` and `Some(false)`, as committee was already finalized on Testnet. @@ -1101,18 +1099,6 @@ impl ProtocolConfig { self.random_beacon_dkg_version.unwrap_or(1) } - pub fn enable_bridge(&self) -> bool { - self.feature_flags.bridge - } - - pub fn should_try_to_finalize_bridge_committee(&self) -> bool { - if !self.enable_bridge() { - return false; - } - // In the older protocol version, always try to finalize the committee. - self.bridge_should_try_to_finalize_committee.unwrap_or(true) - } - pub fn accept_zklogin_in_multisig(&self) -> bool { self.feature_flags.accept_zklogin_in_multisig } @@ -1983,6 +1969,10 @@ impl ProtocolConfig { cfg.feature_flags.congestion_control_min_free_execution_slot = true; } } + 9 => { + // this flag is now deprecated because of the bridge removal. + cfg.bridge_should_try_to_finalize_committee = None; + } // Use this template when making changes: // // // modify an existing constant. @@ -2090,9 +2080,6 @@ impl ProtocolConfig { pub fn set_zklogin_max_epoch_upper_bound_delta_for_testing(&mut self, val: Option) { self.feature_flags.zklogin_max_epoch_upper_bound_delta = val } - pub fn set_disable_bridge_for_testing(&mut self) { - self.feature_flags.bridge = false - } pub fn set_passkey_auth_for_testing(&mut self, val: bool) { self.feature_flags.passkey_auth = val diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_2.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_2.snap index 3f0064e48a4..c228b2f3654 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_2.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_2.snap @@ -1,7 +1,6 @@ --- source: crates/iota-protocol-config/src/lib.rs expression: "ProtocolConfig::get_for_version(cur, *chain_id)" -snapshot_kind: text --- version: 2 feature_flags: diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_3.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_3.snap index c5550f5ac7e..d7dd1e760f4 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_3.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_3.snap @@ -1,7 +1,6 @@ --- source: crates/iota-protocol-config/src/lib.rs expression: "ProtocolConfig::get_for_version(cur, *chain_id)" -snapshot_kind: text --- version: 3 feature_flags: diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_7.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_7.snap index 8c74f5ac8f3..149c48e7504 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_7.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_7.snap @@ -1,7 +1,6 @@ --- source: crates/iota-protocol-config/src/lib.rs expression: "ProtocolConfig::get_for_version(cur, *chain_id)" -snapshot_kind: text --- version: 7 feature_flags: diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_2.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_2.snap index 89cf049133a..f594802daf4 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_2.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_2.snap @@ -1,7 +1,6 @@ --- source: crates/iota-protocol-config/src/lib.rs expression: "ProtocolConfig::get_for_version(cur, *chain_id)" -snapshot_kind: text --- version: 2 feature_flags: diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_3.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_3.snap index 148c0e0f3e0..d23fdbdc94c 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_3.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_3.snap @@ -1,7 +1,6 @@ --- source: crates/iota-protocol-config/src/lib.rs expression: "ProtocolConfig::get_for_version(cur, *chain_id)" -snapshot_kind: text --- version: 3 feature_flags: diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_7.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_7.snap index d81a342e368..6d21ba74e16 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_7.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_7.snap @@ -1,7 +1,6 @@ --- source: crates/iota-protocol-config/src/lib.rs expression: "ProtocolConfig::get_for_version(cur, *chain_id)" -snapshot_kind: text --- version: 7 feature_flags: diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_2.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_2.snap index e23a99f7062..be0682d418b 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_2.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_2.snap @@ -1,7 +1,6 @@ --- source: crates/iota-protocol-config/src/lib.rs expression: "ProtocolConfig::get_for_version(cur, *chain_id)" -snapshot_kind: text --- version: 2 feature_flags: diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_3.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_3.snap index 3e7f5807cab..9d6e9f460dd 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_3.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_3.snap @@ -1,7 +1,6 @@ --- source: crates/iota-protocol-config/src/lib.rs expression: "ProtocolConfig::get_for_version(cur, *chain_id)" -snapshot_kind: text --- version: 3 feature_flags: diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_7.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_7.snap index 429b1589906..72bfac00b51 100644 --- a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_7.snap +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_7.snap @@ -1,7 +1,6 @@ --- source: crates/iota-protocol-config/src/lib.rs expression: "ProtocolConfig::get_for_version(cur, *chain_id)" -snapshot_kind: text --- version: 7 feature_flags: diff --git a/crates/iota-snapshot/src/tests.rs b/crates/iota-snapshot/src/tests.rs index d3874391c6a..ca1edeadf80 100644 --- a/crates/iota-snapshot/src/tests.rs +++ b/crates/iota-snapshot/src/tests.rs @@ -23,7 +23,7 @@ use crate::{FileCompression, reader::StateSnapshotReaderV1, writer::StateSnapsho fn temp_dir() -> std::path::PathBuf { tempdir() .expect("Failed to open temporary directory") - .into_path() + .keep() } pub fn insert_keys( diff --git a/crates/iota-source-validation-service/tests/tests.rs b/crates/iota-source-validation-service/tests/tests.rs index 4a28b9ded4d..c5cb9b1a6e0 100644 --- a/crates/iota-source-validation-service/tests/tests.rs +++ b/crates/iota-source-validation-service/tests/tests.rs @@ -290,7 +290,7 @@ async fn test_api_route() -> anyhow::Result<()> { let address = "0x2"; let module = "address"; let source_path = fixtures - .into_path() + .keep() .join("iota/move-stdlib/sources/address.move"); let mut source_lookup = SourceLookup::new(); diff --git a/crates/iota-swarm-config/src/network_config_builder.rs b/crates/iota-swarm-config/src/network_config_builder.rs index 8396487f8e0..6af97844ec2 100644 --- a/crates/iota-swarm-config/src/network_config_builder.rs +++ b/crates/iota-swarm-config/src/network_config_builder.rs @@ -119,7 +119,7 @@ impl ConfigBuilder { } pub fn new_with_temp_dir() -> Self { - Self::new(nondeterministic!(tempfile::tempdir().unwrap()).into_path()) + Self::new(nondeterministic!(tempfile::tempdir().unwrap()).keep()) } } diff --git a/crates/iota-swarm-config/src/node_config_builder.rs b/crates/iota-swarm-config/src/node_config_builder.rs index ffe63cb4b7b..19ce64c903b 100644 --- a/crates/iota-swarm-config/src/node_config_builder.rs +++ b/crates/iota-swarm-config/src/node_config_builder.rs @@ -119,7 +119,7 @@ impl ValidatorConfigBuilder { let key_path = get_key_path(&validator.authority_key_pair); let config_directory = self .config_directory - .unwrap_or_else(|| tempfile::tempdir().unwrap().into_path()); + .unwrap_or_else(|| tempfile::tempdir().unwrap().keep()); let migration_tx_data_path = Some(config_directory.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME)); let db_path = config_directory @@ -405,7 +405,7 @@ impl FullnodeConfigBuilder { let key_path = get_key_path(&validator_config.authority_key_pair); let config_directory = self .config_directory - .unwrap_or_else(|| tempfile::tempdir().unwrap().into_path()); + .unwrap_or_else(|| tempfile::tempdir().unwrap().keep()); let migration_tx_data_path = Some(config_directory.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME)); diff --git a/crates/iota-tool/src/db_tool/db_dump.rs b/crates/iota-tool/src/db_tool/db_dump.rs index c1ddfd489c8..a7d47297897 100644 --- a/crates/iota-tool/src/db_tool/db_dump.rs +++ b/crates/iota-tool/src/db_tool/db_dump.rs @@ -328,7 +328,7 @@ mod test { #[tokio::test] async fn db_dump_population() -> Result<(), anyhow::Error> { - let primary_path = tempfile::tempdir()?.into_path(); + let primary_path = tempfile::tempdir()?.keep(); // Open the DB for writing let _: AuthorityEpochTables = AuthorityEpochTables::open(0, &primary_path, None); diff --git a/crates/iota-transactional-test-runner/src/simulator_persisted_store.rs b/crates/iota-transactional-test-runner/src/simulator_persisted_store.rs index 7c7199f1a52..2ae762da615 100644 --- a/crates/iota-transactional-test-runner/src/simulator_persisted_store.rs +++ b/crates/iota-transactional-test-runner/src/simulator_persisted_store.rs @@ -115,7 +115,7 @@ impl PersistedStore { where R: rand::RngCore + rand::CryptoRng, { - let path: PathBuf = path.unwrap_or(tempdir().unwrap().into_path()); + let path: PathBuf = path.unwrap_or(tempdir().unwrap().keep()); let mut builder = ConfigBuilder::new_with_temp_dir() .rng(&mut rng) diff --git a/crates/iota-transactional-test-runner/src/test_adapter.rs b/crates/iota-transactional-test-runner/src/test_adapter.rs index 370bbccd531..2c1b9434c99 100644 --- a/crates/iota-transactional-test-runner/src/test_adapter.rs +++ b/crates/iota-transactional-test-runner/src/test_adapter.rs @@ -36,7 +36,7 @@ use iota_storage::{ }; use iota_swarm_config::genesis_config::AccountConfig; use iota_types::{ - BRIDGE_ADDRESS, IOTA_CLOCK_OBJECT_ID, IOTA_DENY_LIST_OBJECT_ID, IOTA_FRAMEWORK_ADDRESS, + IOTA_CLOCK_OBJECT_ID, IOTA_DENY_LIST_OBJECT_ID, IOTA_FRAMEWORK_ADDRESS, IOTA_FRAMEWORK_PACKAGE_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_ADDRESS, IOTA_SYSTEM_PACKAGE_ID, IOTA_SYSTEM_STATE_OBJECT_ID, MOVE_STDLIB_ADDRESS, MOVE_STDLIB_PACKAGE_ID, STARDUST_ADDRESS, STARDUST_PACKAGE_ID, @@ -240,7 +240,7 @@ impl AdapterInitConfig { Some(OffChainConfig { snapshot_config, epochs_to_keep, - data_ingestion_path: data_ingestion_path.unwrap_or(tempdir().unwrap().into_path()), + data_ingestion_path: data_ingestion_path.unwrap_or(tempdir().unwrap().keep()), rest_api_url, }) } else { @@ -2065,13 +2065,6 @@ static NAMED_ADDRESSES: Lazy> = Lazy::new(|| move_compiler::shared::NumberFormat::Hex, ), ); - map.insert( - "bridge".to_string(), - NumericalAddress::new( - BRIDGE_ADDRESS.into_bytes(), - move_compiler::shared::NumberFormat::Hex, - ), - ); map }); @@ -2100,15 +2093,10 @@ pub static PRE_COMPILED: Lazy = Lazy::new(|| { flavor: Flavor::Iota, ..Default::default() }; - let bridge_sources = { - let mut buf = iota_files.to_path_buf(); - buf.extend(["packages", "bridge", "sources"]); - buf.to_string_lossy().to_string() - }; let fully_compiled_res = move_compiler::construct_pre_compiled_lib( vec![PackagePaths { name: Some(("iota-framework".into(), config)), - paths: vec![iota_system_sources, iota_sources, iota_deps, bridge_sources], + paths: vec![iota_system_sources, iota_sources, iota_deps], named_address_map: NAMED_ADDRESSES.clone(), }], None, diff --git a/crates/iota-types/src/bridge.rs b/crates/iota-types/src/bridge.rs deleted file mode 100644 index 193c82a2b94..00000000000 --- a/crates/iota-types/src/bridge.rs +++ /dev/null @@ -1,510 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use enum_dispatch::enum_dispatch; -use move_core_types::{ident_str, identifier::IdentStr}; -use num_enum::TryFromPrimitive; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; - -use crate::{ - IOTA_BRIDGE_OBJECT_ID, - base_types::{IotaAddress, ObjectID, SequenceNumber}, - collection_types::{Bag, LinkedTable, LinkedTableNode, VecMap}, - dynamic_field::{Field, get_dynamic_field_from_store}, - error::{IotaError, IotaResult}, - id::UID, - iota_serde::{BigInt, Readable}, - object::Owner, - storage::ObjectStore, - versioned::Versioned, -}; - -pub type BridgeInnerDynamicField = Field; -pub type BridgeRecordDynamicField = Field< - MoveTypeBridgeMessageKey, - LinkedTableNode, ->; - -pub const BRIDGE_MODULE_NAME: &IdentStr = ident_str!("bridge"); -pub const BRIDGE_TREASURY_MODULE_NAME: &IdentStr = ident_str!("treasury"); -pub const BRIDGE_LIMITER_MODULE_NAME: &IdentStr = ident_str!("limiter"); -pub const BRIDGE_COMMITTEE_MODULE_NAME: &IdentStr = ident_str!("committee"); -pub const BRIDGE_MESSAGE_MODULE_NAME: &IdentStr = ident_str!("message"); -pub const BRIDGE_CREATE_FUNCTION_NAME: &IdentStr = ident_str!("create"); -pub const BRIDGE_INIT_COMMITTEE_FUNCTION_NAME: &IdentStr = ident_str!("init_bridge_committee"); -pub const BRIDGE_REGISTER_FOREIGN_TOKEN_FUNCTION_NAME: &IdentStr = - ident_str!("register_foreign_token"); -pub const BRIDGE_CREATE_ADD_TOKEN_ON_IOTA_MESSAGE_FUNCTION_NAME: &IdentStr = - ident_str!("create_add_tokens_on_iota_message"); -pub const BRIDGE_EXECUTE_SYSTEM_MESSAGE_FUNCTION_NAME: &IdentStr = - ident_str!("execute_system_message"); - -pub const BRIDGE_SUPPORTED_ASSET: &[&str] = &["btc", "eth", "usdc", "usdt"]; - -pub const BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER: u64 = 7500; // out of 10000 (75%) -pub const BRIDGE_COMMITTEE_MAXIMAL_VOTING_POWER: u64 = 10000; // (100%) - -// Threshold for action to be approved by the committee (our of 10000) -pub const APPROVAL_THRESHOLD_TOKEN_TRANSFER: u64 = 3334; -pub const APPROVAL_THRESHOLD_EMERGENCY_PAUSE: u64 = 450; -pub const APPROVAL_THRESHOLD_EMERGENCY_UNPAUSE: u64 = 5001; -pub const APPROVAL_THRESHOLD_COMMITTEE_BLOCKLIST: u64 = 5001; -pub const APPROVAL_THRESHOLD_LIMIT_UPDATE: u64 = 5001; -pub const APPROVAL_THRESHOLD_ASSET_PRICE_UPDATE: u64 = 5001; -pub const APPROVAL_THRESHOLD_EVM_CONTRACT_UPGRADE: u64 = 5001; -pub const APPROVAL_THRESHOLD_ADD_TOKENS_ON_IOTA: u64 = 5001; -pub const APPROVAL_THRESHOLD_ADD_TOKENS_ON_EVM: u64 = 5001; - -// const for initial token ids for convenience -pub const TOKEN_ID_IOTA: u8 = 0; -pub const TOKEN_ID_BTC: u8 = 1; -pub const TOKEN_ID_ETH: u8 = 2; -pub const TOKEN_ID_USDC: u8 = 3; -pub const TOKEN_ID_USDT: u8 = 4; - -#[derive( - Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, TryFromPrimitive, JsonSchema, Hash, -)] -#[repr(u8)] -pub enum BridgeChainId { - IotaMainnet = 0, - IotaTestnet = 1, - IotaCustom = 2, - - EthMainnet = 10, - EthSepolia = 11, - EthCustom = 12, -} - -impl BridgeChainId { - pub fn is_iota_chain(&self) -> bool { - matches!( - self, - BridgeChainId::IotaMainnet | BridgeChainId::IotaTestnet | BridgeChainId::IotaCustom - ) - } -} - -pub fn get_bridge_obj_initial_shared_version( - object_store: &dyn ObjectStore, -) -> IotaResult> { - Ok(object_store - .get_object(&IOTA_BRIDGE_OBJECT_ID)? - .map(|obj| match obj.owner { - Owner::Shared { - initial_shared_version, - } => initial_shared_version, - _ => unreachable!("Bridge object must be shared"), - })) -} - -/// Bridge provides an abstraction over multiple versions of the inner -/// BridgeInner object. This should be the primary interface to the bridge -/// object in Rust. We use enum dispatch to dispatch all methods defined in -/// BridgeTrait to the actual implementation in the inner types. -#[derive(Debug, Serialize, Deserialize, Clone)] -#[enum_dispatch(BridgeTrait)] -pub enum Bridge { - V1(BridgeInnerV1), -} - -/// Rust version of the Move iota::bridge::Bridge type -/// This repreents the object with 0x9 ID. -/// In Rust, this type should be rarely used since it's just a thin -/// wrapper used to access the inner object. -/// Within this module, we use it to determine the current version of the bridge -/// inner object type, so that we could deserialize the inner object correctly. -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct BridgeWrapper { - pub id: UID, - pub version: Versioned, -} - -/// This is the standard API that all bridge inner object type should implement. -#[enum_dispatch] -pub trait BridgeTrait { - fn bridge_version(&self) -> u64; - fn message_version(&self) -> u8; - fn chain_id(&self) -> u8; - fn sequence_nums(&self) -> &VecMap; - fn committee(&self) -> &MoveTypeBridgeCommittee; - fn treasury(&self) -> &MoveTypeBridgeTreasury; - fn bridge_records(&self) -> &LinkedTable; - fn frozen(&self) -> bool; - fn try_into_bridge_summary(self) -> IotaResult; -} - -#[serde_as] -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)] -#[serde(rename_all = "camelCase")] -pub struct BridgeSummary { - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub bridge_version: u64, - // Message version - pub message_version: u8, - /// Self Chain ID - pub chain_id: u8, - /// Sequence numbers of all message types - #[schemars(with = "Vec<(u8, BigInt)>")] - #[serde_as(as = "Vec<(_, Readable, _>)>")] - pub sequence_nums: Vec<(u8, u64)>, - pub committee: BridgeCommitteeSummary, - /// Summary of the treasury - pub treasury: BridgeTreasurySummary, - /// Object ID of bridge Records (dynamic field) - pub bridge_records_id: ObjectID, - /// Summary of the limiter - pub limiter: BridgeLimiterSummary, - /// Whether the bridge is currently frozen or not - pub is_frozen: bool, - // TODO: add treasury -} - -impl Default for BridgeSummary { - fn default() -> Self { - Self { - bridge_version: 1, - message_version: 1, - chain_id: 1, - sequence_nums: vec![], - committee: BridgeCommitteeSummary::default(), - treasury: BridgeTreasurySummary::default(), - bridge_records_id: ObjectID::random(), - limiter: BridgeLimiterSummary::default(), - is_frozen: false, - } - } -} - -pub fn get_bridge_wrapper(object_store: &dyn ObjectStore) -> Result { - let wrapper = object_store - .get_object(&IOTA_BRIDGE_OBJECT_ID)? - // Don't panic here on None because object_store is a generic store. - .ok_or_else(|| IotaError::IotaBridgeRead("BridgeWrapper object not found".to_owned()))?; - let move_object = wrapper.data.try_as_move().ok_or_else(|| { - IotaError::IotaBridgeRead("BridgeWrapper object must be a Move object".to_owned()) - })?; - let result = bcs::from_bytes::(move_object.contents()) - .map_err(|err| IotaError::IotaBridgeRead(err.to_string()))?; - Ok(result) -} - -pub fn get_bridge(object_store: &dyn ObjectStore) -> Result { - let wrapper = get_bridge_wrapper(object_store)?; - let id = wrapper.version.id.id.bytes; - let version = wrapper.version.version; - match version { - 1 => { - let result: BridgeInnerV1 = get_dynamic_field_from_store(object_store, id, &version) - .map_err(|err| { - IotaError::IotaBridgeRead(format!( - "Failed to load bridge inner object with ID {:?} and version {:?}: {:?}", - id, version, err - )) - })?; - Ok(Bridge::V1(result)) - } - _ => Err(IotaError::IotaBridgeRead(format!( - "Unsupported IotaBridge version: {}", - version - ))), - } -} - -/// Rust version of the Move bridge::BridgeInner type. -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct BridgeInnerV1 { - pub bridge_version: u64, - pub message_version: u8, - pub chain_id: u8, - pub sequence_nums: VecMap, - pub committee: MoveTypeBridgeCommittee, - pub treasury: MoveTypeBridgeTreasury, - pub bridge_records: LinkedTable, - pub limiter: MoveTypeBridgeTransferLimiter, - pub frozen: bool, -} - -impl BridgeTrait for BridgeInnerV1 { - fn bridge_version(&self) -> u64 { - self.bridge_version - } - - fn message_version(&self) -> u8 { - self.message_version - } - - fn chain_id(&self) -> u8 { - self.chain_id - } - - fn sequence_nums(&self) -> &VecMap { - &self.sequence_nums - } - - fn committee(&self) -> &MoveTypeBridgeCommittee { - &self.committee - } - - fn treasury(&self) -> &MoveTypeBridgeTreasury { - &self.treasury - } - - fn bridge_records(&self) -> &LinkedTable { - &self.bridge_records - } - - fn frozen(&self) -> bool { - self.frozen - } - - fn try_into_bridge_summary(self) -> IotaResult { - let transfer_limit = self - .limiter - .transfer_limit - .contents - .into_iter() - .map(|e| { - let source = BridgeChainId::try_from(e.key.source).map_err(|_e| { - IotaError::GenericBridge { - error: format!("Unrecognized chain id: {}", e.key.source), - } - })?; - let destination = BridgeChainId::try_from(e.key.destination).map_err(|_e| { - IotaError::GenericBridge { - error: format!("Unrecognized chain id: {}", e.key.destination), - } - })?; - Ok((source, destination, e.value)) - }) - .collect::>>()?; - let supported_tokens = self - .treasury - .supported_tokens - .contents - .into_iter() - .map(|e| (e.key, e.value)) - .collect::>(); - let id_token_type_map = self - .treasury - .id_token_type_map - .contents - .into_iter() - .map(|e| (e.key, e.value)) - .collect::>(); - let transfer_records = self - .limiter - .transfer_records - .contents - .into_iter() - .map(|e| { - let source = BridgeChainId::try_from(e.key.source).map_err(|_e| { - IotaError::GenericBridge { - error: format!("Unrecognized chain id: {}", e.key.source), - } - })?; - let destination = BridgeChainId::try_from(e.key.destination).map_err(|_e| { - IotaError::GenericBridge { - error: format!("Unrecognized chain id: {}", e.key.destination), - } - })?; - Ok((source, destination, e.value)) - }) - .collect::>>()?; - let limiter = BridgeLimiterSummary { - transfer_limit, - transfer_records, - }; - Ok(BridgeSummary { - bridge_version: self.bridge_version, - message_version: self.message_version, - chain_id: self.chain_id, - sequence_nums: self - .sequence_nums - .contents - .into_iter() - .map(|e| (e.key, e.value)) - .collect(), - committee: BridgeCommitteeSummary { - members: self - .committee - .members - .contents - .into_iter() - .map(|e| (e.key, e.value)) - .collect(), - member_registration: self - .committee - .member_registrations - .contents - .into_iter() - .map(|e| (e.key, e.value)) - .collect(), - last_committee_update_epoch: self.committee.last_committee_update_epoch, - }, - bridge_records_id: self.bridge_records.id, - limiter, - treasury: BridgeTreasurySummary { - supported_tokens, - id_token_type_map, - }, - is_frozen: self.frozen, - }) - } -} - -/// Rust version of the Move treasury::BridgeTreasury type. -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveTypeBridgeTreasury { - pub treasuries: Bag, - pub supported_tokens: VecMap, - // Mapping token id to type name - pub id_token_type_map: VecMap, - // Bag for storing potential new token waiting to be approved - pub waiting_room: Bag, -} - -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -pub struct BridgeTokenMetadata { - pub id: u8, - pub decimal_multiplier: u64, - pub notional_value: u64, - pub native_token: bool, -} - -/// Rust version of the Move committee::BridgeCommittee type. -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveTypeBridgeCommittee { - pub members: VecMap, MoveTypeCommitteeMember>, - pub member_registrations: VecMap, - pub last_committee_update_epoch: u64, -} - -/// Rust version of the Move committee::CommitteeMemberRegistration type. -#[serde_as] -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -pub struct MoveTypeCommitteeMemberRegistration { - pub iota_address: IotaAddress, - pub bridge_pubkey_bytes: Vec, - pub http_rest_url: Vec, -} - -#[serde_as] -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default)] -#[serde(rename_all = "camelCase")] -pub struct BridgeCommitteeSummary { - pub members: Vec<(Vec, MoveTypeCommitteeMember)>, - pub member_registration: Vec<(IotaAddress, MoveTypeCommitteeMemberRegistration)>, - pub last_committee_update_epoch: u64, -} - -#[serde_as] -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default)] -#[serde(rename_all = "camelCase")] -pub struct BridgeLimiterSummary { - pub transfer_limit: Vec<(BridgeChainId, BridgeChainId, u64)>, - pub transfer_records: Vec<(BridgeChainId, BridgeChainId, MoveTypeBridgeTransferRecord)>, -} - -#[serde_as] -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default)] -#[serde(rename_all = "camelCase")] -pub struct BridgeTreasurySummary { - pub supported_tokens: Vec<(String, BridgeTokenMetadata)>, - pub id_token_type_map: Vec<(u8, String)>, -} - -/// Rust version of the Move committee::CommitteeMember type. -#[serde_as] -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct MoveTypeCommitteeMember { - pub iota_address: IotaAddress, - pub bridge_pubkey_bytes: Vec, - pub voting_power: u64, - pub http_rest_url: Vec, - pub blocklisted: bool, -} - -/// Rust version of the Move message::BridgeMessageKey type. -#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct MoveTypeBridgeMessageKey { - pub source_chain: u8, - pub message_type: u8, - pub bridge_seq_num: u64, -} - -/// Rust version of the Move limiter::TransferLimiter type. -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveTypeBridgeTransferLimiter { - pub transfer_limit: VecMap, - pub transfer_records: VecMap, -} - -/// Rust version of the Move chain_ids::BridgeRoute type. -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MoveTypeBridgeRoute { - pub source: u8, - pub destination: u8, -} - -/// Rust version of the Move limiter::TransferRecord type. -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)] -pub struct MoveTypeBridgeTransferRecord { - hour_head: u64, - hour_tail: u64, - per_hour_amounts: Vec, - total_amount: u64, -} - -/// Rust version of the Move message::BridgeMessage type. -#[derive(Debug, Serialize, Deserialize)] -pub struct MoveTypeBridgeMessage { - pub message_type: u8, - pub message_version: u8, - pub seq_num: u64, - pub source_chain: u8, - pub payload: Vec, -} - -/// Rust version of the Move message::BridgeMessage type. -#[derive(Debug, Serialize, Deserialize)] -pub struct MoveTypeBridgeRecord { - pub message: MoveTypeBridgeMessage, - pub verified_signatures: Option>>, - pub claimed: bool, -} - -pub fn is_bridge_committee_initiated(object_store: &dyn ObjectStore) -> IotaResult { - match get_bridge(object_store) { - Ok(bridge) => Ok(!bridge.committee().members.contents.is_empty()), - Err(IotaError::IotaBridgeRead(..)) => Ok(false), - Err(other) => Err(other), - } -} - -/// Rust version of the Move message::TokenTransferPayload type. -#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct MoveTypeTokenTransferPayload { - pub sender_address: Vec, - pub target_chain: u8, - pub target_address: Vec, - pub token_type: u8, - pub amount: u64, -} - -/// Rust version of the Move message::ParsedTokenTransferMessage type. -#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct MoveTypeParsedTokenTransferMessage { - pub message_version: u8, - pub seq_num: u64, - pub source_chain: u8, - pub payload: Vec, - pub parsed_payload: MoveTypeTokenTransferPayload, -} diff --git a/crates/iota-types/src/error.rs b/crates/iota-types/src/error.rs index 8920af95568..8b04939b27b 100644 --- a/crates/iota-types/src/error.rs +++ b/crates/iota-types/src/error.rs @@ -641,9 +641,6 @@ pub enum IotaError { #[error("Failed to read or deserialize system state related data structures on-chain: {0}")] IotaSystemStateRead(String), - #[error("Failed to read or deserialize bridge related data structures on-chain: {0}")] - IotaBridgeRead(String), - #[error("Unexpected version error: {0}")] UnexpectedVersion(String), diff --git a/crates/iota-types/src/iota_sdk2_conversions.rs b/crates/iota-types/src/iota_sdk2_conversions.rs index b0dde839b8b..440b0d1bd6e 100644 --- a/crates/iota-types/src/iota_sdk2_conversions.rs +++ b/crates/iota-types/src/iota_sdk2_conversions.rs @@ -632,16 +632,6 @@ impl From for EndOfEpochTransacti .authenticator_obj_initial_shared_version .value(), }), - crate::transaction::EndOfEpochTransactionKind::BridgeStateCreate(chain_identifier) => { - EndOfEpochTransactionKind::BridgeStateCreate { - chain_id: CheckpointDigest::new(chain_identifier.digest().into()), - } - } - crate::transaction::EndOfEpochTransactionKind::BridgeCommitteeInit(sequence_number) => { - EndOfEpochTransactionKind::BridgeCommitteeInit { - bridge_object_version: sequence_number.value(), - } - } } } } @@ -703,12 +693,16 @@ impl From for crate::transaction::EndOfEpochTransacti .into(), }) } - EndOfEpochTransactionKind::BridgeStateCreate { chain_id } => { - Self::BridgeStateCreate(crate::digests::ChainIdentifier(chain_id.into())) + EndOfEpochTransactionKind::BridgeStateCreate { chain_id: _ } => { + todo!("Remove EndOfEpochTransactionKind::BridgeStateCreate from external rust SDK") } EndOfEpochTransactionKind::BridgeCommitteeInit { - bridge_object_version, - } => Self::BridgeCommitteeInit(bridge_object_version.into()), + bridge_object_version: _, + } => { + todo!( + "Remove EndOfEpochTransactionKind::BridgeCommitteeInit from external rust SDK" + ) + } } } } diff --git a/crates/iota-types/src/lib.rs b/crates/iota-types/src/lib.rs index e9730a27757..e14bf42bf4a 100644 --- a/crates/iota-types/src/lib.rs +++ b/crates/iota-types/src/lib.rs @@ -35,7 +35,6 @@ pub mod accumulator; pub mod authenticator_state; pub mod balance; pub mod base_types; -pub mod bridge; pub mod clock; pub mod coin; pub mod coin_manager; @@ -121,7 +120,7 @@ built_in_pkgs! { MOVE_STDLIB_ADDRESS / MOVE_STDLIB_PACKAGE_ID = 0x1; IOTA_FRAMEWORK_ADDRESS / IOTA_FRAMEWORK_PACKAGE_ID = 0x2; IOTA_SYSTEM_ADDRESS / IOTA_SYSTEM_PACKAGE_ID = 0x3; - BRIDGE_ADDRESS / BRIDGE_PACKAGE_ID = 0xb; + GENESIS_BRIDGE_ADDRESS / GENESIS_BRIDGE_PACKAGE_ID = 0xb; STARDUST_ADDRESS / STARDUST_PACKAGE_ID = 0x107a; } @@ -130,7 +129,7 @@ built_in_ids! { IOTA_CLOCK_ADDRESS / IOTA_CLOCK_OBJECT_ID = 0x6; IOTA_AUTHENTICATOR_STATE_ADDRESS / IOTA_AUTHENTICATOR_STATE_OBJECT_ID = 0x7; IOTA_RANDOMNESS_STATE_ADDRESS / IOTA_RANDOMNESS_STATE_OBJECT_ID = 0x8; - IOTA_BRIDGE_ADDRESS / IOTA_BRIDGE_OBJECT_ID = 0x9; + GENESIS_IOTA_BRIDGE_ADDRESS / GENESIS_IOTA_BRIDGE_OBJECT_ID = 0x9; IOTA_DENY_LIST_ADDRESS / IOTA_DENY_LIST_OBJECT_ID = 0x403; } @@ -212,7 +211,6 @@ pub fn resolve_address(addr: &str) -> Option { "iota" => Some(IOTA_FRAMEWORK_ADDRESS), "iota_system" => Some(IOTA_SYSTEM_ADDRESS), "stardust" => Some(STARDUST_ADDRESS), - "bridge" => Some(BRIDGE_ADDRESS), _ => None, } } diff --git a/crates/iota-types/src/transaction.rs b/crates/iota-types/src/transaction.rs index 531440d129b..714eeefffe3 100644 --- a/crates/iota-types/src/transaction.rs +++ b/crates/iota-types/src/transaction.rs @@ -28,7 +28,7 @@ use strum::IntoStaticStr; use tap::Pipe; use tracing::trace; -use super::{IOTA_BRIDGE_OBJECT_ID, base_types::*, error::*}; +use super::{base_types::*, error::*}; use crate::{ IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION, IOTA_CLOCK_OBJECT_ID, IOTA_CLOCK_OBJECT_SHARED_VERSION, IOTA_FRAMEWORK_PACKAGE_ID, @@ -42,8 +42,7 @@ use crate::{ IotaSignatureInner, RandomnessRound, Signature, Signer, ToFromBytes, default_hash, }, digests::{ - CertificateDigest, ChainIdentifier, ConsensusCommitDigest, SenderSignedDataDigest, - ZKLoginInputsDigest, + CertificateDigest, ConsensusCommitDigest, SenderSignedDataDigest, ZKLoginInputsDigest, }, event::Event, execution::SharedInput, @@ -329,8 +328,6 @@ pub enum EndOfEpochTransactionKind { ChangeEpochV2(ChangeEpochV2), AuthenticatorStateCreate, AuthenticatorStateExpire(AuthenticatorStateExpire), - BridgeStateCreate(ChainIdentifier), - BridgeCommitteeInit(SequenceNumber), } impl EndOfEpochTransactionKind { @@ -394,14 +391,6 @@ impl EndOfEpochTransactionKind { Self::AuthenticatorStateCreate } - pub fn new_bridge_create(chain_identifier: ChainIdentifier) -> Self { - Self::BridgeStateCreate(chain_identifier) - } - - pub fn init_bridge_committee(bridge_shared_version: SequenceNumber) -> Self { - Self::BridgeCommitteeInit(bridge_shared_version) - } - fn input_objects(&self) -> Vec { match self { Self::ChangeEpoch(_) => { @@ -426,19 +415,6 @@ impl EndOfEpochTransactionKind { mutable: true, }] } - Self::BridgeStateCreate(_) => vec![], - Self::BridgeCommitteeInit(bridge_version) => vec![ - InputObjectKind::SharedMoveObject { - id: IOTA_BRIDGE_OBJECT_ID, - initial_shared_version: *bridge_version, - mutable: true, - }, - InputObjectKind::SharedMoveObject { - id: IOTA_SYSTEM_STATE_OBJECT_ID, - initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION, - mutable: true, - }, - ], } } @@ -459,18 +435,6 @@ impl EndOfEpochTransactionKind { .into_iter(), ), Self::AuthenticatorStateCreate => Either::Right(iter::empty()), - Self::BridgeStateCreate(_) => Either::Right(iter::empty()), - Self::BridgeCommitteeInit(bridge_version) => Either::Left( - vec![ - SharedInputObject { - id: IOTA_BRIDGE_OBJECT_ID, - initial_shared_version: *bridge_version, - mutable: true, - }, - SharedInputObject::IOTA_SYSTEM_OBJ, - ] - .into_iter(), - ), } } @@ -497,25 +461,6 @@ impl EndOfEpochTransactionKind { )); } } - Self::BridgeStateCreate(_) => { - if !config.enable_bridge() { - return Err(UserInputError::Unsupported( - "bridge not enabled".to_string(), - )); - } - } - Self::BridgeCommitteeInit(_) => { - if !config.enable_bridge() { - return Err(UserInputError::Unsupported( - "bridge not enabled".to_string(), - )); - } - if !config.should_try_to_finalize_bridge_committee() { - return Err(UserInputError::Unsupported( - "should not try to finalize committee yet".to_string(), - )); - } - } } Ok(()) } diff --git a/crates/iota/Cargo.toml b/crates/iota/Cargo.toml index ce07fb3be19..c637d2ccdd8 100644 --- a/crates/iota/Cargo.toml +++ b/crates/iota/Cargo.toml @@ -60,7 +60,6 @@ uuid.workspace = true # internal dependencies bin-version.workspace = true -iota-bridge.workspace = true iota-config.workspace = true iota-execution.workspace = true iota-faucet.workspace = true diff --git a/crates/iota/src/iota_commands.rs b/crates/iota/src/iota_commands.rs index 58721b9ce36..ab2d415ba69 100644 --- a/crates/iota/src/iota_commands.rs +++ b/crates/iota/src/iota_commands.rs @@ -15,10 +15,6 @@ use anyhow::{Context, anyhow, bail, ensure}; use clap::*; use colored::Colorize; use fastcrypto::traits::KeyPair; -use iota_bridge::{ - config::BridgeCommitteeConfig, iota_client::IotaBridgeClient, - iota_transaction_builder::build_committee_register_transaction, -}; use iota_config::{ Config, FULL_NODE_DB_PATH, IOTA_BENCHMARK_GENESIS_GAS_KEYSTORE_FILENAME, IOTA_CLIENT_CONFIG, IOTA_FULLNODE_CONFIG, IOTA_GENESIS_FILENAME, IOTA_KEYSTORE_FILENAME, IOTA_NETWORK_CONFIG, @@ -32,10 +28,7 @@ use iota_graphql_rpc::{ }; #[cfg(feature = "indexer")] use iota_indexer::test_utils::{IndexerTypeConfig, start_test_indexer}; -use iota_keys::{ - keypair_file::read_key, - keystore::{AccountKeystore, FileBasedKeystore, Keystore}, -}; +use iota_keys::keystore::{AccountKeystore, FileBasedKeystore, Keystore}; use iota_move::{self, execute_move_command}; use iota_move_build::IotaPackageHooks; use iota_sdk::{ @@ -51,7 +44,7 @@ use iota_swarm_config::{ }; use iota_types::{ base_types::IotaAddress, - crypto::{IotaKeyPair, SignatureScheme, ToFromBytes}, + crypto::{IotaKeyPair, SignatureScheme}, }; use move_analyzer::analyzer; use move_package::BuildConfig; @@ -363,17 +356,6 @@ pub enum IotaCommand { #[command(subcommand)] cmd: name_commands::NameCommand, }, - /// Command to initialize the bridge committee, usually used when - /// running local bridge cluster. - #[command(name = "bridge-committee-init")] - BridgeInitialize { - #[arg(long = "network.config")] - network_config: Option, - #[arg(long = "client.config")] - client_config: Option, - #[arg(long = "bridge_committee.config")] - bridge_committee_config_path: PathBuf, - }, /// Tool for Fire Drill FireDrill { #[command(subcommand)] @@ -550,86 +532,6 @@ impl IotaCommand { cmd.execute(&mut context).await?.print(!json); Ok(()) } - IotaCommand::BridgeInitialize { - network_config, - client_config, - bridge_committee_config_path, - } => { - // Load the config of the IOTA authority. - let network_config_path = network_config - .clone() - .unwrap_or(iota_config_dir()?.join(IOTA_NETWORK_CONFIG)); - let network_config: NetworkConfig = PersistedConfig::read(&network_config_path) - .map_err(|err| { - err.context(format!( - "Cannot open IOTA network config file at {:?}", - network_config_path - )) - })?; - let bridge_committee_config: BridgeCommitteeConfig = - PersistedConfig::read(&bridge_committee_config_path).map_err(|err| { - err.context(format!( - "Cannot open Bridge Committee config file at {:?}", - bridge_committee_config_path - )) - })?; - - let config_path = - client_config.unwrap_or(iota_config_dir()?.join(IOTA_CLIENT_CONFIG)); - let mut context = WalletContext::new(&config_path, None, None)?; - let rgp = context.get_reference_gas_price().await?; - let rpc_url = context.active_env()?.rpc(); - println!("rpc_url: {}", rpc_url); - let iota_bridge_client = IotaBridgeClient::new(rpc_url).await?; - let bridge_arg = iota_bridge_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - assert_eq!( - network_config.validator_configs().len(), - bridge_committee_config - .bridge_authority_port_and_key_path - .len() - ); - for node_config in network_config.validator_configs() { - let account_kp = node_config.account_key_pair.keypair(); - context.add_account(None, account_kp.copy()); - } - - let context = context; - let mut tasks = vec![]; - for (node_config, (port, key_path)) in network_config - .validator_configs() - .iter() - .zip(bridge_committee_config.bridge_authority_port_and_key_path) - { - let account_kp = node_config.account_key_pair.keypair(); - let iota_address = IotaAddress::from(&account_kp.public()); - let gas_obj_ref = context - .get_one_gas_object_owned_by_address(iota_address) - .await? - .expect("Validator does not own any gas objects"); - let kp = match read_key(&key_path, true)? { - IotaKeyPair::Secp256k1(key) => key, - _ => unreachable!("we required secp256k1 key in `read_key`"), - }; - - // build registration tx - let tx = build_committee_register_transaction( - iota_address, - &gas_obj_ref, - bridge_arg, - kp.public().as_bytes().to_vec(), - &format!("http://127.0.0.1:{port}"), - rgp, - 1000000000, - ) - .unwrap(); - let signed_tx = context.sign_transaction(&tx); - tasks.push(context.execute_transaction_must_succeed(signed_tx)); - } - futures::future::join_all(tasks).await; - Ok(()) - } IotaCommand::FireDrill { fire_drill } => run_fire_drill(fire_drill).await, IotaCommand::Analyzer => { analyzer::run(); @@ -803,7 +705,7 @@ async fn start( // the genesis command, which sets data_ingestion_dir to None. #[cfg(feature = "indexer")] if with_indexer.is_some() && data_ingestion_dir.is_none() { - data_ingestion_dir = Some(tempdir()?.into_path()) + data_ingestion_dir = Some(tempdir()?.keep()) } #[cfg(feature = "indexer")] @@ -905,7 +807,7 @@ async fn start( tracing::info!("Starting the faucet service at {faucet_address}"); let faucet_config_dir = if force_regenesis { // tempdir is used so the faucet file is cleaned up afterwards - tempdir()?.into_path() + tempdir()?.keep() } else { config_path }; diff --git a/crates/iota/src/unit_tests/profiler_tests.rs b/crates/iota/src/unit_tests/profiler_tests.rs index b47d4c6f452..9054d78d4ee 100644 --- a/crates/iota/src/unit_tests/profiler_tests.rs +++ b/crates/iota/src/unit_tests/profiler_tests.rs @@ -64,7 +64,7 @@ async fn test_profiler() { // check that the profile was written let mut found = false; - for entry in fs::read_dir(output_dir.into_path()).unwrap().flatten() { + for entry in fs::read_dir(output_dir.keep()).unwrap().flatten() { if entry .file_name() .into_string() diff --git a/crates/iota/src/validator_commands.rs b/crates/iota/src/validator_commands.rs index acabc7c7d1c..a534c9b5dc5 100644 --- a/crates/iota/src/validator_commands.rs +++ b/crates/iota/src/validator_commands.rs @@ -17,12 +17,6 @@ use fastcrypto::{ traits::{KeyPair, ToFromBytes}, }; use futures::TryStreamExt; -use iota_bridge::{ - iota_client::IotaClient as IotaBridgeClient, - iota_transaction_builder::{ - build_committee_register_transaction, build_committee_update_url_transaction, - }, -}; use iota_genesis_builder::validator_info::GenesisValidatorInfo; use iota_json_rpc_types::{ IotaData, IotaMoveValue, IotaObjectDataOptions, IotaTransactionBlockResponse, @@ -31,8 +25,8 @@ use iota_json_rpc_types::{ use iota_keys::{ key_derive::generate_new_key, keypair_file::{ - read_authority_keypair_from_file, read_key, read_keypair_from_file, - read_network_keypair_from_file, write_authority_keypair_to_file, write_keypair_to_file, + read_authority_keypair_from_file, read_keypair_from_file, read_network_keypair_from_file, + write_authority_keypair_to_file, write_keypair_to_file, }, keystore::AccountKeystore, }; @@ -65,7 +59,6 @@ use tabled::{ object::{Column, Columns}, }, }; -use url::{ParseError, Url}; use crate::{PrintableResult, fire_drill::get_gas_obj_ref}; @@ -155,41 +148,6 @@ pub enum IotaValidatorCommand { #[arg(name = "authority-public-key", long)] authority_public_key: AuthorityPublicKeyBytes, }, - /// IOTA native bridge committee member registration. - RegisterBridgeCommittee { - /// Path to Bridge Authority Key file. - #[arg(long)] - bridge_authority_key_path: PathBuf, - /// Bridge authority URL which clients collects action signatures from. - #[arg(long)] - bridge_authority_url: String, - /// If true, only print the unsigned transaction and do not execute it. - /// This is useful for offline signing. - #[arg(name = "print-only", long, default_value = "false")] - print_unsigned_transaction_only: bool, - /// Must present if `print_unsigned_transaction_only` is true. - #[arg(long)] - validator_address: Option, - /// Gas budget for this transaction. - #[arg(name = "gas-budget", long)] - gas_budget: Option, - }, - /// Update IOTA native bridge committee node url. - UpdateBridgeCommitteeNodeUrl { - /// New node url to be registered in the on chain bridge object. - #[arg(long)] - bridge_authority_url: String, - /// If true, only print the unsigned transaction and do not execute it. - /// This is useful for offline signing. - #[arg(name = "print-only", long, default_value = "false")] - print_unsigned_transaction_only: bool, - /// Must be present if `print_unsigned_transaction_only` is true. - #[arg(long)] - validator_address: Option, - /// Gas budget for this transaction. - #[arg(name = "gas-budget", long)] - gas_budget: Option, - }, /// Get a list of the validators in the network. Use the `display-metadata` /// command to see the complete data for a validator. List, @@ -206,14 +164,6 @@ pub enum IotaValidatorCommandResponse { UpdateMetadata(IotaTransactionBlockResponse), ReportValidator(IotaTransactionBlockResponse), SerializedPayload(String), - RegisterBridgeCommittee { - execution_response: Option, - serialized_unsigned_transaction: Option, - }, - UpdateBridgeCommitteeURL { - execution_response: Option, - serialized_unsigned_transaction: Option, - }, List, } @@ -446,167 +396,6 @@ impl IotaValidatorCommand { DEFAULT_EPOCH_ID.write(&mut intent_msg_bytes); IotaValidatorCommandResponse::SerializedPayload(Base64::encode(&intent_msg_bytes)) } - - IotaValidatorCommand::RegisterBridgeCommittee { - bridge_authority_key_path, - bridge_authority_url, - print_unsigned_transaction_only, - validator_address, - gas_budget, - } => { - let parsed_url = - Url::parse(&bridge_authority_url).map_err(|e: ParseError| anyhow!(e))?; - if parsed_url.scheme() != "http" && parsed_url.scheme() != "https" { - anyhow::bail!( - "URL scheme has to be http or https: {}", - parsed_url.scheme() - ); - } - // Read bridge keypair - let ecdsa_keypair = match read_key(&bridge_authority_key_path, true)? { - IotaKeyPair::Secp256k1(key) => key, - _ => unreachable!("we required secp256k1 key in `read_key`"), - }; - let address = check_address( - context.active_address()?, - validator_address, - print_unsigned_transaction_only, - )?; - - // The bridge should be run by the same committee as the consensus, hence we use - // get committee members here. - let iota_client = context.get_client().await?; - if !iota_client - .governance_api() - .get_latest_iota_system_state() - .await? - .iter_committee_members() - .any(|s| s.iota_address == address) - { - bail!("Address {address} is not in the committee members"); - } - println!( - "Starting bridge committee registration for IOTA committee member: {address}, with bridge public key: {} and url: {}", - ecdsa_keypair.public, bridge_authority_url - ); - let iota_rpc_url = context.active_env().unwrap().rpc(); - let bridge_client = IotaBridgeClient::new(iota_rpc_url).await?; - let bridge = bridge_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - - let gas_budget = gas_budget.unwrap_or(DEFAULT_GAS_BUDGET); - let (_, gas) = context - .gas_for_owner_budget(address, gas_budget, Default::default()) - .await?; - - let gas_price = context.get_reference_gas_price().await?; - let tx_data = build_committee_register_transaction( - address, - &gas.object_ref(), - bridge, - ecdsa_keypair.public().as_bytes().to_vec(), - &bridge_authority_url, - gas_price, - gas_budget, - ) - .map_err(|e| anyhow!("{e:?}"))?; - if print_unsigned_transaction_only { - let serialized_data = Base64::encode(bcs::to_bytes(&tx_data)?); - IotaValidatorCommandResponse::RegisterBridgeCommittee { - execution_response: None, - serialized_unsigned_transaction: Some(serialized_data), - } - } else { - let tx = context.sign_transaction(&tx_data); - let response = context.execute_transaction_must_succeed(tx).await; - println!( - "Committee registration successful. Transaction digest: {}", - response.digest - ); - IotaValidatorCommandResponse::RegisterBridgeCommittee { - execution_response: Some(response), - serialized_unsigned_transaction: None, - } - } - } - IotaValidatorCommand::UpdateBridgeCommitteeNodeUrl { - bridge_authority_url, - print_unsigned_transaction_only, - validator_address, - gas_budget, - } => { - let parsed_url = - Url::parse(&bridge_authority_url).map_err(|e: ParseError| anyhow!(e))?; - if parsed_url.scheme() != "http" && parsed_url.scheme() != "https" { - anyhow::bail!( - "URL scheme has to be http or https: {}", - parsed_url.scheme() - ); - } - // Make sure the address is member of the committee - let address = check_address( - context.active_address()?, - validator_address, - print_unsigned_transaction_only, - )?; - let iota_rpc_url = context.active_env().unwrap().rpc(); - let bridge_client = IotaBridgeClient::new(iota_rpc_url).await?; - let committee_members = bridge_client - .get_bridge_summary() - .await - .map_err(|e| anyhow!("{e:?}"))? - .committee - .members; - if !committee_members - .into_iter() - .any(|(_, m)| m.iota_address == address) - { - bail!("Address {} is not in the committee", address); - } - println!( - "Updating bridge committee node URL for IOTA validator: {address}, url: {}", - bridge_authority_url - ); - - let bridge = bridge_client - .get_mutable_bridge_object_arg_must_succeed() - .await; - - let gas_budget = gas_budget.unwrap_or(DEFAULT_GAS_BUDGET); - let (_, gas) = context - .gas_for_owner_budget(address, gas_budget, Default::default()) - .await?; - - let gas_price = context.get_reference_gas_price().await?; - let tx_data = build_committee_update_url_transaction( - address, - &gas.object_ref(), - bridge, - &bridge_authority_url, - gas_price, - gas_budget, - ) - .map_err(|e| anyhow!("{e:?}"))?; - if print_unsigned_transaction_only { - let serialized_data = Base64::encode(bcs::to_bytes(&tx_data)?); - IotaValidatorCommandResponse::UpdateBridgeCommitteeURL { - execution_response: None, - serialized_unsigned_transaction: Some(serialized_data), - } - } else { - let tx = context.sign_transaction(&tx_data); - let response = context.execute_transaction_must_succeed(tx).await; - println!( - "Update Bridge validator node URL successful. Transaction digest: {}", - response.digest - ); - IotaValidatorCommandResponse::UpdateBridgeCommitteeURL { - execution_response: Some(response), - serialized_unsigned_transaction: None, - } - } - } IotaValidatorCommand::List => { let mut builder = Builder::default(); @@ -673,27 +462,6 @@ impl IotaValidatorCommand { } } -fn check_address( - active_address: IotaAddress, - validator_address: Option, - print_unsigned_transaction_only: bool, -) -> Result { - if !print_unsigned_transaction_only { - if let Some(validator_address) = validator_address { - if validator_address != active_address { - bail!( - "`--validator-address` must be the same as the current active address: {}", - active_address - ); - } - } - Ok(active_address) - } else { - validator_address - .ok_or_else(|| anyhow!("--validator-address must be provided when `print_unsigned_transaction_only` is true")) - } -} - async fn get_cap_object_ref( context: &mut WalletContext, operation_cap_id: Option, @@ -896,24 +664,6 @@ impl Display for IotaValidatorCommandResponse { IotaValidatorCommandResponse::SerializedPayload(response) => { write!(writer, "Serialized payload: {}", response)?; } - IotaValidatorCommandResponse::RegisterBridgeCommittee { - execution_response, - serialized_unsigned_transaction, - } - | IotaValidatorCommandResponse::UpdateBridgeCommitteeURL { - execution_response, - serialized_unsigned_transaction, - } => { - if let Some(response) = execution_response { - write!(writer, "{}", write_transaction_response(response)?)?; - } else { - write!( - writer, - "Serialized transaction for signing: {:?}", - serialized_unsigned_transaction - )?; - } - } IotaValidatorCommandResponse::List => {} } write!(f, "{}", writer.trim_end_matches('\n')) diff --git a/crates/test-cluster/Cargo.toml b/crates/test-cluster/Cargo.toml index 47c4a3d6056..5dfd6570d88 100644 --- a/crates/test-cluster/Cargo.toml +++ b/crates/test-cluster/Cargo.toml @@ -22,7 +22,6 @@ tokio = { workspace = true, features = ["full"] } tracing.workspace = true # internal dependencies -iota-bridge.workspace = true iota-config.workspace = true iota-core = { workspace = true, features = ["test-utils"] } iota-framework.workspace = true diff --git a/crates/test-cluster/src/lib.rs b/crates/test-cluster/src/lib.rs index 9d61d567b84..9d8cd96cfaf 100644 --- a/crates/test-cluster/src/lib.rs +++ b/crates/test-cluster/src/lib.rs @@ -3,40 +3,26 @@ // SPDX-License-Identifier: Apache-2.0 use std::{ - collections::{BTreeMap, HashMap}, + collections::HashMap, net::SocketAddr, num::NonZeroUsize, - path::{Path, PathBuf}, + path::PathBuf, sync::{Arc, Mutex}, time::Duration, }; -use futures::{Future, StreamExt, future::join_all}; -use iota_bridge::{ - crypto::{BridgeAuthorityKeyPair, BridgeAuthoritySignInfo}, - iota_transaction_builder::{ - build_add_tokens_on_iota_transaction, build_committee_register_transaction, - }, - types::{ - BridgeCommitteeValiditySignInfo, CertifiedBridgeAction, VerifiedCertifiedBridgeAction, - }, - utils::{publish_and_register_coins_return_add_coins_on_iota_action, wait_for_server_to_be_up}, -}; +use futures::{StreamExt, future::join_all}; use iota_config::{ Config, IOTA_CLIENT_CONFIG, IOTA_KEYSTORE_FILENAME, IOTA_NETWORK_CONFIG, NodeConfig, PersistedConfig, genesis::Genesis, - local_ip_utils::get_available_port, node::{AuthorityOverloadConfig, DBCheckpointConfig, RunWithRange}, }; use iota_core::{ authority_aggregator::AuthorityAggregator, authority_client::NetworkAuthorityClient, }; use iota_genesis_builder::SnapshotSource; -use iota_json_rpc_api::{ - BridgeReadApiClient, IndexerApiClient, TransactionBuilderClient, WriteApiClient, - error_object_from_rpc, -}; +use iota_json_rpc_api::{IndexerApiClient, TransactionBuilderClient, WriteApiClient}; use iota_json_rpc_types::{ IotaExecutionStatus, IotaObjectDataOptions, IotaObjectResponse, IotaObjectResponseQuery, IotaTransactionBlockEffectsAPI, IotaTransactionBlockResponse, @@ -63,14 +49,9 @@ use iota_swarm_config::{ }; use iota_test_transaction_builder::TestTransactionBuilder; use iota_types::{ - IOTA_BRIDGE_OBJECT_ID, base_types::{AuthorityName, ConciseableName, IotaAddress, ObjectID, ObjectRef}, - bridge::{ - BridgeSummary, BridgeTrait, TOKEN_ID_BTC, TOKEN_ID_ETH, TOKEN_ID_USDC, TOKEN_ID_USDT, - get_bridge, get_bridge_obj_initial_shared_version, - }, committee::{Committee, CommitteeTrait, EpochId}, - crypto::{AccountKeyPair, IotaKeyPair, KeypairTraits, ToFromBytes, get_key_pair}, + crypto::{AccountKeyPair, IotaKeyPair, KeypairTraits, get_key_pair}, effects::{TransactionEffects, TransactionEvents}, error::IotaResult, governance::MIN_VALIDATOR_JOINING_STAKE_NANOS, @@ -85,15 +66,11 @@ use iota_types::{ supported_protocol_versions::SupportedProtocolVersions, traffic_control::{PolicyConfig, RemoteFirewallConfig}, transaction::{ - CertifiedTransaction, ObjectArg, Transaction, TransactionData, TransactionDataAPI, - TransactionKind, + CertifiedTransaction, Transaction, TransactionData, TransactionDataAPI, TransactionKind, }, utils::to_sender_signed_transaction, }; -use jsonrpsee::{ - core::RpcResult, - http_client::{HttpClient, HttpClientBuilder}, -}; +use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use rand::{distributions::*, rngs::OsRng, seq::SliceRandom}; use tokio::{ task::JoinHandle, @@ -135,8 +112,6 @@ pub struct TestCluster { pub swarm: Swarm, pub wallet: WalletContext, pub fullnode_handle: FullNodeHandle, - pub bridge_authority_keys: Option>, - pub bridge_server_ports: Option>, faucet: Option, } @@ -295,14 +270,6 @@ impl TestCluster { .compute_object_reference() } - pub async fn get_bridge_summary(&self) -> RpcResult { - self.iota_client() - .http() - .get_latest_bridge() - .await - .map_err(error_object_from_rpc) - } - pub async fn get_object_or_tombstone_from_fullnode_store( &self, object_id: ObjectID, @@ -549,54 +516,6 @@ impl TestCluster { } } - pub async fn trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized(&self) { - let mut bridge = - get_bridge(self.fullnode_handle.iota_node.state().get_object_store()).unwrap(); - if !bridge.committee().members.contents.is_empty() { - assert_eq!( - self.swarm.active_validators().count(), - bridge.committee().members.contents.len() - ); - return; - } - // wait for next epoch - self.force_new_epoch().await; - bridge = get_bridge(self.fullnode_handle.iota_node.state().get_object_store()).unwrap(); - // Committee should be initiated - assert!(bridge.committee().member_registrations.contents.is_empty()); - assert_eq!( - self.swarm.active_validators().count(), - bridge.committee().members.contents.len() - ); - } - - // Wait for bridge node in the cluster to be up and running. - pub async fn wait_for_bridge_cluster_to_be_up(&self, timeout_sec: u64) { - let bridge_ports = self.bridge_server_ports.as_ref().unwrap(); - let mut tasks = vec![]; - for port in bridge_ports.iter() { - let server_url = format!("http://127.0.0.1:{}", port); - tasks.push(wait_for_server_to_be_up(server_url, timeout_sec)); - } - join_all(tasks) - .await - .into_iter() - .collect::>>() - .unwrap(); - } - - pub async fn get_mut_bridge_arg(&self) -> Option { - get_bridge_obj_initial_shared_version( - self.fullnode_handle.iota_node.state().get_object_store(), - ) - .unwrap() - .map(|seq| ObjectArg::SharedObject { - id: IOTA_BRIDGE_OBJECT_ID, - initial_shared_version: seq, - mutable: true, - }) - } - pub async fn wait_for_authenticator_state_update(&self) { timeout( Duration::from_secs(60), @@ -1383,189 +1302,10 @@ impl TestClusterBuilder { swarm, wallet, fullnode_handle, - bridge_authority_keys: None, - bridge_server_ports: None, faucet, } } - pub async fn build_with_bridge( - self, - bridge_authority_keys: Vec, - deploy_tokens: bool, - ) -> TestCluster { - let timer = Instant::now(); - let gas_objects_for_authority_keys = bridge_authority_keys - .iter() - .map(|k| { - let address = IotaAddress::from(k.public()); - Object::with_id_owner_for_testing(ObjectID::random(), address) - }) - .collect::>(); - let mut test_cluster = self - .with_objects(gas_objects_for_authority_keys) - .build() - .await; - info!( - "TestCluster build took {:?} secs", - timer.elapsed().as_secs() - ); - let ref_gas_price = test_cluster.get_reference_gas_price().await; - let bridge_arg = test_cluster.get_mut_bridge_arg().await.unwrap(); - assert_eq!( - bridge_authority_keys.len(), - test_cluster.swarm.active_validators().count() - ); - - // Committee registers themselves - let mut server_ports = vec![]; - let mut tasks = vec![]; - let quorum_driver_api = test_cluster.quorum_driver_api().clone(); - for (node, kp) in test_cluster - .swarm - .active_validators() - .zip(bridge_authority_keys.iter()) - { - let validator_address = node.config().iota_address(); - // create committee registration tx - let gas = test_cluster - .wallet - .get_one_gas_object_owned_by_address(validator_address) - .await - .unwrap() - .unwrap(); - - let server_port = get_available_port("127.0.0.1"); - let server_url = format!("http://127.0.0.1:{}", server_port); - server_ports.push(server_port); - let data = build_committee_register_transaction( - validator_address, - &gas, - bridge_arg, - kp.public().as_bytes().to_vec(), - &server_url, - ref_gas_price, - 1000000000, - ) - .unwrap(); - - let tx = Transaction::from_data_and_signer( - data, - vec![node.config().account_key_pair.keypair()], - ); - let api_clone = quorum_driver_api.clone(); - tasks.push(async move { - api_clone - .execute_transaction_block( - tx, - IotaTransactionBlockResponseOptions::new().with_effects(), - None, - ) - .await - }); - } - - if deploy_tokens { - let timer = Instant::now(); - let token_ids = vec![TOKEN_ID_BTC, TOKEN_ID_ETH, TOKEN_ID_USDC, TOKEN_ID_USDT]; - let token_prices = vec![500_000_000u64, 30_000_000u64, 1_000u64, 1_000u64]; - let action = publish_and_register_coins_return_add_coins_on_iota_action( - test_cluster.wallet(), - bridge_arg, - vec![ - Path::new("../../bridge/move/tokens/btc").into(), - Path::new("../../bridge/move/tokens/eth").into(), - Path::new("../../bridge/move/tokens/usdc").into(), - Path::new("../../bridge/move/tokens/usdt").into(), - ], - token_ids, - token_prices, - 0, - ); - let action = action.await; - info!("register tokens took {:?} secs", timer.elapsed().as_secs()); - let sig_map = bridge_authority_keys - .iter() - .map(|key| { - ( - key.public().into(), - BridgeAuthoritySignInfo::new(&action, key).signature, - ) - }) - .collect::>(); - let certified_action = CertifiedBridgeAction::new_from_data_and_sig( - action, - BridgeCommitteeValiditySignInfo { - signatures: sig_map.clone(), - }, - ); - let verifired_action_cert = - VerifiedCertifiedBridgeAction::new_from_verified(certified_action); - let sender_address = test_cluster.get_address_0(); - - await_committee_register_tasks(&test_cluster, tasks).await; - - // Wait until committee is set up - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; - - let tx = build_add_tokens_on_iota_transaction( - sender_address, - &test_cluster - .wallet - .get_one_gas_object_owned_by_address(sender_address) - .await - .unwrap() - .unwrap(), - verifired_action_cert, - bridge_arg, - ref_gas_price, - ) - .unwrap(); - - let response = test_cluster.sign_and_execute_transaction(&tx).await; - assert_eq!( - response.effects.unwrap().status(), - &IotaExecutionStatus::Success - ); - info!("Deploy tokens took {:?} secs", timer.elapsed().as_secs()); - } else { - await_committee_register_tasks(&test_cluster, tasks).await; - } - - async fn await_committee_register_tasks( - test_cluster: &TestCluster, - tasks: Vec< - impl Future>, - >, - ) { - // The tx may fail if a member tries to register when the committee is already - // finalized. In that case, we just need to check the committee - // members is not empty since once the committee is finalized, it - // should not be empty. - let responses = join_all(tasks).await; - let mut has_failure = false; - for response in responses { - if response.unwrap().effects.unwrap().status() != &IotaExecutionStatus::Success { - has_failure = true; - } - } - if has_failure { - let bridge_summary = test_cluster.get_bridge_summary().await.unwrap(); - assert_ne!(bridge_summary.committee.members.len(), 0); - } - } - - info!( - "TestCluster build_with_bridge took {:?} secs", - timer.elapsed().as_secs() - ); - test_cluster.bridge_authority_keys = Some(bridge_authority_keys); - test_cluster.bridge_server_ports = Some(server_ports); - test_cluster - } - /// Start a Swarm and set up WalletConfig async fn start_swarm(&mut self) -> Result { let mut builder: SwarmBuilder = Swarm::builder() diff --git a/crates/typed-store-derive/src/lib.rs b/crates/typed-store-derive/src/lib.rs index e1997e40824..617c7aec810 100644 --- a/crates/typed-store-derive/src/lib.rs +++ b/crates/typed-store-derive/src/lib.rs @@ -475,7 +475,7 @@ pub fn derive_dbmap_utils_general(input: TokenStream) -> TokenStream { None => { let p: std::path::PathBuf = tempfile::tempdir() .expect("Failed to open temporary directory") - .into_path(); + .keep(); #intermediate_db_map_struct_name::open_tables_impl(primary_path, Some(p), false, metric_conf, global_db_options_override, None, false) } }; @@ -854,7 +854,7 @@ pub fn derive_sallydb_general(input: TokenStream) -> TokenStream { None => { let p: std::path::PathBuf = tempfile::tempdir() .expect("Failed to open temporary directory") - .into_path(); + .keep(); #intermediate_db_map_struct_name::init(SallyDBOptions::RocksDB((b.0, b.1, RocksDBAccessType::Secondary(Some(p)), b.3, None))) } }; diff --git a/crates/typed-store/src/lib.rs b/crates/typed-store/src/lib.rs index b1cc7dff0b0..72108ce5c2d 100644 --- a/crates/typed-store/src/lib.rs +++ b/crates/typed-store/src/lib.rs @@ -94,7 +94,7 @@ pub type StoreError = typed_store_error::TypedStoreError; /// "Failed to open temporary /// directory", /// ) -/// .into_path(); +/// .keep(); /// /// // We can then open the DB with the configs /// let _ = Tables::open_tables_read_write( @@ -152,7 +152,7 @@ pub type StoreError = typed_store_error::TypedStoreError; /// use typed_store::rocks::MetricConf; /// let primary_path = tempfile::tempdir() /// .expect("Failed to open temporary directory") -/// .into_path(); +/// .keep(); /// let _ = Tables::open_tables_read_write( /// primary_path.clone(), /// typed_store::rocks::MetricConf::default(), diff --git a/crates/typed-store/src/rocks/tests.rs b/crates/typed-store/src/rocks/tests.rs index fae2863e736..c43625b2ca1 100644 --- a/crates/typed-store/src/rocks/tests.rs +++ b/crates/typed-store/src/rocks/tests.rs @@ -18,7 +18,7 @@ use crate::{ fn temp_dir() -> std::path::PathBuf { tempfile::tempdir() .expect("Failed to open temporary directory") - .into_path() + .keep() } // A wrapper that holds different type of iterators for testing purpose. We use diff --git a/crates/typed-store/src/sally/mod.rs b/crates/typed-store/src/sally/mod.rs index 2766865b64c..7f1be81bc6c 100644 --- a/crates/typed-store/src/sally/mod.rs +++ b/crates/typed-store/src/sally/mod.rs @@ -48,9 +48,7 @@ //! let mut table = ExampleTable::init(SallyDBOptions::TestDB); //! insert_key_vals(&table).await; //! // switch to rocksdb backend -//! let primary_path = tempfile::tempdir() -//! .expect("Failed to open db path") -//! .into_path(); +//! let primary_path = tempfile::tempdir().expect("Failed to open db path").keep(); //! table = ExampleTable::init(SallyDBOptions::RocksDB(( //! primary_path, //! MetricConf::default(), diff --git a/crates/typed-store/tests/macro_tests.rs b/crates/typed-store/tests/macro_tests.rs index d9a9711a7d6..b056da64dc0 100644 --- a/crates/typed-store/tests/macro_tests.rs +++ b/crates/typed-store/tests/macro_tests.rs @@ -19,7 +19,7 @@ use typed_store::{ fn temp_dir() -> std::path::PathBuf { tempfile::tempdir() .expect("Failed to open temporary directory") - .into_path() + .keep() } /// This struct is used to illustrate how the utility works #[derive(DBMapUtils)] diff --git a/docker/iota-bridge-indexer/Dockerfile b/docker/iota-bridge-indexer/Dockerfile deleted file mode 100644 index 893e366a264..00000000000 --- a/docker/iota-bridge-indexer/Dockerfile +++ /dev/null @@ -1,73 +0,0 @@ -# Build image (the specific rust version can also be passed, e.g. "1.82-bookworm") -ARG RUST_IMAGE_VERSION=bookworm -FROM rust:${RUST_IMAGE_VERSION} AS builder - -ARG PROFILE=release -ARG CARGO_BUILD_FEATURES -# The GIT_REVISION environment variable is used during build time inside the rust crates -ARG GIT_REVISION -ENV GIT_REVISION=$GIT_REVISION - -WORKDIR "/iota" - -# Install build dependencies, including clang and lld for faster linking -RUN apt update && apt install -y cmake clang lld - -# Configure Rust to use clang and lld as the linker -RUN mkdir -p ~/.cargo && \ - echo -e "[target.x86_64-unknown-linux-gnu]\nlinker = \"clang\"\nrustflags = [\"-C\", \"link-arg=-fuse-ld=lld\"]" > ~/.cargo/config.toml - -# Install additional dependencies -RUN apt install -y libpq5 libpq-dev ca-certificates - -# Copy in all crates, Cargo.toml, and Cargo.lock -COPY consensus consensus -COPY crates crates -COPY docs docs -COPY external-crates external-crates -COPY iota-execution iota-execution -COPY Cargo.toml Cargo.lock ./ - -RUN cargo build --profile ${PROFILE} --bin bridge-indexer --features ${CARGO_BUILD_FEATURES:=default} - -# Copy the built binary to the working directory depending on the output folder of the profile, -# so we can copy it to the runtime image -RUN if [ -d target/release ]; then \ - TARGET_DIR="target/release"; \ -elif [ -d target/debug ]; then \ - TARGET_DIR="target/debug"; \ -else \ - echo "Error: No build directory found"; \ - exit 1; \ -fi && \ -mv $TARGET_DIR/bridge-indexer ./; - -# Production image -FROM debian:bookworm-slim AS runtime - -ARG WORKDIR="/iota" -WORKDIR "$WORKDIR" - -# Install runtime dependencies and tools -RUN apt update && apt install -y libpq5 ca-certificates curl - -# Install jemalloc as the default allocator used for memory profiling on supported -# architectures and create a symlink for the correct version based on the architecture -RUN ARCH=$(dpkg --print-architecture) && \ - if [ "$ARCH" = "amd64" ] || [ "$ARCH" = "arm64" ]; then \ - apt update && apt install -y libjemalloc-dev; \ - ln -sf $(ldconfig -p | grep jemalloc | awk '{print $4}' | sort -u | head -n 1) /usr/lib/libjemalloc.so; \ - else \ - echo "Unsupported architecture: $ARCH. Only amd64 and arm64 are supported."; \ - exit 1; \ -fi - -# Set LD_PRELOAD to the symlinked path -ENV LD_PRELOAD=/usr/lib/libjemalloc.so - -COPY --from=builder /iota/bridge-indexer /usr/local/bin - -ARG BUILD_DATE -ARG GIT_REVISION -LABEL build-date=$BUILD_DATE -LABEL git-revision=$GIT_REVISION diff --git a/docker/iota-bridge-indexer/build.sh b/docker/iota-bridge-indexer/build.sh deleted file mode 100755 index 6919516eaf4..00000000000 --- a/docker/iota-bridge-indexer/build.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# Copyright (c) Mysten Labs, Inc. -# Modifications Copyright (c) 2024 IOTA Stiftung -# SPDX-License-Identifier: Apache-2.0 -./../utils/build-script.sh --image-tag "iotaledger/bridge-indexer" diff --git a/docker/iota-tools/Dockerfile b/docker/iota-tools/Dockerfile index b54d0687be7..5e7a7676dc1 100644 --- a/docker/iota-tools/Dockerfile +++ b/docker/iota-tools/Dockerfile @@ -32,9 +32,6 @@ COPY Cargo.toml Cargo.lock ./ RUN cargo build --profile ${PROFILE} \ --bin iota-node \ --bin stress \ - --bin iota-bridge \ - --bin bridge-indexer \ - --bin iota-bridge-cli \ --bin iota-analytics-indexer \ --bin iota \ --bin iota-faucet \ @@ -55,9 +52,6 @@ else \ fi && \ mv $TARGET_DIR/iota-node ./ && \ mv $TARGET_DIR/stress ./ && \ -mv $TARGET_DIR/iota-bridge ./ && \ -mv $TARGET_DIR/bridge-indexer ./ && \ -mv $TARGET_DIR/iota-bridge-cli ./ && \ mv $TARGET_DIR/iota-analytics-indexer ./ && \ mv $TARGET_DIR/iota ./ && \ mv $TARGET_DIR/iota-faucet ./ && \ @@ -76,9 +70,6 @@ RUN apt update && apt install -y libpq5 ca-certificates curl git COPY --from=builder /iota/iota-node /usr/local/bin COPY --from=builder /iota/stress /usr/local/bin -COPY --from=builder /iota/iota-bridge /usr/local/bin -COPY --from=builder /iota/bridge-indexer /usr/local/bin -COPY --from=builder /iota/iota-bridge-cli /usr/local/bin COPY --from=builder /iota/iota-analytics-indexer /usr/local/bin COPY --from=builder /iota/iota /usr/local/bin COPY --from=builder /iota/iota-faucet /usr/local/bin diff --git a/dprint.json b/dprint.json index b43205897c4..5e752195ddd 100644 --- a/dprint.json +++ b/dprint.json @@ -11,7 +11,6 @@ "**/target/", "**/build/", "external-crates/move/crates/move-docgen/tests/sources/*.md", - "bridge/evm/lib/", "examples/trading/api/prisma/migrations/migration_lock.toml", "sdk/create-dapp/templates/", "crates/iota-core/tests/staged/iota.yaml", diff --git a/external-crates/move/crates/move-stdlib/src/tests.rs b/external-crates/move/crates/move-stdlib/src/tests.rs index 480ce4dcc38..ed3ec7c3a07 100644 --- a/external-crates/move/crates/move-stdlib/src/tests.rs +++ b/external-crates/move/crates/move-stdlib/src/tests.rs @@ -88,5 +88,5 @@ fn sorted_walk_dir>(path: P) -> Result String { - d.into_path().to_string_lossy().to_string() + d.keep().to_string_lossy().to_string() } diff --git a/iota-execution/latest/iota-adapter/src/execution_engine.rs b/iota-execution/latest/iota-adapter/src/execution_engine.rs index 0e7e292ac62..bdaa3960925 100644 --- a/iota-execution/latest/iota-adapter/src/execution_engine.rs +++ b/iota-execution/latest/iota-adapter/src/execution_engine.rs @@ -14,9 +14,8 @@ mod checked { #[cfg(msim)] use iota_types::iota_system_state::advance_epoch_result_injection::maybe_modify_result; use iota_types::{ - BRIDGE_ADDRESS, IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_BRIDGE_OBJECT_ID, - IOTA_FRAMEWORK_ADDRESS, IOTA_FRAMEWORK_PACKAGE_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, - IOTA_SYSTEM_PACKAGE_ID, + IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_FRAMEWORK_ADDRESS, IOTA_FRAMEWORK_PACKAGE_ID, + IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_PACKAGE_ID, authenticator_state::{ AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME, AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME, AUTHENTICATOR_STATE_MODULE_NAME, @@ -29,13 +28,8 @@ mod checked { base_types::{ IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest, TxContext, }, - bridge::{ - BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER, BRIDGE_CREATE_FUNCTION_NAME, - BRIDGE_INIT_COMMITTEE_FUNCTION_NAME, BRIDGE_MODULE_NAME, BridgeChainId, - }, clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME}, committee::EpochId, - digests::{ChainIdentifier, get_mainnet_chain_identifier, get_testnet_chain_identifier}, effects::TransactionEffects, error::{ExecutionError, ExecutionErrorKind}, execution::{ExecutionResults, ExecutionResultsV1, is_certificate_denied}, @@ -43,7 +37,6 @@ mod checked { execution_status::{CongestedObjects, ExecutionStatus}, gas::{GasCostSummary, IotaGasStatus}, gas_coin::GAS, - id::UID, inner_temporary_store::InnerTemporaryStore, iota_system_state::{ ADVANCE_EPOCH_FUNCTION_NAME, AdvanceEpochParams, IOTA_SYSTEM_MODULE_NAME, @@ -62,7 +55,6 @@ mod checked { }, }; use move_binary_format::CompiledModule; - use move_core_types::ident_str; use move_trace_format::format::MoveTraceBuilder; use move_vm_runtime::move_vm::MoveVM; use tracing::{info, instrument, trace, warn}; @@ -714,15 +706,6 @@ mod checked { // safe mode. builder = setup_authenticator_state_expire(builder, expire); } - EndOfEpochTransactionKind::BridgeStateCreate(chain_id) => { - assert!(protocol_config.enable_bridge()); - builder = setup_bridge_create(builder, chain_id) - } - EndOfEpochTransactionKind::BridgeCommitteeInit(bridge_shared_version) => { - assert!(protocol_config.enable_bridge()); - assert!(protocol_config.should_try_to_finalize_bridge_committee()); - builder = setup_bridge_committee_update(builder, bridge_shared_version) - } } } unreachable!( @@ -1181,80 +1164,6 @@ mod checked { builder } - /// Configures a `ProgrammableTransactionBuilder` to create a bridge. - fn setup_bridge_create( - mut builder: ProgrammableTransactionBuilder, - chain_id: ChainIdentifier, - ) -> ProgrammableTransactionBuilder { - let bridge_uid = builder - .input(CallArg::Pure( - UID::new(IOTA_BRIDGE_OBJECT_ID).to_bcs_bytes(), - )) - .expect("Unable to create Bridge object UID!"); - - let bridge_chain_id = if chain_id == get_mainnet_chain_identifier() { - BridgeChainId::IotaMainnet as u8 - } else if chain_id == get_testnet_chain_identifier() { - BridgeChainId::IotaTestnet as u8 - } else { - // How do we distinguish devnet from other test envs? - BridgeChainId::IotaCustom as u8 - }; - - let bridge_chain_id = builder.pure(bridge_chain_id).unwrap(); - builder.programmable_move_call( - BRIDGE_ADDRESS.into(), - BRIDGE_MODULE_NAME.to_owned(), - BRIDGE_CREATE_FUNCTION_NAME.to_owned(), - vec![], - vec![bridge_uid, bridge_chain_id], - ); - builder - } - - /// Configures a `ProgrammableTransactionBuilder` to update the bridge - /// committee. - fn setup_bridge_committee_update( - mut builder: ProgrammableTransactionBuilder, - bridge_shared_version: SequenceNumber, - ) -> ProgrammableTransactionBuilder { - let bridge = builder - .obj(ObjectArg::SharedObject { - id: IOTA_BRIDGE_OBJECT_ID, - initial_shared_version: bridge_shared_version, - mutable: true, - }) - .expect("Unable to create Bridge object arg!"); - let system_state = builder - .obj(ObjectArg::IOTA_SYSTEM_MUT) - .expect("Unable to create System State object arg!"); - - let voting_power = builder.programmable_move_call( - IOTA_SYSTEM_PACKAGE_ID, - IOTA_SYSTEM_MODULE_NAME.to_owned(), - ident_str!("validator_voting_powers").to_owned(), - vec![], - vec![system_state], - ); - - // Hardcoding min stake participation to 75.00% - // TODO: We need to set a correct value or make this configurable. - let min_stake_participation_percentage = builder - .input(CallArg::Pure( - bcs::to_bytes(&BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER).unwrap(), - )) - .unwrap(); - - builder.programmable_move_call( - BRIDGE_ADDRESS.into(), - BRIDGE_MODULE_NAME.to_owned(), - BRIDGE_INIT_COMMITTEE_FUNCTION_NAME.to_owned(), - vec![], - vec![bridge, voting_power, min_stake_participation_percentage], - ); - builder - } - /// Sets up and executes a `ProgrammableTransaction` to update the /// authenticator state. This function constructs a transaction that /// invokes the `authenticator_state_update` function from the IOTA diff --git a/iota-execution/latest/iota-move-natives/src/object_runtime/mod.rs b/iota-execution/latest/iota-move-natives/src/object_runtime/mod.rs index ac03f0893df..dd623772ce4 100644 --- a/iota-execution/latest/iota-move-natives/src/object_runtime/mod.rs +++ b/iota-execution/latest/iota-move-natives/src/object_runtime/mod.rs @@ -13,7 +13,7 @@ use better_any::{Tid, TidAble}; use indexmap::{map::IndexMap, set::IndexSet}; use iota_protocol_config::{LimitThresholdCrossed, ProtocolConfig, check_limit_by_meter}; use iota_types::{ - IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_BRIDGE_OBJECT_ID, IOTA_CLOCK_OBJECT_ID, + GENESIS_IOTA_BRIDGE_OBJECT_ID, IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_CLOCK_OBJECT_ID, IOTA_DENY_LIST_OBJECT_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_STATE_OBJECT_ID, base_types::{IotaAddress, MoveObjectType, ObjectID, SequenceNumber}, committee::EpochId, @@ -259,7 +259,7 @@ impl<'a> ObjectRuntime<'a> { IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_DENY_LIST_OBJECT_ID, - IOTA_BRIDGE_OBJECT_ID, + GENESIS_IOTA_BRIDGE_OBJECT_ID, ] .contains(&id); let transfer_result = if self.state.new_ids.contains(&id) { diff --git a/iota-execution/latest/iota-verifier/src/id_leak_verifier.rs b/iota-execution/latest/iota-verifier/src/id_leak_verifier.rs index 50383e4624a..70dac7ce929 100644 --- a/iota-execution/latest/iota-verifier/src/id_leak_verifier.rs +++ b/iota-execution/latest/iota-verifier/src/id_leak_verifier.rs @@ -16,9 +16,8 @@ use std::{collections::BTreeMap, error::Error, num::NonZeroU64}; use iota_types::{ - BRIDGE_ADDRESS, IOTA_FRAMEWORK_ADDRESS, IOTA_SYSTEM_ADDRESS, + GENESIS_BRIDGE_ADDRESS, IOTA_FRAMEWORK_ADDRESS, IOTA_SYSTEM_ADDRESS, authenticator_state::AUTHENTICATOR_STATE_MODULE_NAME, - bridge::BRIDGE_MODULE_NAME, clock::CLOCK_MODULE_NAME, deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE}, error::{ExecutionError, VMMVerifierErrorSubStatusCode}, @@ -98,8 +97,11 @@ const IOTA_DENY_LIST_CREATE: FunctionIdent = ( DENY_LIST_CREATE_FUNC, ); -const IOTA_BRIDGE_CREATE: FunctionIdent = - (&BRIDGE_ADDRESS, BRIDGE_MODULE_NAME, ident_str!("create")); +const IOTA_BRIDGE_CREATE: FunctionIdent = ( + &GENESIS_BRIDGE_ADDRESS, + ident_str!("bridge"), + ident_str!("create"), +); const FRESH_ID_FUNCTIONS: &[FunctionIdent] = &[OBJECT_NEW, OBJECT_NEW_UID_FROM_HASH, TS_NEW_OBJECT]; const FUNCTIONS_TO_SKIP: &[FunctionIdent] = &[ IOTA_SYSTEM_CREATE, diff --git a/iota-execution/latest/iota-verifier/src/one_time_witness_verifier.rs b/iota-execution/latest/iota-verifier/src/one_time_witness_verifier.rs index 53ea3d80d64..7af60c97072 100644 --- a/iota-execution/latest/iota-verifier/src/one_time_witness_verifier.rs +++ b/iota-execution/latest/iota-verifier/src/one_time_witness_verifier.rs @@ -21,9 +21,8 @@ //! initializer //! - it is never instantiated anywhere in its defining module use iota_types::{ - BRIDGE_ADDRESS, IOTA_FRAMEWORK_ADDRESS, + IOTA_FRAMEWORK_ADDRESS, base_types::{TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME}, - bridge::BRIDGE_SUPPORTED_ASSET, error::ExecutionError, move_package::{FnInfoMap, is_test_fun}, }; @@ -54,13 +53,6 @@ pub fn verify_module( return Ok(()); } - if BRIDGE_SUPPORTED_ASSET - .iter() - .any(|token| ModuleId::new(BRIDGE_ADDRESS, ident_str!(token).to_owned()) == self_id) - { - return Ok(()); - } - let mod_handle = module.module_handle_at(module.self_module_handle_idx); let mod_name = module.identifier_at(mod_handle.name).as_str(); let struct_defs = &module.struct_defs;