diff --git a/Cargo.lock b/Cargo.lock index b7ca706c5004d..313cc1759bb34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,9 +133,9 @@ checksum = "a3c5a28f166629752f2e7246b813cdea3243cca59aab2d4264b1fd68392c10eb" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", - "alloy-primitives", + "alloy-primitives 1.1.2", "alloy-rlp", - "alloy-sol-types", + "alloy-sol-types 1.1.2", ] [[package]] @@ -145,9 +145,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18cc14d832bc3331ca22a1c7819de1ede99f58f61a7d123952af7dde8de124a6" dependencies = [ "alloy-json-abi", - "alloy-primitives", + "alloy-primitives 1.1.2", "alloy-sol-type-parser", - "alloy-sol-types", + "alloy-sol-types 1.1.2", "itoa", "serde", "serde_json", @@ -160,12 +160,29 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ccaa79753d7bf15f06399ea76922afbfaf8d18bebed9e8fc452984b4a90dcc9" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.1.2", "alloy-sol-type-parser", "serde", "serde_json", ] +[[package]] +name = "alloy-primitives" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0628ec0ba5b98b3370bb6be17b12f23bfce8ee4ad83823325a20546d9b03b78" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 0.99.17", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + [[package]] name = "alloy-primitives" version = "1.1.2" @@ -204,6 +221,23 @@ dependencies = [ "smol_str", ] +[[package]] +name = "alloy-sol-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a98ad1696a2e17f010ae8e43e9f2a1e930ed176a8e3ff77acfeff6dfb07b42c" +dependencies = [ + "const-hex", + "dunce", + "heck 0.4.1", + "proc-macro-error", + "proc-macro2 1.0.101", + "quote 1.0.40", + "syn 2.0.98", + "syn-solidity 0.4.2", + "tiny-keccak", +] + [[package]] name = "alloy-sol-macro" version = "1.1.2" @@ -213,7 +247,7 @@ dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -229,10 +263,10 @@ dependencies = [ "heck 0.5.0", "indexmap 2.9.0", "proc-macro-error2", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", - "syn-solidity", + "syn-solidity 1.1.2", "tiny-keccak", ] @@ -246,10 +280,10 @@ dependencies = [ "dunce", "heck 0.5.0", "macro-string", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", - "syn-solidity", + "syn-solidity 1.1.2", ] [[package]] @@ -262,6 +296,17 @@ dependencies = [ "winnow 0.7.10", ] +[[package]] +name = "alloy-sol-types" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d7107bed88e8f09f0ddcc3335622d87bfb6821f3e0c7473329fb1cfad5e015" +dependencies = [ + "alloy-primitives 0.4.2", + "alloy-sol-macro 0.4.2", + "const-hex", +] + [[package]] name = "alloy-sol-types" version = "1.1.2" @@ -269,8 +314,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "584cb97bfc5746cb9dcc4def77da11694b5d6d7339be91b7480a6a68dc129387" dependencies = [ "alloy-json-abi", - "alloy-primitives", - "alloy-sol-macro", + "alloy-primitives 1.1.2", + "alloy-sol-macro 1.1.2", "serde", ] @@ -373,7 +418,7 @@ dependencies = [ "include_dir", "itertools 0.10.5", "proc-macro-error", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -684,7 +729,7 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -697,7 +742,7 @@ checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -812,7 +857,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -823,7 +868,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -889,7 +934,7 @@ dependencies = [ "digest 0.10.7", "rand_chacha 0.3.1", "rayon", - "sha2 0.10.8", + "sha2 0.10.9", "w3f-ring-proof", "zeroize", ] @@ -974,7 +1019,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", "synstructure 0.13.1", @@ -986,7 +1031,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", "synstructure 0.13.1", @@ -998,7 +1043,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -1621,7 +1666,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -1638,7 +1683,7 @@ version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -1708,7 +1753,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -1832,7 +1877,7 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "regex", "rustc-hash 1.1.0", @@ -1851,7 +1896,7 @@ dependencies = [ "k256", "rand_core 0.6.4", "ripemd", - "sha2 0.10.8", + "sha2 0.10.9", "subtle 2.5.0", "zeroize", ] @@ -2088,6 +2133,19 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32ed0a820ed50891d36358e997d27741a6142e382242df40ff01c89bcdcc7a2b" dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "bounded-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee8eddd066a8825ec5570528e6880471210fd5d88cb6abbe1cfdd51ca249c33" +dependencies = [ + "jam-codec", "log", "parity-scale-codec", "scale-info", @@ -2817,7 +2875,7 @@ 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", ] @@ -2966,10 +3024,11 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.1.24" +version = "1.2.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -3220,7 +3279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -3252,7 +3311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb844bd05be34d91eb67101329aeba9d3337094c04fd8507d821db7ebb488eaf" dependencies = [ "proc-macro-error2", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -3426,7 +3485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b" dependencies = [ "nom", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -3493,7 +3552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a54b9c40054eb8999c5d1d36fdc90e4e5f7ff0d1d9621706f360b3cbc8beb828" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -3505,7 +3564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5437e327e861081c91270becff184859f706e3e50f5301a9d4dc8eb50752c3" dependencies = [ "convert_case 0.6.0", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -3588,7 +3647,7 @@ version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "unicode-xid 0.2.4", ] @@ -4207,7 +4266,7 @@ dependencies = [ "parachains-common", "parity-scale-codec", "prost 0.12.6", - "prost-build", + "prost-build 0.13.2", "sc-network", "sc-service", "sp-consensus-babe", @@ -4603,8 +4662,8 @@ dependencies = [ name = "cumulus-pallet-parachain-system-proc-macro" version = "0.6.0" dependencies = [ - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -4673,7 +4732,7 @@ name = "cumulus-pallet-xcmp-queue" version = "0.21.0" dependencies = [ "approx", - "bounded-collections 0.2.3", + "bounded-collections 0.3.2", "bp-xcm-bridge-hub-router", "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -5225,7 +5284,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5264,7 +5323,7 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "scratch", "syn 2.0.98", @@ -5282,7 +5341,7 @@ version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5305,7 +5364,7 @@ checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "strsim", "syn 2.0.98", @@ -5424,7 +5483,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -5435,7 +5494,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5446,7 +5505,7 @@ version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5457,7 +5516,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5469,7 +5528,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "rustc_version 0.4.0", "syn 1.0.109", @@ -5499,7 +5558,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", "unicode-xid 0.2.4", @@ -5511,7 +5570,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", "unicode-xid 0.2.4", @@ -5616,7 +5675,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5660,7 +5719,7 @@ dependencies = [ "common-path", "derive-syn-parse", "once_cell", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "regex", "syn 2.0.98", @@ -5734,7 +5793,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -5789,7 +5848,7 @@ dependencies = [ "ed25519", "rand_core 0.6.4", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "subtle 2.5.0", "zeroize", ] @@ -5819,7 +5878,7 @@ dependencies = [ "hashbrown 0.14.5", "hex", "rand_core 0.6.4", - "sha2 0.10.8", + "sha2 0.10.9", "zeroize", ] @@ -5830,7 +5889,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5927,7 +5986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5947,7 +6006,7 @@ version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5967,7 +6026,7 @@ version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -5978,7 +6037,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -6189,7 +6248,7 @@ dependencies = [ "file-guard", "fs-err", "prettyplease", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -6293,8 +6352,8 @@ checksum = "eb42427514b063d97ce21d5199f36c0c307d981434a6be32582bc79fe5bd2303" dependencies = [ "expander", "indexmap 2.9.0", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -6405,6 +6464,12 @@ dependencies = [ "relay-utils", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + [[package]] name = "findshlibs" version = "0.10.2" @@ -6685,8 +6750,8 @@ dependencies = [ "frame-election-provider-support", "frame-support", "parity-scale-codec", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "scale-info", "sp-arithmetic 27.0.0", @@ -6934,7 +6999,7 @@ dependencies = [ "parity-scale-codec", "pretty_assertions", "proc-macro-warning", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "regex", "scale-info", @@ -6950,8 +7015,8 @@ name = "frame-support-procedural-tools" version = "13.0.1" dependencies = [ "frame-support-procedural-tools-derive", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -6960,7 +7025,7 @@ dependencies = [ name = "frame-support-procedural-tools-derive" version = "12.0.0" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -7224,7 +7289,7 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -7802,7 +7867,7 @@ dependencies = [ "ipnet", "once_cell", "rand 0.9.0", - "ring 0.17.8", + "ring 0.17.14", "thiserror 2.0.12", "tinyvec", "tokio", @@ -8277,7 +8342,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -8429,7 +8494,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -8449,7 +8514,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", ] @@ -8674,6 +8739,34 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jam-codec" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb948eace373d99de60501a02fb17125d30ac632570de20dccc74370cdd611b9" +dependencies = [ + "arrayvec 0.7.6", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "jam-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "jam-codec-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "319af585c4c8a6b5552a52b7787a1ab3e4d59df7614190b1f85b9b842488789d" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", + "quote 1.0.40", + "syn 2.0.98", +] + [[package]] name = "jni" version = "0.19.0" @@ -8849,8 +8942,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" dependencies = [ "heck 0.5.0", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -8929,7 +9022,7 @@ dependencies = [ "elliptic-curve", "once_cell", "serdect", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -9400,7 +9493,7 @@ dependencies = [ "multihash 0.19.1", "quick-protobuf 0.8.1", "rand 0.8.5", - "sha2 0.10.8", + "sha2 0.10.9", "thiserror 1.0.65", "tracing", "zeroize", @@ -9426,7 +9519,7 @@ dependencies = [ "quick-protobuf 0.8.1", "quick-protobuf-codec", "rand 0.8.5", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", "thiserror 1.0.65", "tracing", @@ -9491,7 +9584,7 @@ dependencies = [ "once_cell", "quick-protobuf 0.8.1", "rand 0.8.5", - "sha2 0.10.8", + "sha2 0.10.9", "snow", "static_assertions", "thiserror 1.0.65", @@ -9534,7 +9627,7 @@ dependencies = [ "parking_lot 0.12.3", "quinn", "rand 0.8.5", - "ring 0.17.8", + "ring 0.17.14", "rustls 0.23.18", "socket2 0.5.9", "thiserror 1.0.65", @@ -9593,7 +9686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206e0aa0ebe004d778d79fb0966aa0de996c19894e2c0605ba2f8524dd4443d8" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -9626,7 +9719,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "rcgen", - "ring 0.17.8", + "ring 0.17.14", "rustls 0.23.18", "rustls-webpki 0.101.4", "thiserror 1.0.65", @@ -9683,7 +9776,7 @@ dependencies = [ "thiserror 1.0.65", "tracing", "yamux 0.12.1", - "yamux 0.13.5", + "yamux 0.13.8", ] [[package]] @@ -9861,9 +9954,8 @@ checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "litep2p" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14fb10e63363204b89d91e1292df83322fd9de5d7fa76c3d5c78ddc2f8f3efa9" +version = "0.11.1" +source = "git+https://github.com/moondance-labs/litep2p?branch=girazoki-simple-dns-update-2#db08ad8204dc442c320ca4388c24d0f6bafdcd46" dependencies = [ "async-trait", "bs58", @@ -9882,10 +9974,11 @@ dependencies = [ "parking_lot 0.12.3", "pin-project", "prost 0.13.5", - "prost-build", + "prost-build 0.14.1", "rand 0.8.5", + "ring 0.17.14", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "simple-dns", "smallvec", "snow", @@ -9893,7 +9986,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tokio-stream", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite 0.27.0", "tokio-util", "tracing", "uint 0.10.0", @@ -9901,7 +9994,7 @@ dependencies = [ "url", "x25519-dalek", "x509-parser 0.17.0", - "yamux 0.13.5", + "yamux 0.13.8", "yasna", "zeroize", ] @@ -10013,7 +10106,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -10039,7 +10132,7 @@ dependencies = [ "const-random", "derive-syn-parse", "macro_magic_core_macros", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -10050,7 +10143,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -10363,7 +10456,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" dependencies = [ "cfg-if", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -10480,7 +10573,7 @@ dependencies = [ "core2", "digest 0.10.7", "multihash-derive", - "sha2 0.10.8", + "sha2 0.10.9", "sha3 0.10.8", "unsigned-varint 0.7.2", ] @@ -10503,7 +10596,7 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", "synstructure 0.12.6", @@ -10551,7 +10644,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -10965,7 +11058,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -11144,7 +11237,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -11200,8 +11293,8 @@ dependencies = [ "indexmap 2.9.0", "itertools 0.11.0", "petgraph", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -11958,7 +12051,7 @@ dependencies = [ name = "pallet-contracts-proc-macro" version = "23.0.3" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -13123,7 +13216,7 @@ dependencies = [ name = "pallet-revive-proc-macro" version = "0.3.0" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -13666,8 +13759,8 @@ dependencies = [ name = "pallet-staking-reward-curve" version = "12.0.0" dependencies = [ - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "sp-runtime 42.0.0", "syn 2.0.98", @@ -13966,7 +14059,7 @@ dependencies = [ name = "pallet-xcm" version = "20.1.3" dependencies = [ - "bounded-collections 0.2.3", + "bounded-collections 0.3.2", "frame-benchmarking", "frame-support", "frame-system", @@ -14231,8 +14324,8 @@ version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -14671,7 +14764,7 @@ checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -14684,7 +14777,7 @@ checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -14712,7 +14805,7 @@ version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -15888,7 +15981,7 @@ dependencies = [ name = "polkadot-parachain-primitives" version = "17.0.0" dependencies = [ - "bounded-collections 0.2.3", + "bounded-collections 0.3.2", "derive_more 0.99.17", "parity-scale-codec", "polkadot-core-primitives", @@ -15904,7 +15997,7 @@ name = "polkadot-primitives" version = "19.0.0" dependencies = [ "bitvec", - "bounded-collections 0.2.3", + "bounded-collections 0.3.2", "hex-literal", "log", "parity-scale-codec", @@ -17174,7 +17267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ "polkavm-common 0.9.0", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -17186,7 +17279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24fd6c6215450c3e57511df5c38a82eb4bde208de15ee15046ac33852f3c3eaa" dependencies = [ "polkavm-common 0.21.0", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -17198,7 +17291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5a21844afdfcc10c92b9ef288ccb926211af27478d1730fcd55e4aec710179d" dependencies = [ "polkavm-common 0.24.0", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -17427,7 +17520,7 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "syn 2.0.98", ] @@ -17487,11 +17580,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.21.0", + "toml_edit 0.23.5", ] [[package]] @@ -17501,7 +17594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", "version_check", @@ -17513,7 +17606,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "version_check", ] @@ -17524,7 +17617,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", ] @@ -17535,7 +17628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ "proc-macro-error-attr2", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -17552,7 +17645,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -17568,9 +17661,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -17633,7 +17726,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -17700,6 +17793,16 @@ dependencies = [ "prost-derive 0.13.5", ] +[[package]] +name = "prost" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +dependencies = [ + "bytes", + "prost-derive 0.14.1", +] + [[package]] name = "prost-build" version = "0.13.2" @@ -17708,14 +17811,34 @@ checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", "heck 0.5.0", - "itertools 0.12.1", + "itertools 0.11.0", "log", "multimap", "once_cell", "petgraph", "prettyplease", "prost 0.13.5", - "prost-types", + "prost-types 0.13.2", + "regex", + "syn 2.0.98", + "tempfile", +] + +[[package]] +name = "prost-build" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" +dependencies = [ + "heck 0.5.0", + "itertools 0.11.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost 0.14.1", + "prost-types 0.14.1", "regex", "syn 2.0.98", "tempfile", @@ -17729,7 +17852,7 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -17742,7 +17865,7 @@ checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -17754,8 +17877,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.12.1", - "proc-macro2 1.0.95", + "itertools 0.11.0", + "proc-macro2 1.0.101", + "quote 1.0.40", + "syn 2.0.98", +] + +[[package]] +name = "prost-derive" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +dependencies = [ + "anyhow", + "itertools 0.11.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -17769,6 +17905,15 @@ dependencies = [ "prost 0.13.5", ] +[[package]] +name = "prost-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" +dependencies = [ + "prost 0.14.1", +] + [[package]] name = "psm" version = "0.1.21" @@ -17910,7 +18055,7 @@ checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ "bytes", "rand 0.8.5", - "ring 0.17.8", + "ring 0.17.14", "rustc-hash 2.1.1", "rustls 0.23.18", "slab", @@ -17947,7 +18092,7 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", ] [[package]] @@ -18185,7 +18330,7 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -18437,15 +18582,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", "getrandom 0.2.10", "libc", - "spin 0.9.8", "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -18763,7 +18907,7 @@ checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" dependencies = [ "cfg-if", "glob", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "regex", "relative-path", @@ -18950,7 +19094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.8", + "ring 0.17.14", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle 2.5.0", @@ -18965,7 +19109,7 @@ checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" dependencies = [ "log", "once_cell", - "ring 0.17.8", + "ring 0.17.14", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle 2.5.0", @@ -19078,7 +19222,7 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "ring 0.17.8", + "ring 0.17.14", "rustls-pki-types", "untrusted 0.9.0", ] @@ -19210,7 +19354,7 @@ dependencies = [ "log", "parity-scale-codec", "prost 0.12.6", - "prost-build", + "prost-build 0.13.2", "quickcheck", "rand 0.8.5", "sc-client-api", @@ -19308,8 +19452,8 @@ dependencies = [ name = "sc-chain-spec-derive" version = "12.0.0" dependencies = [ - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -19335,6 +19479,7 @@ dependencies = [ "rpassword", "sc-client-api", "sc-client-db", + "sc-executor 0.43.0", "sc-keystore", "sc-mixnet", "sc-network", @@ -19351,6 +19496,8 @@ dependencies = [ "sp-keystore 0.43.0", "sp-panic-handler 13.0.2", "sp-runtime 42.0.0", + "sp-state-machine 0.46.0", + "sp-storage 22.0.0", "sp-tracing 17.1.0", "sp-version 40.0.0", "tempfile", @@ -19808,6 +19955,7 @@ dependencies = [ "array-bytes 6.2.2", "assert_matches", "criterion", + "log", "num_cpus", "parity-scale-codec", "parking_lot 0.12.3", @@ -19857,6 +20005,7 @@ dependencies = [ name = "sc-executor-common" version = "0.39.0" dependencies = [ + "parity-scale-codec", "polkavm 0.24.0", "sc-allocator 32.0.0", "sp-maybe-compressed-blob 11.0.0", @@ -19912,6 +20061,8 @@ version = "0.39.0" dependencies = [ "anyhow", "cargo_metadata", + "cfg-if", + "libc", "log", "parity-scale-codec", "parking_lot 0.12.3", @@ -20012,7 +20163,7 @@ dependencies = [ "partial_sort", "pin-project", "prost 0.12.6", - "prost-build", + "prost-build 0.13.2", "rand 0.8.5", "sc-block-builder", "sc-client-api", @@ -20086,7 +20237,7 @@ dependencies = [ "log", "parity-scale-codec", "prost 0.12.6", - "prost-build", + "prost-build 0.13.2", "sc-client-api", "sc-network", "sc-network-types", @@ -20128,7 +20279,7 @@ dependencies = [ "mockall", "parity-scale-codec", "prost 0.12.6", - "prost-build", + "prost-build 0.13.2", "quickcheck", "sc-block-builder", "sc-client-api", @@ -20661,8 +20812,8 @@ dependencies = [ name = "sc-tracing-proc-macro" version = "11.1.0" dependencies = [ - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -20801,7 +20952,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ed9401effa946b493f9f84dc03714cca98119b230497df6f3df6b84a2b03648" dependencies = [ "darling", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -20813,7 +20964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f4b54a1211260718b92832b661025d1f1a4b6930fbadd6908e00edd265fa5f7" dependencies = [ "darling", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -20855,8 +21006,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "102fbc6236de6c53906c0b262f12c7aa69c2bdc604862c12728f5f4d370bc137" dependencies = [ "darling", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -20868,8 +21019,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78a3993a13b4eafa89350604672c8757b7ea84c7c5947d4b3691e3169c96379b" dependencies = [ "darling", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -20894,8 +21045,8 @@ version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -20916,7 +21067,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc4c70c7fea2eef1740f0081d3fe385d8bee1eef11e9272d3bec7dc8e5438e0" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "scale-info", "syn 2.0.98", @@ -20929,7 +21080,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05c61b6b706a3eaad63b506ab50a1d2319f817ae01cf753adcc3f055f9f0fcd6" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "scale-info", "syn 2.0.98", @@ -21002,7 +21153,7 @@ version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "serde_derive_internals", "syn 1.0.109", @@ -21050,7 +21201,7 @@ dependencies = [ "merlin", "rand_core 0.6.4", "serde_bytes", - "sha2 0.10.8", + "sha2 0.10.9", "subtle 2.5.0", "zeroize", ] @@ -21082,7 +21233,7 @@ dependencies = [ "password-hash", "pbkdf2", "salsa20", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -21259,10 +21410,11 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.225" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" dependencies = [ + "serde_core", "serde_derive", ] @@ -21294,13 +21446,22 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_core" +version = "1.0.225" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.225" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -21311,7 +21472,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -21382,7 +21543,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -21449,9 +21610,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", @@ -21549,9 +21710,9 @@ dependencies = [ [[package]] name = "simple-dns" -version = "0.9.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" +checksum = "f8cba3b4c122239e3b4473674cb7c79ad2693f008f0746bfe2fc3fe1ffcd936a" dependencies = [ "bitflags 2.6.0", ] @@ -21713,7 +21874,7 @@ dependencies = [ "schnorrkel 0.10.2", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sha3 0.10.8", "siphasher 0.3.11", "slab", @@ -21767,7 +21928,7 @@ dependencies = [ "schnorrkel 0.11.4", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sha3 0.10.8", "siphasher 1.0.1", "slab", @@ -21868,9 +22029,9 @@ dependencies = [ "chacha20poly1305", "curve25519-dalek 4.1.3", "rand_core 0.6.4", - "ring 0.17.8", + "ring 0.17.14", "rustc_version 0.4.0", - "sha2 0.10.8", + "sha2 0.10.9", "subtle 2.5.0", ] @@ -21953,9 +22114,12 @@ name = "snowbridge-inbound-queue-primitives" version = "0.3.0" dependencies = [ "alloy-core", + "alloy-primitives 0.4.2", + "alloy-sol-types 0.4.2", "frame-support", "frame-system", "hex-literal", + "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", @@ -21970,6 +22134,7 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", + "tracing", ] [[package]] @@ -22197,11 +22362,10 @@ dependencies = [ [[package]] name = "snowbridge-pallet-outbound-queue-v2" -version = "0.3.0" +version = "0.2.0" dependencies = [ "alloy-core", "bp-relayers", - "bridge-hub-common", "ethabi-decode", "frame-benchmarking", "frame-support", @@ -22592,8 +22756,8 @@ dependencies = [ "Inflector", "blake2 0.10.6", "expander", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -22606,8 +22770,8 @@ dependencies = [ "assert_matches", "blake2 0.10.6", "expander", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -23030,11 +23194,10 @@ dependencies = [ name = "sp-core" version = "37.0.0" dependencies = [ - "ark-vrf", "array-bytes 6.2.2", "bitflags 1.3.2", "blake2 0.10.6", - "bounded-collections 0.2.3", + "bounded-collections 0.3.2", "bs58", "criterion", "dyn-clonable", @@ -23062,7 +23225,7 @@ dependencies = [ "secrecy 0.8.0", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sp-crypto-hashing 0.1.0", "sp-debug-derive 14.0.0", "sp-externalities 0.30.0", @@ -23127,7 +23290,7 @@ dependencies = [ "byteorder", "criterion", "digest 0.10.7", - "sha2 0.10.8", + "sha2 0.10.9", "sha3 0.10.8", "sp-crypto-hashing-proc-macro 0.1.0", "twox-hash", @@ -23142,7 +23305,7 @@ dependencies = [ "blake2b_simd 1.0.2", "byteorder", "digest 0.10.7", - "sha2 0.10.8", + "sha2 0.10.9", "sha3 0.10.8", "twox-hash", ] @@ -23179,7 +23342,7 @@ dependencies = [ name = "sp-debug-derive" version = "14.0.0" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -23190,7 +23353,7 @@ version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -23655,8 +23818,8 @@ checksum = "0195f32c628fee3ce1dfbbf2e7e52a30ea85f3589da9fe62a8b816d70fc06294" dependencies = [ "Inflector", "expander", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -23667,8 +23830,8 @@ version = "19.0.0" dependencies = [ "Inflector", "expander", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -23812,7 +23975,7 @@ dependencies = [ "parity-scale-codec", "rand 0.8.5", "scale-info", - "sha2 0.10.8", + "sha2 0.10.9", "sp-api 37.0.0", "sp-application-crypto 41.0.0", "sp-core 37.0.0", @@ -24056,7 +24219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aee8f6730641a65fcf0c8f9b1e448af4b3bb083d08058b47528188bccc7b7a7" dependencies = [ "parity-scale-codec", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -24067,7 +24230,7 @@ version = "15.0.0" dependencies = [ "parity-scale-codec", "proc-macro-warning", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "sp-version 40.0.0", "syn 2.0.98", @@ -24116,7 +24279,7 @@ dependencies = [ name = "sp-weights" version = "32.0.0" dependencies = [ - "bounded-collections 0.2.3", + "bounded-collections 0.3.2", "parity-scale-codec", "scale-info", "schemars", @@ -24214,7 +24377,7 @@ dependencies = [ "percent-encoding", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", "sqlformat", "thiserror 1.0.65", @@ -24230,7 +24393,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "sqlx-core", "sqlx-macros-core", @@ -24248,11 +24411,11 @@ dependencies = [ "heck 0.5.0", "hex", "once_cell", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sqlx-core", "sqlx-mysql", "sqlx-postgres", @@ -24296,7 +24459,7 @@ dependencies = [ "rsa", "serde", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", @@ -24334,7 +24497,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", @@ -24374,7 +24537,7 @@ checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" dependencies = [ "Inflector", "num-format", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "serde", "serde_json", @@ -24399,7 +24562,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f07d54c4d01a1713eb363b55ba51595da15f6f1211435b71466460da022aa140" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -24500,7 +24663,7 @@ name = "staging-xcm" version = "17.0.0" dependencies = [ "array-bytes 6.2.2", - "bounded-collections 0.2.3", + "bounded-collections 0.3.2", "derive-where", "environmental", "frame-support", @@ -24598,7 +24761,7 @@ checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" dependencies = [ "cfg_aliases 0.1.1", "memchr", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -24656,7 +24819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "rustversion", "syn 1.0.109", @@ -24669,7 +24832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "rustversion", "syn 2.0.98", @@ -24703,7 +24866,7 @@ dependencies = [ "pbkdf2", "rustc-hex", "schnorrkel 0.11.4", - "sha2 0.10.8", + "sha2 0.10.9", "zeroize", ] @@ -24716,7 +24879,7 @@ dependencies = [ "hmac 0.12.1", "pbkdf2", "schnorrkel 0.11.4", - "sha2 0.10.8", + "sha2 0.10.9", "zeroize", ] @@ -25218,7 +25381,7 @@ checksum = "3cfcfb7d9589f3df0ac87c4988661cf3fb370761fcb19f2fd33104cc59daf22a" dependencies = [ "heck 0.5.0", "parity-scale-codec", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "scale-info", "scale-typegen 0.9.0", @@ -25235,7 +25398,7 @@ checksum = "324c52c09919fec8c22a4b572a466878322e99fe14a9e3d50d6c3700a226ec25" dependencies = [ "heck 0.5.0", "parity-scale-codec", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "scale-info", "scale-typegen 0.11.1", @@ -25446,7 +25609,7 @@ dependencies = [ "secrecy 0.10.3", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "subxt-core 0.38.0", "zeroize", ] @@ -25474,7 +25637,7 @@ dependencies = [ "secrecy 0.10.3", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "subxt-core 0.41.0", "thiserror 2.0.12", @@ -25611,7 +25774,7 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "unicode-ident", ] @@ -25622,11 +25785,23 @@ version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b837ef12ab88835251726eb12237655e61ec8dc8a280085d1961cdc3dfd047" +dependencies = [ + "paste", + "proc-macro2 1.0.101", + "quote 1.0.40", + "syn 2.0.98", +] + [[package]] name = "syn-solidity" version = "1.1.2" @@ -25634,7 +25809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5d879005cc1b5ba4e18665be9e9501d9da3a9b95f625497c4cb7ee082b532e" dependencies = [ "paste", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -25654,7 +25829,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", "unicode-xid 0.2.4", @@ -25666,7 +25841,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -25837,7 +26012,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -25985,7 +26160,7 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 1.0.109", ] @@ -25996,7 +26171,7 @@ version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -26007,7 +26182,7 @@ version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -26179,7 +26354,7 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -26228,9 +26403,9 @@ dependencies = [ [[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", @@ -26268,6 +26443,18 @@ name = "tokio-tungstenite" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.26.2", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" dependencies = [ "futures-util", "log", @@ -26276,7 +26463,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", - "tungstenite 0.26.2", + "tungstenite 0.27.0", ] [[package]] @@ -26311,7 +26498,7 @@ checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", - "toml_datetime", + "toml_datetime 0.6.8", "toml_edit 0.22.22", ] @@ -26325,24 +26512,22 @@ dependencies = [ ] [[package]] -name = "toml_edit" -version = "0.19.15" +name = "toml_datetime" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "a197c0ec7d131bfc6f7e82c8442ba1595aeab35da7adbf05b6b73cd06a16b6be" dependencies = [ - "indexmap 2.9.0", - "toml_datetime", - "winnow 0.5.15", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.9.0", - "toml_datetime", + "toml_datetime 0.6.8", "winnow 0.5.15", ] @@ -26355,10 +26540,31 @@ dependencies = [ "indexmap 2.9.0", "serde", "serde_spanned", - "toml_datetime", + "toml_datetime 0.6.8", "winnow 0.6.18", ] +[[package]] +name = "toml_edit" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ad0b7ae9cfeef5605163839cb9221f453399f15cfb5c10be9885fcf56611f9" +dependencies = [ + "indexmap 2.9.0", + "toml_datetime 0.7.1", + "toml_parser", + "winnow 0.7.10", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow 0.7.10", +] + [[package]] name = "tower" version = "0.4.13" @@ -26443,7 +26649,7 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -26484,8 +26690,8 @@ version = "5.0.0" dependencies = [ "assert_matches", "expander", - "proc-macro-crate 3.1.0", - "proc-macro2 1.0.95", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -26655,6 +26861,23 @@ name = "tungstenite" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand 0.9.0", + "sha1", + "thiserror 2.0.12", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" dependencies = [ "bytes", "data-encoding", @@ -27012,7 +27235,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_core 0.6.4", - "sha2 0.10.8", + "sha2 0.10.9", "sha3 0.10.8", "zeroize", ] @@ -27143,7 +27366,7 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", "wasm-bindgen-shared", @@ -27177,7 +27400,7 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", "wasm-bindgen-backend", @@ -27446,7 +27669,7 @@ dependencies = [ "log", "rustix 0.36.15", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "toml 0.5.11", "windows-sys 0.45.0", "zstd 0.11.2+zstd.1.5.2", @@ -27936,7 +28159,7 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -27947,7 +28170,7 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -28369,6 +28592,8 @@ dependencies = [ "polkadot-primitives", "polkadot-runtime-parachains", "sp-arithmetic 27.0.0", + "sp-consensus-aura", + "sp-consensus-slots", "sp-core 37.0.0", "sp-crypto-hashing 0.1.0", "sp-io 41.0.1", @@ -28409,7 +28634,7 @@ version = "11.0.2" dependencies = [ "Inflector", "frame-support", - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "staging-xcm", "syn 2.0.98", @@ -28543,9 +28768,9 @@ dependencies = [ [[package]] name = "yamux" -version = "0.13.5" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da1acad1c2dc53f0dde419115a38bd8221d8c3e47ae9aeceaf453266d29307e" +checksum = "deab71f2e20691b4728b349c6cee8fc7223880fa67b6b4f92225ec32225447e5" dependencies = [ "futures", "log", @@ -28658,7 +28883,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", "synstructure 0.13.1", @@ -28688,7 +28913,7 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -28699,7 +28924,7 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -28719,7 +28944,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", "synstructure 0.13.1", @@ -28740,7 +28965,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -28762,7 +28987,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ - "proc-macro2 1.0.95", + "proc-macro2 1.0.101", "quote 1.0.40", "syn 2.0.98", ] @@ -28822,7 +29047,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sp-core 35.0.0", "subxt 0.38.1", "subxt-signer 0.38.1", @@ -28866,7 +29091,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "sha2 0.10.8", + "sha2 0.10.9", "tar", "thiserror 1.0.65", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 5bbccd58af1b5..2524de8cb9497 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -626,6 +626,10 @@ Inflector = { version = "0.11.4" } aes-gcm = { version = "0.10" } ahash = { version = "0.8.2" } alloy-core = { version = "1.1.0", default-features = false } +# TODO: remove alloy-primitives and alloy-sol-types, they are re-exported in alloy-core +# but the version is different so it doesnt compile +alloy-primitives = { version = "0.4.2", default-features = false } +alloy-sol-types = { version = "0.4.2", default-features = false } always-assert = { version = "0.1" } anyhow = { version = "1.0.81", default-features = false } approx = { version = "0.5.1" } @@ -643,7 +647,6 @@ ark-ed-on-bls12-377-ext = { version = "0.4.1", default-features = false } ark-ed-on-bls12-381-bandersnatch = { version = "0.4.0", default-features = false } ark-ed-on-bls12-381-bandersnatch-ext = { version = "0.4.1", default-features = false } ark-scale = { version = "0.0.12", default-features = false } -ark-vrf = { version = "0.1.0", default-features = false } array-bytes = { version = "6.2.2", default-features = false } arrayvec = { version = "0.7.4" } assert_cmd = { version = "2.0.14" } @@ -671,7 +674,7 @@ blake2 = { version = "0.10.4", default-features = false } blake2b_simd = { version = "1.0.2", default-features = false } blake3 = { version = "1.5" } bn = { package = "substrate-bn", version = "0.6", default-features = false } -bounded-collections = { version = "0.2.3", default-features = false } +bounded-collections = { version = "0.3.2", default-features = false } bounded-vec = { version = "0.7" } bp-asset-hub-rococo = { path = "cumulus/parachains/runtimes/assets/asset-hub-rococo/bridge-primitives", default-features = false } bp-asset-hub-westend = { path = "cumulus/parachains/runtimes/assets/asset-hub-westend/bridge-primitives", default-features = false } @@ -892,7 +895,7 @@ linked-hash-map = { version = "0.5.4" } linked_hash_set = { version = "0.1.4" } linregress = { version = "0.5.1" } lite-json = { version = "0.2.0", default-features = false } -litep2p = { version = "0.9.5", features = ["websocket"] } +litep2p = { git = "https://github.com/moondance-labs/litep2p", branch = "girazoki-simple-dns-update-2", features = ["rsa", "websocket"] } log = { version = "0.4.22", default-features = false } macro_magic = { version = "0.5.1" } maplit = { version = "1.0.2" } @@ -1499,7 +1502,6 @@ overflow-checks = true # # This list is ordered alphabetically. [profile.dev.package] -ark-vrf = { opt-level = 3 } blake2 = { opt-level = 3 } blake2b_simd = { opt-level = 3 } chacha20poly1305 = { opt-level = 3 } diff --git a/bridges/primitives/header-chain/src/justification/verification/mod.rs b/bridges/primitives/header-chain/src/justification/verification/mod.rs index 9941537eb0953..9d9f6a36b5e8b 100644 --- a/bridges/primitives/header-chain/src/justification/verification/mod.rs +++ b/bridges/primitives/header-chain/src/justification/verification/mod.rs @@ -306,7 +306,9 @@ trait JustificationVerifier { justification.round, context.authority_set_id, &mut signature_buffer, - ) { + ) + .is_valid() + { self.process_invalid_signature_vote(precommit_idx).map_err(Error::Precommit)?; continue } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs b/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs index 56c9f2b4c37dc..093f354a92381 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs @@ -118,6 +118,7 @@ mod benchmarks { capella: Fork { version: hex!("03000000"), epoch: 0 }, deneb: Fork { version: hex!("04000000"), epoch: 0 }, electra: Fork { version: hex!("05000000"), epoch: 80000000000 }, + fulu: Fork { version: hex!("06000000"), epoch: 80000000001 }, }; let finalized_root_gindex = EthereumBeaconClient::::finalized_root_gindex_at_slot( update.attested_header.slot, diff --git a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs index 6ec613840abf7..8ed00b82a451e 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs @@ -26,7 +26,7 @@ pub mod weights; #[cfg(any(test, feature = "fuzzing"))] pub mod mock; -#[cfg(test)] +#[cfg(any(test, feature = "fuzzing"))] pub mod mock_electra; #[cfg(test)] @@ -633,6 +633,9 @@ pub mod pallet { /// Returns the fork version based on the current epoch. pub(super) fn select_fork_version(fork_versions: &ForkVersions, epoch: u64) -> ForkVersion { + if epoch >= fork_versions.fulu.epoch { + return fork_versions.fulu.version + } if epoch >= fork_versions.electra.epoch { return fork_versions.electra.version } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs index 9eec93f98c855..7032e5949f860 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs @@ -8,6 +8,7 @@ use snowbridge_beacon_primitives::{Fork, ForkVersions}; use snowbridge_verification_primitives::{Log, Proof}; use sp_std::default::Default; use std::{fs::File, path::PathBuf}; +use hex_literal::hex; type Block = frame_system::mocking::MockBlock; use frame_support::traits::ConstU32; @@ -114,28 +115,32 @@ impl pallet_timestamp::Config for Test { parameter_types! { pub const ChainForkVersions: ForkVersions = ForkVersions { genesis: Fork { - version: [0, 0, 0, 0], // 0x00000000 + version: hex!("00000000"), // 0x00000000 epoch: 0, }, altair: Fork { - version: [1, 0, 0, 0], // 0x01000000 + version: hex!("01000000"), // 0x01000000 epoch: 0, }, bellatrix: Fork { - version: [2, 0, 0, 0], // 0x02000000 + version: hex!("02000000"), // 0x02000000 epoch: 0, }, capella: Fork { - version: [3, 0, 0, 0], // 0x03000000 + version: hex!("03000000"), // 0x03000000 epoch: 0, }, deneb: Fork { - version: [4, 0, 0, 0], // 0x04000000 + version: hex!("04000000"), // 0x04000000 epoch: 0, }, electra: Fork { - version: [5, 0, 0, 0], // 0x05000000 - epoch: 80000000000, + version: hex!("05000000"), + epoch: 0, + }, + fulu: Fork { + version: hex!("06000000"), + epoch: 100000000, } }; } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/mock_electra.rs b/bridges/snowbridge/pallets/ethereum-client/src/mock_electra.rs index d0c9f5bd9eaef..f80983e7858e3 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/mock_electra.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/mock_electra.rs @@ -116,6 +116,10 @@ parameter_types! { electra: Fork { version: [5, 0, 0, 0], // 0x05000000 epoch: 0, + }, + fulu: Fork { + version: [6, 0, 0, 0], + epoch: 800000000000, } }; } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs index fd27505a547c1..38329f61329fb 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs @@ -230,6 +230,7 @@ fn compute_fork_version() { capella: Fork { version: [0, 0, 0, 3], epoch: 30 }, deneb: Fork { version: [0, 0, 0, 4], epoch: 40 }, electra: Fork { version: [0, 0, 0, 5], epoch: 50 }, + fulu: Fork { version: [0, 0, 0, 6], epoch: 60 }, }; new_tester().execute_with(|| { assert_eq!(EthereumBeaconClient::select_fork_version(&mock_fork_versions, 0), [0, 0, 0, 0]); diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml b/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml index 359524ee98319..8e57965cc559e 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml +++ b/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml @@ -28,9 +28,10 @@ log = { workspace = true } pallet-balances.workspace = true scale-info = { features = ["derive"], workspace = true } serde = { optional = true, workspace = true, default-features = true } -snowbridge-beacon-primitives.workspace = true -snowbridge-core.workspace = true -snowbridge-inbound-queue-primitives.workspace = true + +snowbridge-beacon-primitives = { workspace = true } +snowbridge-core = { workspace = true } +snowbridge-inbound-queue-primitives = { workspace = true } snowbridge-pallet-inbound-queue-v2-fixtures = { optional = true, workspace = true } sp-core.workspace = true sp-io.workspace = true @@ -47,6 +48,9 @@ hex-literal = { workspace = true, default-features = true } snowbridge-test-utils = { workspace = true } sp-keyring = { default-features = true, workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } + [features] default = ["std"] std = [ diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index 9c85d73ea3f4b..5fd0c085d9306 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -38,21 +38,21 @@ mod test; pub use crate::weights::WeightInfo; use bp_relayers::RewardLedger; use frame_system::ensure_signed; +use frame_support::dispatch::PostDispatchInfo; use snowbridge_core::{ reward::{AddTip, AddTipError}, sparse_bitmap::{SparseBitmap, SparseBitmapImpl}, BasicOperatingMode, }; use snowbridge_inbound_queue_primitives::{ - v2::{ConvertMessage, ConvertMessageError, Message}, + v2::{ConvertMessageError, Message, MessageProcessor, MessageProcessorError}, EventProof, VerificationError, Verifier, }; use sp_core::H160; -use sp_runtime::traits::TryConvert; use sp_std::prelude::*; -use xcm::prelude::{ExecuteXcm, Junction::*, Location, SendXcm, *}; #[cfg(feature = "runtime-benchmarks")] use {snowbridge_beacon_primitives::BeaconHeader, sp_core::H256}; +use xcm::latest::SendError; pub use pallet::*; @@ -83,17 +83,11 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The verifier for inbound messages from Ethereum. type Verifier: Verifier; - /// XCM message sender. - type XcmSender: SendXcm; - /// Handler for XCM fees. - type XcmExecutor: ExecuteXcm; /// Address of the Gateway contract. #[pallet::constant] type GatewayAddress: Get; - /// AssetHub parachain ID. - type AssetHubParaId: Get; - /// Convert a command from Ethereum to an XCM message. - type MessageConverter: ConvertMessage; + /// Process the message that was submitted. + type MessageProcessor: MessageProcessor; #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; /// Reward discriminator type. @@ -103,8 +97,6 @@ pub mod pallet { type DefaultRewardKind: Get; /// Relayer reward payment. type RewardPayment: RewardLedger; - /// AccountId to Location converter - type AccountToLocation: for<'a> TryConvert<&'a Self::AccountId, Location>; type WeightInfo: WeightInfo; } @@ -126,22 +118,10 @@ pub mod pallet { pub enum Error { /// Message came from an invalid outbound channel on the Ethereum side. InvalidGateway, - /// Account could not be converted to bytes - InvalidAccount, /// Message has an invalid envelope. InvalidMessage, /// Message has an unexpected nonce. InvalidNonce, - /// Fee provided is invalid. - InvalidFee, - /// Message has an invalid payload. - InvalidPayload, - /// Message channel is invalid - InvalidChannel, - /// The max nonce for the type has been reached - MaxNonceReached, - /// Cannot convert location - InvalidAccountConversion, /// Invalid network specified InvalidNetwork, /// Pallet is halted @@ -201,8 +181,8 @@ pub mod pallet { impl Pallet { /// Submit an inbound message originating from the Gateway contract on Ethereum #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::submit())] - pub fn submit(origin: OriginFor, event: Box) -> DispatchResult { + #[pallet::weight(T::WeightInfo::submit().saturating_add(T::MessageProcessor::worst_case_message_processor_weight()))] + pub fn submit(origin: OriginFor, event: Box) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; ensure!(!OperatingMode::::get().is_halted(), Error::::Halted); @@ -232,7 +212,7 @@ pub mod pallet { } impl Pallet { - pub fn process_message(relayer: T::AccountId, message: Message) -> DispatchResult { + pub fn process_message(relayer: T::AccountId, message: Message) -> DispatchResultWithPostInfo { // Verify that the message was submitted from the known Gateway contract ensure!(T::GatewayAddress::get() == message.gateway, Error::::InvalidGateway); @@ -241,58 +221,36 @@ pub mod pallet { // Verify the message has not been processed ensure!(!Nonce::::get(nonce), Error::::InvalidNonce); - let xcm = - T::MessageConverter::convert(message).map_err(|error| Error::::from(error))?; - - // Forward XCM to AH - let dest = Location::new(1, [Parachain(T::AssetHubParaId::get())]); - // Mark message as received Nonce::::set(nonce); - let message_id = - Self::send_xcm(dest.clone(), &relayer, xcm.clone()).map_err(|error| { - tracing::error!(target: LOG_TARGET, ?error, ?dest, ?xcm, "XCM send failed with error"); - Error::::from(error) + let (message_id, maybe_corrected_weight) = T::MessageProcessor::process_message(relayer.clone(), message) + .map_err(|e| match e { + MessageProcessorError::ProcessMessage(e) => e, + MessageProcessorError::ConvertMessage(e) => Error::::from(e).into(), + MessageProcessorError::SendMessage(e) => Error::::from(e).into(), })?; // Pay relayer reward - if !relayer_fee.is_zero() { - T::RewardPayment::register_reward( - &relayer, - T::DefaultRewardKind::get(), - relayer_fee, - ); + let tip = Tips::::take(nonce).unwrap_or_default(); + let total_tip = relayer_fee.saturating_add(tip); + if total_tip > 0 { + T::RewardPayment::register_reward(&relayer, T::DefaultRewardKind::get(), total_tip); } + // Emit event with the message_id Self::deposit_event(Event::MessageReceived { nonce, message_id }); - Ok(()) - } - - fn send_xcm( - dest: Location, - fee_payer: &T::AccountId, - xcm: Xcm<()>, - ) -> Result { - let (ticket, fee) = validate_send::(dest, xcm)?; - let fee_payer = T::AccountToLocation::try_convert(fee_payer).map_err(|err| { - tracing::error!( - target: LOG_TARGET, - ?err, - "Failed to convert account to XCM location", - ); - SendError::NotApplicable - })?; - T::XcmExecutor::charge_fees(fee_payer.clone(), fee.clone()).map_err(|error| { - tracing::error!( - target: LOG_TARGET, - ?error, - "Charging fees failed with error", - ); - SendError::Fees - })?; - T::XcmSender::deliver(ticket) + if let Some(corrected_weight) = maybe_corrected_weight { + Ok(PostDispatchInfo { + actual_weight: Some(corrected_weight.saturating_add(T::WeightInfo::submit())), + ..Default::default() + }) + } + else { + // Pays fees and non-corrected-weight + Ok(().into()) + } } } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index b1ea349b4a081..78d63016358ea 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -3,16 +3,19 @@ use super::*; use crate::{self as inbound_queue_v2}; -use frame_support::{derive_impl, parameter_types, traits::ConstU32}; +use frame_support::{derive_impl, parameter_types}; use hex_literal::hex; use snowbridge_beacon_primitives::{ types::deneb, BeaconHeader, ExecutionProof, VersionedExecutionPayloadHeader, }; -use snowbridge_core::TokenId; -use snowbridge_inbound_queue_primitives::{v2::MessageToXcm, Log, Proof, VerificationError}; +use snowbridge_core::{ParaId, TokenId}; +use snowbridge_inbound_queue_primitives::{ + v2::{MessageProcessorError, CreateAssetCallInfo, MessageToXcm, XcmMessageProcessor}, + Log, Proof, VerificationError, +}; use sp_core::H160; use sp_runtime::{ - traits::{IdentityLookup, MaybeConvert}, + traits::{IdentityLookup, MaybeConvert, TryConvert}, BuildStorage, }; use sp_std::{convert::From, default::Default, marker::PhantomData}; @@ -91,41 +94,77 @@ impl<'a, AccountId: Clone + Clone> TryConvert<&'a AccountId, Location> } parameter_types! { - pub const EthereumNetwork: xcm::v5::NetworkId = xcm::v5::NetworkId::Ethereum { chain_id: 11155111 }; + pub const EthereumNetwork: NetworkId = Ethereum { chain_id: 11155111 }; pub const GatewayAddress: H160 = H160(GATEWAY_ADDRESS); pub InboundQueueLocation: InteriorLocation = [PalletInstance(84)].into(); - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1002)].into(); - pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)),Parachain(1000)]); - pub AssetHubUniversalLocation: InteriorLocation = [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000)].into(); pub SnowbridgeReward: BridgeReward = BridgeReward::Snowbridge; - pub const CreateAssetCall: [u8;2] = [53, 0]; + pub const CreateAssetCallIndex: [u8;2] = [53, 0]; pub const CreateAssetDeposit: u128 = 10_000_000_000u128; + pub const LocalNetwork: NetworkId = ByGenesis(WESTEND_GENESIS_HASH); + pub CreateAssetCall: CreateAssetCallInfo = CreateAssetCallInfo{call: CreateAssetCallIndex::get(),deposit: CreateAssetDeposit::get(),min_balance:1}; + pub AssetHubParaId: ParaId = ParaId::from(1000); + pub TargetLocation: Location = Location::new(1, [Parachain(AssetHubParaId::get().into())]); +} + +pub struct DummyPrefix; + +impl MessageProcessor for DummyPrefix { + fn can_process_message(_relayer: &AccountId, _message: &Message) -> bool { + false + } + + fn process_message( + _relayer: AccountId, + _message: Message, + ) -> Result<[u8; 32], MessageProcessorError> { + panic!("DummyPrefix::process_message shouldn't be called"); + } +} + +pub struct DummySuffix; + +impl MessageProcessor for DummySuffix { + fn can_process_message(_relayer: &AccountId, _message: &Message) -> bool { + true + } + + fn process_message( + _relayer: AccountId, + _message: Message, + ) -> Result<[u8; 32], MessageProcessorError> { + panic!("DummySuffix::process_message shouldn't be called"); + } } impl inbound_queue_v2::Config for Test { type RuntimeEvent = RuntimeEvent; type Verifier = MockVerifier; - type XcmSender = MockXcmSender; - type XcmExecutor = MockXcmExecutor; type GatewayAddress = GatewayAddress; - type AssetHubParaId = ConstU32<1000>; - type MessageConverter = MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - EthereumNetwork, - InboundQueueLocation, - MockTokenIdConvert, - GatewayAddress, - UniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, - AccountId, - >; + // Passively test that the implementation of MessageProcessor trait works correctly for tuple + type MessageProcessor = ( + DummyPrefix, + XcmMessageProcessor< + Test, + MockXcmSender, + MockXcmExecutor, + MessageToXcm< + CreateAssetCall, + EthereumNetwork, + LocalNetwork, + GatewayAddress, + InboundQueueLocation, + AssetHubParaId, + MockTokenIdConvert, + AccountId, + >, + MockAccountLocationConverter, + TargetLocation, + >, + DummySuffix, + ); #[cfg(feature = "runtime-benchmarks")] type Helper = Test; type WeightInfo = (); - type AccountToLocation = MockAccountLocationConverter; type RewardKind = BridgeReward; type DefaultRewardKind = SnowbridgeReward; type RewardPayment = MockRewardLedger; diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs index 0e66dd4d33d5f..fe08daeaba803 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs @@ -5,13 +5,14 @@ use super::*; use crate::{mock::*, Error}; use codec::Encode; use frame_support::{assert_noop, assert_ok}; -use snowbridge_inbound_queue_primitives::{v2::XcmPayload, EventProof, Proof}; +use snowbridge_inbound_queue_primitives::{v2::Payload, EventProof, Proof}; use snowbridge_test_utils::{ - mock_rewards::RegisteredRewardsCount, + mock_rewards::{RegisteredRewardAmount, RegisteredRewardsCount}, mock_xcm::{set_charge_fees_override, set_sender_override}, }; use sp_keyring::sr25519::Keyring; use sp_runtime::DispatchError; +use xcm::prelude::*; #[test] fn test_submit_happy_path() { @@ -334,7 +335,7 @@ fn zero_reward_does_not_register_reward() { Message { nonce: 0, assets: vec![], - xcm: XcmPayload::Raw(vec![]), + payload: Payload::Raw(vec![]), claimer: None, execution_fee: 1_000_000_000, relayer_fee: 0, @@ -389,3 +390,141 @@ fn test_add_tip_amount_zero() { assert_eq!(Tips::::get(nonce), None); }); } + +#[test] +fn inbound_tip_is_paid_out_to_relayer() { + new_tester().execute_with(|| { + let nonce: u64 = 77; + let tip: u128 = 12_345; + let relayer_fee: u128 = 2_000; + + // Add tip for nonce before message is processed + assert_ok!(InboundQueue::add_tip(nonce, tip)); + assert_eq!(Tips::::get(nonce), Some(tip)); + + // Process inbound message with relayer_fee + let relayer: AccountId = Keyring::Bob.into(); + assert_ok!(InboundQueue::process_message( + relayer, + Message { + nonce, + assets: vec![], + payload: Payload::Raw(vec![]), + claimer: None, + execution_fee: 1_000_000_000, + relayer_fee, + gateway: mock::GatewayAddress::get(), + origin: H160::random(), + value: 3_000_000_000, + }, + )); + + // Reward should be registered from relayer_fee + tip + assert_eq!( + RegisteredRewardsCount::get(), + 1, + "Reward should be registered from relayer_fee + tip" + ); + + // Check the actual reward amount paid out (should be relayer_fee + tip) + assert_eq!( + RegisteredRewardAmount::get(), + relayer_fee + tip, + "Reward amount should equal relayer_fee + tip" + ); + + // Tip should be consumed from storage + assert_eq!(Tips::::get(nonce), None); + }); +} + +#[test] +fn relayer_fee_paid_out_when_no_tip_exists() { + new_tester().execute_with(|| { + let nonce: u64 = 88; + let relayer_fee: u128 = 5_000; + + // Ensure no tip exists for this nonce + assert_eq!(Tips::::get(nonce), None); + + // Process inbound message with relayer_fee but no tip + let relayer: AccountId = Keyring::Bob.into(); + assert_ok!(InboundQueue::process_message( + relayer, + Message { + nonce, + assets: vec![], + payload: Payload::Raw(vec![]), + claimer: None, + execution_fee: 1_000_000_000, + relayer_fee, + gateway: mock::GatewayAddress::get(), + origin: H160::random(), + value: 3_000_000_000, + }, + )); + + // Relayer fee should be paid out even without tip + assert_eq!( + RegisteredRewardsCount::get(), + 1, + "Relayer fee should be paid out even when no tip exists" + ); + + // Check the actual reward amount paid out + assert_eq!( + RegisteredRewardAmount::get(), + relayer_fee, + "Reward amount should equal relayer_fee when no tip exists" + ); + + // Confirm no tip storage was affected + assert_eq!(Tips::::get(nonce), None); + }); +} + +#[test] +fn tip_paid_out_when_no_relayer_fee() { + new_tester().execute_with(|| { + let nonce: u64 = 99; + let tip: u128 = 8_500; + + // Add tip for nonce before message is processed + assert_ok!(InboundQueue::add_tip(nonce, tip)); + assert_eq!(Tips::::get(nonce), Some(tip)); + + // Process inbound message with zero relayer_fee but with tip + let relayer: AccountId = Keyring::Bob.into(); + assert_ok!(InboundQueue::process_message( + relayer, + Message { + nonce, + assets: vec![], + payload: Payload::Raw(vec![]), + claimer: None, + execution_fee: 1_000_000_000, + relayer_fee: 0, + gateway: mock::GatewayAddress::get(), + origin: H160::random(), + value: 3_000_000_000, + }, + )); + + // Tip should be paid out even without relayer fee + assert_eq!( + RegisteredRewardsCount::get(), + 1, + "Tip should be paid out even when relayer_fee is 0" + ); + + // Check the actual reward amount paid out (should be just the tip) + assert_eq!( + RegisteredRewardAmount::get(), + tip, + "Reward amount should equal tip when relayer_fee is 0" + ); + + // Tip should be consumed from storage + assert_eq!(Tips::::get(nonce), None); + }); +} diff --git a/bridges/snowbridge/pallets/inbound-queue/src/benchmarking/mod.rs b/bridges/snowbridge/pallets/inbound-queue/src/benchmarking/mod.rs index d694436d9cff3..e32047a9433c0 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/benchmarking/mod.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/benchmarking/mod.rs @@ -16,12 +16,7 @@ mod benchmarks { fn submit() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); - let create_message = make_register_token_message(); - - T::Helper::initialize_storage( - create_message.finalized_header, - create_message.block_roots_root, - ); + let create_message = T::Helper::initialize_storage(); let sovereign_account = sibling_sovereign_account::(1000u32.into()); diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 440572958adc1..0613b729b2e69 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -23,8 +23,6 @@ //! parachain. #![cfg_attr(not(feature = "std"), no_std)] -mod envelope; - #[cfg(feature = "runtime-benchmarks")] mod benchmarking; @@ -35,10 +33,11 @@ mod mock; #[cfg(test)] mod test; +pub mod xcm_message_processor; -use codec::{Decode, DecodeAll, Encode}; -use envelope::Envelope; +use codec::{Decode, Encode}; use frame_support::{ + pallet_prelude::DispatchResult, traits::{ fungible::{Inspect, Mutate}, tokens::{Fortitude, Preservation}, @@ -48,7 +47,7 @@ use frame_support::{ }; use frame_system::ensure_signed; use scale_info::TypeInfo; -use sp_core::H160; +use sp_core::{H160, H256}; use sp_runtime::traits::Zero; use sp_std::vec; use xcm::prelude::{ @@ -61,16 +60,20 @@ use snowbridge_core::{ StaticLookup, }; use snowbridge_inbound_queue_primitives::{ - v1::{ConvertMessage, ConvertMessageError, VersionedMessage}, + v1::{ConvertMessage, ConvertMessageError, VersionedXcmMessage}, EventProof, VerificationError, Verifier, }; use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError}; +use snowbridge_inbound_queue_primitives::v1::{Envelope, MessageProcessor}; + pub use weights::WeightInfo; #[cfg(feature = "runtime-benchmarks")] use snowbridge_beacon_primitives::BeaconHeader; +#[cfg(feature = "runtime-benchmarks")] +use snowbridge_inbound_queue_primitives::EventFixture; type BalanceOf = <::Token as Inspect<::AccountId>>::Balance; @@ -79,20 +82,54 @@ pub use pallet::*; pub const LOG_TARGET: &str = "snowbridge-inbound-queue"; +pub trait RewardProcessor { + fn process_reward(who: T::AccountId, channel: Channel, message: EventProof) -> DispatchResult; +} + +impl RewardProcessor for () { + fn process_reward( + _who: T::AccountId, + _channel: Channel, + _message: EventProof, + ) -> DispatchResult { + Ok(()) + } +} + +pub struct RewardThroughSovereign(sp_std::marker::PhantomData); + +impl RewardProcessor for RewardThroughSovereign { + fn process_reward(who: T::AccountId, channel: Channel, event: EventProof) -> DispatchResult { + let length = event.encode().len() as u32; + let delivery_cost = pallet::Pallet::::calculate_delivery_cost(length); + let sovereign_account: T::AccountId = sibling_sovereign_account::(channel.para_id); + + let amount = T::Token::reducible_balance( + &sovereign_account, + Preservation::Preserve, + Fortitude::Polite, + ) + .min(delivery_cost); + if !amount.is_zero() { + T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; + } + + Ok(()) + } +} #[frame_support::pallet] pub mod pallet { use super::*; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use sp_core::H256; #[pallet::pallet] pub struct Pallet(_); #[cfg(feature = "runtime-benchmarks")] pub trait BenchmarkHelper { - fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256); + fn initialize_storage() -> EventFixture; } #[pallet::config] @@ -141,6 +178,12 @@ pub mod pallet { /// To withdraw and deposit an asset. type AssetTransactor: TransactAsset; + + /// Process the message that was submitted + type MessageProcessor: MessageProcessor; + + /// Process the reward to the relayer + type RewardProcessor: RewardProcessor; } #[pallet::hooks] @@ -265,48 +308,8 @@ pub mod pallet { } })?; - // Reward relayer from the sovereign account of the destination parachain, only if funds - // are available - let sovereign_account = sibling_sovereign_account::(channel.para_id); - let delivery_cost = Self::calculate_delivery_cost(event.encode().len() as u32); - let amount = T::Token::reducible_balance( - &sovereign_account, - Preservation::Preserve, - Fortitude::Polite, - ) - .min(delivery_cost); - if !amount.is_zero() { - T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; - } - - // Decode payload into `VersionedMessage` - let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref()) - .map_err(|_| Error::::InvalidPayload)?; - - // Decode message into XCM - let (xcm, fee) = Self::do_convert(envelope.message_id, message.clone())?; - - log::info!( - target: LOG_TARGET, - "💫 xcm decoded as {:?} with fee {:?}", - xcm, - fee - ); - - // Burning fees for teleport - Self::burn_fees(channel.para_id, fee)?; - - // Attempt to send XCM to a dest parachain - let message_id = Self::send_xcm(xcm, channel.para_id)?; - - Self::deposit_event(Event::MessageReceived { - channel_id: envelope.channel_id, - nonce: envelope.nonce, - message_id, - fee_burned: fee, - }); - - Ok(()) + T::RewardProcessor::process_reward(who, channel.clone(), event)?; + T::MessageProcessor::process_message(channel, envelope) } /// Halt or resume all pallet operations. May only be called by root. @@ -326,7 +329,7 @@ pub mod pallet { impl Pallet { pub fn do_convert( message_id: H256, - message: VersionedMessage, + message: VersionedXcmMessage, ) -> Result<(Xcm<()>, BalanceOf), Error> { let (xcm, fee) = T::MessageConverter::convert(message_id, message) .map_err(|e| Error::::ConvertMessage(e))?; diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index 4a81e4243c12d..7fc94808753ac 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -2,7 +2,19 @@ // SPDX-FileCopyrightText: 2023 Snowfork use super::*; -use frame_support::{derive_impl, parameter_types, traits::ConstU32, weights::IdentityFee}; +use crate::{ + xcm_message_processor::XcmMessageProcessor, + {self as inbound_queue}, +}; +use frame_support::{ + derive_impl, parameter_types, + storage::Key, + traits::{ + tokens::{Fortitude, Preservation}, + ConstU32, + }, + weights::IdentityFee, +}; use hex_literal::hex; use snowbridge_beacon_primitives::{ types::deneb, BeaconHeader, ExecutionProof, Fork, ForkVersions, VersionedExecutionPayloadHeader, @@ -11,10 +23,11 @@ use snowbridge_core::{ gwei, meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, TokenId, }; use snowbridge_inbound_queue_primitives::{v1::MessageToXcm, Log, Proof, VerificationError}; -use sp_core::{H160, H256}; +use sp_core::{Get, H160, H256}; +use sp_keyring::AccountKeyring as Keyring; use sp_runtime::{ - traits::{IdentifyAccount, IdentityLookup, MaybeConvert, Verify}, - BuildStorage, FixedU128, MultiSignature, + traits::{IdentifyAccount, IdentityLookup, MaybeConvert, MaybeEquivalence, Verify, Zero}, + BuildStorage, DispatchError, FixedU128, MultiSignature, }; use sp_std::{convert::From, default::Default}; use xcm::{ @@ -23,8 +36,6 @@ use xcm::{ }; use xcm_executor::AssetsInHolding; -use crate::{self as inbound_queue}; - type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( @@ -64,28 +75,32 @@ impl pallet_balances::Config for Test { parameter_types! { pub const ChainForkVersions: ForkVersions = ForkVersions { genesis: Fork { - version: [0, 0, 0, 1], // 0x00000001 + version: hex!("00000001"), epoch: 0, }, altair: Fork { - version: [1, 0, 0, 1], // 0x01000001 + version: hex!("01000001"), epoch: 0, }, bellatrix: Fork { - version: [2, 0, 0, 1], // 0x02000001 + version: hex!("02000001"), epoch: 0, }, capella: Fork { - version: [3, 0, 0, 1], // 0x03000001 + version: hex!("03000001"), epoch: 0, }, deneb: Fork { - version: [4, 0, 0, 1], // 0x04000001 + version: hex!("04000001"), epoch: 0, }, electra: Fork { - version: [5, 0, 0, 0], // 0x05000000 + version: hex!("05000000"), epoch: 80000000000, + }, + fulu: Fork { + version: hex!("06000000"), + epoch: 80000000001, } }; } @@ -220,6 +235,30 @@ impl MaybeConvert for MockTokenIdConvert { } } +pub struct DummyPrefix; + +impl MessageProcessor for DummyPrefix { + fn can_process_message(_channel: &Channel, _envelope: &Envelope) -> bool { + false + } + + fn process_message(_channel: Channel, _envelope: Envelope) -> Result<(), DispatchError> { + panic!("DummyPrefix::process_message shouldn't be called"); + } +} + +pub struct DummySuffix; + +impl MessageProcessor for DummySuffix { + fn can_process_message(_channel: &Channel, _envelope: &Envelope) -> bool { + true + } + + fn process_message(_channel: Channel, _envelope: Envelope) -> Result<(), DispatchError> { + panic!("DummySuffix::process_message shouldn't be called"); + } +} + impl inbound_queue::Config for Test { type RuntimeEvent = RuntimeEvent; type Verifier = MockVerifier; @@ -245,6 +284,9 @@ impl inbound_queue::Config for Test { type LengthToFee = IdentityFee; type MaxMessageSize = ConstU32<1024>; type AssetTransactor = SuccessfulTransactor; + type MessageProcessor = (DummyPrefix, XcmMessageProcessor, DummySuffix); // We are passively testing if implementation of MessageProcessor trait works correctly for + // tuple + type RewardProcessor = RewardThroughSovereign; } pub fn setup() { diff --git a/bridges/snowbridge/pallets/inbound-queue/src/test.rs b/bridges/snowbridge/pallets/inbound-queue/src/test.rs index 60065ccb15b24..ef4ae7261bbc4 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/test.rs @@ -22,8 +22,8 @@ fn test_submit_happy_path() { let origin = RuntimeOrigin::signed(relayer.clone()); - // Submit event proof - let event = EventProof { + // Submit message + let message = Message { event_log: mock_event_log(), proof: Proof { receipt_proof: Default::default(), @@ -72,8 +72,8 @@ fn test_submit_xcm_invalid_channel() { println!("account: {}", sovereign_account); let _ = Balances::mint_into(&sovereign_account, 10000); - // Submit event proof - let event = EventProof { + // Submit message + let message = Message { event_log: mock_event_log_invalid_channel(), proof: Proof { receipt_proof: Default::default(), @@ -97,8 +97,8 @@ fn test_submit_with_invalid_gateway() { let sovereign_account = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); let _ = Balances::mint_into(&sovereign_account, 10000); - // Submit event proof - let event = EventProof { + // Submit message + let message = Message { event_log: mock_event_log_invalid_gateway(), proof: Proof { receipt_proof: Default::default(), @@ -237,7 +237,7 @@ fn test_submit_no_funds_to_reward_relayers_and_ed_preserved() { execution_proof: mock_execution_proof(), }, }; - assert_ok!(InboundQueue::submit(origin.clone(), event.clone())); + assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); // Check balance of sovereign account as ED does not change let amount = Balances::balance(&sovereign_account); assert_eq!(amount, ExistentialDeposit::get()); diff --git a/bridges/snowbridge/pallets/inbound-queue/src/xcm_message_processor.rs b/bridges/snowbridge/pallets/inbound-queue/src/xcm_message_processor.rs new file mode 100644 index 0000000000000..17519f66be6ec --- /dev/null +++ b/bridges/snowbridge/pallets/inbound-queue/src/xcm_message_processor.rs @@ -0,0 +1,47 @@ +use crate::{Error, Event, LOG_TARGET}; +use codec::DecodeAll; +use core::marker::PhantomData; +use snowbridge_core::Channel; +use snowbridge_inbound_queue_primitives::v1::{Envelope, MessageProcessor, VersionedXcmMessage}; +use sp_runtime::DispatchError; + +pub struct XcmMessageProcessor(PhantomData); + +impl MessageProcessor for XcmMessageProcessor +where + T: crate::Config, +{ + fn can_process_message(_channel: &Channel, envelope: &Envelope) -> bool { + VersionedXcmMessage::decode_all(&mut envelope.payload.as_ref()).is_ok() + } + + fn process_message(channel: Channel, envelope: Envelope) -> Result<(), DispatchError> { + // Decode message into XCM + let (xcm, fee) = match VersionedXcmMessage::decode_all(&mut envelope.payload.as_ref()) { + Ok(message) => crate::Pallet::::do_convert(envelope.message_id, message)?, + Err(_) => return Err(Error::::InvalidPayload.into()), + }; + + log::info!( + target: LOG_TARGET, + "💫 xcm decoded as {:?} with fee {:?}", + xcm, + fee + ); + + // Burning fees for teleport + crate::Pallet::::burn_fees(channel.para_id, fee)?; + + // Attempt to send XCM to a dest parachain + let message_id = crate::Pallet::::send_xcm(xcm, channel.para_id)?; + + crate::Pallet::::deposit_event(Event::MessageReceived { + channel_id: envelope.channel_id, + nonce: envelope.nonce, + message_id, + fee_burned: fee, + }); + + Ok(()) + } +} diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml index 95a7a8fab7908..37c0e5ea3c065 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-outbound-queue-v2" description = "Snowbridge Outbound Queue Pallet V2" -version = "0.3.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -19,32 +19,35 @@ exclude-from-umbrella = true [dependencies] alloy-core = { workspace = true, features = ["sol-types"] } -bp-relayers.workspace = true -bridge-hub-common.workspace = true codec = { features = ["derive"], workspace = true } ethabi = { workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support.workspace = true -frame-system.workspace = true hex-literal = { workspace = true, default-features = true } scale-info = { features = ["derive"], workspace = true } serde = { features = ["alloc", "derive"], workspace = true } -snowbridge-beacon-primitives.workspace = true -snowbridge-core.workspace = true -snowbridge-merkle-tree.workspace = true -snowbridge-outbound-queue-primitives.workspace = true -snowbridge-verification-primitives.workspace = true -sp-arithmetic.workspace = true -sp-core.workspace = true -sp-io.workspace = true -sp-runtime.workspace = true -sp-std.workspace = true -xcm-builder.workspace = true -xcm-executor.workspace = true -xcm.workspace = true + +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-arithmetic = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +bp-relayers = { workspace = true } + +snowbridge-beacon-primitives = { workspace = true } +snowbridge-core = { workspace = true } +snowbridge-merkle-tree = { workspace = true } +snowbridge-outbound-queue-primitives = { workspace = true } +snowbridge-verification-primitives = { workspace = true } + +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } [dev-dependencies] -pallet-message-queue = { workspace = true, default-features = false } +pallet-message-queue = { workspace = true } snowbridge-test-utils = { workspace = true } [features] @@ -52,7 +55,6 @@ default = ["std"] std = [ "alloy-core/std", "bp-relayers/std", - "bridge-hub-common/std", "codec/std", "ethabi/std", "frame-benchmarking/std", @@ -76,7 +78,6 @@ std = [ "xcm/std", ] runtime-benchmarks = [ - "bridge-hub-common/runtime-benchmarks", "frame-benchmarking", "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", @@ -94,4 +95,4 @@ try-runtime = [ "frame-system/try-runtime", "pallet-message-queue/try-runtime", "sp-runtime/try-runtime", -] +] \ No newline at end of file diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/benchmarking.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/benchmarking.rs index 68820c1078510..e568b50cc98ee 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/benchmarking.rs @@ -3,7 +3,6 @@ use super::*; use crate::fixture::make_submit_delivery_receipt_message; -use bridge_hub_common::AggregateMessageOrigin; use codec::Encode; use frame_benchmarking::v2::*; use frame_support::{traits::Hooks, BoundedVec}; @@ -77,7 +76,7 @@ mod benchmarks { #[benchmark] fn do_process_message() -> Result<(), BenchmarkError> { let (enqueued_message, _) = build_message::(); - let origin = AggregateMessageOrigin::SnowbridgeV2([1; 32].into()); + let origin = T::AggregateMessageOrigin::from([1; 32].into()); let message = enqueued_message.encode(); #[block] @@ -135,7 +134,7 @@ mod benchmarks { #[benchmark] fn process() -> Result<(), BenchmarkError> { initialize_worst_case::(); - let origin = AggregateMessageOrigin::SnowbridgeV2([1; 32].into()); + let origin = T::AggregateMessageOrigin::from([1; 32].into()); let (enqueued_message, _) = build_message::(); let message = enqueued_message.encode(); @@ -143,7 +142,7 @@ mod benchmarks { { OutboundQueue::::on_initialize(1_u32.into()); for _ in 0..T::MaxMessagesPerBlock::get() { - OutboundQueue::::do_process_message(origin, &message).unwrap(); + OutboundQueue::::do_process_message(origin.clone(), &message).unwrap(); } OutboundQueue::::commit(); } diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs index b0c2708a0e096..0057ec4604634 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs @@ -72,14 +72,14 @@ use alloy_core::{ sol_types::SolValue, }; use bp_relayers::RewardLedger; -use bridge_hub_common::{AggregateMessageOrigin, CustomDigestItem}; -use codec::Decode; +use codec::{Decode, FullCodec}; use frame_support::{ storage::StorageStreamIter, traits::{tokens::Balance, EnqueueMessage, Get, ProcessMessageError}, weights::{Weight, WeightToFee}, }; use snowbridge_core::{ + digest_item::SnowbridgeDigestItem, reward::{AddTip, AddTipError}, BasicOperatingMode, }; @@ -93,11 +93,11 @@ use snowbridge_outbound_queue_primitives::{ }; use sp_core::{H160, H256}; use sp_runtime::{ - traits::{BlockNumberProvider, Hash}, + traits::{BlockNumberProvider, Debug, Hash}, DigestItem, }; use sp_std::prelude::*; -pub use types::{PendingOrder, ProcessMessageOriginOf}; +pub use types::{OnNewCommitment, PendingOrder, ProcessMessageOriginOf}; pub use weights::WeightInfo; use xcm::prelude::NetworkId; @@ -122,7 +122,16 @@ pub mod pallet { type Hashing: Hash; - type MessageQueue: EnqueueMessage; + type AggregateMessageOrigin: FullCodec + + MaxEncodedLen + + Clone + + Eq + + PartialEq + + TypeInfo + + Debug + + From; + + type MessageQueue: EnqueueMessage; /// Measures the maximum gas used to execute a command on Ethereum type GasMeter: GasMeter; @@ -137,6 +146,9 @@ pub mod pallet { #[pallet::constant] type MaxMessagesPerBlock: Get; + /// Hook that is called whenever there is a new commitment. + type OnNewCommitment: OnNewCommitment; + /// Convert a weight value into a deductible fee based. type WeightToFee: WeightToFee; @@ -163,7 +175,7 @@ pub mod pallet { } #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::generate_deposit(pub fn deposit_event)] pub enum Event { /// Message has been queued and will be processed in the future MessageQueued { @@ -239,7 +251,7 @@ pub mod pallet { /// Inspired by the `frame_system::Pallet::Events` storage value #[pallet::storage] #[pallet::unbounded] - pub(super) type Messages = StorageValue<_, Vec, ValueQuery>; + pub type Messages = StorageValue<_, Vec, ValueQuery>; /// Hashes of the ABI-encoded messages in the [`Messages`] storage value. Used to generate a /// merkle root during `on_finalize`. This storage value is killed in `on_initialize`, so state @@ -247,7 +259,7 @@ pub mod pallet { /// it doesn't have to be included in PoV. #[pallet::storage] #[pallet::unbounded] - pub(super) type MessageLeaves = StorageValue<_, Vec, ValueQuery>; + pub type MessageLeaves = StorageValue<_, Vec, ValueQuery>; /// The current nonce for the messages #[pallet::storage] @@ -316,11 +328,13 @@ pub mod pallet { // Create merkle root of messages let root = merkle_root::<::Hashing, _>(MessageLeaves::::stream_iter()); - let digest_item: DigestItem = CustomDigestItem::SnowbridgeV2(root).into(); + let digest_item: DigestItem = SnowbridgeDigestItem::SnowbridgeV2(root).into(); // Insert merkle root into the header digest >::deposit_log(digest_item); + T::OnNewCommitment::on_new_commitment(root); + Self::deposit_event(Event::MessagesCommitted { root, count }); } diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs index 2250d9f56eb13..3fe37f94f437d 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs @@ -9,12 +9,14 @@ use frame_support::{ BoundedVec, }; +use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; use hex_literal::hex; use snowbridge_core::{ gwei, meth, pricing::{PricingParameters, Rewards}, - AgentId, AgentIdOf, ParaId, + AgentId, AgentIdOf, ChannelId, ParaId, }; +use scale_info::TypeInfo; use snowbridge_outbound_queue_primitives::{v2::*, Log, Proof, VerificationError, Verifier}; use snowbridge_test_utils::mock_rewards::{BridgeReward, MockRewardLedger}; use sp_core::{ConstU32, H160, H256}; @@ -99,6 +101,28 @@ parameter_types! { } pub const DOT: u128 = 10_000_000_000; +#[derive( + Encode, + Decode, + DecodeWithMemTracking, + Copy, + MaxEncodedLen, + Clone, + Eq, + PartialEq, + TypeInfo, + Debug, +)] +pub enum AggregateMessageOrigin { + Snowbridge(ChannelId), + SnowbridgeV2(H256), +} + +impl From for AggregateMessageOrigin { + fn from(hash: H256) -> Self { + Self::SnowbridgeV2(hash) + } +} impl crate::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -116,8 +140,10 @@ impl crate::Config for Test { type EthereumNetwork = EthereumNetwork; type RewardKind = BridgeReward; type DefaultRewardKind = DefaultMyRewardKind; + type OnNewCommitment = (); #[cfg(feature = "runtime-benchmarks")] type Helper = Test; + type AggregateMessageOrigin = AggregateMessageOrigin; } fn setup() { diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/process_message_impl.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/process_message_impl.rs index 28eee2b804a58..0301e9e2208d0 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/process_message_impl.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/process_message_impl.rs @@ -9,7 +9,7 @@ use frame_support::{ }; impl ProcessMessage for Pallet { - type Origin = AggregateMessageOrigin; + type Origin = T::AggregateMessageOrigin; fn process_message( message: &[u8], origin: Self::Origin, diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/send_message_impl.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/send_message_impl.rs index 95f756c4d4003..4527178c72441 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/send_message_impl.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/send_message_impl.rs @@ -2,7 +2,6 @@ // SPDX-FileCopyrightText: 2023 Snowfork //! Implementation for [`snowbridge_outbound_queue_primitives::v2::SendMessage`] use super::*; -use bridge_hub_common::AggregateMessageOrigin; use codec::Encode; use frame_support::{ ensure, @@ -33,7 +32,7 @@ where } fn deliver(ticket: Self::Ticket) -> Result { - let origin = AggregateMessageOrigin::SnowbridgeV2(ticket.origin); + let origin = ticket.origin.into(); let message = BoundedVec::try_from(ticket.encode()).map_err(|_| SendError::MessageTooLarge)?; diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/test.rs index 8cfb3ab62e77c..3778b28c5eec2 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/test.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork -use crate::{mock::*, *}; +use crate::{ + mock::{AggregateMessageOrigin::*, *}, + *, +}; use alloy_core::primitives::FixedBytes; use codec::Encode; use frame_support::{ @@ -10,7 +13,7 @@ use frame_support::{ BoundedVec, }; use hex_literal::hex; -use snowbridge_core::{ChannelId, ParaId}; +use snowbridge_core::{digest_item::SnowbridgeDigestItem, ChannelId, ParaId}; use snowbridge_outbound_queue_primitives::{ v2::{abi::OutboundMessageWrapper, Command, Initializer, SendMessage}, SendError, @@ -179,8 +182,6 @@ fn process_message_fails_on_overweight_message() { #[test] fn governance_message_not_processed_in_same_block_when_queue_congested_with_low_priority_messages() { - use AggregateMessageOrigin::*; - let sibling_id: u32 = 1000; new_tester().execute_with(|| { @@ -240,7 +241,7 @@ fn governance_message_not_processed_in_same_block_when_queue_congested_with_low_ #[test] fn encode_digest_item_with_correct_index() { new_tester().execute_with(|| { - let digest_item: DigestItem = CustomDigestItem::Snowbridge(H256::default()).into(); + let digest_item: DigestItem = SnowbridgeDigestItem::Snowbridge(H256::default()).into(); let enum_prefix = match digest_item { DigestItem::Other(data) => data[0], _ => u8::MAX, @@ -252,10 +253,10 @@ fn encode_digest_item_with_correct_index() { #[test] fn encode_digest_item() { new_tester().execute_with(|| { - let digest_item: DigestItem = CustomDigestItem::Snowbridge([5u8; 32].into()).into(); + let digest_item: DigestItem = SnowbridgeDigestItem::Snowbridge([5u8; 32].into()).into(); let digest_item_raw = digest_item.encode(); assert_eq!(digest_item_raw[0], 0); // DigestItem::Other - assert_eq!(digest_item_raw[2], 0); // CustomDigestItem::Snowbridge + assert_eq!(digest_item_raw[2], 0); // SnowbridgeDigestItem::Snowbridge assert_eq!( digest_item_raw, [ diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/types.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/types.rs index d16cd031e6188..b001381d0e642 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/types.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/types.rs @@ -5,6 +5,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::traits::ProcessMessage; use scale_info::TypeInfo; pub use snowbridge_merkle_tree::MerkleProof; +use sp_core::H256; use sp_runtime::RuntimeDebug; use sp_std::prelude::*; @@ -21,3 +22,12 @@ pub struct PendingOrder { #[codec(compact)] pub fee: u128, } + +/// Hook that will be called when a new message commitment is constructed. +pub trait OnNewCommitment { + fn on_new_commitment(commitment: H256); +} + +impl OnNewCommitment for () { + fn on_new_commitment(_commitment: H256) {} +} diff --git a/bridges/snowbridge/pallets/outbound-queue/src/benchmarking.rs b/bridges/snowbridge/pallets/outbound-queue/src/benchmarking.rs index 1ff8df5793581..c3d0347438af0 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/benchmarking.rs @@ -2,7 +2,6 @@ // SPDX-FileCopyrightText: 2023 Snowfork use super::*; -use bridge_hub_common::AggregateMessageOrigin; use codec::Encode; use frame_benchmarking::v2::*; use snowbridge_core::ChannelId; @@ -34,7 +33,7 @@ mod benchmarks { }), }, }; - let origin = AggregateMessageOrigin::Snowbridge([1; 32].into()); + let origin = T::GetAggregateMessageOrigin::convert([1; 32].into()); let encoded_enqueued_message = enqueued_message.encode(); #[block] diff --git a/bridges/snowbridge/pallets/outbound-queue/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue/src/lib.rs index aeae2cd3f245b..3311d74dfd906 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/lib.rs @@ -103,25 +103,24 @@ mod mock; #[cfg(test)] mod test; -use bridge_hub_common::{AggregateMessageOrigin, CustomDigestItem}; -use codec::Decode; +use codec::{Decode, FullCodec}; use frame_support::{ storage::StorageStreamIter, traits::{tokens::Balance, Contains, Defensive, EnqueueMessage, Get, ProcessMessageError}, weights::{Weight, WeightToFee}, }; -use snowbridge_core::{BasicOperatingMode, ChannelId}; +use snowbridge_core::{digest_item::SnowbridgeDigestItem, BasicOperatingMode, ChannelId}; use snowbridge_merkle_tree::merkle_root; use snowbridge_outbound_queue_primitives::v1::{ Fee, GasMeter, QueuedMessage, VersionedQueuedMessage, ETHER_DECIMALS, }; use sp_core::{H256, U256}; use sp_runtime::{ - traits::{CheckedDiv, Hash}, + traits::{CheckedDiv, Convert, Debug, Hash}, DigestItem, Saturating, }; use sp_std::prelude::*; -pub use types::{CommittedMessage, ProcessMessageOriginOf}; +pub use types::{CommittedMessage, OnNewCommitment, ProcessMessageOriginOf}; pub use weights::WeightInfo; pub use pallet::*; @@ -144,7 +143,15 @@ pub mod pallet { type Hashing: Hash; - type MessageQueue: EnqueueMessage; + type AggregateMessageOrigin: FullCodec + + MaxEncodedLen + + Clone + + Eq + + PartialEq + + TypeInfo + + Debug; + type GetAggregateMessageOrigin: Convert; + type MessageQueue: EnqueueMessage; /// Measures the maximum gas used to execute a command on Ethereum type GasMeter: GasMeter; @@ -168,6 +175,8 @@ pub mod pallet { type PricingParameters: Get>; + type OnNewCommitment: OnNewCommitment; + /// Convert a weight value into a deductible fee based. type WeightToFee: WeightToFee; @@ -176,7 +185,7 @@ pub mod pallet { } #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::generate_deposit(pub fn deposit_event)] pub enum Event { /// Message has been queued and will be processed in the future MessageQueued { @@ -220,7 +229,7 @@ pub mod pallet { /// Inspired by the `frame_system::Pallet::Events` storage value #[pallet::storage] #[pallet::unbounded] - pub(super) type Messages = StorageValue<_, Vec, ValueQuery>; + pub type Messages = StorageValue<_, Vec, ValueQuery>; /// Hashes of the ABI-encoded messages in the [`Messages`] storage value. Used to generate a /// merkle root during `on_finalize`. This storage value is killed in @@ -228,7 +237,7 @@ pub mod pallet { #[pallet::storage] #[pallet::unbounded] #[pallet::getter(fn message_leaves)] - pub(super) type MessageLeaves = StorageValue<_, Vec, ValueQuery>; + pub type MessageLeaves = StorageValue<_, Vec, ValueQuery>; /// The current nonce for each message origin #[pallet::storage] @@ -289,8 +298,10 @@ pub mod pallet { // Create merkle root of messages let root = merkle_root::<::Hashing, _>(MessageLeaves::::stream_iter()); - let digest_item: DigestItem = CustomDigestItem::Snowbridge(root).into(); + T::OnNewCommitment::on_new_commitment(root); + let digest_item: DigestItem = SnowbridgeDigestItem::Snowbridge(root).into(); + // Insert merkle root into the header digest >::deposit_log(digest_item); diff --git a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs index 236cca7d83817..93dca69018f1c 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs @@ -92,6 +92,7 @@ impl crate::Config for Test { type PricingParameters = Parameters; type Channels = Everything; type WeightToFee = IdentityFee; + type OnNewCommitment = (); type WeightInfo = (); } diff --git a/bridges/snowbridge/pallets/outbound-queue/src/process_message_impl.rs b/bridges/snowbridge/pallets/outbound-queue/src/process_message_impl.rs index 731aa6fa6d5ca..b9f4032d3de30 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/process_message_impl.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/process_message_impl.rs @@ -9,7 +9,7 @@ use frame_support::{ }; impl ProcessMessage for Pallet { - type Origin = AggregateMessageOrigin; + type Origin = T::AggregateMessageOrigin; fn process_message( message: &[u8], origin: Self::Origin, diff --git a/bridges/snowbridge/pallets/outbound-queue/src/send_message_impl.rs b/bridges/snowbridge/pallets/outbound-queue/src/send_message_impl.rs index 6d13703d864eb..f799aeec7d430 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/send_message_impl.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/send_message_impl.rs @@ -2,7 +2,6 @@ // SPDX-FileCopyrightText: 2023 Snowfork //! Implementation for [`snowbridge_outbound_queue_primitives::v1::SendMessage`] use super::*; -use bridge_hub_common::AggregateMessageOrigin; use codec::Encode; use frame_support::{ ensure, @@ -19,8 +18,9 @@ use sp_core::H256; use sp_runtime::BoundedVec; /// The maximal length of an enqueued message, as determined by the MessageQueue pallet -pub type MaxEnqueuedMessageSizeOf = - <::MessageQueue as EnqueueMessage>::MaxMessageLen; +pub type MaxEnqueuedMessageSizeOf = <::MessageQueue as EnqueueMessage< + ::AggregateMessageOrigin, +>>::MaxMessageLen; #[derive(Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound)] pub struct Ticket @@ -74,7 +74,7 @@ where } fn deliver(ticket: Self::Ticket) -> Result { - let origin = AggregateMessageOrigin::Snowbridge(ticket.channel_id); + let origin = T::GetAggregateMessageOrigin::convert(ticket.channel_id); if ticket.channel_id != PRIMARY_GOVERNANCE_CHANNEL { ensure!(!Self::operating_mode().is_halted(), SendError::Halted); diff --git a/bridges/snowbridge/pallets/outbound-queue/src/test.rs b/bridges/snowbridge/pallets/outbound-queue/src/test.rs index a7c4a446b6470..26dae652f03e4 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/test.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/test.rs @@ -9,7 +9,7 @@ use frame_support::{ }; use codec::Encode; -use snowbridge_core::{ParaId, PricingParameters, Rewards}; +use snowbridge_core::{digest_item::SnowbridgeDigestItem, ParaId, PricingParameters, Rewards}; use snowbridge_outbound_queue_primitives::{ v1::{Command, SendMessage}, SendError, @@ -242,7 +242,7 @@ fn convert_local_currency() { #[test] fn encode_digest_item_with_correct_index() { new_tester().execute_with(|| { - let digest_item: DigestItem = CustomDigestItem::Snowbridge(H256::default()).into(); + let digest_item: DigestItem = SnowbridgeDigestItem::Snowbridge(H256::default()).into(); let enum_prefix = match digest_item { DigestItem::Other(data) => data[0], _ => u8::MAX, @@ -254,10 +254,10 @@ fn encode_digest_item_with_correct_index() { #[test] fn encode_digest_item() { new_tester().execute_with(|| { - let digest_item: DigestItem = CustomDigestItem::Snowbridge([5u8; 32].into()).into(); + let digest_item: DigestItem = SnowbridgeDigestItem::Snowbridge([5u8; 32].into()).into(); let digest_item_raw = digest_item.encode(); assert_eq!(digest_item_raw[0], 0); // DigestItem::Other - assert_eq!(digest_item_raw[2], 0); // CustomDigestItem::Snowbridge + assert_eq!(digest_item_raw[2], 0); // SnowbridgeDigestItem::Snowbridge assert_eq!( digest_item_raw, [ diff --git a/bridges/snowbridge/pallets/outbound-queue/src/types.rs b/bridges/snowbridge/pallets/outbound-queue/src/types.rs index 76d32fab462a4..181894499243d 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/types.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/types.rs @@ -55,3 +55,12 @@ impl From for Token { ]) } } + +/// Hook that will be called when new message commitment is constructed +pub trait OnNewCommitment { + fn on_new_commitment(commitment: H256); +} + +impl OnNewCommitment for () { + fn on_new_commitment(_commitment: H256) {} +} diff --git a/bridges/snowbridge/pallets/system-v2/src/lib.rs b/bridges/snowbridge/pallets/system-v2/src/lib.rs index 095b263f8f257..9f7bfccbfb609 100644 --- a/bridges/snowbridge/pallets/system-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/system-v2/src/lib.rs @@ -43,10 +43,10 @@ use snowbridge_outbound_queue_primitives::{ v2::{Command, Initializer, Message, SendMessage}, OperatingMode, SendError, }; -use snowbridge_pallet_system::ForeignToNativeId; +use snowbridge_pallet_system::{ForeignToNativeId, NativeToForeignId}; use sp_core::{H160, H256}; use sp_io::hashing::blake2_256; -use sp_runtime::traits::MaybeConvert; +use sp_runtime::traits::{MaybeConvert, MaybeEquivalence}; use sp_std::prelude::*; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; @@ -227,6 +227,7 @@ pub mod pallet { .ok_or(Error::::LocationConversionFailed)?; if !ForeignToNativeId::::contains_key(token_id) { + NativeToForeignId::::insert(location.clone(), token_id); ForeignToNativeId::::insert(token_id, location.clone()); } @@ -318,4 +319,13 @@ pub mod pallet { snowbridge_pallet_system::Pallet::::maybe_convert(foreign_id) } } + + impl MaybeEquivalence for Pallet { + fn convert(foreign_id: &TokenId) -> Option { + snowbridge_pallet_system::Pallet::::convert(foreign_id) + } + fn convert_back(location: &Location) -> Option { + snowbridge_pallet_system::Pallet::::convert_back(location) + } + } } diff --git a/bridges/snowbridge/pallets/system-v2/src/tests.rs b/bridges/snowbridge/pallets/system-v2/src/tests.rs index 2475dfa67c399..d92a7aa249c78 100644 --- a/bridges/snowbridge/pallets/system-v2/src/tests.rs +++ b/bridges/snowbridge/pallets/system-v2/src/tests.rs @@ -138,6 +138,11 @@ fn register_all_tokens_succeeds() { let foreign_token_id = EthereumSystemV2::location_to_message_origin(tc.native.clone()).unwrap(); + assert_eq!( + NativeToForeignId::::get(reanchored_location.clone()), + Some(foreign_token_id) + ); + assert_eq!( ForeignToNativeId::::get(foreign_token_id), Some(reanchored_location.clone()) diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index cba1483a193cd..80910e5f3f245 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -7,11 +7,19 @@ use super::*; use crate::Pallet as SnowbridgeControl; use frame_benchmarking::v2::*; use frame_system::RawOrigin; -use snowbridge_core::eth; +use snowbridge_core::{eth, sibling_sovereign_account}; use snowbridge_outbound_queue_primitives::OperatingMode; use sp_runtime::SaturatedConversion; use xcm::prelude::*; +#[allow(clippy::result_large_err)] +fn fund_sovereign_account(para_id: ParaId) -> Result<(), BenchmarkError> { + let amount: BalanceOf = (10_000_000_000_000_u64).saturated_into::().saturated_into(); + let sovereign_account = sibling_sovereign_account::(para_id); + T::Token::mint_into(&sovereign_account, amount)?; + Ok(()) +} + #[benchmarks] mod benchmarks { use super::*; @@ -53,6 +61,43 @@ mod benchmarks { Ok(()) } + #[benchmark] + fn force_update_channel() -> Result<(), BenchmarkError> { + let origin_para_id = 2000; + let origin_location = Location::new(1, [Parachain(origin_para_id)]); + let origin = T::Helper::make_xcm_origin(origin_location.clone()); + let channel_id: ChannelId = ParaId::from(origin_para_id).into(); + + fund_sovereign_account::(origin_para_id.into())?; + let (para_id, agent_id) = ensure_sibling::(&origin_location)?; + Agents::::insert(agent_id, ()); + let channel_id: ChannelId = para_id.into(); + let channel = Channel { agent_id, para_id }; + Channels::::insert(channel_id, channel); + + #[extrinsic_call] + _(RawOrigin::Root, channel_id, OperatingMode::RejectingOutboundMessages); + + Ok(()) + } + + #[benchmark] + fn force_transfer_native_from_agent() -> Result<(), BenchmarkError> { + let origin_para_id = 2000; + let origin_location = Location::new(1, [Parachain(origin_para_id)]); + let origin = T::Helper::make_xcm_origin(origin_location.clone()); + fund_sovereign_account::(origin_para_id.into())?; + let (para_id, agent_id) = ensure_sibling::(&origin_location)?; + Agents::::insert(agent_id, ()); + + let versioned_location: VersionedLocation = origin_location.into(); + + #[extrinsic_call] + _(RawOrigin::Root, Box::new(versioned_location), H160::default(), 1); + + Ok(()) + } + #[benchmark] fn set_token_transfer_fees() -> Result<(), BenchmarkError> { #[extrinsic_call] diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index d277186bd5b9d..42e7f29935495 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -52,7 +52,10 @@ use snowbridge_outbound_queue_primitives::{ }; use sp_core::{RuntimeDebug, H160, H256}; use sp_io::hashing::blake2_256; -use sp_runtime::{traits::MaybeConvert, DispatchError, SaturatedConversion}; +use sp_runtime::{ + traits::{BadOrigin, MaybeConvert, MaybeEquivalence}, + DispatchError, SaturatedConversion, +}; use sp_std::prelude::*; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; @@ -67,6 +70,20 @@ pub type BalanceOf = pub type AccountIdOf = ::AccountId; pub type PricingParametersOf = PricingParametersRecord>; +/// Ensure origin location is a sibling +fn ensure_sibling(location: &Location) -> Result<(ParaId, H256), DispatchError> +where + T: Config, +{ + match location.unpack() { + (1, [Parachain(para_id)]) => { + let agent_id = agent_id_of::(location)?; + Ok(((*para_id).into(), agent_id)) + }, + _ => Err(BadOrigin.into()), + } +} + /// Hash the location to produce an agent id pub fn agent_id_of(location: &Location) -> Result { T::AgentIdOf::convert_location(location).ok_or(Error::::LocationConversionFailed.into()) @@ -233,6 +250,11 @@ pub mod pallet { pub type ForeignToNativeId = StorageMap<_, Blake2_128Concat, TokenId, Location, OptionQuery>; + /// Lookup table for native location relative to ethereum to foreign token ID + #[pallet::storage] + pub type NativeToForeignId = + StorageMap<_, Blake2_128Concat, Location, TokenId, OptionQuery>; + #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { @@ -333,6 +355,69 @@ pub mod pallet { Ok(()) } + /// Sends a message to the Gateway contract to update an arbitrary channel + /// + /// Fee required: No + /// + /// - `origin`: Must be root + /// - `channel_id`: ID of channel + /// - `mode`: Initial operating mode of the channel + /// - `outbound_fee`: Fee charged to users for sending outbound messages to Polkadot + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::force_update_channel())] + pub fn force_update_channel( + origin: OriginFor, + channel_id: ChannelId, + mode: OperatingMode, + ) -> DispatchResult { + ensure_root(origin)?; + + ensure!(Channels::::contains_key(channel_id), Error::::NoChannel); + + let command = Command::UpdateChannel { channel_id, mode }; + Self::send(PRIMARY_GOVERNANCE_CHANNEL, command, PaysFee::::No)?; + + Self::deposit_event(Event::::UpdateChannel { channel_id, mode }); + Ok(()) + } + + /// Sends a message to the Gateway contract to transfer ether from an agent to `recipient`. + /// + /// Privileged. Can only be called by root. + /// + /// Fee required: No + /// + /// - `origin`: Must be root + /// - `location`: Location used to resolve the agent + /// - `recipient`: Recipient of funds + /// - `amount`: Amount to transfer + #[pallet::call_index(8)] + #[pallet::weight(T::WeightInfo::force_transfer_native_from_agent())] + pub fn force_transfer_native_from_agent( + origin: OriginFor, + location: Box, + recipient: H160, + amount: u128, + ) -> DispatchResult { + ensure_root(origin)?; + + // Ensure that location is some consensus system on a sibling parachain + let location: Location = + (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; + let (_, agent_id) = + ensure_sibling::(&location).map_err(|_| Error::::InvalidLocation)?; + + let pays_fee = PaysFee::::No; + + Self::do_transfer_native_from_agent( + agent_id, + PRIMARY_GOVERNANCE_CHANNEL, + recipient, + amount, + pays_fee, + ) + } + /// Sends a message to the Gateway contract to update fee related parameters for /// token transfers. /// @@ -433,6 +518,28 @@ pub mod pallet { Ok(()) } + /// Issue a `Command::TransferNativeFromAgent` command. The command will be sent on the + /// channel `channel_id` + pub fn do_transfer_native_from_agent( + agent_id: H256, + channel_id: ChannelId, + recipient: H160, + amount: u128, + pays_fee: PaysFee, + ) -> DispatchResult { + ensure!(Agents::::contains_key(agent_id), Error::::NoAgent); + + let command = Command::TransferNativeFromAgent { agent_id, recipient, amount }; + Self::send(channel_id, command, pays_fee)?; + + Self::deposit_event(Event::::TransferNativeFromAgent { + agent_id, + recipient, + amount, + }); + Ok(()) + } + /// Initializes agents and channels. pub fn initialize(para_id: ParaId, asset_hub_para_id: ParaId) -> Result<(), DispatchError> { // Asset Hub @@ -489,6 +596,7 @@ pub mod pallet { .ok_or(Error::::LocationConversionFailed)?; if !ForeignToNativeId::::contains_key(token_id) { + NativeToForeignId::::insert(location.clone(), token_id); ForeignToNativeId::::insert(token_id, location.clone()); } @@ -534,4 +642,13 @@ pub mod pallet { ForeignToNativeId::::get(foreign_id) } } + + impl MaybeEquivalence for Pallet { + fn convert(foreign_id: &TokenId) -> Option { + ForeignToNativeId::::get(foreign_id) + } + fn convert_back(location: &Location) -> Option { + NativeToForeignId::::get(location) + } + } } diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 311981a873b01..54f7e680597ad 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -171,6 +171,7 @@ impl snowbridge_pallet_outbound_queue::Config for Test { type PricingParameters = EthereumSystem; type Channels = EthereumSystem; type WeightToFee = IdentityFee; + type OnNewCommitment = (); type WeightInfo = (); } diff --git a/bridges/snowbridge/pallets/system/src/weights.rs b/bridges/snowbridge/pallets/system/src/weights.rs index 7b2513775601e..6d56a6a71cda9 100644 --- a/bridges/snowbridge/pallets/system/src/weights.rs +++ b/bridges/snowbridge/pallets/system/src/weights.rs @@ -37,6 +37,8 @@ pub trait WeightInfo { fn set_token_transfer_fees() -> Weight; fn set_pricing_parameters() -> Weight; fn register_token() -> Weight; + fn force_update_channel() -> Weight; + fn force_transfer_native_from_agent() -> Weight; } // For backwards compatibility and tests. @@ -60,6 +62,25 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + /// Storage: EthereumSystem Channels (r:1 w:0) + /// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn force_update_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 41_000_000 picoseconds. + Weight::from_parts(41_000_000, 6044) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } /// Storage: ParachainInfo ParachainId (r:1 w:0) /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) @@ -79,6 +100,25 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + /// Storage: EthereumSystem Agents (r:1 w:0) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn force_transfer_native_from_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `6044` + // Minimum execution time: 42_000_000 picoseconds. + Weight::from_parts(42_000_000, 6044) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } /// Storage: ParachainInfo ParachainId (r:1 w:0) /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) diff --git a/bridges/snowbridge/primitives/beacon/src/types.rs b/bridges/snowbridge/primitives/beacon/src/types.rs index 7ec6a180b5634..4863a70bb8d42 100644 --- a/bridges/snowbridge/primitives/beacon/src/types.rs +++ b/bridges/snowbridge/primitives/beacon/src/types.rs @@ -37,6 +37,7 @@ pub struct ForkVersions { pub capella: Fork, pub deneb: Fork, pub electra: Fork, + pub fulu: Fork, } #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/digest_item.rs b/bridges/snowbridge/primitives/core/src/digest_item.rs similarity index 90% rename from cumulus/parachains/runtimes/bridge-hubs/common/src/digest_item.rs rename to bridges/snowbridge/primitives/core/src/digest_item.rs index 5823b15b8d557..fec6fa83a8bfa 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/src/digest_item.rs +++ b/bridges/snowbridge/primitives/core/src/digest_item.rs @@ -20,7 +20,7 @@ use sp_runtime::generic::DigestItem; /// Custom header digest items, inserted as DigestItem::Other #[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, RuntimeDebug)] -pub enum CustomDigestItem { +pub enum SnowbridgeDigestItem { #[codec(index = 0)] /// Merkle root of outbound Snowbridge messages. Snowbridge(H256), @@ -30,8 +30,8 @@ pub enum CustomDigestItem { } /// Convert custom application digest item into a concrete digest item -impl From for DigestItem { - fn from(val: CustomDigestItem) -> Self { +impl From for DigestItem { + fn from(val: SnowbridgeDigestItem) -> Self { DigestItem::Other(val.encode()) } } diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index 9a28ad465b5ef..258fd1dcf01fa 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -8,6 +8,7 @@ #[cfg(test)] mod tests; +pub mod digest_item; pub mod location; pub mod operating_mode; pub mod pricing; diff --git a/bridges/snowbridge/primitives/inbound-queue/Cargo.toml b/bridges/snowbridge/primitives/inbound-queue/Cargo.toml index 3e36331435f79..ed85b66413da6 100644 --- a/bridges/snowbridge/primitives/inbound-queue/Cargo.toml +++ b/bridges/snowbridge/primitives/inbound-queue/Cargo.toml @@ -15,22 +15,29 @@ workspace = true exclude-from-umbrella = true [dependencies] +alloy-primitives = { features = ["rlp"], workspace = true } +alloy-sol-types = { workspace = true } alloy-core = { workspace = true, features = ["sol-types"] } codec = { workspace = true } -frame-support.workspace = true -frame-system.workspace = true +impl-trait-for-tuples = { workspace = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } -snowbridge-beacon-primitives.workspace = true -snowbridge-core.workspace = true -snowbridge-verification-primitives.workspace = true -sp-core.workspace = true -sp-io.workspace = true -sp-runtime.workspace = true -sp-std.workspace = true -xcm-builder.workspace = true -xcm-executor.workspace = true -xcm.workspace = true +tracing = { workspace = true } + +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } + +snowbridge-beacon-primitives = { workspace = true } +snowbridge-core = { workspace = true } +snowbridge-verification-primitives = { workspace = true } hex-literal = { workspace = true, default-features = true } @@ -53,6 +60,7 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "tracing/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", diff --git a/bridges/snowbridge/pallets/inbound-queue/src/envelope.rs b/bridges/snowbridge/primitives/inbound-queue/src/envelope.rs similarity index 91% rename from bridges/snowbridge/pallets/inbound-queue/src/envelope.rs rename to bridges/snowbridge/primitives/inbound-queue/src/envelope.rs index 2ba779b410709..ad778ea1749fc 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/envelope.rs +++ b/bridges/snowbridge/primitives/inbound-queue/src/envelope.rs @@ -1,12 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork use snowbridge_core::ChannelId; -use snowbridge_inbound_queue_primitives::Log; +use snowbridge_verification_primitives::Log; use sp_core::{RuntimeDebug, H160, H256}; use sp_std::prelude::*; -use alloy_core::{primitives::B256, sol, sol_types::SolEvent}; +use alloy_primitives::B256; +use alloy_sol_types::{sol, SolEvent}; sol! { event OutboundMessageAccepted(bytes32 indexed channel_id, uint64 nonce, bytes32 indexed message_id, bytes payload); @@ -44,7 +45,7 @@ impl TryFrom<&Log> for Envelope { channel_id: ChannelId::from(event.channel_id.as_ref()), nonce: event.nonce, message_id: H256::from(event.message_id.as_ref()), - payload: event.payload.into(), + payload: event.payload, }) } } diff --git a/bridges/snowbridge/primitives/inbound-queue/src/v1.rs b/bridges/snowbridge/primitives/inbound-queue/src/v1.rs index 6eba682f72f74..b58eb0d0a82a0 100644 --- a/bridges/snowbridge/primitives/inbound-queue/src/v1.rs +++ b/bridges/snowbridge/primitives/inbound-queue/src/v1.rs @@ -3,13 +3,16 @@ //! Converts messages from Ethereum to XCM messages use crate::{CallIndex, EthereumLocationsConverterFor}; +use alloy_primitives::B256; +use alloy_sol_types::{sol, SolEvent}; use codec::{Decode, DecodeWithMemTracking, Encode}; use core::marker::PhantomData; use frame_support::{traits::tokens::Balance as BalanceT, PalletError}; use scale_info::TypeInfo; -use snowbridge_core::TokenId; +use snowbridge_core::{Channel, ChannelId, TokenId}; +use snowbridge_verification_primitives::Log; use sp_core::{Get, RuntimeDebug, H160, H256}; -use sp_runtime::{traits::MaybeConvert, MultiAddress}; +use sp_runtime::{traits::MaybeConvert, DispatchError, MultiAddress}; use sp_std::prelude::*; use xcm::prelude::{Junction::AccountKey20, *}; @@ -19,7 +22,7 @@ const MINIMUM_DEPOSIT: u128 = 1; /// we may want to evolve the protocol so that the ethereum side sends XCM messages directly. /// Instead having BridgeHub transcode the messages into XCM. #[derive(Clone, Encode, Decode, RuntimeDebug)] -pub enum VersionedMessage { +pub enum VersionedXcmMessage { V1(MessageV1), } @@ -141,7 +144,7 @@ pub trait ConvertMessage { /// Converts a versioned message into an XCM message and an optional topicID fn convert( message_id: H256, - message: VersionedMessage, + message: VersionedXcmMessage, ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; } @@ -180,10 +183,10 @@ where fn convert( message_id: H256, - message: VersionedMessage, + message: VersionedXcmMessage, ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { use Command::*; - use VersionedMessage::*; + use VersionedXcmMessage::*; match message { V1(MessageV1 { chain_id, command: RegisterToken { token, fee } }) => Ok(Self::convert_register_token(message_id, chain_id, token, fee)), @@ -456,6 +459,47 @@ where } } +sol! { + event OutboundMessageAccepted(bytes32 indexed channel_id, uint64 nonce, bytes32 indexed message_id, bytes payload); +} + +/// An inbound message that has had its outer envelope decoded. +#[derive(Clone, RuntimeDebug)] +pub struct Envelope { + /// The address of the outbound queue on Ethereum that emitted this message as an event log + pub gateway: H160, + /// The message Channel + pub channel_id: ChannelId, + /// A nonce for enforcing replay protection and ordering. + pub nonce: u64, + /// An id for tracing the message on its route (has no role in bridge consensus) + pub message_id: H256, + /// The inner payload generated from the source application. + pub payload: Vec, +} + +#[derive(Copy, Clone, RuntimeDebug)] +pub struct EnvelopeDecodeError; + +impl TryFrom<&Log> for Envelope { + type Error = EnvelopeDecodeError; + + fn try_from(log: &Log) -> Result { + let topics: Vec = log.topics.iter().map(|x| B256::from_slice(x.as_ref())).collect(); + + let event = OutboundMessageAccepted::decode_log(topics, &log.data, true) + .map_err(|_| EnvelopeDecodeError)?; + + Ok(Self { + gateway: log.address, + channel_id: ChannelId::from(event.channel_id.as_ref()), + nonce: event.nonce, + message_id: H256::from(event.message_id.as_ref()), + payload: event.payload, + }) + } +} + #[cfg(test)] mod tests { use crate::{ @@ -671,3 +715,38 @@ mod tests { assert_eq!(actual_assets, Some(expected_assets)) } } +pub trait MessageProcessor { + /// Lightweight function to check if this processor can handle the message + fn can_process_message(channel: &Channel, envelope: &Envelope) -> bool; + /// Process the message + fn process_message(channel: Channel, envelope: Envelope) -> Result<(), DispatchError>; +} + +#[impl_trait_for_tuples::impl_for_tuples(10)] +impl MessageProcessor for Tuple { + fn can_process_message(channel: &Channel, envelope: &Envelope) -> bool { + for_tuples!( #( + match Tuple::can_process_message(&channel, &envelope) { + true => { + return true; + }, + _ => {} + } + )* ); + + false + } + + fn process_message(channel: Channel, envelope: Envelope) -> Result<(), DispatchError> { + for_tuples!( #( + match Tuple::can_process_message(&channel, &envelope) { + true => { + return Tuple::process_message(channel, envelope) + }, + _ => {} + } + )* ); + + Err(DispatchError::Other("No handler for message found")) + } +} diff --git a/bridges/snowbridge/primitives/inbound-queue/src/v2/converter.rs b/bridges/snowbridge/primitives/inbound-queue/src/v2/converter.rs index 43c086e2f7448..b143dba2cbd41 100644 --- a/bridges/snowbridge/primitives/inbound-queue/src/v2/converter.rs +++ b/bridges/snowbridge/primitives/inbound-queue/src/v2/converter.rs @@ -7,7 +7,7 @@ use crate::{v2::LOG_TARGET, CallIndex}; use codec::{Decode, DecodeLimit, Encode}; use core::marker::PhantomData; use frame_support::ensure; -use snowbridge_core::TokenId; +use snowbridge_core::{ParaId, TokenId}; use sp_core::{Get, RuntimeDebug, H160}; use sp_io::hashing::blake2_256; use sp_runtime::{traits::MaybeConvert, MultiAddress}; @@ -19,8 +19,6 @@ use xcm::{ use xcm_builder::ExternalConsensusLocationsConverterFor; use xcm_executor::traits::ConvertLocation; -const MINIMUM_DEPOSIT: u128 = 1; - /// Topic prefix used for generating unique identifiers for messages const INBOUND_QUEUE_TOPIC_PREFIX: &str = "SnowbridgeInboundQueueV2"; @@ -47,67 +45,78 @@ pub enum AssetTransfer { ReserveWithdraw(Asset), } +#[derive(Clone, RuntimeDebug, Encode)] +pub struct CreateAssetCallInfo { + pub call: CallIndex, + pub deposit: u128, + pub min_balance: u128, +} + +pub struct AssetHubUniversal( + PhantomData<(LocalNetwork, AssetHubParaId)>, +); +impl Get + for AssetHubUniversal +where + LocalNetwork: Get, + AssetHubParaId: Get, +{ + fn get() -> InteriorLocation { + [GlobalConsensus(LocalNetwork::get()), Parachain(AssetHubParaId::get().into())].into() + } +} + /// Concrete implementation of `ConvertMessage` pub struct MessageToXcm< CreateAssetCall, - CreateAssetDeposit, EthereumNetwork, + LocalNetwork, + GatewayProxyAddress, InboundQueueLocation, + AssetHubParaId, ConvertAssetId, - GatewayProxyAddress, - EthereumUniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, AccountId, > { _phantom: PhantomData<( CreateAssetCall, - CreateAssetDeposit, EthereumNetwork, + LocalNetwork, + GatewayProxyAddress, InboundQueueLocation, + AssetHubParaId, ConvertAssetId, - GatewayProxyAddress, - EthereumUniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, AccountId, )>, } impl< CreateAssetCall, - CreateAssetDeposit, EthereumNetwork, + LocalNetwork, + GatewayProxyAddress, InboundQueueLocation, + AssetHubParaId, ConvertAssetId, - GatewayProxyAddress, - EthereumUniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, AccountId, > MessageToXcm< CreateAssetCall, - CreateAssetDeposit, EthereumNetwork, + LocalNetwork, + GatewayProxyAddress, InboundQueueLocation, + AssetHubParaId, ConvertAssetId, - GatewayProxyAddress, - EthereumUniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, AccountId, > where - CreateAssetCall: Get, - CreateAssetDeposit: Get, + CreateAssetCall: Get, EthereumNetwork: Get, + LocalNetwork: Get, + GatewayProxyAddress: Get, InboundQueueLocation: Get, + AssetHubParaId: Get, ConvertAssetId: MaybeConvert, - GatewayProxyAddress: Get, - EthereumUniversalLocation: Get, - AssetHubFromEthereum: Get, - AssetHubUniversalLocation: Get, AccountId: Into<[u8; 32]> + From<[u8; 32]> + Clone, { /// Parse the message into an intermediate form, with all fields decoded @@ -126,9 +135,9 @@ where Location::new(0, [AccountId32 { network: None, id: bridge_owner.clone().into() }]) }); - let mut remote_xcm: Xcm<()> = match &message.xcm { - XcmPayload::Raw(raw) => Self::decode_raw_xcm(raw), - XcmPayload::CreateAsset { token, network } => Self::make_create_asset_xcm( + let mut remote_xcm: Xcm<()> = match &message.payload { + Payload::Raw(raw) => Self::decode_raw_xcm(raw), + Payload::CreateAsset { token, network } => Self::make_create_asset_xcm( token, *network, message.value, @@ -163,12 +172,21 @@ where assets.push(AssetTransfer::ReserveDeposit(asset)); }, EthereumAsset::ForeignTokenERC20 { token_id, value } => { - let asset_loc = ConvertAssetId::maybe_convert(*token_id) + let asset_location = ConvertAssetId::maybe_convert(*token_id) .ok_or(ConvertMessageError::InvalidAsset)?; - let reanchored_asset_loc = asset_loc - .reanchored(&AssetHubFromEthereum::get(), &EthereumUniversalLocation::get()) + let asset_hub_from_ethereum: Location = Location::new( + 1, + [ + GlobalConsensus(LocalNetwork::get()), + Parachain(AssetHubParaId::get().into()), + ], + ); + let ethereum_universal: InteriorLocation = + [GlobalConsensus(EthereumNetwork::get())].into(); + let reanchored_asset_location = asset_location + .reanchored(&asset_hub_from_ethereum, ðereum_universal) .map_err(|_| ConvertMessageError::CannotReanchor)?; - let asset: Asset = (reanchored_asset_loc, *value).into(); + let asset: Asset = (reanchored_asset_location, *value).into(); assets.push(AssetTransfer::ReserveWithdraw(asset)); }, } @@ -194,9 +212,10 @@ where /// Get sovereign account of Ethereum on Asset Hub. fn bridge_owner() -> Result { let account = - ExternalConsensusLocationsConverterFor::::convert_location( - &Location::new(2, [GlobalConsensus(EthereumNetwork::get())]), - ) + ExternalConsensusLocationsConverterFor::< + AssetHubUniversal, + AccountId, + >::convert_location(&Location::new(2, [GlobalConsensus(EthereumNetwork::get())])) .ok_or(ConvertMessageError::CannotReanchor)?; Ok(account) @@ -212,12 +231,13 @@ where claimer: Location, ) -> Result, ConvertMessageError> { let dot_asset = Location::new(1, Here); - let dot_fee: xcm::prelude::Asset = (dot_asset, CreateAssetDeposit::get()).into(); + let dot_fee: xcm::prelude::Asset = (dot_asset, CreateAssetCall::get().deposit).into(); let eth_asset: xcm::prelude::Asset = (Location::new(2, [GlobalConsensus(EthereumNetwork::get())]), eth_value).into(); - let create_call_index: [u8; 2] = CreateAssetCall::get(); + let create_call_index: [u8; 2] = CreateAssetCall::get().call; + let create_min_blance: u128 = CreateAssetCall::get().min_balance; let asset_id = Location::new( 2, @@ -230,6 +250,7 @@ where match network { super::message::Network::Polkadot => Ok(Self::make_create_asset_xcm_for_polkadot( create_call_index, + create_min_blance, asset_id, bridge_owner, dot_fee, @@ -242,6 +263,7 @@ where /// Construct the asset creation XCM for the Polkdot network. fn make_create_asset_xcm_for_polkadot( create_call_index: [u8; 2], + create_min_blance: u128, asset_id: Location, bridge_owner: AccountId, dot_fee_asset: xcm::prelude::Asset, @@ -270,7 +292,7 @@ where create_call_index, asset_id.clone(), MultiAddress::<[u8; 32], ()>::Id(bridge_owner_bytes.into()), - MINIMUM_DEPOSIT, + create_min_blance, ) .encode() .into(), @@ -301,39 +323,33 @@ where } impl< - CreateAssetCall, - CreateAssetDeposit, + CreateAsset, EthereumNetwork, + LocalNetwork, + GatewayProxyAddress, InboundQueueLocation, + AssetHubParaId, ConvertAssetId, - GatewayProxyAddress, - EthereumUniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, AccountId, > ConvertMessage for MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, + CreateAsset, EthereumNetwork, + LocalNetwork, + GatewayProxyAddress, InboundQueueLocation, + AssetHubParaId, ConvertAssetId, - GatewayProxyAddress, - EthereumUniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, AccountId, > where - CreateAssetCall: Get, - CreateAssetDeposit: Get, + CreateAsset: Get, EthereumNetwork: Get, + LocalNetwork: Get, + GatewayProxyAddress: Get, InboundQueueLocation: Get, + AssetHubParaId: Get, ConvertAssetId: MaybeConvert, - GatewayProxyAddress: Get, - EthereumUniversalLocation: Get, - AssetHubFromEthereum: Get, - AssetHubUniversalLocation: Get, AccountId: Into<[u8; 32]> + From<[u8; 32]> + Clone, { fn convert(message: Message) -> Result, ConvertMessageError> { @@ -405,17 +421,19 @@ mod tests { const GATEWAY_ADDRESS: [u8; 20] = hex!["eda338e4dc46038493b885327842fd3e301cab39"]; parameter_types! { - pub const EthereumNetwork: xcm::v5::NetworkId = xcm::v5::NetworkId::Ethereum { chain_id: 1 }; + pub const EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 1 }; + pub const LocalNetwork: NetworkId = NetworkId::Polkadot; pub const GatewayAddress: H160 = H160(GATEWAY_ADDRESS); pub InboundQueueLocation: InteriorLocation = [PalletInstance(84)].into(); pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); - pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(Polkadot),Parachain(1000)]); - pub AssetHubUniversalLocation: InteriorLocation = [GlobalConsensus(Polkadot),Parachain(1000)].into(); - pub const CreateAssetCall: [u8;2] = [53, 0]; + pub AssetHubParaId: ParaId = 1000.into(); + pub const CreateAssetCallIndex: [u8;2] = [53, 0]; pub const CreateAssetDeposit: u128 = 10_000_000_000u128; + pub const CreateAssetMinBalance: u128 = 1; pub EthereumLocation: Location = Location::new(2,EthereumUniversalLocation::get()); pub BridgeHubContext: InteriorLocation = [GlobalConsensus(Polkadot),Parachain(1002)].into(); + pub CreateAssetCall: CreateAssetCallInfo = CreateAssetCallInfo{call: CreateAssetCallIndex::get(),deposit: CreateAssetDeposit::get(),min_balance: CreateAssetMinBalance::get()}; } pub struct MockFailedTokenConvert; @@ -427,27 +445,23 @@ mod tests { type Converter = MessageToXcm< CreateAssetCall, - CreateAssetDeposit, EthereumNetwork, + LocalNetwork, + GatewayAddress, InboundQueueLocation, + AssetHubParaId, LocationIdConvert, - GatewayAddress, - EthereumUniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, [u8; 32], >; type ConverterFailing = MessageToXcm< CreateAssetCall, - CreateAssetDeposit, EthereumNetwork, + LocalNetwork, + GatewayAddress, InboundQueueLocation, + AssetHubParaId, MockFailedTokenConvert, - GatewayAddress, - EthereumUniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, [u8; 32], >; @@ -488,7 +502,7 @@ mod tests { nonce: 0, origin, assets, - xcm: XcmPayload::Raw(versioned_xcm.encode()), + payload: Payload::Raw(versioned_xcm.encode()), claimer, value, execution_fee, @@ -625,7 +639,7 @@ mod tests { nonce: 0, origin, assets, - xcm: XcmPayload::Raw(versioned_xcm.encode()), + payload: Payload::Raw(versioned_xcm.encode()), claimer, value, execution_fee, @@ -677,7 +691,7 @@ mod tests { nonce: 0, origin, assets, - xcm: XcmPayload::Raw(versioned_xcm.encode()), + payload: Payload::Raw(versioned_xcm.encode()), claimer, value, execution_fee, @@ -714,7 +728,7 @@ mod tests { nonce: 0, origin, assets, - xcm: XcmPayload::Raw(versioned_xcm.encode()), + payload: Payload::Raw(versioned_xcm.encode()), claimer, value, execution_fee, @@ -746,7 +760,7 @@ mod tests { // actual claimer should default to Snowbridge sovereign account let bridge_owner = ExternalConsensusLocationsConverterFor::< - AssetHubUniversalLocation, + AssetHubUniversal, [u8; 32], >::convert_location(&Location::new( 2, @@ -783,7 +797,7 @@ mod tests { nonce: 0, origin, assets, - xcm: XcmPayload::Raw(versioned_xcm), + payload: Payload::Raw(versioned_xcm), claimer: Some(claimer.encode()), value, execution_fee, @@ -819,7 +833,7 @@ mod tests { nonce: 0, origin, assets: vec![], - xcm: XcmPayload::Raw(versioned_xcm.encode()), + payload: Payload::Raw(versioned_xcm.encode()), claimer: None, value, execution_fee, @@ -856,7 +870,7 @@ mod tests { nonce: 0, origin, assets: vec![], - xcm: XcmPayload::Raw(vec![]), + payload: Payload::Raw(vec![]), claimer: None, value, execution_fee, @@ -897,7 +911,7 @@ mod tests { nonce: 0, origin, assets: vec![], - xcm: XcmPayload::Raw(versioned_xcm.encode()), + payload: Payload::Raw(versioned_xcm.encode()), claimer: None, value, execution_fee, diff --git a/bridges/snowbridge/primitives/inbound-queue/src/v2/message.rs b/bridges/snowbridge/primitives/inbound-queue/src/v2/message.rs index 177f62ab0ef58..9b78acad87f41 100644 --- a/bridges/snowbridge/primitives/inbound-queue/src/v2/message.rs +++ b/bridges/snowbridge/primitives/inbound-queue/src/v2/message.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 2023 Snowfork //! Converts messages from Ethereum to XCM messages -use crate::{v2::IGatewayV2::Payload, Log}; +use crate::{v2::IGatewayV2::Payload as GatewayV2Payload, Log}; use alloy_core::{ primitives::B256, sol, @@ -81,8 +81,8 @@ impl core::fmt::Debug for IGatewayV2::Xcm { } #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum XcmPayload { - /// Represents raw XCM bytes +pub enum Payload { + /// Raw bytes payload. Commonly used to represent raw XCM bytes Raw(Vec), /// A token registration template CreateAsset { token: H160, network: Network }, @@ -108,7 +108,7 @@ pub struct Message { /// The assets sent from Ethereum (ERC-20s). pub assets: Vec, /// The command originating from the Gateway contract. - pub xcm: XcmPayload, + pub payload: Payload, /// The claimer in the case that funds get trapped. Expected to be an XCM::v5::Location. pub claimer: Option>, /// Native ether bridged over from Ethereum @@ -152,27 +152,27 @@ impl TryFrom<&Log> for Message { let event = IGatewayV2::OutboundMessageAccepted::decode_raw_log_validate(topics, &log.data) .map_err(|_| MessageDecodeError)?; - let payload = event.payload; + let event_payload = event.payload; - let substrate_assets = Self::extract_assets(&payload)?; + let substrate_assets = Self::extract_assets(&event_payload)?; - let xcm = XcmPayload::try_from(&payload)?; + let message_payload = Payload::try_from(&event_payload)?; let mut claimer = None; - if payload.claimer.len() > 0 { - claimer = Some(payload.claimer.to_vec()); + if event_payload.claimer.len() > 0 { + claimer = Some(event_payload.claimer.to_vec()); } let message = Message { gateway: log.address, nonce: event.nonce, - origin: H160::from(payload.origin.as_ref()), + origin: H160::from(event_payload.origin.as_ref()), assets: substrate_assets, - xcm, + payload: message_payload, claimer, - value: payload.value, - execution_fee: payload.executionFee, - relayer_fee: payload.relayerFee, + value: event_payload.value, + execution_fee: event_payload.executionFee, + relayer_fee: event_payload.relayerFee, }; Ok(message) @@ -191,12 +191,12 @@ impl Message { } } -impl TryFrom<&IGatewayV2::Payload> for XcmPayload { +impl TryFrom<&IGatewayV2::Payload> for Payload { type Error = MessageDecodeError; - fn try_from(payload: &Payload) -> Result { + fn try_from(payload: &GatewayV2Payload) -> Result { let xcm = match payload.xcm.kind { - 0 => XcmPayload::Raw(payload.xcm.data.to_vec()), + 0 => Payload::Raw(payload.xcm.data.to_vec()), 1 => { let create_asset = IGatewayV2::XcmCreateAsset::abi_decode_validate(&payload.xcm.data) @@ -206,7 +206,7 @@ impl TryFrom<&IGatewayV2::Payload> for XcmPayload { 0 => Network::Polkadot, _ => return Err(MessageDecodeError), }; - XcmPayload::CreateAsset { token: H160::from(create_asset.token.as_ref()), network } + Payload::CreateAsset { token: H160::from(create_asset.token.as_ref()), network } }, _ => return Err(MessageDecodeError), }; diff --git a/bridges/snowbridge/primitives/inbound-queue/src/v2/mod.rs b/bridges/snowbridge/primitives/inbound-queue/src/v2/mod.rs index 98207c7944a5e..b4784eec91c8b 100644 --- a/bridges/snowbridge/primitives/inbound-queue/src/v2/mod.rs +++ b/bridges/snowbridge/primitives/inbound-queue/src/v2/mod.rs @@ -4,10 +4,12 @@ pub mod converter; pub mod message; +pub mod processor; pub mod traits; pub use converter::*; pub use message::*; +pub use processor::*; pub use traits::*; const LOG_TARGET: &str = "snowbridge-inbound-queue-primitives"; diff --git a/bridges/snowbridge/primitives/inbound-queue/src/v2/processor.rs b/bridges/snowbridge/primitives/inbound-queue/src/v2/processor.rs new file mode 100644 index 0000000000000..d7e28eb9c62fa --- /dev/null +++ b/bridges/snowbridge/primitives/inbound-queue/src/v2/processor.rs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +//! Message processor for inbound queue v2 + +use super::*; +use frame_support::traits::Get; +use sp_runtime::traits::TryConvert; +use sp_std::marker::PhantomData; +use xcm::prelude::*; + +/// A message processor that converts messages to XCM and forwards them to AssetHub +/// Generic parameters: T = pallet Config, Sender = XCM sender, Executor = fee handler, +/// Converter = message converter, AccountToLocation = account-to-location converter +pub struct XcmMessageProcessor( + pub PhantomData<(T, Sender, Executor, Converter, AccountToLocation, TargetLocation)>, +); + +impl + MessageProcessor + for XcmMessageProcessor +where + T: frame_system::Config, + Sender: SendXcm, + Executor: ExecuteXcm, + Converter: ConvertMessage, + AccountToLocation: for<'a> TryConvert<&'a AccountId, Location>, + TargetLocation: Get, +{ + fn can_process_message(_relayer: &AccountId, _message: &Message) -> bool { + true + } + + fn process_message( + relayer: AccountId, + message: Message, + ) -> Result<([u8; 32], Option), MessageProcessorError> { + // Process the message and return its ID + let id = Self::process_xcm(relayer, message)?; + Ok((id, None)) + } +} + +impl + XcmMessageProcessor +where + T: frame_system::Config, + Sender: SendXcm, + Executor: ExecuteXcm, + Converter: ConvertMessage, + AccountToLocation: for<'a> TryConvert<&'a T::AccountId, Location>, + TargetLocation: Get, +{ + /// Process a message and return the message ID + pub fn process_xcm( + who: T::AccountId, + message: Message, + ) -> Result { + // Convert the message to XCM + let xcm = Converter::convert(message).map_err(|error| { + tracing::error!(target: LOG_TARGET, ?error, "XCM conversion failed with error"); + MessageProcessorError::ConvertMessage(error) + })?; + + // Forward XCM to a target location + let dest = TargetLocation::get(); + let message_id = Self::send_xcm(dest.clone(), &who, xcm.clone()).map_err(|error| { + tracing::error!(target: LOG_TARGET, ?error, ?dest, ?xcm, "XCM send failed with error"); + MessageProcessorError::SendMessage(error) + })?; + + // Return the message_id + Ok(message_id) + } +} + +impl + XcmMessageProcessor +where + T: frame_system::Config, + Sender: SendXcm, + Executor: ExecuteXcm, + Converter: ConvertMessage, + AccountToLocation: for<'a> TryConvert<&'a T::AccountId, Location>, + TargetLocation: Get, +{ + fn send_xcm( + dest: Location, + fee_payer: &T::AccountId, + xcm: Xcm<()>, + ) -> Result { + let fee_payer = AccountToLocation::try_convert(fee_payer).map_err(|err| { + tracing::error!( + target: LOG_TARGET, + ?err, + "Failed to convert account to XCM location", + ); + SendError::NotApplicable + })?; + let (ticket, fee) = validate_send::(dest, xcm)?; + Executor::charge_fees(fee_payer, fee).map_err(|error| { + tracing::error!( + target: LOG_TARGET, + ?error, + "Charging fees failed with error", + ); + SendError::Fees + })?; + Sender::deliver(ticket) + } +} diff --git a/bridges/snowbridge/primitives/inbound-queue/src/v2/traits.rs b/bridges/snowbridge/primitives/inbound-queue/src/v2/traits.rs index 3ad07bd0d6f4f..c8fbce78c13f9 100644 --- a/bridges/snowbridge/primitives/inbound-queue/src/v2/traits.rs +++ b/bridges/snowbridge/primitives/inbound-queue/src/v2/traits.rs @@ -3,7 +3,8 @@ // SPDX-FileCopyrightText: 2021-2025 Parity Technologies (UK) Ltd. use super::Message; use sp_core::RuntimeDebug; -use xcm::latest::Xcm; +use sp_runtime::{DispatchError, Weight}; +use xcm::latest::{SendError, Xcm}; /// Converts an inbound message from Ethereum to an XCM message that can be /// executed on a parachain. @@ -21,3 +22,76 @@ pub enum ConvertMessageError { /// Invalid network specified (not from Ethereum) InvalidNetwork, } + +/// Reason why a message processor failed. +#[derive(Clone, RuntimeDebug, PartialEq)] +pub enum MessageProcessorError { + /// Message processing failed. + ProcessMessage(DispatchError), + /// Message conversion failed. + ConvertMessage(ConvertMessageError), + /// Message sending failed. + SendMessage(SendError), +} + +/// Trait to define the logic for checking and processing inbound messages. +pub trait MessageProcessor { + /// Lightweight function to check if this processor can handle the message + fn can_process_message(relayer: &AccountId, message: &Message) -> bool; + /// Process the message and return the message ID + fn process_message( + relayer: AccountId, + message: Message, + ) -> Result<([u8; 32], Option), MessageProcessorError>; + + /// Returns the worst case message processor weight + fn worst_case_message_processor_weight() -> Weight { + Weight::default() + } +} + +#[impl_trait_for_tuples::impl_for_tuples(10)] +impl MessageProcessor for Tuple { + fn can_process_message(relayer: &AccountId, message: &Message) -> bool { + for_tuples!( #( + match Tuple::can_process_message(&relayer, &message) { + true => { + return true; + }, + _ => {} + } + )* ); + + false + } + + fn process_message( + relayer: AccountId, + message: Message, + ) -> Result<([u8; 32], Option), MessageProcessorError> { + for_tuples!( #( + match Tuple::can_process_message(&relayer, &message) { + true => { + return Tuple::process_message(relayer, message) + }, + _ => {} + } + )* ); + + Err(MessageProcessorError::ProcessMessage(DispatchError::Other( + "No handler found for message!", + ))) + } + + fn worst_case_message_processor_weight() -> Weight { + let mut max_weight = Weight::zero(); + + for_tuples!( #( + max_weight = max_weight.max( + Tuple::worst_case_message_processor_weight() + ); + )* ); + + max_weight + } +} diff --git a/bridges/snowbridge/primitives/outbound-queue/src/v1/converter/mod.rs b/bridges/snowbridge/primitives/outbound-queue/src/v1/converter/mod.rs index 7f6275fa331e7..68ac819b4fb36 100644 --- a/bridges/snowbridge/primitives/outbound-queue/src/v1/converter/mod.rs +++ b/bridges/snowbridge/primitives/outbound-queue/src/v1/converter/mod.rs @@ -11,9 +11,9 @@ use codec::{Decode, Encode}; use super::message::{Command, Message, SendMessage}; use frame_support::{ensure, traits::Get}; -use snowbridge_core::{AgentId, ChannelId, ParaId, TokenId, TokenIdOf}; +use snowbridge_core::{AgentId, ChannelId, ParaId, TokenId}; use sp_core::{H160, H256}; -use sp_runtime::traits::MaybeConvert; +use sp_runtime::traits::MaybeEquivalence; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::{ConvertLocation, ExportXcm}; @@ -48,7 +48,7 @@ where EthereumNetwork: Get, OutboundQueue: SendMessage, AgentHashedDescription: ConvertLocation, - ConvertAssetId: MaybeConvert, + ConvertAssetId: MaybeEquivalence, { type Ticket = (Vec, XcmHash); @@ -194,7 +194,7 @@ struct XcmConverter<'a, ConvertAssetId, Call> { } impl<'a, ConvertAssetId, Call> XcmConverter<'a, ConvertAssetId, Call> where - ConvertAssetId: MaybeConvert, + ConvertAssetId: MaybeEquivalence, { fn new(message: &'a Xcm, ethereum_network: NetworkId, agent_id: AgentId) -> Self { Self { @@ -411,9 +411,7 @@ where // transfer amount must be greater than 0. ensure!(amount > 0, ZeroAssetTransfer); - let token_id = TokenIdOf::convert_location(&asset_id).ok_or(InvalidAsset)?; - - ConvertAssetId::maybe_convert(token_id).ok_or(InvalidAsset)?; + let token_id = ConvertAssetId::convert_back(&asset_id).ok_or(InvalidAsset)?; // Check if there is a SetTopic and skip over it if found. let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; diff --git a/bridges/snowbridge/primitives/outbound-queue/src/v1/converter/tests.rs b/bridges/snowbridge/primitives/outbound-queue/src/v1/converter/tests.rs index 9ce5bb8b29d30..158c4604a9435 100644 --- a/bridges/snowbridge/primitives/outbound-queue/src/v1/converter/tests.rs +++ b/bridges/snowbridge/primitives/outbound-queue/src/v1/converter/tests.rs @@ -62,10 +62,13 @@ impl SendMessageFeeProvider for MockErrOutboundQueue { } pub struct MockTokenIdConvert; -impl MaybeConvert for MockTokenIdConvert { - fn maybe_convert(_id: TokenId) -> Option { +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { Some(Location::new(1, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))])) } + fn convert_back(_loc: &Location) -> Option { + Some(1) + } } #[test] diff --git a/bridges/snowbridge/primitives/outbound-queue/src/v1/message.rs b/bridges/snowbridge/primitives/outbound-queue/src/v1/message.rs index c5a1ebb391072..fcc7cf8d5b8b3 100644 --- a/bridges/snowbridge/primitives/outbound-queue/src/v1/message.rs +++ b/bridges/snowbridge/primitives/outbound-queue/src/v1/message.rs @@ -6,7 +6,7 @@ use crate::{OperatingMode, SendError, SendMessageFeeProvider}; use codec::{Decode, DecodeWithMemTracking, Encode}; use ethabi::Token; use scale_info::TypeInfo; -use snowbridge_core::{pricing::UD60x18, ChannelId}; +use snowbridge_core::{pricing::UD60x18, AgentId, ChannelId}; use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; use sp_core::{RuntimeDebug, H160, H256, U256}; use sp_std::{borrow::ToOwned, vec, vec::Vec}; @@ -74,11 +74,15 @@ pub enum Command { /// Optionally invoke an initializer in the implementation contract initializer: Option, }, + /// An UpdateChannel message was sent to the Gateway + UpdateChannel { channel_id: ChannelId, mode: OperatingMode }, /// Set the global operating mode of the Gateway contract SetOperatingMode { /// The new operating mode mode: OperatingMode, }, + /// An TransferNativeFromAgent message was sent to the Gateway + TransferNativeFromAgent { agent_id: AgentId, recipient: H160, amount: u128 }, /// Set token fees of the Gateway contract SetTokenTransferFees { /// The fee(DOT) for the cost of creating asset on AssetHub @@ -136,7 +140,9 @@ impl Command { match self { Command::AgentExecute { .. } => 0, Command::Upgrade { .. } => 1, + Command::UpdateChannel { .. } => 4, Command::SetOperatingMode { .. } => 5, + Command::TransferNativeFromAgent { .. } => 6, Command::SetTokenTransferFees { .. } => 7, Command::SetPricingParameters { .. } => 8, Command::UnlockNativeToken { .. } => 9, @@ -158,8 +164,18 @@ impl Command { Token::FixedBytes(impl_code_hash.as_bytes().to_owned()), initializer.clone().map_or(Token::Bytes(vec![]), |i| Token::Bytes(i.params)), ])]), + Command::UpdateChannel { channel_id, mode } => ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(channel_id.as_ref().to_owned()), + Token::Uint(U256::from((*mode) as u64)), + ])]), Command::SetOperatingMode { mode } => ethabi::encode(&[Token::Tuple(vec![Token::Uint(U256::from((*mode) as u64))])]), + Command::TransferNativeFromAgent { agent_id, recipient, amount } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(agent_id.as_bytes().to_owned()), + Token::Address(*recipient), + Token::Uint(U256::from(*amount)), + ])]), Command::SetTokenTransferFees { create_asset_xcm, transfer_asset_xcm, @@ -360,6 +376,8 @@ impl GasMeter for ConstantGasMeter { // the the initializer is called. 50_000 + initializer_max_gas }, + Command::UpdateChannel { .. } => 50_000, + Command::TransferNativeFromAgent { .. } => 60_000, Command::SetTokenTransferFees { .. } => 60_000, Command::SetPricingParameters { .. } => 60_000, Command::UnlockNativeToken { .. } => 200_000, diff --git a/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/convert.rs b/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/convert.rs index f64554e42756a..06f9f1c63057d 100644 --- a/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/convert.rs +++ b/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/convert.rs @@ -5,7 +5,7 @@ use codec::DecodeAll; use core::slice::Iter; use frame_support::{ensure, BoundedVec}; -use snowbridge_core::{AgentIdOf, TokenId, TokenIdOf}; +use snowbridge_core::{AgentIdOf, TokenId}; use crate::v2::{ message::{Command, Message}, @@ -14,7 +14,7 @@ use crate::v2::{ use crate::v2::convert::XcmConverterError::{AssetResolutionFailed, FilterDoesNotConsumeAllAssets}; use sp_core::H160; -use sp_runtime::traits::MaybeConvert; +use sp_runtime::traits::MaybeEquivalence; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; @@ -64,7 +64,7 @@ pub struct XcmConverter<'a, ConvertAssetId, Call> { } impl<'a, ConvertAssetId, Call> XcmConverter<'a, ConvertAssetId, Call> where - ConvertAssetId: MaybeConvert, + ConvertAssetId: MaybeEquivalence, { pub fn new(message: &'a Xcm, ethereum_network: NetworkId) -> Self { Self { @@ -173,8 +173,7 @@ where ensure!(amount > 0, ZeroAssetTransfer); // Ensure PNA already registered - let token_id = TokenIdOf::convert_location(&asset_id).ok_or(InvalidAsset)?; - ConvertAssetId::maybe_convert(token_id).ok_or(InvalidAsset)?; + let token_id = ConvertAssetId::convert_back(&asset_id).ok_or(InvalidAsset)?; commands.push(Command::MintForeignToken { token_id, recipient, amount }); } diff --git a/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/mod.rs b/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/mod.rs index c4459a05c7c51..5da68a01626ca 100644 --- a/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/mod.rs +++ b/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/mod.rs @@ -15,7 +15,7 @@ use frame_support::{ traits::{Contains, Get, ProcessMessageError}, }; use snowbridge_core::{ParaId, TokenId}; -use sp_runtime::traits::MaybeConvert; +use sp_runtime::traits::MaybeEquivalence; use sp_std::{marker::PhantomData, ops::ControlFlow, prelude::*}; use xcm::prelude::*; use xcm_builder::{CreateMatcher, ExporterFor, MatchXcm}; @@ -53,7 +53,7 @@ where UniversalLocation: Get, EthereumNetwork: Get, OutboundQueue: SendMessage, - ConvertAssetId: MaybeConvert, + ConvertAssetId: MaybeEquivalence, AssetHubParaId: Get, { type Ticket = (Vec, XcmHash); diff --git a/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/tests.rs b/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/tests.rs index bb5a88c73c809..e5baa10b90d83 100644 --- a/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/tests.rs +++ b/bridges/snowbridge/primitives/outbound-queue/src/v2/converter/tests.rs @@ -62,10 +62,13 @@ impl SendMessageFeeProvider for MockErrOutboundQueue { } pub struct MockTokenIdConvert; -impl MaybeConvert for MockTokenIdConvert { - fn maybe_convert(_id: TokenId) -> Option { +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { Some(Location::new(1, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))])) } + fn convert_back(_loc: &Location) -> Option { + Some(1) + } } #[test] diff --git a/bridges/snowbridge/test-utils/src/mock_rewards.rs b/bridges/snowbridge/test-utils/src/mock_rewards.rs index d0fa81cd60bb5..e90801312fd0f 100644 --- a/bridges/snowbridge/test-utils/src/mock_rewards.rs +++ b/bridges/snowbridge/test-utils/src/mock_rewards.rs @@ -50,6 +50,7 @@ impl From for RewardsAccountParams { parameter_types! { pub static RegisteredRewardsCount: u128 = 0; + pub static RegisteredRewardAmount: u128 = 0; } pub struct MockRewardLedger; @@ -58,8 +59,9 @@ impl RewardLedger for MockRewardLed fn register_reward( _relayer: &sp_runtime::AccountId32, _reward: BridgeReward, - _reward_balance: u128, + reward_balance: u128, ) { RegisteredRewardsCount::set(RegisteredRewardsCount::get().saturating_add(1)); + RegisteredRewardAmount::set(reward_balance); } } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs index ceb4d23c294f6..052d7eb074475 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs @@ -43,6 +43,7 @@ decl_test_parachains! { ParachainInfo: asset_hub_rococo_runtime::ParachainInfo, MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, DigestProvider: (), + AdditionalInherentCode: (), }, pallets = { PolkadotXcm: asset_hub_rococo_runtime::PolkadotXcm, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs index 0ae7067025fa4..6465e4fece3fd 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs @@ -43,6 +43,7 @@ decl_test_parachains! { ParachainInfo: asset_hub_westend_runtime::ParachainInfo, MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, DigestProvider: (), + AdditionalInherentCode: (), }, pallets = { PolkadotXcm: asset_hub_westend_runtime::PolkadotXcm, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs new file mode 100644 index 0000000000000..7125a4e8b60ee --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -0,0 +1,892 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::imports::*; +use ahr_xcm_config::UniversalLocation as AssetHubRococoUniversalLocation; +use codec::{Decode, Encode}; +use emulated_integration_tests_common::xcm_emulator::ConvertLocation; +use frame_support::pallet_prelude::TypeInfo; +use hex_literal::hex; +use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender; +use snowbridge_inbound_queue_primitives::{ + v1::{Command, Destination, MessageV1, VersionedMessage}, + EventFixture, +}; +use snowbridge_inbound_queue_primitives::{ + v1::{ConvertMessage, ConvertMessageError, VersionedMessage}, + EventProof, VerificationError, Verifier, +}; +use snowbridge_outbound_queue_primitives::OperatingMode; +use snowbridge_pallet_inbound_queue_fixtures::{ + register_token::make_register_token_message, send_native_eth::make_send_native_eth_message, + send_token::make_send_token_message, send_token_to_penpal::make_send_token_to_penpal_message, +}; +use snowbridge_pallet_system; +use sp_core::H256; +use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable}; +use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; + +const INITIAL_FUND: u128 = 5_000_000_000 * ROCOCO_ED; +pub const CHAIN_ID: u64 = 11155111; +const TREASURY_ACCOUNT: [u8; 32] = + hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000"); +pub const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); +const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); +const INSUFFICIENT_XCM_FEE: u128 = 1000; +const XCM_FEE: u128 = 4_000_000_000; + +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum ControlCall { + #[codec(index = 3)] + CreateAgent, + #[codec(index = 4)] + CreateChannel { mode: OperatingMode }, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum SnowbridgeControl { + #[codec(index = 83)] + Control(ControlCall), +} + +pub fn send_inbound_message(fixture: EventFixture) -> DispatchResult { + EthereumBeaconClient::store_finalized_header( + fixture.finalized_header, + fixture.block_roots_root, + ) + .unwrap(); + EthereumInboundQueue::submit( + BridgeHubRococoRuntimeOrigin::signed(BridgeHubRococoSender::get()), + fixture.event, + ) +} + +/// Create an agent on Ethereum. An agent is a representation of an entity in the Polkadot +/// ecosystem (like a parachain) on Ethereum. +#[test] +#[ignore] +fn create_agent() { + let origin_para: u32 = 1001; + // Fund the origin parachain sovereign account so that it can pay execution fees. + BridgeHubRococo::fund_para_sovereign(origin_para.into(), INITIAL_FUND); + + let sudo_origin = ::RuntimeOrigin::root(); + let destination = Rococo::child_location_of(BridgeHubRococo::para_id()).into(); + + let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); + // Construct XCM to create an agent for para 1001 + let remote_xcm = VersionedXcm::from(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + DescendOrigin(Parachain(origin_para).into()), + Transact { + origin_kind: OriginKind::Xcm, + call: create_agent_call.encode().into(), + fallback_max_weight: None, + }, + ])); + + // Rococo Global Consensus + // Send XCM message from Relay Chain to Bridge Hub source Parachain + Rococo::execute_with(|| { + assert_ok!(::XcmPallet::send( + sudo_origin, + bx!(destination), + bx!(remote_xcm), + )); + + type RuntimeEvent = ::RuntimeEvent; + // Check that the Transact message was sent + assert_expected_events!( + Rococo, + vec![ + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that a message was sent to Ethereum to create the agent + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::CreateAgent { + .. + }) => {}, + ] + ); + }); +} + +/// Create a channel for a consensus system. A channel is a bidirectional messaging channel +/// between BridgeHub and Ethereum. +#[test] +#[ignore] +fn create_channel() { + let origin_para: u32 = 1001; + // Fund AssetHub sovereign account so that it can pay execution fees. + BridgeHubRococo::fund_para_sovereign(origin_para.into(), INITIAL_FUND); + + let sudo_origin = ::RuntimeOrigin::root(); + let destination: VersionedLocation = + Rococo::child_location_of(BridgeHubRococo::para_id()).into(); + + let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); + // Construct XCM to create an agent for para 1001 + let create_agent_xcm = VersionedXcm::from(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + DescendOrigin(Parachain(origin_para).into()), + Transact { + origin_kind: OriginKind::Xcm, + call: create_agent_call.encode().into(), + fallback_max_weight: None, + }, + ])); + + let create_channel_call = + SnowbridgeControl::Control(ControlCall::CreateChannel { mode: OperatingMode::Normal }); + // Construct XCM to create a channel for para 1001 + let create_channel_xcm = VersionedXcm::from(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + DescendOrigin(Parachain(origin_para).into()), + Transact { + origin_kind: OriginKind::Xcm, + call: create_channel_call.encode().into(), + fallback_max_weight: None, + }, + ])); + + // Rococo Global Consensus + // Send XCM message from Relay Chain to Bridge Hub source Parachain + Rococo::execute_with(|| { + assert_ok!(::XcmPallet::send( + sudo_origin.clone(), + bx!(destination.clone()), + bx!(create_agent_xcm), + )); + + assert_ok!(::XcmPallet::send( + sudo_origin, + bx!(destination), + bx!(create_channel_xcm), + )); + + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + Rococo, + vec![ + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the Channel was created + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::CreateChannel { + .. + }) => {}, + ] + ); + }); +} + +/// Tests the registering of a token as an asset on AssetHub. +#[test] +fn register_weth_token_from_ethereum_to_asset_hub() { + // Fund AssetHub sovereign account so that it can pay execution fees. + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND); + // Fund ethereum sovereign on AssetHub to satisfy ED + AssetHubRococo::fund_accounts(vec![(snowbridge_sovereign(), INITIAL_FUND)]); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Construct RegisterToken message and sent to inbound queue + let register_token_message = make_register_token_message(); + assert_ok!(send_inbound_message(register_token_message.clone())); + + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {}, + ] + ); + }); +} + +/// Tests the registering of a token as an asset on AssetHub, and then subsequently sending +/// a token from Ethereum to AssetHub. +#[test] +fn send_weth_token_from_ethereum_to_asset_hub() { + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND); + // Fund ethereum sovereign and receiver on AssetHub to satisfy ED + AssetHubRococo::fund_accounts(vec![ + (snowbridge_sovereign(), INITIAL_FUND), + (AssetHubRococoReceiver::get(), INITIAL_FUND), + ]); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Construct RegisterToken message and sent to inbound queue + assert_ok!(send_inbound_message(make_register_token_message())); + + // Construct SendToken message and sent to inbound queue + assert_ok!(send_inbound_message(make_send_token_message())); + + // Check that the message was sent + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the token was received and issued as a foreign asset on AssetHub + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + ] + ); + }); +} + +/// Tests sending a token to a 3rd party parachain, called PenPal. The token reserve is +/// still located on AssetHub. +#[test] +fn send_weth_from_ethereum_to_penpal() { + let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new( + 1, + [Parachain(AssetHubRococo::para_id().into())], + )); + // Fund AssetHub sovereign account so it can pay execution fees for the asset transfer + BridgeHubRococo::fund_accounts(vec![(asset_hub_sovereign.clone(), INITIAL_FUND)]); + + // Fund PenPal receiver (covering ED) + let native_id: Location = Parent.into(); + let receiver: AccountId = [ + 28, 189, 45, 67, 83, 10, 68, 112, 90, 208, 136, 175, 49, 62, 24, 248, 11, 83, 239, 22, 179, + 97, 119, 205, 75, 119, 184, 70, 242, 165, 240, 124, + ] + .into(); + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + native_id, + receiver, + penpal_runtime::EXISTENTIAL_DEPOSIT, + ); + + PenpalA::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]).encode(), + )], + )); + }); + + let ethereum_network_v5: NetworkId = EthereumNetwork::get().into(); + + // The Weth asset location, identified by the contract address on Ethereum + let weth_asset_location: Location = + (Parent, Parent, ethereum_network_v5, AccountKey20 { network: None, key: WETH }).into(); + + let origin_location = (Parent, Parent, ethereum_network_v5).into(); + + // Fund ethereum sovereign on AssetHub + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::::convert_location(&origin_location) + .unwrap(); + AssetHubRococo::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + // Create asset on the Penpal parachain. + PenpalA::execute_with(|| { + assert_ok!(::ForeignAssets::force_create( + ::RuntimeOrigin::root(), + weth_asset_location.clone(), + asset_hub_sovereign.into(), + false, + 1000, + )); + + assert!(::ForeignAssets::asset_exists(weth_asset_location)); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Construct RegisterToken message and sent to inbound queue + assert_ok!(send_inbound_message(make_register_token_message())); + + // Construct SendToken message to AssetHub(only for increase the nonce as the same order in + // smoke test) + assert_ok!(send_inbound_message(make_send_token_message())); + + // Construct SendToken message and sent to inbound queue + assert_ok!(send_inbound_message(make_send_token_to_penpal_message())); + + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the assets were issued on AssetHub + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + PenpalA::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the assets were issued on PenPal + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + ] + ); + }); +} + +/// Tests the full cycle of token transfers: +/// - registering a token on AssetHub +/// - sending a token to AssetHub +/// - returning the token to Ethereum +#[test] +fn send_weth_asset_from_asset_hub_to_ethereum() { + use ahr_xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; + let assethub_location = BridgeHubRococo::sibling_location_of(AssetHubRococo::para_id()); + let assethub_sovereign = BridgeHubRococo::sovereign_account_id_of(assethub_location); + + AssetHubRococo::force_default_xcm_version(Some(XCM_VERSION)); + BridgeHubRococo::force_default_xcm_version(Some(XCM_VERSION)); + AssetHubRococo::force_xcm_version( + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), + XCM_VERSION, + ); + + BridgeHubRococo::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + AssetHubRococo::fund_accounts(vec![ + (AssetHubRococoReceiver::get(), INITIAL_FUND), + (snowbridge_sovereign(), INITIAL_FUND), + ]); + + const WETH_AMOUNT: u128 = 1_000_000_000; + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Construct RegisterToken message and sent to inbound queue + assert_ok!(send_inbound_message(make_register_token_message())); + + // Check that the register token message was sent using xcm + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + + // Construct SendToken message and sent to inbound queue + assert_ok!(send_inbound_message(make_send_token_message())); + + // Check that the send token message was sent using xcm + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + // Check that AssetHub has issued the foreign asset + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + ] + ); + let assets = vec![Asset { + id: AssetId(Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: CHAIN_ID }), + AccountKey20 { network: None, key: WETH }, + ], + )), + fun: Fungible(WETH_AMOUNT), + }]; + let multi_assets = VersionedAssets::from(Assets::from(assets)); + + let destination = VersionedLocation::from(Location::new( + 2, + [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], + )); + + let beneficiary = VersionedLocation::from(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + let free_balance_before = ::Balances::free_balance( + AssetHubRococoReceiver::get(), + ); + // Send the Weth back to Ethereum + ::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubRococoReceiver::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + Unlimited, + ) + .unwrap(); + let free_balance_after = ::Balances::free_balance( + AssetHubRococoReceiver::get(), + ); + // Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender + let free_balance_diff = free_balance_before - free_balance_after; + assert!(free_balance_diff > DefaultBridgeHubEthereumBaseFee::get()); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued {..}) => {}, + ] + ); + let events = BridgeHubRococo::events(); + // Check that the local fee was credited to the Snowbridge sovereign account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _amount }) + if *who == TREASURY_ACCOUNT.into() + )), + "Snowbridge sovereign takes local fee." + ); + // Check that the remote fee was credited to the AssetHub sovereign account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _amount }) + if *who == assethub_sovereign + )), + "AssetHub sovereign takes remote fee." + ); + }); +} + +/// Tests the full cycle of eth transfers: +/// - sending a token to AssetHub +/// - returning the token to Ethereum +#[test] +fn send_eth_asset_from_asset_hub_to_ethereum_and_back() { + let ethereum_network: NetworkId = EthereumNetwork::get().into(); + let origin_location = (Parent, Parent, ethereum_network).into(); + + use ahr_xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; + let assethub_location = BridgeHubRococo::sibling_location_of(AssetHubRococo::para_id()); + let assethub_sovereign = BridgeHubRococo::sovereign_account_id_of(assethub_location); + let ethereum_sovereign: AccountId = AssetHubRococo::execute_with(|| { + ExternalConsensusLocationsConverterFor::< + AssetHubRococoUniversalLocation, + AccountId, + >::convert_location(&origin_location) + .unwrap() + }); + + AssetHubRococo::force_default_xcm_version(Some(XCM_VERSION)); + BridgeHubRococo::force_default_xcm_version(Some(XCM_VERSION)); + AssetHubRococo::force_xcm_version(origin_location.clone(), XCM_VERSION); + + BridgeHubRococo::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + AssetHubRococo::fund_accounts(vec![ + (AssetHubRococoReceiver::get(), INITIAL_FUND), + (ethereum_sovereign.clone(), INITIAL_FUND), + ]); + + const ETH_AMOUNT: u128 = 1_000_000_000_000_000_000; + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + // Set the gateway. This is needed because new fixtures use a different gateway address. + assert_ok!(::System::set_storage( + RuntimeOrigin::root(), + vec![( + EthereumGatewayAddress::key().to_vec(), + sp_core::H160(hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d")).encode(), + )], + )); + + // Construct SendToken message and sent to inbound queue + assert_ok!(send_inbound_message(make_send_native_eth_message())); + + // Check that the send token message was sent using xcm + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + let _issued_event = RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { + asset_id: origin_location.clone(), + owner: AssetHubRococoReceiver::get().into(), + amount: ETH_AMOUNT, + }); + // Check that AssetHub has issued the foreign asset + assert_expected_events!( + AssetHubRococo, + vec![ + _issued_event => {}, + ] + ); + let assets = + vec![Asset { id: AssetId(origin_location.clone()), fun: Fungible(ETH_AMOUNT) }]; + let multi_assets = VersionedAssets::from(Assets::from(assets)); + + let destination = origin_location.clone().into(); + + let beneficiary = VersionedLocation::from(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + let free_balance_before = ::Balances::free_balance( + AssetHubRococoReceiver::get(), + ); + // Send the Weth back to Ethereum + ::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubRococoReceiver::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + Unlimited, + ) + .unwrap(); + + let _burned_event = RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { + asset_id: origin_location.clone(), + owner: AssetHubRococoReceiver::get().into(), + balance: ETH_AMOUNT, + }); + // Check that AssetHub has issued the foreign asset + let _destination = origin_location.clone(); + assert_expected_events!( + AssetHubRococo, + vec![ + _burned_event => {}, + RuntimeEvent::PolkadotXcm(pallet_xcm::Event::Sent { + destination: _destination, .. + }) => {}, + ] + ); + + let free_balance_after = ::Balances::free_balance( + AssetHubRococoReceiver::get(), + ); + // Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender + let free_balance_diff = free_balance_before - free_balance_after; + assert!(free_balance_diff > DefaultBridgeHubEthereumBaseFee::get()); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageAccepted {..}) => {}, + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued {..}) => {}, + ] + ); + + let events = BridgeHubRococo::events(); + // Check that the local fee was credited to the Snowbridge sovereign account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _amount }) + if *who == TREASURY_ACCOUNT.into() + )), + "Snowbridge sovereign takes local fee." + ); + // Check that the remote fee was credited to the AssetHub sovereign account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _amount }) + if *who == assethub_sovereign + )), + "AssetHub sovereign takes remote fee." + ); + }); +} + +#[test] +fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() { + // Insufficient fund + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), 1_000); + + BridgeHubRococo::execute_with(|| { + assert_err!(send_inbound_message(make_register_token_message()), Token(FundsUnavailable)); + }); +} + +#[test] +fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type EthereumInboundQueue = + ::EthereumInboundQueue; + let message_id: H256 = [0; 32].into(); + let message = VersionedXcmMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::RegisterToken { + token: WETH.into(), + // Insufficient fee which should trigger the trap + fee: INSUFFICIENT_XCM_FEE, + }, + }); + let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {}, + ] + ); + }); +} + +fn send_weth_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u128) { + let ethereum_network_v5: NetworkId = EthereumNetwork::get().into(); + let weth_asset_location: Location = + Location::new(2, [ethereum_network_v5.into(), AccountKey20 { network: None, key: WETH }]); + // Fund asset hub sovereign on bridge hub + let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new( + 1, + [Parachain(AssetHubRococo::para_id().into())], + )); + BridgeHubRococo::fund_accounts(vec![(asset_hub_sovereign.clone(), INITIAL_FUND)]); + + // Register WETH + AssetHubRococo::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_ok!(::ForeignAssets::force_create( + RuntimeOrigin::root(), + weth_asset_location.clone().try_into().unwrap(), + asset_hub_sovereign.into(), + false, + 1, + )); + + assert!(::ForeignAssets::asset_exists( + weth_asset_location.clone().try_into().unwrap(), + )); + }); + + // Send WETH to an existent account on asset hub + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + type EthereumInboundQueue = + ::EthereumInboundQueue; + let message_id: H256 = [0; 32].into(); + let message = VersionedXcmMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendToken { + token: WETH.into(), + destination: Destination::AccountId32 { id: account_id }, + amount: 1_000_000, + fee, + }, + }); + let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); + assert_ok!(EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into())); + + // Check that the message was sent + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); +} + +#[test] +fn send_weth_from_ethereum_to_existent_account_on_asset_hub() { + send_weth_from_ethereum_to_asset_hub_with_fee(AssetHubRococoSender::get().into(), XCM_FEE); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the token was received and issued as a foreign asset on AssetHub + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + ] + ); + }); +} + +#[test] +fn send_weth_from_ethereum_to_non_existent_account_on_asset_hub() { + send_weth_from_ethereum_to_asset_hub_with_fee([1; 32], XCM_FEE); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the token was received and issued as a foreign asset on AssetHub + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + ] + ); + }); +} + +#[test] +fn send_weth_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficient_fee() { + send_weth_from_ethereum_to_asset_hub_with_fee([1; 32], INSUFFICIENT_XCM_FEE); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the message was not processed successfully due to insufficient fee + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {}, + ] + ); + }); +} + +#[test] +fn send_weth_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed( +) { + // On AH the xcm fee is 26_789_690 and the ED is 3_300_000 + send_weth_from_ethereum_to_asset_hub_with_fee([1; 32], 30_000_000); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the message was not processed successfully due to insufficient ED + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {}, + ] + ); + }); +} + +#[test] +fn create_foreign_asset_deposit_is_equal_to_asset_hub_foreign_asset_pallet_deposit() { + let asset_hub_deposit = asset_hub_rococo_runtime::ForeignAssetsAssetDeposit::get(); + let bridge_hub_deposit = bp_asset_hub_rococo::CreateForeignAssetDeposit::get(); + assert!( + bridge_hub_deposit >= + asset_hub_deposit, + "The BridgeHub asset creation deposit must be equal to or larger than the asset creation deposit configured on BridgeHub" + ); +} + +pub fn snowbridge_sovereign() -> sp_runtime::AccountId32 { + use asset_hub_rococo_runtime::xcm_config::UniversalLocation as AssetHubWestendUniversalLocation; + let ethereum_sovereign: AccountId = AssetHubRococo::execute_with(|| { + ExternalConsensusLocationsConverterFor::< + AssetHubWestendUniversalLocation, + [u8; 32], + >::convert_location(&Location::new( + 2, + [xcm::v5::Junction::GlobalConsensus(EthereumNetwork::get())], + )) + .unwrap() + .into() + }); + + ethereum_sovereign +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 994b4566569ac..82e1748cf3cb0 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -38,12 +38,12 @@ use asset_hub_westend_runtime::xcm_config::{ use bridge_hub_westend_runtime::{ bridge_to_ethereum_config::EthereumGatewayAddress, EthereumBeaconClient, EthereumInboundQueue, }; -use codec::{Decode, Encode}; +use codec::Encode; use emulated_integration_tests_common::{ snowbridge::{SEPOLIA_ID, WETH}, PENPAL_B_ID, RESERVABLE_ASSET_ID, }; -use frame_support::{pallet_prelude::TypeInfo, traits::fungibles::Mutate}; +use frame_support::traits::fungibles::Mutate; use hex_literal::hex; use rococo_westend_system_emulated_network::{ asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner, @@ -67,19 +67,6 @@ const INSUFFICIENT_XCM_FEE: u128 = 1000; const TOKEN_AMOUNT: u128 = 100_000_000_000; const BRIDGE_FEE: u128 = 4_000_000_000_000; -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub enum ControlCall { - #[codec(index = 3)] - CreateAgent, -} - -#[allow(clippy::large_enum_variant)] -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub enum SnowbridgeControl { - #[codec(index = 83)] - Control(ControlCall), -} - pub fn send_inbound_message(fixture: EventFixture) -> DispatchResult { EthereumBeaconClient::store_finalized_header( fixture.finalized_header, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_inbound.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_inbound.rs index 8e3439965afde..2733f45610a07 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_inbound.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_inbound.rs @@ -23,7 +23,7 @@ use crate::{ use asset_hub_westend_runtime::ForeignAssets; use bridge_hub_westend_runtime::{ bridge_common_config::BridgeReward, - bridge_to_ethereum_config::{CreateAssetCall, EthereumGatewayAddress}, + bridge_to_ethereum_config::{CreateAssetCallIndex, EthereumGatewayAddress}, EthereumInboundQueueV2, }; use codec::Encode; @@ -36,7 +36,7 @@ use rococo_westend_system_emulated_network::penpal_emulated_chain::PARA_ID_B; use snowbridge_core::{reward::MessageId, AssetMetadata, TokenIdOf}; use snowbridge_inbound_queue_primitives::v2::{ EthereumAsset::{ForeignTokenERC20, NativeTokenERC20}, - Message, Network, XcmPayload, + Message, Network, Payload, }; use sp_core::{H160, H256}; use sp_io::hashing::blake2_256; @@ -77,7 +77,7 @@ fn register_token_v2() { nonce: 1, origin, assets: vec![], - xcm: XcmPayload::CreateAsset { token, network: Network::Polkadot }, + payload: Payload::CreateAsset { token, network: Network::Polkadot }, claimer: Some(claimer_bytes), // Used to pay the asset creation deposit. value: 9_000_000_000_000u128, @@ -188,7 +188,7 @@ fn send_token_v2() { nonce: 1, origin, assets, - xcm: XcmPayload::Raw(versioned_message_xcm.encode()), + payload: Payload::Raw(versioned_message_xcm.encode()), claimer: Some(claimer_bytes), value: 1_500_000_000_000u128, execution_fee: 1_500_000_000_000u128, @@ -298,7 +298,7 @@ fn send_weth_v2() { nonce: 1, origin, assets, - xcm: XcmPayload::Raw(versioned_message_xcm.encode()), + payload: Payload::Raw(versioned_message_xcm.encode()), claimer: Some(claimer_bytes), value: 3_500_000_000_000u128, execution_fee: 1_500_000_000_000u128, @@ -422,7 +422,7 @@ fn register_and_send_multiple_tokens_v2() { origin_kind: OriginKind::Xcm, fallback_max_weight: None, call: ( - CreateAssetCall::get(), + CreateAssetCallIndex::get(), token_location.clone(), MultiAddress::<[u8; 32], ()>::Id(bridge_owner.clone().into()), 1u128, @@ -444,7 +444,7 @@ fn register_and_send_multiple_tokens_v2() { nonce: 1, origin, assets, - xcm: XcmPayload::Raw(versioned_message_xcm.encode()), + payload: Payload::Raw(versioned_message_xcm.encode()), claimer: Some(claimer_bytes), value: 3_500_000_000_000u128, execution_fee: 1_500_000_000_000u128, @@ -636,7 +636,7 @@ fn send_token_to_penpal_v2() { nonce: 1, origin, assets, - xcm: XcmPayload::Raw(versioned_message_xcm.encode()), + payload: Payload::Raw(versioned_message_xcm.encode()), claimer: Some(claimer_bytes), value: 3_500_000_000_000u128, execution_fee: 1_500_000_000_000u128, @@ -820,7 +820,7 @@ fn send_foreign_erc20_token_back_to_polkadot() { nonce: 1, origin, assets, - xcm: XcmPayload::Raw(versioned_message_xcm.encode()), + payload: Payload::Raw(versioned_message_xcm.encode()), claimer: Some(claimer_bytes), value: 1_500_000_000_000u128, execution_fee: 3_500_000_000_000u128, @@ -916,7 +916,7 @@ fn invalid_xcm_traps_funds_on_ah() { nonce: 1, origin, assets, - xcm: XcmPayload::Raw(instructions.to_vec()), + payload: Payload::Raw(instructions.to_vec()), claimer: Some(claimer_bytes), value: 1_500_000_000_000u128, execution_fee: 1_500_000_000_000u128, @@ -983,7 +983,7 @@ fn invalid_claimer_does_not_fail_the_message() { nonce: 1, origin, assets, - xcm: XcmPayload::Raw(versioned_message_xcm.encode()), + payload: Payload::Raw(versioned_message_xcm.encode()), // Set an invalid claimer claimer: Some(hex!("2b7ce7bc7e87e4d6619da21487c7a53f").to_vec()), value: 1_500_000_000_000u128, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_inbound_to_rococo.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_inbound_to_rococo.rs index 0a19df6bbbf29..767730bf7d1e6 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_inbound_to_rococo.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_inbound_to_rococo.rs @@ -36,7 +36,7 @@ use hex_literal::hex; use snowbridge_core::TokenIdOf; use snowbridge_inbound_queue_primitives::v2::{ EthereumAsset::{ForeignTokenERC20, NativeTokenERC20}, - Message, XcmPayload, + Message, Payload, }; use sp_core::{H160, H256}; use xcm::opaque::latest::AssetTransferFilter::{ReserveDeposit, ReserveWithdraw}; @@ -152,7 +152,7 @@ fn send_token_to_rococo_v2() { nonce: 1, origin, assets, - xcm: XcmPayload::Raw(versioned_message_xcm.encode()), + payload: Payload::Raw(versioned_message_xcm.encode()), claimer: Some(claimer_bytes), value: 3_500_000_000_000u128, execution_fee: 1_500_000_000_000u128, @@ -314,7 +314,7 @@ fn send_ether_to_rococo_v2() { nonce: 1, origin, assets: vec![], - xcm: XcmPayload::Raw(versioned_message_xcm.encode()), + payload: Payload::Raw(versioned_message_xcm.encode()), claimer: Some(claimer_bytes), value: 6_500_000_000_000u128, execution_fee: 1_500_000_000_000u128, @@ -505,7 +505,7 @@ fn send_roc_from_ethereum_to_rococo() { nonce: 1, origin, assets, - xcm: XcmPayload::Raw(versioned_message_xcm.encode()), + payload: Payload::Raw(versioned_message_xcm.encode()), claimer: Some(claimer_bytes), value: 9_500_000_000_000u128, execution_fee: 3_500_000_000_000u128, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs index 495cf6f6f5601..3493082dbbb56 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs @@ -228,3 +228,204 @@ pub fn register_relay_token_from_asset_hub_user_origin_will_fail() { ); }); } + +pub const INSUFFICIENT_REMOTE_FEE_AMOUNT: u128 = 1_000_000_000; + +// Test that the asset trapping and claim flow work correctly. +#[test] +fn transfer_from_penpal_to_ethereum_trapped_on_ah_and_then_claim_can_work() { + create_pools_on_ah(); + register_pal_on_ah(); + register_pal_on_bh(); + fund_on_ah(); + // penpal + set_trust_reserve_on_penpal(); + register_assets_on_penpal(); + fund_on_penpal(); + + let penpal_user_location = Location::new( + 1, + [ + Parachain(PenpalB::para_id().into()), + AccountId32 { + network: Some(ByGenesis(WESTEND_GENESIS_HASH)), + id: PenpalBSender::get().into(), + }, + ], + ); + + // Since fee is insufficient, asset got trapped on AH + PenpalB::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + let local_fee_asset_on_penpal = + Asset { id: AssetId(Location::parent()), fun: Fungible(LOCAL_FEE_AMOUNT_IN_DOT) }; + + let remote_fee_asset_on_ah = + Asset { id: AssetId(ethereum()), fun: Fungible(INSUFFICIENT_REMOTE_FEE_AMOUNT) }; + + let remote_fee_asset_on_ethereum = + Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) }; + + let ena = Asset { id: AssetId(weth_location()), fun: Fungible(TOKEN_AMOUNT) }; + + let assets = vec![ + local_fee_asset_on_penpal.clone(), + remote_fee_asset_on_ah.clone(), + remote_fee_asset_on_ethereum.clone(), + ena.clone(), + ]; + + let xcm = VersionedXcm::from(Xcm(vec![ + WithdrawAsset(assets.clone().into()), + PayFees { asset: local_fee_asset_on_penpal.clone() }, + InitiateTransfer { + destination: asset_hub(), + remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite( + remote_fee_asset_on_ah.clone().into(), + ))), + preserve_origin: true, + assets: BoundedVec::truncate_from(vec![ + AssetTransferFilter::ReserveWithdraw(Definite( + remote_fee_asset_on_ethereum.clone().into(), + )), + AssetTransferFilter::ReserveWithdraw(Definite(ena.clone().into())), + ]), + remote_xcm: Xcm(vec![ + SetAppendix(Xcm(vec![SetHints { + hints: vec![AssetClaimer { location: penpal_user_location }] + .try_into() + .unwrap(), + }])), + InitiateTransfer { + destination: ethereum(), + remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite( + remote_fee_asset_on_ethereum.clone().into(), + ))), + preserve_origin: true, + assets: BoundedVec::truncate_from(vec![ + AssetTransferFilter::ReserveWithdraw(Definite(ena.clone().into())), + ]), + remote_xcm: Xcm(vec![DepositAsset { + assets: Wild(All), + beneficiary: beneficiary(), + }]), + }, + ]), + }, + ])); + + assert_ok!(::PolkadotXcm::execute( + RuntimeOrigin::signed(PenpalBSender::get()), + bx!(xcm.clone()), + Weight::from(EXECUTION_WEIGHT), + )); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::PolkadotXcm(pallet_xcm::Event::ProcessXcmError { .. }) => {},] + ); + }); + + // Claim the trapped asset and deposit on AH. + PenpalB::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + let local_fee_asset_on_penpal = + Asset { id: AssetId(Location::parent()), fun: Fungible(LOCAL_FEE_AMOUNT_IN_DOT) }; + + let remote_fee_asset_on_ah = + Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) }; + + let assets = vec![local_fee_asset_on_penpal.clone(), remote_fee_asset_on_ah.clone()]; + + let xcm = VersionedXcm::from(Xcm(vec![ + WithdrawAsset(assets.clone().into()), + PayFees { asset: local_fee_asset_on_penpal.clone() }, + InitiateTransfer { + destination: asset_hub(), + remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite( + remote_fee_asset_on_ah.clone().into(), + ))), + preserve_origin: true, + assets: BoundedVec::truncate_from(vec![]), + remote_xcm: Xcm(vec![ + ClaimAsset { + assets: vec![ + Asset { id: AssetId(ethereum()), fun: Fungible(600422871584) }, + Asset { id: AssetId(weth_location()), fun: Fungible(TOKEN_AMOUNT) }, + ] + .into(), + ticket: GeneralIndex(5).into(), + }, + RefundSurplus, + DepositAsset { + assets: Wild(All), + beneficiary: AssetHubWestendReceiver::get().into(), + }, + ]), + }, + ])); + + assert_ok!(::PolkadotXcm::execute( + RuntimeOrigin::signed(PenpalBSender::get()), + bx!(xcm.clone()), + Weight::from(EXECUTION_WEIGHT), + )); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsClaimed { .. }) => {},] + ); + }); +} + +// A malicious user attempted to exploit the bridge by manually adding an AliasOrigin in the +// remoteXcm, successfully routing to the V2 path, but ultimately failing at the BH Exporter. +#[test] +pub fn exploit_v2_route_with_legacy_v1_transfer_will_fail() { + create_pools_on_ah(); + fund_on_ah(); + + let remote_fee_asset = + Asset { id: AssetId(eth_location()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) }; + + let reserve_asset = Asset { id: AssetId(eth_location()), fun: Fungible(TOKEN_AMOUNT) }; + + let assets = vec![reserve_asset.clone(), remote_fee_asset.clone()]; + + let custom_xcm_on_dest = Xcm::<()>(vec![ + AliasOrigin(Location::parent()), + DepositAsset { assets: Wild(AllCounted(2)), beneficiary: beneficiary() }, + ]); + + assert_ok!(AssetHubWestend::execute_with(|| { + ::PolkadotXcm::transfer_assets_using_type_and_then( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + bx!(eth_location().into()), + bx!(assets.into()), + bx!(TransferType::DestinationReserve), + bx!(AssetId(eth_location()).into()), + bx!(TransferType::DestinationReserve), + bx!(VersionedXcm::from(custom_xcm_on_dest)), + Unlimited, + ) + })); + + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the process failed in MessageQueue + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed{ success: false, .. }) => {}, + ] + ); + }) +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index eea61e217c6ba..611bff4df1fb7 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -17,34 +17,35 @@ #[cfg(not(feature = "runtime-benchmarks"))] use crate::XcmRouter; use crate::{ - xcm_config, xcm_config::UniversalLocation, Balances, EthereumInboundQueue, - EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, RuntimeEvent, TransactionByteFee, - TreasuryAccount, + xcm_config, + xcm_config::{RelayNetwork, UniversalLocation}, + Balances, EthereumInboundQueue, EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, + RuntimeEvent, TransactionByteFee, TreasuryAccount, }; +use bridge_hub_common::AggregateMessageOrigin; +use frame_support::{parameter_types, weights::ConstantMultiplier}; +use pallet_xcm::EnsureXcm; use parachains_common::{AccountId, Balance}; use snowbridge_beacon_primitives::{Fork, ForkVersions}; -use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; +use snowbridge_core::{gwei, meth, AllowSiblingsOnly, ChannelId, PricingParameters, Rewards}; use snowbridge_inbound_queue_primitives::v1::MessageToXcm; use snowbridge_outbound_queue_primitives::v1::EthereumBlobExporter; -use sp_core::H160; -use testnet_parachains_constants::rococo::{ - currency::*, - fee::WeightToFee, - snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, -}; - -use crate::xcm_config::RelayNetwork; #[cfg(feature = "runtime-benchmarks")] use benchmark_helpers::DoNothingRouter; use bp_asset_hub_rococo::CreateForeignAssetDeposit; -use frame_support::{parameter_types, weights::ConstantMultiplier}; use hex_literal::hex; -use pallet_xcm::EnsureXcm; +use snowbridge_pallet_inbound_queue::RewardThroughSovereign; +use sp_core::H160; use sp_runtime::{ - traits::{ConstU32, ConstU8, Keccak256}, + traits::{ConstU32, ConstU8, Convert, Keccak256}, FixedU128, }; +use testnet_parachains_constants::rococo::{ + currency::*, + fee::WeightToFee, + snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, +}; use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, Parachain}; /// Exports message to the Ethereum Gateway contract. @@ -101,6 +102,17 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo; type PricingParameters = EthereumSystem; type AssetTransactor = ::AssetTransactor; + type MessageProcessor = + snowbridge_pallet_inbound_queue::xcm_message_processor::XcmMessageProcessor; + type RewardProcessor = RewardThroughSovereign; +} + +pub struct GetAggregateMessageOrigin; + +impl Convert for GetAggregateMessageOrigin { + fn convert(channel_id: ChannelId) -> AggregateMessageOrigin { + AggregateMessageOrigin::Snowbridge(channel_id) + } } impl snowbridge_pallet_outbound_queue::Config for Runtime { @@ -116,6 +128,9 @@ impl snowbridge_pallet_outbound_queue::Config for Runtime { type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue::WeightInfo; type PricingParameters = EthereumSystem; type Channels = EthereumSystem; + type AggregateMessageOrigin = AggregateMessageOrigin; + type GetAggregateMessageOrigin = GetAggregateMessageOrigin; + type OnNewCommitment = (); } #[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] @@ -143,7 +158,11 @@ parameter_types! { }, electra: Fork { version: hex!("05000000"), - epoch: 80000000000, + epoch: 0, + }, + fulu: Fork { + version: hex!("06000000"), + epoch: 5000000, } }; } @@ -173,7 +192,11 @@ parameter_types! { }, electra: Fork { version: hex!("90000074"), - epoch: 222464, // https://github.com/ethereum/EIPs/pull/9322/files + epoch: 222464, + }, + fulu: Fork { + version: hex!("90000075"), + epoch: 272640, // https://notes.ethereum.org/@bbusa/fusaka-bpo-timeline }, }; } @@ -209,13 +232,16 @@ pub mod benchmark_helpers { use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; use codec::Encode; use snowbridge_beacon_primitives::BeaconHeader; + use snowbridge_inbound_queue_primitives::EventFixture; use snowbridge_pallet_inbound_queue::BenchmarkHelper; use sp_core::H256; use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; impl BenchmarkHelper for Runtime { - fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { - EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); + fn initialize_storage() -> EventFixture { + //EthereumBeaconClient::store_finalized_header(beacon_header, + // block_roots_root).unwrap(); + todo!() } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs index 1ce2329a97618..a1df98bde966f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs @@ -70,6 +70,26 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: EthereumSystem Channels (r:1 w:0) + /// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn force_update_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 41_000_000 picoseconds. + Weight::from_parts(41_000_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } /// Storage: `EthereumSystem::Channels` (r:1 w:0) /// Proof: `EthereumSystem::Channels` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`) /// Storage: `EthereumSystem::PricingParameters` (r:1 w:0) @@ -90,6 +110,26 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: EthereumSystem Agents (r:1 w:0) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn force_transfer_native_from_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `6044` + // Minimum execution time: 42_000_000 picoseconds. + Weight::from_parts(42_000_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } /// Storage: `EthereumSystem::Channels` (r:1 w:0) /// Proof: `EthereumSystem::Channels` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`) /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index dc14c050f60f7..98b4d4ee52d58 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -18,24 +18,31 @@ use crate::{ bridge_common_config::BridgeReward, xcm_config, xcm_config::{RelayNetwork, RootLocation, TreasuryAccount, UniversalLocation, XcmConfig}, - Balances, BridgeRelayers, EthereumInboundQueue, EthereumInboundQueueV2, EthereumOutboundQueue, + Balances, BridgeRelayers, EthereumBeaconClient, EthereumInboundQueue, EthereumInboundQueueV2, EthereumOutboundQueue, EthereumOutboundQueueV2, EthereumSystem, EthereumSystemV2, MessageQueue, Runtime, RuntimeEvent, TransactionByteFee, }; +#[cfg(feature = "runtime-benchmarks")] +use benchmark_helpers::DoNothingRouter; use bp_asset_hub_westend::CreateForeignAssetDeposit; +use bridge_hub_common::AggregateMessageOrigin; use frame_support::{parameter_types, traits::Contains, weights::ConstantMultiplier}; use frame_system::EnsureRootWithSuccess; use pallet_xcm::EnsureXcm; use parachains_common::{AccountId, Balance}; use snowbridge_beacon_primitives::{Fork, ForkVersions}; -use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; +use snowbridge_core::{gwei, meth, AllowSiblingsOnly, ChannelId, PricingParameters, Rewards}; +use snowbridge_inbound_queue_primitives::v2::{ + CreateAssetCallInfo, MessageToXcm, XcmMessageProcessor as InboundXcmMessageProcessor, +}; use snowbridge_outbound_queue_primitives::{ v1::{ConstantGasMeter, EthereumBlobExporter}, v2::{ConstantGasMeter as ConstantGasMeterV2, EthereumBlobExporter as EthereumBlobExporterV2}, }; +use snowbridge_pallet_inbound_queue::RewardThroughSovereign; use sp_core::H160; use sp_runtime::{ - traits::{ConstU32, ConstU8, Keccak256}, + traits::{ConstU32, ConstU8, Convert, Keccak256}, FixedU128, }; use testnet_parachains_constants::westend::{ @@ -76,7 +83,7 @@ parameter_types! { } parameter_types! { - pub const CreateAssetCall: [u8;2] = [53, 0]; + pub const CreateAssetCallIndex: [u8;2] = [53, 0]; pub Parameters: PricingParameters = PricingParameters { exchange_rate: FixedU128::from_rational(1, 400), fee_per_gas: gwei(20), @@ -88,9 +95,11 @@ parameter_types! { pub AssetHubLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID)]); pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); pub InboundQueueV2Location: InteriorLocation = [PalletInstance(INBOUND_QUEUE_PALLET_INDEX_V2)].into(); + pub CreateAssetCall: CreateAssetCallInfo = CreateAssetCallInfo{call: CreateAssetCallIndex::get(),deposit: CreateForeignAssetDeposit::get(),min_balance:1}; pub SnowbridgeFrontendLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID), PalletInstance(FRONTEND_PALLET_INDEX)]); pub AssetHubXCMFee: u128 = 1_000_000_000_000u128; pub const SnowbridgeReward: BridgeReward = BridgeReward::Snowbridge; + pub TargetLocation: Location = Location::new(1, [Parachain(AssetHubParaId::get().into())]); } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -106,7 +115,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type Helper = Runtime; type MessageConverter = snowbridge_inbound_queue_primitives::v1::MessageToXcm< - CreateAssetCall, + CreateAssetCallIndex, CreateForeignAssetDeposit, ConstU8, AccountId, @@ -121,40 +130,53 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo; type PricingParameters = EthereumSystem; type AssetTransactor = ::AssetTransactor; + type MessageProcessor = + snowbridge_pallet_inbound_queue::xcm_message_processor::XcmMessageProcessor; + type RewardProcessor = RewardThroughSovereign; } -impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Verifier = snowbridge_pallet_ethereum_client::Pallet; - #[cfg(not(feature = "runtime-benchmarks"))] - type XcmSender = crate::XcmRouter; - #[cfg(feature = "runtime-benchmarks")] - type XcmSender = benchmark_helpers::DoNothingRouter; - type GatewayAddress = EthereumGatewayAddress; - #[cfg(feature = "runtime-benchmarks")] - type Helper = Runtime; - type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue_v2::WeightInfo; - type AssetHubParaId = ConstU32; - type XcmExecutor = XcmExecutor; - type MessageConverter = snowbridge_inbound_queue_primitives::v2::MessageToXcm< +pub type XcmMessageProcessor = InboundXcmMessageProcessor< + Runtime, + crate::XcmRouter, + XcmExecutor, + MessageToXcm< CreateAssetCall, - CreateForeignAssetDeposit, EthereumNetwork, + RelayNetwork, + EthereumGatewayAddress, InboundQueueV2Location, + AssetHubParaId, EthereumSystem, - EthereumGatewayAddress, - EthereumUniversalLocation, - AssetHubFromEthereum, - AssetHubUniversalLocation, AccountId, - >; - type AccountToLocation = xcm_builder::AliasesIntoAccountId32< + >, + xcm_builder::AliasesIntoAccountId32< xcm_config::RelayNetwork, ::AccountId, - >; + >, + TargetLocation, +>; + +impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Verifier = EthereumBeaconClient; + type GatewayAddress = EthereumGatewayAddress; + type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue_v2::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = benchmark_helpers::DummyXcmProcessor; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = XcmMessageProcessor; type RewardKind = BridgeReward; type DefaultRewardKind = SnowbridgeReward; type RewardPayment = BridgeRelayers; + #[cfg(feature = "runtime-benchmarks")] + type Helper = Runtime; +} +pub struct GetAggregateMessageOrigin; + +impl Convert for GetAggregateMessageOrigin { + fn convert(channel_id: ChannelId) -> AggregateMessageOrigin { + AggregateMessageOrigin::Snowbridge(channel_id) + } } impl snowbridge_pallet_outbound_queue::Config for Runtime { @@ -170,6 +192,9 @@ impl snowbridge_pallet_outbound_queue::Config for Runtime { type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue::WeightInfo; type PricingParameters = EthereumSystem; type Channels = EthereumSystem; + type AggregateMessageOrigin = AggregateMessageOrigin; + type GetAggregateMessageOrigin = GetAggregateMessageOrigin; + type OnNewCommitment = (); } impl snowbridge_pallet_outbound_queue_v2::Config for Runtime { @@ -194,6 +219,9 @@ impl snowbridge_pallet_outbound_queue_v2::Config for Runtime { type RewardKind = BridgeReward; type DefaultRewardKind = SnowbridgeReward; type RewardPayment = BridgeRelayers; + type AggregateMessageOrigin = AggregateMessageOrigin; + // No consumers of this notifier on Bridge Hub. + type OnNewCommitment = (); #[cfg(feature = "runtime-benchmarks")] type Helper = Runtime; } @@ -225,6 +253,10 @@ parameter_types! { version: hex!("05000000"), epoch: 80000000000, // setting to a future epoch for local testing to remain on Deneb. }, + fulu: Fork { + version: hex!("90000075"), + epoch: 272640, // https://notes.ethereum.org/@bbusa/fusaka-bpo-timeline + }, }; } @@ -253,7 +285,11 @@ parameter_types! { }, electra: Fork { version: hex!("90000074"), - epoch: 222464, // https://github.com/ethereum/EIPs/pull/9322/files + epoch: 222464, + }, + fulu: Fork { + version: hex!("90000075"), + epoch: 272640, // https://notes.ethereum.org/@bbusa/fusaka-bpo-timeline }, }; } @@ -312,6 +348,7 @@ pub mod benchmark_helpers { use codec::Encode; use hex_literal::hex; use snowbridge_beacon_primitives::BeaconHeader; + use snowbridge_inbound_queue_primitives::EventFixture; use snowbridge_pallet_inbound_queue::BenchmarkHelper; use snowbridge_pallet_inbound_queue_v2::BenchmarkHelper as InboundQueueBenchmarkHelperV2; use snowbridge_pallet_outbound_queue_v2::BenchmarkHelper as OutboundQueueBenchmarkHelperV2; @@ -319,7 +356,8 @@ pub mod benchmark_helpers { use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; impl BenchmarkHelper for Runtime { - fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { + fn initialize_storage() -> EventFixture { + /* EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); System::set_storage( RuntimeOrigin::root(), @@ -329,6 +367,8 @@ pub mod benchmark_helpers { )], ) .unwrap(); + */ + todo!() } } @@ -360,6 +400,27 @@ pub mod benchmark_helpers { } } + pub type DummyXcmProcessor = InboundXcmMessageProcessor< + Runtime, + DoNothingRouter, + XcmExecutor, + MessageToXcm< + CreateAssetCall, + CreateForeignAssetDeposit, + EthereumNetwork, + InboundQueueV2Location, + EthereumSystem, + EthereumGatewayAddress, + EthereumUniversalLocation, + AssetHubFromEthereum, + >, + xcm_builder::AliasesIntoAccountId32< + xcm_config::RelayNetwork, + ::AccountId, + >, + ConstU32<1000>, + >; + impl snowbridge_pallet_system::BenchmarkHelper for () { fn make_xcm_origin(location: Location) -> RuntimeOrigin { RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs index fd1080de5a794..3513cd92b6918 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs @@ -70,6 +70,26 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: EthereumSystem Channels (r:1 w:0) + /// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn force_update_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 41_000_000 picoseconds. + Weight::from_parts(41_000_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } /// Storage: `EthereumSystem::Channels` (r:1 w:0) /// Proof: `EthereumSystem::Channels` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`) /// Storage: `EthereumSystem::PricingParameters` (r:1 w:0) @@ -90,6 +110,26 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: EthereumSystem Agents (r:1 w:0) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:2 w:2) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:0) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn force_transfer_native_from_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `6044` + // Minimum execution time: 42_000_000 picoseconds. + Weight::from_parts(42_000_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } /// Storage: `EthereumSystem::Channels` (r:1 w:0) /// Proof: `EthereumSystem::Channels` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`) /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs index 0793c2654d4c9..b648add019ad7 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs @@ -15,12 +15,10 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod barriers; -pub mod digest_item; pub mod message_queue; pub mod xcm_version; pub use barriers::DenyExportMessageFrom; -pub use digest_item::CustomDigestItem; pub use message_queue::{ AggregateMessageOrigin, BridgeHubDualMessageRouter, BridgeHubMessageRouter, }; diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/message_queue.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/message_queue.rs index 903634ec07175..a258b0bd4451e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/src/message_queue.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/message_queue.rs @@ -83,6 +83,12 @@ impl From for AggregateMessageOrigin { } } +impl From for AggregateMessageOrigin { + fn from(hash: H256) -> Self { + Self::SnowbridgeV2(hash) + } +} + #[cfg(feature = "runtime-benchmarks")] impl From for AggregateMessageOrigin { fn from(x: u32) -> Self { diff --git a/cumulus/xcm/xcm-emulator/Cargo.toml b/cumulus/xcm/xcm-emulator/Cargo.toml index cd9658f191d59..a6c1bff7d8cea 100644 --- a/cumulus/xcm/xcm-emulator/Cargo.toml +++ b/cumulus/xcm/xcm-emulator/Cargo.toml @@ -45,6 +45,8 @@ polkadot-runtime-parachains.default-features = true polkadot-runtime-parachains.workspace = true sp-arithmetic.default-features = true sp-arithmetic.workspace = true +sp-consensus-slots = { workspace = true, default-features = true } +sp-consensus-aura = { workspace = true, default-features = true } sp-core.default-features = true sp-core.workspace = true sp-crypto-hashing.default-features = true diff --git a/cumulus/xcm/xcm-emulator/src/lib.rs b/cumulus/xcm/xcm-emulator/src/lib.rs index d5a1d6ed5992d..6868aa79a1878 100644 --- a/cumulus/xcm/xcm-emulator/src/lib.rs +++ b/cumulus/xcm/xcm-emulator/src/lib.rs @@ -38,7 +38,7 @@ pub use frame_support::{ assert_ok, sp_runtime::{ traits::{Convert, Dispatchable, Header as HeaderT, Zero}, - Digest, DispatchResult, + BoundedSlice, Digest, DispatchResult, }, traits::{ EnqueueMessage, ExecuteOverweightError, Get, Hooks, OnFinalize, OnIdle, OnInitialize, @@ -52,14 +52,13 @@ pub use frame_system::{ }; pub use pallet_balances::AccountData; pub use pallet_message_queue; -pub use pallet_timestamp::Call as TimestampCall; +pub use pallet_timestamp::{Call as TimestampCall, Config as TimestampConfig}; pub use sp_arithmetic::traits::Bounded; pub use sp_core::{ crypto::get_public_from_string_or_panic, parameter_types, sr25519, storage::Storage, Pair, }; pub use sp_crypto_hashing::blake2_256; pub use sp_io::TestExternalities; -pub use sp_runtime::BoundedSlice; pub use sp_tracing; // Cumulus @@ -142,6 +141,16 @@ where } } +// Implement optional inherent code to be executed +// This will be executed after on-initialize and before on-finalize +pub trait AdditionalInherentCode { + fn on_new_block() -> DispatchResult { + Ok(()) + } +} + +impl AdditionalInherentCode for () {} + pub trait TestExt { fn build_new_ext(storage: Storage) -> TestExternalities; fn new_ext() -> TestExternalities; @@ -269,6 +278,7 @@ pub trait Parachain: Chain { type ParachainSystem; type MessageProcessor: ProcessMessage + ServiceQueues; type DigestProvider: Convert, Digest>; + type AdditionalInherentCode: AdditionalInherentCode; fn init(); @@ -604,7 +614,8 @@ macro_rules! decl_test_parachains { LocationToAccountId: $location_to_account:path, ParachainInfo: $parachain_info:path, MessageOrigin: $message_origin:path, - $( DigestProvider: $digest_provider:ty, )? + $( DigestProvider: $digest_provider:ty,)? + $( AdditionalInherentCode: $additional_inherent_code:ty,)? }, pallets = { $($pallet_name:ident: $pallet_path:path,)* @@ -646,6 +657,7 @@ macro_rules! decl_test_parachains { type ParachainInfo = $parachain_info; type MessageProcessor = $crate::DefaultParaMessageProcessor<$name, $message_origin>; $crate::decl_test_parachains!(@inner_digest_provider $($digest_provider)?); + $crate::decl_test_parachains!(@inner_additional_inherent_code $($additional_inherent_code)?); // We run an empty block during initialisation to open HRMP channels // and have them ready for the next block @@ -666,7 +678,7 @@ macro_rules! decl_test_parachains { fn new_block() { use $crate::{ - Dispatchable, Chain, Convert, TestExt, Zero, + Dispatchable, Chain, Convert, TestExt, Zero, TimestampConfig, AdditionalInherentCode }; let para_id = Self::para_id().into(); @@ -705,14 +717,19 @@ macro_rules! decl_test_parachains { set_validation_data.dispatch(::RuntimeOrigin::none()) ); + let timestamp: u64 = (relay_block_number*6000).into(); + // 2. inherent: pallet_timestamp::Call::set (we expect the parachain has `pallet_timestamp`) let timestamp_set: ::RuntimeCall = $crate::TimestampCall::set { // We need to satisfy `pallet_timestamp::on_finalize`. - now: Zero::zero(), + now: timestamp, }.into(); $crate::assert_ok!( timestamp_set.dispatch(::RuntimeOrigin::none()) ); + $crate::assert_ok!( + ::AdditionalInherentCode::on_new_block() + ); }); } @@ -775,6 +792,8 @@ macro_rules! decl_test_parachains { }; ( @inner_digest_provider $digest_provider:ty ) => { type DigestProvider = $digest_provider; }; ( @inner_digest_provider /* none */ ) => { type DigestProvider = (); }; + ( @inner_additional_inherent_code $additional_inherent_code:ty ) => { type AdditionalInherentCode = $additional_inherent_code; }; + ( @inner_additional_inherent_code /* none */ ) => { type AdditionalInherentCode = (); }; } #[macro_export] @@ -1160,6 +1179,7 @@ macro_rules! decl_test_networks { let mut sproof = $crate::RelayStateSproofBuilder::default(); sproof.para_id = para_id.into(); sproof.current_slot = $crate::polkadot_primitives::Slot::from(relay_parent_number as u64); + sproof.host_config.max_upward_message_size = 1024 * 1024; // egress channel let e_index = sproof.hrmp_egress_channel_index.get_or_insert_with(Vec::new); @@ -1450,15 +1470,17 @@ where } } +pub type MessageOriginFor = + <<::Runtime as MessageQueueConfig>::MessageProcessor as ProcessMessage>::Origin; + pub struct DefaultRelayMessageProcessor(PhantomData); // Process UMP messages on the relay impl ProcessMessage for DefaultRelayMessageProcessor where T: RelayChain, T::Runtime: MessageQueueConfig, - <::MessageProcessor as ProcessMessage>::Origin: - PartialEq, - MessageQueuePallet: EnqueueMessage + ServiceQueues, + MessageOriginFor: From, + MessageQueuePallet: EnqueueMessage> + ServiceQueues, { type Origin = ParaId; @@ -1470,7 +1492,7 @@ where ) -> Result { MessageQueuePallet::::enqueue_message( msg.try_into().expect("Message too long"), - AggregateMessageOrigin::Ump(UmpQueueId::Para(para)), + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)).into(), ); MessageQueuePallet::::service_queues(Weight::MAX); @@ -1482,9 +1504,8 @@ impl ServiceQueues for DefaultRelayMessageProcessor where T: RelayChain, T::Runtime: MessageQueueConfig, - <::MessageProcessor as ProcessMessage>::Origin: - PartialEq, - MessageQueuePallet: EnqueueMessage + ServiceQueues, + MessageOriginFor: From, + MessageQueuePallet: EnqueueMessage> + ServiceQueues, { type OverweightMessageAddress = (); diff --git a/polkadot/node/core/pvf/common/src/executor_interface.rs b/polkadot/node/core/pvf/common/src/executor_interface.rs index 47f9ed1604e78..c02db118b7e49 100644 --- a/polkadot/node/core/pvf/common/src/executor_interface.rs +++ b/polkadot/node/core/pvf/common/src/executor_interface.rs @@ -191,7 +191,7 @@ pub fn prepare( executor_params: &ExecutorParams, ) -> Result, sc_executor_common::error::WasmError> { let (semantics, _) = params_to_wasmtime_semantics(executor_params); - sc_executor_wasmtime::prepare_runtime_artifact(blob, &semantics) + sc_executor_wasmtime::prepare_runtime_artifact(blob, Default::default(), &semantics) } /// Available host functions. We leave out: diff --git a/prdoc/pr_10056.prdoc b/prdoc/pr_10056.prdoc new file mode 100644 index 0000000000000..f46be95954b0c --- /dev/null +++ b/prdoc/pr_10056.prdoc @@ -0,0 +1,12 @@ +title: Upgrade litep2p 0.10.0 -> 0.11.0 +doc: +- audience: Node Dev + description: This upgrade fixes the initialization of mDNS in the environment with + no multicast addresses available and compatibility with kubo IPFS >= 0.37 when + run with `--ipfs-server`. It also enables communication with remote peers having + RSA network identity keys. +crates: +- name: sc-network + bump: major +- name: sc-network-types + bump: major diff --git a/prdoc/pr_10158.prdoc b/prdoc/pr_10158.prdoc new file mode 100644 index 0000000000000..992461d6f6906 --- /dev/null +++ b/prdoc/pr_10158.prdoc @@ -0,0 +1,10 @@ +title: allow more generic origins in xcm-emulator message processor +doc: +- audience: Runtime Dev + description: |- + Right now the default message processor is somewhat tied to specific `AggregateMessageOrigin` from `polkadot_runtime_parachains::inclusion`. this pr changes it so that we require a conversion from that AggregateMessageOrigin, but nothing else, the rest stays generic. + + In the longer run we can allow customizing the processor to some other thing, but this was a sufficiently easier change +crates: +- name: xcm-emulator + bump: patch diff --git a/prdoc/pr_10313.prdoc b/prdoc/pr_10313.prdoc new file mode 100644 index 0000000000000..26847619f3b83 --- /dev/null +++ b/prdoc/pr_10313.prdoc @@ -0,0 +1,12 @@ +title: Increase `max_upward_message_size` in `RelayStateSproofBuilder` +doc: +- audience: Runtime Dev + description: |- + ## Description + + While testing different XCM messages via xcm-emulator, I noticed that the limit of 256 for `max_upward_message_size` in `RelayStateSproofBuilder` can be reached very easily. + + I suggest increasing it to `1024 * 1024` so that we can have a good range for testing. +crates: +- name: xcm-emulator + bump: patch diff --git a/prdoc/pr_8053.prdoc b/prdoc/pr_8053.prdoc new file mode 100644 index 0000000000000..6d58dbcf5194a --- /dev/null +++ b/prdoc/pr_8053.prdoc @@ -0,0 +1,10 @@ +title: Add `OnNewCommitment` hook to outbound-queue pallet +doc: +- audience: Runtime Dev + description: |- + Add a simple hook to outbound-queue-v2 pallet which allows to perform actions whenever there is a new commitment. +crates: +- name: snowbridge-pallet-outbound-queue-v2 + bump: major +- name: bridge-hub-westend-runtime + bump: major diff --git a/prdoc/pr_8106.prdoc b/prdoc/pr_8106.prdoc new file mode 100644 index 0000000000000..c4ee2ec78fd55 --- /dev/null +++ b/prdoc/pr_8106.prdoc @@ -0,0 +1,36 @@ +title: 'Snowbridge V2: Add generic AggregateMessageOrigin' +doc: +- audience: Runtime Dev + description: |- + # Description + + This PR introduces a generic `AggregateMessageOrigin` in Snowbridge V2 to provide a more flexible mechanism for message-queue origin recognition and handling. This approach aims to support broader use cases beyond parachains. By enabling custom origins, scenarios like solo chains, can build specialized message routing and processing making use of this configuration. + + ## Motivation + + - **Enhanced Integration**: While Snowbridge traditionally focuses on bridging parachains, there is also a need to send messages from various substrate-based chains (e.g., solo chains) and in different contexts. This change expands the protocol's ability to handle multiple, distinct origin types. + + - **Custom Command Structures**: Some projects need to incorporate custom Snowbridge commands or message structures, which cannot be processed by the default outbound queue. By introducing a more generic origin, these commands can be cleanly differentiated and securely processed. + + - **Flexibility**: Parachains, solo chains, or any environment running Snowbridge benefit from a uniform pattern to handle both common and custom message processing. + + ## Use Case + + Consider a scenario where you need to send messages to Ethereum directly from your runtime: + + - You have your own set of commands or data structures different from those provided by the traditional Snowbridge outbound queue. + - You want to introduce a custom message processor that specifically handles your commands. + - You need a distinct origin to differentiate your messages from standard Snowbridge traffic. + + By using the `AggregateMessageOrigin`, you can verify whether a message comes from a parachain, from Snowbridge itself, or from a custom origin. This is especially relevant in solo chain contexts, where you might not rely on the parachain assumptions but still want to leverage a robust bridging framework. +crates: +- name: snowbridge-pallet-outbound-queue-v2 + bump: major +- name: bridge-hub-westend-runtime + bump: minor +- name: bridge-hub-common + bump: major +- name: snowbridge-core + bump: minor +- name: snowbridge-pallet-outbound-queue + bump: patch diff --git a/prdoc/pr_8809.prdoc b/prdoc/pr_8809.prdoc new file mode 100644 index 0000000000000..eebbd8675a285 --- /dev/null +++ b/prdoc/pr_8809.prdoc @@ -0,0 +1,10 @@ +title: Add possibility of executing or mocking additional inherents in xcm-emulator +doc: +- audience: Runtime Dev + description: |- + This extends the `decl_test_parachains` macro to accept a `AdditionalInherentCode` type. This type needs to + implement the `AdditionalInherentCode` trait and will be called after `on_initialize` and before `on_finalize`. + It can be used to mock additional inherents. +crates: +- name: xcm-emulator + bump: minor diff --git a/prdoc/pr_9004.prdoc b/prdoc/pr_9004.prdoc new file mode 100644 index 0000000000000..58f8314395fd6 --- /dev/null +++ b/prdoc/pr_9004.prdoc @@ -0,0 +1,22 @@ +title: 'chore: Bump bounded-collections dep' +doc: +- audience: Runtime Dev + description: |- + # Description + + Bump bounded-collections dep from `0.2.3` to `0.3.2` based on the latest [release](https://crates.io/crates/bounded-collections/0.3.2) +crates: +- name: sp-core + bump: minor +- name: sp-weights + bump: minor +- name: polkadot-parachain-primitives + bump: minor +- name: polkadot-primitives + bump: minor +- name: staging-xcm + bump: minor +- name: pallet-xcm + bump: minor +- name: cumulus-pallet-xcmp-queue + bump: minor diff --git a/prdoc/pr_9015.prdoc b/prdoc/pr_9015.prdoc new file mode 100644 index 0000000000000..7aff98cf2b740 --- /dev/null +++ b/prdoc/pr_9015.prdoc @@ -0,0 +1,45 @@ +title: 'consensus/grandpa: Fix high number of peer disconnects with invalid justification' +doc: +- audience: [Node Dev, Node Operator] + description: |- + A grandpa race-casse has been identified in the versi-net stack around authority set changes, which leads to the following: + + - T0 / Node A: Completes round (15) + - T1 / Node A: Applies new authority set change and increments the SetID (from 0 to 1) + - T2 / Node B: Sends Precommit for round (15) with SetID (0) -- previous set ID + - T3 / Node B: Applies new authority set change and increments the SetID (1) + + In this scenario, Node B is not aware at the moment of sending justifications that the Set ID has changed. + The downstream effect is that Node A will not be able to verify the signature of justifications, since a different SetID is taken into account. This will cascade through the sync engine, where the Node B is wrongfully banned and disconnected. + + This PR aims to fix the edge-case by making the grandpa resilient to verifying prior setIDs for signatures. + When the signature of the grandpa justification fails to decode, the prior SetID is also verified. If the prior SetID produces a valid signature, then the outdated justification error is propagated through the code (ie `SignatureResult::OutdatedSet`). + + The sync engine will handle the outdated justifications as invalid, but without banning the peer. This leads to increased stability of the network during authority changes, which caused frequent disconnects to versi-net in the past. + + ### Review Notes + - Main changes that verify prior SetId on failures are placed in [check_message_signature_with_buffer](https://github.com/paritytech/polkadot-sdk/pull/9015/files#diff-359d7a46ea285177e5d86979f62f0f04baabf65d595c61bfe44b6fc01af70d89R458-R501) + - Sync engine no longer disconnects outdated justifications in [process_service_command](https://github.com/paritytech/polkadot-sdk/pull/9015/files#diff-9ab3391aa82ee2b2868ece610100f84502edcf40638dba9ed6953b6e572dfba5R678-R703) + + ### Testing Done + - Deployed the PR to versi-net with 40 validators + - Prior we have noticed 10/40 validators disconnecting every 15-20 minutes, leading to instability + - Over past 24h the issue has been mitigated: https://grafana.teleport.parity.io/goto/FPNWlmsHR?orgId=1 + - Note: bootnodes 0 and 1 are currently running outdated versions that do not incorporate this SetID verification improvement + + Part of: https://github.com/paritytech/polkadot-sdk/issues/8872 +crates: +- name: sp-consensus-grandpa + bump: minor +- name: bp-header-chain + bump: patch +- name: sc-consensus-grandpa + bump: patch +- name: sp-blockchain + bump: minor +- name: sp-consensus + bump: minor +- name: sc-consensus + bump: minor +- name: sc-network-sync + bump: patch diff --git a/prdoc/pr_9251.prdoc b/prdoc/pr_9251.prdoc new file mode 100644 index 0000000000000..03f12332d1542 --- /dev/null +++ b/prdoc/pr_9251.prdoc @@ -0,0 +1,10 @@ +title: Allow setting idle connection timeout value in custom node implementations +doc: +- audience: Node Dev + description: Allow setting idle connection timeout value. This can be helpful in + custom networks to allow maintaining long-lived connections. +crates: +- name: sc-cli + bump: major +- name: sc-network + bump: major diff --git a/prdoc/pr_9287.prdoc b/prdoc/pr_9287.prdoc new file mode 100644 index 0000000000000..ca2ca69786929 --- /dev/null +++ b/prdoc/pr_9287.prdoc @@ -0,0 +1,23 @@ +title: 'network: Upgrade litep2p to v0.10.0' +doc: +- audience: Node Dev + description: |- + ## litep2p v0.10.0 + + This release adds the ability to use system DNS resolver and change Kademlia DNS memory store capacity. It also fixes the Bitswap protocol implementation and correctly handles the dropped notification substreams by unregistering them from the protocol list. + + ### Added + + - kad: Expose memory store configuration ([#407](https://github.com/paritytech/litep2p/pull/407)) + - transport: Allow changing DNS resolver config ([#384](https://github.com/paritytech/litep2p/pull/384)) + + ### Fixed + + - notification: Unregister dropped protocols ([#391](https://github.com/paritytech/litep2p/pull/391)) + - bitswap: Fix protocol implementation ([#402](https://github.com/paritytech/litep2p/pull/402)) + - transport-manager: stricter supported multiaddress check ([#403](https://github.com/paritytech/litep2p/pull/403)) +crates: +- name: sc-network + bump: major +- name: sc-network-types + bump: major diff --git a/prdoc/pr_9315.prdoc b/prdoc/pr_9315.prdoc new file mode 100644 index 0000000000000..ac30287b309a3 --- /dev/null +++ b/prdoc/pr_9315.prdoc @@ -0,0 +1,15 @@ +title: 'network/kad: Increase memory store capacity for providers' +doc: +- audience: Node Dev + description: |- + Increase Kademlia memory store capacity for DHT content providers (used by parachain DHT-based bootnodes) and reduce provider republish interval & TTL. This is needed to support testnets with 1-minute fast runtime and up to 13 parachains. + + Parameters set: + - 10000 provider keys per node + - 10h provider record TTL + - 3.5h provider republish interval + + Closes https://github.com/paritytech/litep2p/issues/405. +crates: +- name: sc-network + bump: minor diff --git a/prdoc/pr_9321.prdoc b/prdoc/pr_9321.prdoc new file mode 100644 index 0000000000000..18aaec6d7b08c --- /dev/null +++ b/prdoc/pr_9321.prdoc @@ -0,0 +1,10 @@ +title: 'network/litep2p: Switch to system DNS resolver' +doc: +- audience: Node Dev + description: |- + Switch to system DNS resolver instead of 8.8.8.8 that litep2p uses by default. This enables full administrator control of what upstream DNS servers to use, including resolution of local names using custom DNS servers. + + Fixes https://github.com/paritytech/polkadot-sdk/issues/9298. +crates: +- name: sc-network + bump: patch diff --git a/prdoc/pr_9510.prdoc b/prdoc/pr_9510.prdoc new file mode 100644 index 0000000000000..25738dcfeb28f --- /dev/null +++ b/prdoc/pr_9510.prdoc @@ -0,0 +1,17 @@ +title: "Snowbridge: Refactor inbound message conversion" +doc: +- audience: Runtime Dev + description: |- + Refactor MessageToXcm inbound converter to make it more compact and organized, + update runtime configuration accordingly +crates: +- name: snowbridge-inbound-queue-primitives + bump: major +- name: snowbridge-pallet-inbound-queue-v2 + bump: patch +- name: bridge-hub-westend-runtime + bump: major +- name: asset-hub-westend-runtime + bump: patch +- name: bridge-hub-westend-integration-tests + bump: patch diff --git a/prdoc/pr_9746.prdoc b/prdoc/pr_9746.prdoc new file mode 100644 index 0000000000000..5e27888723f9f --- /dev/null +++ b/prdoc/pr_9746.prdoc @@ -0,0 +1,13 @@ +title: Snowbridge Inbound Queue V2 relayer tip payout fix + +doc: +- audience: Runtime Dev + description: | + Fixes a bug where relayer tips were not properly paid out, causing the tips to be lost since it had already been + burnt. + +crates: +- name: snowbridge-pallet-inbound-queue-v2 + bump: patch +- name: snowbridge-test-utils + bump: minor diff --git a/prdoc/pr_9938.prdoc b/prdoc/pr_9938.prdoc new file mode 100644 index 0000000000000..1bd7d93d7737c --- /dev/null +++ b/prdoc/pr_9938.prdoc @@ -0,0 +1,15 @@ +title: 'Snowbridge - Adds Fulu hardfork' +doc: +- audience: Runtime Dev + description: Set fork version for Fulu hardfork on Sepolia. +crates: +- name: snowbridge-pallet-ethereum-client + bump: patch +- name: snowbridge-pallet-inbound-queue + bump: patch +- name: snowbridge-beacon-primitives + bump: patch +- name: bridge-hub-westend-runtime + bump: patch +- name: bridge-hub-rococo-runtime + bump: patch diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index c5a8663337157..b17fdba1f937f 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -109,6 +109,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { announce_block: true, data_path: base_path.path().into(), base_path, + wasmtime_precompiled: None, wasm_runtime_overrides: None, }; diff --git a/substrate/bin/node/cli/benches/transaction_pool.rs b/substrate/bin/node/cli/benches/transaction_pool.rs index 12d1340c0ba0a..24d089b08e519 100644 --- a/substrate/bin/node/cli/benches/transaction_pool.rs +++ b/substrate/bin/node/cli/benches/transaction_pool.rs @@ -95,6 +95,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { announce_block: true, data_path: base_path.path().into(), base_path, + wasmtime_precompiled: None, wasm_runtime_overrides: None, }; diff --git a/substrate/bin/node/cli/src/cli.rs b/substrate/bin/node/cli/src/cli.rs index c2187e77e7f1b..0539b94524e50 100644 --- a/substrate/bin/node/cli/src/cli.rs +++ b/substrate/bin/node/cli/src/cli.rs @@ -108,4 +108,7 @@ pub enum Subcommand { /// Db meta columns information. ChainInfo(sc_cli::ChainInfoCmd), + + /// Precompile the WASM runtime into native code + PrecompileWasm(sc_cli::PrecompileWasmCmd), } diff --git a/substrate/bin/node/cli/src/command.rs b/substrate/bin/node/cli/src/command.rs index 5f3be4d8b2159..e20f0da7a8b54 100644 --- a/substrate/bin/node/cli/src/command.rs +++ b/substrate/bin/node/cli/src/command.rs @@ -234,5 +234,12 @@ pub fn run() -> Result<()> { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run::(&config)) }, + Some(Subcommand::PrecompileWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { task_manager, backend, .. } = new_partial(&config, None)?; + Ok((cmd.run(backend, config.chain_spec), task_manager)) + }) + }, } } diff --git a/substrate/client/cli/Cargo.toml b/substrate/client/cli/Cargo.toml index 1a3c24aa20d4c..89ca08c99a7d1 100644 --- a/substrate/client/cli/Cargo.toml +++ b/substrate/client/cli/Cargo.toml @@ -68,6 +68,9 @@ sp-version.default-features = true sp-version.workspace = true thiserror = { workspace = true } tokio = { features = ["parking_lot", "rt-multi-thread", "signal"], workspace = true, default-features = true } +sc-executor = { workspace = true } +sp-storage = { workspace = true, default-features = true } +sp-state-machine = { workspace = true, default-features = true } [dev-dependencies] futures-timer = { workspace = true } diff --git a/substrate/client/cli/src/commands/mod.rs b/substrate/client/cli/src/commands/mod.rs index b63fb1d904234..27137a1f22143 100644 --- a/substrate/client/cli/src/commands/mod.rs +++ b/substrate/client/cli/src/commands/mod.rs @@ -31,6 +31,7 @@ mod insert_key; mod inspect_key; mod inspect_node_key; mod key; +mod precompile_wasm_cmd; mod purge_chain_cmd; mod revert_cmd; mod run_cmd; @@ -46,6 +47,6 @@ pub use self::{ export_state_cmd::ExportStateCmd, generate::GenerateCmd, generate_node_key::GenerateKeyCmdCommon, import_blocks_cmd::ImportBlocksCmd, insert_key::InsertKeyCmd, inspect_key::InspectKeyCmd, inspect_node_key::InspectNodeKeyCmd, - key::KeySubcommand, purge_chain_cmd::PurgeChainCmd, revert_cmd::RevertCmd, run_cmd::RunCmd, - sign::SignCmd, vanity::VanityCmd, verify::VerifyCmd, + key::KeySubcommand, precompile_wasm_cmd::PrecompileWasmCmd, purge_chain_cmd::PurgeChainCmd, + revert_cmd::RevertCmd, run_cmd::RunCmd, sign::SignCmd, vanity::VanityCmd, verify::VerifyCmd, }; diff --git a/substrate/client/cli/src/commands/precompile_wasm_cmd.rs b/substrate/client/cli/src/commands/precompile_wasm_cmd.rs new file mode 100644 index 0000000000000..ba2948a449953 --- /dev/null +++ b/substrate/client/cli/src/commands/precompile_wasm_cmd.rs @@ -0,0 +1,148 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{ + arg_enums::{ + execution_method_from_cli, WasmExecutionMethod, WasmtimeInstantiationStrategy, + DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, + }, + error::{self, Error}, + params::{DatabaseParams, PruningParams, SharedParams}, + CliConfiguration, +}; + +use clap::Parser; +use sc_client_api::{Backend, HeaderBackend, TrieCacheContext}; +use sc_executor::{ + precompile_and_serialize_versioned_wasm_runtime, HeapAllocStrategy, DEFAULT_HEAP_ALLOC_PAGES, +}; +use sc_service::ChainSpec; +use sp_core::traits::RuntimeCode; +use sp_runtime::traits::Block as BlockT; +use sp_state_machine::backend::BackendRuntimeCode; +use std::{fmt::Debug, path::PathBuf, sync::Arc}; + +/// The `precompile-wasm` command used to serialize a precompiled WASM module. +#[derive(Debug, Parser)] +pub struct PrecompileWasmCmd { + #[allow(missing_docs)] + #[clap(flatten)] + pub database_params: DatabaseParams, + + /// The default number of 64KB pages to ever allocate for Wasm execution. + /// Don't alter this unless you know what you're doing. + #[arg(long, value_name = "COUNT")] + pub default_heap_pages: Option, + + /// path to the directory where precompiled artifact will be written + #[arg()] + pub output_dir: PathBuf, + + #[allow(missing_docs)] + #[clap(flatten)] + pub pruning_params: PruningParams, + + #[allow(missing_docs)] + #[clap(flatten)] + pub shared_params: SharedParams, + + /// The WASM instantiation method to use. + /// Only has an effect when `wasm-execution` is set to `compiled`. + /// The copy-on-write strategies are only supported on Linux. + /// If the copy-on-write variant of a strategy is unsupported + /// the executor will fall back to the non-CoW equivalent. + /// The fastest (and the default) strategy available is `pooling-copy-on-write`. + /// The `legacy-instance-reuse` strategy is deprecated and will + /// be removed in the future. It should only be used in case of + /// issues with the default instantiation strategy. + #[arg( + long, + value_name = "STRATEGY", + default_value_t = DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, + value_enum, + )] + pub wasmtime_instantiation_strategy: WasmtimeInstantiationStrategy, +} + +impl PrecompileWasmCmd { + /// Run the precompile-wasm command + pub async fn run(&self, backend: Arc, spec: Box) -> error::Result<()> + where + B: BlockT, + BA: Backend, + { + let heap_pages = self.default_heap_pages.unwrap_or(DEFAULT_HEAP_ALLOC_PAGES); + + let blockchain_info = backend.blockchain().info(); + + if backend.have_state_at(blockchain_info.finalized_hash, blockchain_info.finalized_number) { + let state = backend.state_at( + backend.blockchain().info().finalized_hash, + TrieCacheContext::Untrusted, + )?; + + precompile_and_serialize_versioned_wasm_runtime( + HeapAllocStrategy::Static { extra_pages: heap_pages }, + &BackendRuntimeCode::new(&state).runtime_code()?, + execution_method_from_cli( + WasmExecutionMethod::Compiled, + self.wasmtime_instantiation_strategy, + ), + &self.output_dir, + ) + .map_err(|e| Error::Application(Box::new(e)))?; + } else { + let storage = spec.as_storage_builder().build_storage()?; + if let Some(wasm_bytecode) = storage.top.get(sp_storage::well_known_keys::CODE) { + let runtime_code = RuntimeCode { + code_fetcher: &sp_core::traits::WrappedRuntimeCode( + wasm_bytecode.as_slice().into(), + ), + hash: sp_core::blake2_256(&wasm_bytecode).to_vec(), + heap_pages: Some(heap_pages as u64), + }; + precompile_and_serialize_versioned_wasm_runtime( + HeapAllocStrategy::Static { extra_pages: heap_pages }, + &runtime_code, + execution_method_from_cli( + WasmExecutionMethod::Compiled, + self.wasmtime_instantiation_strategy, + ), + &self.output_dir, + ) + .map_err(|e| Error::Application(Box::new(e)))?; + } + } + + Ok(()) + } +} + +impl CliConfiguration for PrecompileWasmCmd { + fn shared_params(&self) -> &SharedParams { + &self.shared_params + } + + fn pruning_params(&self) -> Option<&PruningParams> { + Some(&self.pruning_params) + } + + fn database_params(&self) -> Option<&DatabaseParams> { + Some(&self.database_params) + } +} diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs index d456a4072d0e0..5e1abc7e8a1ed 100644 --- a/substrate/client/cli/src/config.rs +++ b/substrate/client/cli/src/config.rs @@ -304,6 +304,13 @@ pub trait CliConfiguration: Sized { Ok(self.import_params().map(|x| x.wasm_method()).unwrap_or_default()) } + /// Get the path where WASM precompiled artifacts live. + /// + /// By default this is `None`. + fn wasmtime_precompiled(&self) -> Option { + self.import_params().map(|x| x.wasmtime_precompiled()).unwrap_or_default() + } + /// Get the path where WASM overrides live. /// /// By default this is `None`. @@ -543,6 +550,7 @@ pub trait CliConfiguration: Sized { blocks_pruning: self.blocks_pruning()?, executor: ExecutorConfiguration { wasm_method: self.wasm_method()?, + wasmtime_precompiled: self.wasmtime_precompiled(), default_heap_pages: self.default_heap_pages()?, max_runtime_instances, runtime_cache_size, diff --git a/substrate/client/cli/src/params/import_params.rs b/substrate/client/cli/src/params/import_params.rs index 236907957df67..4efe78322eba7 100644 --- a/substrate/client/cli/src/params/import_params.rs +++ b/substrate/client/cli/src/params/import_params.rs @@ -65,6 +65,16 @@ pub struct ImportParams { )] pub wasmtime_instantiation_strategy: WasmtimeInstantiationStrategy, + /// Specify the path where local precompiled WASM runtimes are stored. + /// Only has an effect when `wasm-execution` is set to `compiled`. + /// + /// The precompiled runtimes must have been generated using the `precompile-runtimes` + /// subcommand with the same version of wasmtime and the exact same configuration. + /// The file name must end with the hash of the configuration. This hash must match, otherwise + /// the runtime will be recompiled. + #[arg(long, value_name = "PATH")] + pub wasmtime_precompiled: Option, + /// Specify the path where local WASM runtimes are stored. /// /// These runtimes will override on-chain runtimes when the version matches. @@ -136,6 +146,12 @@ impl ImportParams { crate::execution_method_from_cli(self.wasm_method, self.wasmtime_instantiation_strategy) } + /// Enable using precompiled WASM module with locally-stored artifacts + /// by specifying the path where artifacts are stored. + pub fn wasmtime_precompiled(&self) -> Option { + self.wasmtime_precompiled.clone() + } + /// Enable overriding on-chain WASM with locally-stored WASM /// by specifying the path where local WASM is stored. pub fn wasm_runtime_overrides(&self) -> Option { diff --git a/substrate/client/cli/src/params/network_params.rs b/substrate/client/cli/src/params/network_params.rs index cc6c5ca1f4ff4..57be3de3f0e47 100644 --- a/substrate/client/cli/src/params/network_params.rs +++ b/substrate/client/cli/src/params/network_params.rs @@ -24,6 +24,7 @@ use clap::Args; use sc_network::{ config::{ NetworkConfiguration, NodeKeyConfig, NonReservedPeerMode, SetConfig, TransportConfig, + DEFAULT_IDLE_CONNECTION_TIMEOUT, }, multiaddr::Protocol, }; @@ -273,6 +274,7 @@ impl NetworkParams { enable_mdns: !is_dev && !self.no_mdns, allow_private_ip, }, + idle_connection_timeout: DEFAULT_IDLE_CONNECTION_TIMEOUT, max_parallel_downloads: self.max_parallel_downloads, max_blocks_per_request: self.max_blocks_per_request, min_peers_to_start_warp_sync: None, diff --git a/substrate/client/cli/src/runner.rs b/substrate/client/cli/src/runner.rs index 2cc55f2fccd01..6c8dce4349079 100644 --- a/substrate/client/cli/src/runner.rs +++ b/substrate/client/cli/src/runner.rs @@ -267,6 +267,7 @@ mod tests { .build(), ), executor: ExecutorConfiguration::default(), + wasmtime_precompiled: None, wasm_runtime_overrides: None, rpc: RpcConfiguration { addr: None, diff --git a/substrate/client/consensus/common/src/import_queue.rs b/substrate/client/consensus/common/src/import_queue.rs index 602683907d482..be14d780b2e06 100644 --- a/substrate/client/consensus/common/src/import_queue.rs +++ b/substrate/client/consensus/common/src/import_queue.rs @@ -141,6 +141,19 @@ pub trait ImportQueue: Send { async fn run(self, link: &dyn Link); } +/// The result of importing a justification. +#[derive(Debug, PartialEq)] +pub enum JustificationImportResult { + /// Justification was imported successfully. + Success, + + /// Justification was not imported successfully. + Failure, + + /// Justification was not imported successfully, because it is outdated. + OutdatedJustification, +} + /// Hooks that the verification queue can use to influence the synchronization /// algorithm. pub trait Link: Send + Sync { @@ -159,7 +172,7 @@ pub trait Link: Send + Sync { _who: RuntimeOrigin, _hash: &B::Hash, _number: NumberFor, - _success: bool, + _import_result: JustificationImportResult, ) { } diff --git a/substrate/client/consensus/common/src/import_queue/basic_queue.rs b/substrate/client/consensus/common/src/import_queue/basic_queue.rs index 21270859dd75c..d8879731654c1 100644 --- a/substrate/client/consensus/common/src/import_queue/basic_queue.rs +++ b/substrate/client/consensus/common/src/import_queue/basic_queue.rs @@ -34,7 +34,8 @@ use crate::{ buffered_link::{self, BufferedLinkReceiver, BufferedLinkSender}, import_single_block_metered, verify_single_block_metered, BlockImportError, BlockImportStatus, BoxBlockImport, BoxJustificationImport, ImportQueue, ImportQueueService, - IncomingBlock, Link, RuntimeOrigin, SingleBlockVerificationOutcome, Verifier, LOG_TARGET, + IncomingBlock, JustificationImportResult, Link, RuntimeOrigin, + SingleBlockVerificationOutcome, Verifier, LOG_TARGET, }, metrics::Metrics, }; @@ -342,8 +343,9 @@ impl BlockImportWorker { ) { let started = std::time::Instant::now(); - let success = match self.justification_import.as_mut() { - Some(justification_import) => justification_import + let import_result = match self.justification_import.as_mut() { + Some(justification_import) => { + let result = justification_import .import_justification(hash, number, justification) .await .map_err(|e| { @@ -356,16 +358,22 @@ impl BlockImportWorker { e, ); e - }) - .is_ok(), - None => false, + }); + match result { + Ok(()) => JustificationImportResult::Success, + Err(sp_consensus::Error::OutdatedJustification) => + JustificationImportResult::OutdatedJustification, + Err(_) => JustificationImportResult::Failure, + } + }, + None => JustificationImportResult::Failure, }; if let Some(metrics) = self.metrics.as_ref() { metrics.justification_import_time.observe(started.elapsed().as_secs_f64()); } - self.result_sender.justification_imported(who, &hash, number, success); + self.result_sender.justification_imported(who, &hash, number, import_result); } } @@ -579,7 +587,7 @@ mod tests { _who: RuntimeOrigin, hash: &Hash, _number: BlockNumber, - _success: bool, + _import_result: JustificationImportResult, ) { self.events.lock().push(Event::JustificationImported(*hash)) } diff --git a/substrate/client/consensus/common/src/import_queue/buffered_link.rs b/substrate/client/consensus/common/src/import_queue/buffered_link.rs index 67131b06a32e5..19b0668da24dc 100644 --- a/substrate/client/consensus/common/src/import_queue/buffered_link.rs +++ b/substrate/client/consensus/common/src/import_queue/buffered_link.rs @@ -38,7 +38,7 @@ //! }); //! ``` -use crate::import_queue::{Link, RuntimeOrigin}; +use crate::import_queue::{JustificationImportResult, Link, RuntimeOrigin}; use futures::prelude::*; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_runtime::traits::{Block as BlockT, NumberFor}; @@ -84,7 +84,7 @@ impl Clone for BufferedLinkSender { /// Internal buffered message. pub enum BlockImportWorkerMsg { BlocksProcessed(usize, usize, Vec<(BlockImportResult, B::Hash)>), - JustificationImported(RuntimeOrigin, B::Hash, NumberFor, bool), + JustificationImported(RuntimeOrigin, B::Hash, NumberFor, JustificationImportResult), RequestJustification(B::Hash, NumberFor), } @@ -105,9 +105,9 @@ impl Link for BufferedLinkSender { who: RuntimeOrigin, hash: &B::Hash, number: NumberFor, - success: bool, + import_result: JustificationImportResult, ) { - let msg = BlockImportWorkerMsg::JustificationImported(who, *hash, number, success); + let msg = BlockImportWorkerMsg::JustificationImported(who, *hash, number, import_result); let _ = self.tx.unbounded_send(msg); } @@ -129,8 +129,8 @@ impl BufferedLinkReceiver { match msg { BlockImportWorkerMsg::BlocksProcessed(imported, count, results) => link.blocks_processed(imported, count, results), - BlockImportWorkerMsg::JustificationImported(who, hash, number, success) => - link.justification_imported(who, &hash, number, success), + BlockImportWorkerMsg::JustificationImported(who, hash, number, import_result) => + link.justification_imported(who, &hash, number, import_result), BlockImportWorkerMsg::RequestJustification(hash, number) => link.request_justification(&hash, number), } diff --git a/substrate/client/consensus/common/src/lib.rs b/substrate/client/consensus/common/src/lib.rs index 6bf1ed0b48b4d..070e663451ffb 100644 --- a/substrate/client/consensus/common/src/lib.rs +++ b/substrate/client/consensus/common/src/lib.rs @@ -29,7 +29,8 @@ pub use block_import::{ }; pub use import_queue::{ import_single_block, BasicQueue, BlockImportError, BlockImportStatus, BoxBlockImport, - BoxJustificationImport, DefaultImportQueue, ImportQueue, IncomingBlock, Link, Verifier, + BoxJustificationImport, DefaultImportQueue, ImportQueue, IncomingBlock, + JustificationImportResult, Link, Verifier, }; mod longest_chain; diff --git a/substrate/client/consensus/grandpa/src/communication/gossip.rs b/substrate/client/consensus/grandpa/src/communication/gossip.rs index a6aa063357cb8..79696310aa40d 100644 --- a/substrate/client/consensus/grandpa/src/communication/gossip.rs +++ b/substrate/client/consensus/grandpa/src/communication/gossip.rs @@ -927,7 +927,9 @@ impl Inner { &full.message.signature, full.round.0, full.set_id.0, - ) { + ) + .is_valid() + { debug!(target: LOG_TARGET, "Bad message signature {}", full.message.id); telemetry!( self.config.telemetry; diff --git a/substrate/client/consensus/grandpa/src/communication/mod.rs b/substrate/client/consensus/grandpa/src/communication/mod.rs index 336a2a939a688..d1a1c158ba473 100644 --- a/substrate/client/consensus/grandpa/src/communication/mod.rs +++ b/substrate/client/consensus/grandpa/src/communication/mod.rs @@ -865,7 +865,9 @@ fn check_compact_commit( round.0, set_id.0, &mut buf, - ) { + ) + .is_valid() + { debug!(target: LOG_TARGET, "Bad commit message signature {}", id); telemetry!( telemetry; @@ -952,7 +954,9 @@ fn check_catch_up( if !sp_consensus_grandpa::check_message_signature_with_buffer( &msg, id, sig, round, set_id, buf, - ) { + ) + .is_valid() + { debug!(target: LOG_TARGET, "Bad catch up message signature {}", id); telemetry!( telemetry; diff --git a/substrate/client/consensus/grandpa/src/import.rs b/substrate/client/consensus/grandpa/src/import.rs index 5cec5204c7396..e2343409f3ea2 100644 --- a/substrate/client/consensus/grandpa/src/import.rs +++ b/substrate/client/consensus/grandpa/src/import.rs @@ -795,7 +795,13 @@ where ); let justification = match justification { - Err(e) => return Err(ConsensusError::ClientImport(e.to_string())), + Err(e) => { + return match e { + sp_blockchain::Error::OutdatedJustification => + Err(ConsensusError::OutdatedJustification), + _ => Err(ConsensusError::ClientImport(e.to_string())), + }; + }, Ok(justification) => justification, }; diff --git a/substrate/client/consensus/grandpa/src/justification.rs b/substrate/client/consensus/grandpa/src/justification.rs index 934c0d695fdab..d46fe5935c59c 100644 --- a/substrate/client/consensus/grandpa/src/justification.rs +++ b/substrate/client/consensus/grandpa/src/justification.rs @@ -205,17 +205,22 @@ impl GrandpaJustification { let mut buf = Vec::new(); let mut visited_hashes = HashSet::new(); for signed in self.justification.commit.precommits.iter() { - if !sp_consensus_grandpa::check_message_signature_with_buffer( + let signature_result = sp_consensus_grandpa::check_message_signature_with_buffer( &finality_grandpa::Message::Precommit(signed.precommit.clone()), &signed.id, &signed.signature, self.justification.round, set_id, &mut buf, - ) { - return Err(ClientError::BadJustification( - "invalid signature for precommit in grandpa justification".to_string(), - )) + ); + match signature_result { + sp_consensus_grandpa::SignatureResult::Invalid => + return Err(ClientError::BadJustification( + "invalid signature for precommit in grandpa justification".to_string(), + )), + sp_consensus_grandpa::SignatureResult::OutdatedSet => + return Err(ClientError::OutdatedJustification), + sp_consensus_grandpa::SignatureResult::Valid => {}, } if base_hash == signed.precommit.target_hash { diff --git a/substrate/client/executor/Cargo.toml b/substrate/client/executor/Cargo.toml index 04132bbdeac85..85a680015c144 100644 --- a/substrate/client/executor/Cargo.toml +++ b/substrate/client/executor/Cargo.toml @@ -21,6 +21,7 @@ name = "bench" harness = false [dependencies] +log = { workspace = true, default-features = true } parking_lot = { workspace = true, default-features = true } schnellru = { workspace = true } tracing = { workspace = true, default-features = true } diff --git a/substrate/client/executor/benches/bench.rs b/substrate/client/executor/benches/bench.rs index 8e460955396c4..6baeab03358f5 100644 --- a/substrate/client/executor/benches/bench.rs +++ b/substrate/client/executor/benches/bench.rs @@ -67,13 +67,17 @@ fn initialize( wasm_bulk_memory: false, wasm_reference_types: false, wasm_simd: false, + module_version_strategy: Default::default(), }, }; if precompile { - let precompiled_blob = - sc_executor_wasmtime::prepare_runtime_artifact(blob, &config.semantics) - .unwrap(); + let precompiled_blob = sc_executor_wasmtime::prepare_runtime_artifact( + blob, + Default::default(), + &config.semantics, + ) + .unwrap(); // Create a fresh temporary directory to make absolutely sure // we'll use the right module. @@ -85,7 +89,7 @@ fn initialize( unsafe { sc_executor_wasmtime::create_runtime_from_artifact::< sp_io::SubstrateHostFunctions, - >(&path, config) + >(&path, Default::defaut(), config) } } else { sc_executor_wasmtime::create_runtime::(blob, config) diff --git a/substrate/client/executor/common/Cargo.toml b/substrate/client/executor/common/Cargo.toml index b6776ba2a98b6..eead785dcde8e 100644 --- a/substrate/client/executor/common/Cargo.toml +++ b/substrate/client/executor/common/Cargo.toml @@ -24,6 +24,7 @@ sp-maybe-compressed-blob.default-features = true sp-maybe-compressed-blob.workspace = true sp-wasm-interface.default-features = true sp-wasm-interface.workspace = true +codec = { features = ["derive"], workspace = true } thiserror = { workspace = true } wasm-instrument = { workspace = true, default-features = true } diff --git a/substrate/client/executor/common/src/wasm_runtime.rs b/substrate/client/executor/common/src/wasm_runtime.rs index e8f429a3dbb2a..778731c964c40 100644 --- a/substrate/client/executor/common/src/wasm_runtime.rs +++ b/substrate/client/executor/common/src/wasm_runtime.rs @@ -74,7 +74,7 @@ pub trait WasmInstance: Send { /// Defines the heap pages allocation strategy the wasm runtime should use. /// /// A heap page is defined as 64KiB of memory. -#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, codec::Encode)] pub enum HeapAllocStrategy { /// Allocate a static number of heap pages. /// diff --git a/substrate/client/executor/src/executor.rs b/substrate/client/executor/src/executor.rs index 198a2a31ab8db..b59bbb7ff0633 100644 --- a/substrate/client/executor/src/executor.rs +++ b/substrate/client/executor/src/executor.rs @@ -92,6 +92,7 @@ pub struct WasmExecutorBuilder { max_runtime_instances: usize, cache_path: Option, allow_missing_host_functions: bool, + wasmtime_precompiled_path: Option, runtime_cache_size: u8, } @@ -110,6 +111,7 @@ impl WasmExecutorBuilder { runtime_cache_size: 4, allow_missing_host_functions: false, cache_path: None, + wasmtime_precompiled_path: None, } } @@ -193,6 +195,20 @@ impl WasmExecutorBuilder { self } + /// Create the wasm executor with the given `wasmtime_precompiled_path`. + /// + /// The `wasmtime_precompiled_path` is a path to a directory where the executor load precompiled + /// wasmtime modules. + /// + /// By default there is no `wasmtime_precompiled_path` given. + pub fn with_wasmtime_precompiled_path( + mut self, + wasmtime_precompiled_path: impl Into, + ) -> Self { + self.wasmtime_precompiled_path = Some(wasmtime_precompiled_path.into()); + self + } + /// Build the configured [`WasmExecutor`]. pub fn build(self) -> WasmExecutor { WasmExecutor { @@ -211,6 +227,7 @@ impl WasmExecutorBuilder { )), cache_path: self.cache_path, allow_missing_host_functions: self.allow_missing_host_functions, + wasmtime_precompiled_path: self.wasmtime_precompiled_path, phantom: PhantomData, } } @@ -234,6 +251,8 @@ pub struct WasmExecutor { cache_path: Option, /// Ignore missing function imports. allow_missing_host_functions: bool, + /// TODO + wasmtime_precompiled_path: Option, phantom: PhantomData, } @@ -247,6 +266,7 @@ impl Clone for WasmExecutor { cache: self.cache.clone(), cache_path: self.cache_path.clone(), allow_missing_host_functions: self.allow_missing_host_functions, + wasmtime_precompiled_path: self.wasmtime_precompiled_path.clone(), phantom: self.phantom, } } @@ -301,6 +321,7 @@ impl WasmExecutor { )), cache_path, allow_missing_host_functions: false, + wasmtime_precompiled_path: None, phantom: PhantomData, } } @@ -353,6 +374,7 @@ where runtime_code, ext, self.method, + self.wasmtime_precompiled_path.as_deref(), heap_alloc_strategy, self.allow_missing_host_functions, |module, instance, version, ext| { @@ -430,6 +452,8 @@ where runtime_blob, allow_missing_host_functions, self.cache_path.as_deref(), + None, + &[], ) .map_err(|e| format!("Failed to create module: {}", e))?; diff --git a/substrate/client/executor/src/lib.rs b/substrate/client/executor/src/lib.rs index 204f1ff22d74d..ab713a4aafb5c 100644 --- a/substrate/client/executor/src/lib.rs +++ b/substrate/client/executor/src/lib.rs @@ -36,17 +36,22 @@ mod executor; mod integration_tests; mod wasm_runtime; -pub use codec::Codec; #[allow(deprecated)] -pub use executor::NativeElseWasmExecutor; -pub use executor::{with_externalities_safe, NativeExecutionDispatch, WasmExecutor}; +pub use self::{ + executor::{ + with_externalities_safe, NativeElseWasmExecutor, NativeExecutionDispatch, WasmExecutor, + }, + wasm_runtime::{ + precompile_and_serialize_versioned_wasm_runtime, read_embedded_version, WasmExecutionMethod, + }, +}; +pub use codec::Codec; #[doc(hidden)] pub use sp_core::traits::Externalities; pub use sp_version::{NativeVersion, RuntimeVersion}; #[doc(hidden)] pub use sp_wasm_interface; pub use sp_wasm_interface::HostFunctions; -pub use wasm_runtime::{read_embedded_version, WasmExecutionMethod}; pub use sc_executor_common::{ error, diff --git a/substrate/client/executor/src/wasm_runtime.rs b/substrate/client/executor/src/wasm_runtime.rs index 8f189ca92388a..31e4192e078e9 100644 --- a/substrate/client/executor/src/wasm_runtime.rs +++ b/substrate/client/executor/src/wasm_runtime.rs @@ -23,7 +23,7 @@ use crate::error::{Error, WasmError}; -use codec::Decode; +use codec::{Decode, Encode}; use parking_lot::Mutex; use sc_executor_common::{ runtime_blob::RuntimeBlob, @@ -35,6 +35,7 @@ use sp_version::RuntimeVersion; use sp_wasm_interface::HostFunctions; use std::{ + io::Write, panic::AssertUnwindSafe, path::{Path, PathBuf}, sync::Arc, @@ -220,6 +221,7 @@ impl RuntimeCache { runtime_code: &'c RuntimeCode<'c>, ext: &mut dyn Externalities, wasm_method: WasmExecutionMethod, + wasmtime_precompiled: Option<&Path>, heap_alloc_strategy: HeapAllocStrategy, allow_missing_func_imports: bool, f: F, @@ -255,6 +257,8 @@ impl RuntimeCache { allow_missing_func_imports, self.max_runtime_instances, self.cache_path.as_deref(), + wasmtime_precompiled, + code_hash, ); match result { @@ -293,6 +297,8 @@ pub fn create_wasm_runtime_with_code( blob: RuntimeBlob, allow_missing_func_imports: bool, cache_path: Option<&Path>, + wasmtime_precompiled_path: Option<&Path>, + code_hash: &[u8], ) -> Result, WasmError> where H: HostFunctions, @@ -302,29 +308,200 @@ where } match wasm_method { - WasmExecutionMethod::Compiled { instantiation_strategy } => - sc_executor_wasmtime::create_runtime::( - blob, - sc_executor_wasmtime::Config { - allow_missing_func_imports, - cache_path: cache_path.map(ToOwned::to_owned), - semantics: sc_executor_wasmtime::Semantics { - heap_alloc_strategy, - instantiation_strategy, - deterministic_stack_limit: None, - canonicalize_nans: false, - parallel_compilation: true, - wasm_multi_value: false, - wasm_bulk_memory: false, - wasm_reference_types: false, - wasm_simd: false, + WasmExecutionMethod::Compiled { instantiation_strategy } => { + let semantics = sc_executor_wasmtime::Semantics { + heap_alloc_strategy, + instantiation_strategy, + deterministic_stack_limit: None, + canonicalize_nans: false, + parallel_compilation: true, + wasm_multi_value: false, + wasm_bulk_memory: false, + wasm_reference_types: false, + wasm_simd: false, + }; + if let Some(wasmtime_precompiled_dir) = wasmtime_precompiled_path { + if !wasmtime_precompiled_dir.is_dir() { + return Err(WasmError::Instantiation(format!( + "--wasmtime-precompiled is not a directory: {}", + wasmtime_precompiled_dir.display() + ))); + } + let handle_err = |e: std::io::Error| -> WasmError { + return WasmError::Instantiation(format!( + "Io error when loading wasmtime precompiled folder ({}): {}", + wasmtime_precompiled_dir.display(), + e + )); + }; + let mut maybe_compiled_artifact = None; + + let artifact_version = + compute_artifact_version(allow_missing_func_imports, code_hash, &semantics); + log::debug!( + target: "wasmtime-runtime", + "Searching for wasm hash: {}", + artifact_version.clone() + ); + + for entry in std::fs::read_dir(wasmtime_precompiled_dir).map_err(handle_err)? { + let entry = entry.map_err(handle_err)?; + if let Some(file_name) = entry.file_name().to_str() { + // We check that the artifact was generated for this specific artifact + // version and with the same wasm interface version and configuration. + if file_name.contains(&artifact_version.clone()) { + log::info!( + target: "wasmtime-runtime", + "Found precompiled wasm: {}", + file_name + ); + // We change the version check strategy to make sure that the file + // content was serialized with the exact same config as well + maybe_compiled_artifact = Some(( + entry.path(), + sc_executor_wasmtime::ModuleVersionStrategy::Custom( + artifact_version.clone(), + ), + )); + } + } else { + return Err(WasmError::Instantiation( + "wasmtime precompiled folder contain a file with invalid utf8 name" + .to_owned(), + )); + } + } + + if let Some((compiled_artifact_path, module_version_strategy)) = + maybe_compiled_artifact + { + // # Safety + // + // The file name of the artifact was checked before, + // so if the user has not renamed nor modified the file, + // it's certain that the file has been generated by + // `prepare_runtime_artifact` and with the same wasmtime + // version and configuration. + unsafe { + sc_executor_wasmtime::create_runtime_from_artifact::( + &compiled_artifact_path, + module_version_strategy, + sc_executor_wasmtime::Config { + allow_missing_func_imports, + cache_path: cache_path.map(ToOwned::to_owned), + semantics, + }, + ) + } + .map(|runtime| -> Box { Box::new(runtime) }) + } else { + sc_executor_wasmtime::create_runtime::( + blob, + sc_executor_wasmtime::Config { + allow_missing_func_imports, + cache_path: cache_path.map(ToOwned::to_owned), + semantics, + }, + ) + .map(|runtime| -> Box { Box::new(runtime) }) + } + } else { + sc_executor_wasmtime::create_runtime::( + blob, + sc_executor_wasmtime::Config { + allow_missing_func_imports, + cache_path: cache_path.map(ToOwned::to_owned), + semantics, }, - }, - ) - .map(|runtime| -> Box { Box::new(runtime) }), + ) + .map(|runtime| -> Box { Box::new(runtime) }) + } + }, } } +/// Create and serialize a precompiled artifact of a wasm runtime with the given `code`. +pub fn precompile_and_serialize_versioned_wasm_runtime<'c>( + heap_alloc_strategy: HeapAllocStrategy, + runtime_code: &'c RuntimeCode<'c>, + wasm_method: WasmExecutionMethod, + wasmtime_precompiled_path: &Path, +) -> Result<(), WasmError> { + let semantics = match wasm_method { + WasmExecutionMethod::Compiled { instantiation_strategy } => + sc_executor_wasmtime::Semantics { + heap_alloc_strategy, + instantiation_strategy, + deterministic_stack_limit: None, + canonicalize_nans: false, + parallel_compilation: true, + wasm_multi_value: false, + wasm_bulk_memory: false, + wasm_reference_types: false, + wasm_simd: false, + }, + }; + + let code_hash = &runtime_code.hash; + + let artifact_version = compute_artifact_version(false, code_hash, &semantics); + log::debug!( + target: "wasmtime-runtime", + "Generated precompiled wasm hash: {}", + artifact_version.clone() + ); + + let code = runtime_code.fetch_runtime_code().ok_or(WasmError::CodeNotFound)?; + + // The incoming code may be actually compressed. We decompress it here and then work with + // the uncompressed code from now on. + let blob = sc_executor_common::runtime_blob::RuntimeBlob::uncompress_if_needed(code.as_ref())?; + + let serialized_precompiled_wasm = sc_executor_wasmtime::prepare_runtime_artifact( + blob, + sc_executor_wasmtime::ModuleVersionStrategy::Custom(artifact_version.clone()), + &semantics, + )?; + + // Write in a file + let mut file = std::fs::File::create( + wasmtime_precompiled_path.join(format!("precompiled_wasm_{}", &artifact_version)), + ) + .map_err(|e| { + WasmError::Other(format!( + "Fail to create file 'precompiled_wasm_0x{}', I/O Error: {}", + &artifact_version, e + )) + })?; + file.write_all(&serialized_precompiled_wasm).map_err(|e| { + WasmError::Other(format!("Fail to write precompiled artifact, I/O Error: {}", e)) + })?; + + Ok(()) +} + +/// Compute a hash that aggregates all the metadata relating to the artifact that must not change +/// so that it can be reused with confidence. +fn compute_artifact_version( + allow_missing_func_imports: bool, + code_hash: &[u8], + semantics: &sc_executor_wasmtime::Semantics, +) -> String { + log::trace!( + target: "wasmtime-runtime", + "Computing wasm runtime hash [allow_missing_func_imports: {}, code_hash: {}, semantics: {:?}]", + allow_missing_func_imports, sp_core::bytes::to_hex(&code_hash, false), semantics + ); + let mut buffer = Vec::new(); + buffer.extend_from_slice(code_hash); + buffer.extend_from_slice(sp_wasm_interface::VERSION.as_bytes()); + buffer.push(allow_missing_func_imports as u8); + semantics.encode_to(&mut buffer); + + let hash = sp_core::hashing::blake2_256(&buffer); + sp_core::bytes::to_hex(&hash, false) +} + fn decode_version(mut version: &[u8]) -> Result { Decode::decode(&mut version).map_err(|_| { WasmError::Instantiation( @@ -389,6 +566,8 @@ fn create_versioned_wasm_runtime( allow_missing_func_imports: bool, max_instances: usize, cache_path: Option<&Path>, + wasmtime_precompiled: Option<&Path>, + code_hash: &[u8], ) -> Result where H: HostFunctions, @@ -408,6 +587,8 @@ where blob, allow_missing_func_imports, cache_path, + wasmtime_precompiled, + code_hash, )?; // If the runtime blob doesn't embed the runtime version then use the legacy version query diff --git a/substrate/client/executor/wasmtime/Cargo.toml b/substrate/client/executor/wasmtime/Cargo.toml index 34b5fcad00b51..a5b364453993e 100644 --- a/substrate/client/executor/wasmtime/Cargo.toml +++ b/substrate/client/executor/wasmtime/Cargo.toml @@ -17,6 +17,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] log = { workspace = true, default-features = true } +codec = { workspace = true, default-features = true } +cfg-if = { workspace = true } +libc = { workspace = true } parking_lot = { workspace = true, default-features = true } # When bumping wasmtime do not forget to also bump rustix @@ -46,9 +49,9 @@ wasmtime = { features = [ rustix = { features = ["fs", "mm", "param", "std", "use-libc"], workspace = true } [dev-dependencies] +paste = { workspace = true, default-features = true } cargo_metadata = { workspace = true } codec = { workspace = true, default-features = true } -paste = { workspace = true, default-features = true } sc-runtime-test = { workspace = true } sp-io = { default-features = true, workspace = true } tempfile = { workspace = true } diff --git a/substrate/client/executor/wasmtime/src/lib.rs b/substrate/client/executor/wasmtime/src/lib.rs index 8e8e92017df91..0047c0ff8be4a 100644 --- a/substrate/client/executor/wasmtime/src/lib.rs +++ b/substrate/client/executor/wasmtime/src/lib.rs @@ -38,8 +38,8 @@ mod tests; pub use runtime::{ create_runtime, create_runtime_from_artifact, create_runtime_from_artifact_bytes, - prepare_runtime_artifact, Config, DeterministicStackLimit, InstantiationStrategy, Semantics, - WasmtimeRuntime, + prepare_runtime_artifact, Config, DeterministicStackLimit, InstantiationStrategy, + ModuleVersionStrategy, Semantics, WasmtimeRuntime, }; pub use sc_executor_common::{ runtime_blob::RuntimeBlob, diff --git a/substrate/client/executor/wasmtime/src/runtime.rs b/substrate/client/executor/wasmtime/src/runtime.rs index 286d134ecd171..c824c40010bf0 100644 --- a/substrate/client/executor/wasmtime/src/runtime.rs +++ b/substrate/client/executor/wasmtime/src/runtime.rs @@ -18,6 +18,8 @@ //! Defines the compiled Wasm runtime that uses Wasmtime internally. +pub use wasmtime::ModuleVersionStrategy; + use crate::{ host::HostState, instance_wrapper::{EntryPoint, InstanceWrapper, MemoryWrapper}, @@ -354,7 +356,7 @@ fn common_config(semantics: &Semantics) -> std::result::Result { /// /// We use a `Path` here instead of simply passing a byte slice to allow `wasmtime` to /// map the runtime's linear memory on supported platforms in a copy-on-write fashion. - Precompiled(&'a Path), + Precompiled { + /// Path to the precompiled artifact + compiled_artifact_path: &'a Path, + /// Configure the strategy used for versioning check in deserializing precompiled artifact + module_version_strategy: ModuleVersionStrategy, + }, /// The runtime is instantiated using a precompiled module with the given bytes. /// @@ -527,12 +534,16 @@ where /// different configuration flags. In such case the caller will receive an `Err` deterministically. pub unsafe fn create_runtime_from_artifact( compiled_artifact_path: &Path, + module_version_strategy: ModuleVersionStrategy, config: Config, ) -> std::result::Result where H: HostFunctions, { - do_create_runtime::(CodeSupplyMode::Precompiled(compiled_artifact_path), config) + do_create_runtime::( + CodeSupplyMode::Precompiled { compiled_artifact_path, module_version_strategy }, + config, + ) } /// The same as [`create_runtime`] but takes the bytes of a precompiled artifact, @@ -574,6 +585,13 @@ where replace_strategy_if_broken(&mut config.semantics.instantiation_strategy); let mut wasmtime_config = common_config(&config.semantics)?; + + if let CodeSupplyMode::Precompiled { ref module_version_strategy, .. } = code_supply_mode { + wasmtime_config.module_version(module_version_strategy.clone()).map_err(|e| { + WasmError::Other(format!("fail to apply module_version_strategy: {:#}", e)) + })?; + } + if let Some(ref cache_path) = config.cache_path { if let Err(reason) = setup_wasmtime_caching(cache_path, &mut wasmtime_config) { log::warn!( @@ -602,7 +620,7 @@ where (module, InternalInstantiationStrategy::Builtin), } }, - CodeSupplyMode::Precompiled(compiled_artifact_path) => { + CodeSupplyMode::Precompiled { compiled_artifact_path, .. } => { // SAFETY: The unsafety of `deserialize_file` is covered by this function. The // responsibilities to maintain the invariants are passed to the caller. // @@ -661,6 +679,7 @@ fn prepare_blob_for_compilation( /// can then be used for calling [`create_runtime`] avoiding long compilation times. pub fn prepare_runtime_artifact( blob: RuntimeBlob, + module_version_strategy: ModuleVersionStrategy, semantics: &Semantics, ) -> std::result::Result, WasmError> { let mut semantics = semantics.clone(); @@ -668,7 +687,13 @@ pub fn prepare_runtime_artifact( let blob = prepare_blob_for_compilation(blob, &semantics)?; - let engine = Engine::new(&common_config(&semantics)?) + let mut wasmtime_config = common_config(&semantics)?; + + wasmtime_config + .module_version(module_version_strategy) + .map_err(|e| WasmError::Other(format!("fail to apply module_version_strategy: {:#}", e)))?; + + let engine = Engine::new(&wasmtime_config) .map_err(|e| WasmError::Other(format!("cannot create the engine: {:#}", e)))?; engine diff --git a/substrate/client/executor/wasmtime/src/tests.rs b/substrate/client/executor/wasmtime/src/tests.rs index 1ea1557fa6634..18effc8cbb0d0 100644 --- a/substrate/client/executor/wasmtime/src/tests.rs +++ b/substrate/client/executor/wasmtime/src/tests.rs @@ -146,6 +146,7 @@ impl RuntimeBuilder { wasm_bulk_memory: false, wasm_reference_types: false, wasm_simd: false, + module_version_strategy: Default::default(), }, }; @@ -156,9 +157,17 @@ impl RuntimeBuilder { // Delay the removal of the temporary directory until we're dropped. self.tmpdir = Some(dir); - let artifact = crate::prepare_runtime_artifact(blob, &config.semantics).unwrap(); + let artifact = + crate::prepare_runtime_artifact(blob, Default::default(), &config.semantics) + .unwrap(); std::fs::write(&path, artifact).unwrap(); - unsafe { crate::create_runtime_from_artifact::(&path, config) } + unsafe { + crate::create_runtime_from_artifact::( + &path, + Default::default(), + config, + ) + } } else { crate::create_runtime::(blob, config) } @@ -473,6 +482,7 @@ fn test_instances_without_reuse_are_not_leaked() { wasm_bulk_memory: false, wasm_reference_types: false, wasm_simd: false, + module_version_strategy: Default::default(), }, }, ) diff --git a/substrate/client/network/src/config.rs b/substrate/client/network/src/config.rs index 89e5acd6810ce..fc316e382bb15 100644 --- a/substrate/client/network/src/config.rs +++ b/substrate/client/network/src/config.rs @@ -66,8 +66,29 @@ use std::{ pin::Pin, str::{self, FromStr}, sync::Arc, + time::Duration, }; +/// Default timeout for idle connections of 10 seconds is good enough for most networks. +/// It doesn't make sense to expose it as a CLI parameter on individual nodes, but customizations +/// are possible in custom nodes through [`NetworkConfiguration`]. +pub const DEFAULT_IDLE_CONNECTION_TIMEOUT: Duration = Duration::from_secs(10); + +/// Maximum number of locally kept Kademlia provider keys. +/// +/// 10000 keys is enough for a testnet with fast runtime (1-minute epoch) and 13 parachains. +pub const KADEMLIA_MAX_PROVIDER_KEYS: usize = 10000; + +/// Time to keep Kademlia content provider records. +/// +/// 10 h is enough time to keep the parachain bootnode record for two 4-hour epochs. +pub const KADEMLIA_PROVIDER_RECORD_TTL: Duration = Duration::from_secs(10 * 3600); + +/// Interval of republishing Kademlia provider records. +/// +/// 3.5 h means we refresh next epoch provider record 30 minutes before next 4-hour epoch comes. +pub const KADEMLIA_PROVIDER_REPUBLISH_INTERVAL: Duration = Duration::from_secs(12600); + /// Protocol name prefix, transmitted on the wire for legacy protocol names. /// I.e., `dot` in `/dot/sync/2`. Should be unique for each chain. Always UTF-8. /// Deprecated in favour of genesis hash & fork ID based protocol names. @@ -620,6 +641,11 @@ pub struct NetworkConfiguration { /// Configuration for the transport layer. pub transport: TransportConfig, + /// Idle connection timeout. + /// + /// Set by default to [`DEFAULT_IDLE_CONNECTION_TIMEOUT`]. + pub idle_connection_timeout: Duration, + /// Maximum number of peers to ask the same blocks in parallel. pub max_parallel_downloads: u32, @@ -677,6 +703,7 @@ impl NetworkConfiguration { client_version: client_version.into(), node_name: node_name.into(), transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }, + idle_connection_timeout: DEFAULT_IDLE_CONNECTION_TIMEOUT, max_parallel_downloads: 5, max_blocks_per_request: 64, min_peers_to_start_warp_sync: None, diff --git a/substrate/client/network/src/discovery.rs b/substrate/client/network/src/discovery.rs index b0917ed363b65..a5d693f699443 100644 --- a/substrate/client/network/src/discovery.rs +++ b/substrate/client/network/src/discovery.rs @@ -46,7 +46,13 @@ //! active mechanism that asks nodes for the addresses they are listening on. Whenever we learn //! of a node's address, you must call `add_self_reported_address`. -use crate::{config::ProtocolId, utils::LruHashSet}; +use crate::{ + config::{ + ProtocolId, KADEMLIA_MAX_PROVIDER_KEYS, KADEMLIA_PROVIDER_RECORD_TTL, + KADEMLIA_PROVIDER_REPUBLISH_INTERVAL, + }, + utils::LruHashSet, +}; use array_bytes::bytes2hex; use futures::prelude::*; @@ -56,7 +62,7 @@ use libp2p::{ core::{transport::PortUse, Endpoint, Multiaddr}, kad::{ self, - store::{MemoryStore, RecordStore}, + store::{MemoryStore, MemoryStoreConfig, RecordStore}, Behaviour as Kademlia, BucketInserts, Config as KademliaConfig, Event as KademliaEvent, Event, GetClosestPeersError, GetClosestPeersOk, GetProvidersError, GetProvidersOk, GetRecordOk, PeerRecord, QueryId, QueryResult, Quorum, Record, RecordKey, @@ -239,7 +245,18 @@ impl DiscoveryConfig { // auto-insertion and instead add peers manually. config.set_kbucket_inserts(BucketInserts::Manual); config.disjoint_query_paths(kademlia_disjoint_query_paths); - let store = MemoryStore::new(local_peer_id); + + config.set_provider_record_ttl(Some(KADEMLIA_PROVIDER_RECORD_TTL)); + config.set_provider_publication_interval(Some(KADEMLIA_PROVIDER_REPUBLISH_INTERVAL)); + + let store = MemoryStore::with_config( + local_peer_id, + MemoryStoreConfig { + max_provided_keys: KADEMLIA_MAX_PROVIDER_KEYS, + ..Default::default() + }, + ); + let mut kad = Kademlia::with_config(local_peer_id, store, config); kad.set_mode(Some(kad::Mode::Server)); diff --git a/substrate/client/network/src/litep2p/discovery.rs b/substrate/client/network/src/litep2p/discovery.rs index 0db4b5d623b22..4a9b84b3a215a 100644 --- a/substrate/client/network/src/litep2p/discovery.rs +++ b/substrate/client/network/src/litep2p/discovery.rs @@ -19,7 +19,10 @@ //! libp2p-related discovery code for litep2p backend. use crate::{ - config::{NetworkConfiguration, ProtocolId}, + config::{ + NetworkConfiguration, ProtocolId, KADEMLIA_MAX_PROVIDER_KEYS, KADEMLIA_PROVIDER_RECORD_TTL, + KADEMLIA_PROVIDER_REPUBLISH_INTERVAL, + }, peer_store::PeerStoreProvider, }; @@ -299,6 +302,9 @@ impl Discovery { .with_known_peers(known_peers) .with_protocol_names(protocol_names) .with_incoming_records_validation_mode(IncomingRecordValidationMode::Manual) + .with_provider_record_ttl(KADEMLIA_PROVIDER_RECORD_TTL) + .with_provider_refresh_interval(KADEMLIA_PROVIDER_REPUBLISH_INTERVAL) + .with_max_provider_keys(KADEMLIA_MAX_PROVIDER_KEYS) .build() }; diff --git a/substrate/client/network/src/litep2p/mod.rs b/substrate/client/network/src/litep2p/mod.rs index 8af076678961c..95229ad5ce126 100644 --- a/substrate/client/network/src/litep2p/mod.rs +++ b/substrate/client/network/src/litep2p/mod.rs @@ -99,9 +99,6 @@ mod peerstore; mod service; mod shim; -/// Timeout for connection waiting new substreams. -const KEEP_ALIVE_TIMEOUT: Duration = Duration::from_secs(10); - /// Litep2p bandwidth sink. struct Litep2pBandwidthSink { sink: litep2p::BandwidthSink, @@ -519,9 +516,10 @@ impl NetworkBackend for Litep2pNetworkBac .with_connection_limits(ConnectionLimitsConfig::default().max_incoming_connections( Some(crate::MAX_CONNECTIONS_ESTABLISHED_INCOMING as usize), )) - // This has the same effect as `libp2p::Swarm::with_idle_connection_timeout` which is - // set to 10 seconds as well. - .with_keep_alive_timeout(KEEP_ALIVE_TIMEOUT) + .with_keep_alive_timeout(network_config.idle_connection_timeout) + // Use system DNS resolver to enable intranet domain resolution and administrator + // control over DNS lookup. + .with_system_resolver() .with_executor(executor); if let Some(config) = maybe_mdns_config { diff --git a/substrate/client/network/src/litep2p/service.rs b/substrate/client/network/src/litep2p/service.rs index c2a994d532956..307d071cd00cb 100644 --- a/substrate/client/network/src/litep2p/service.rs +++ b/substrate/client/network/src/litep2p/service.rs @@ -264,11 +264,12 @@ impl NetworkSigner for Litep2pNetworkService { signature: &Vec, message: &Vec, ) -> Result { - let public_key = litep2p::crypto::PublicKey::from_protobuf_encoding(&public_key) + let identity = litep2p::PeerId::from_public_key_protobuf(&public_key); + let public_key = litep2p::crypto::RemotePublicKey::from_protobuf_encoding(&public_key) .map_err(|error| error.to_string())?; let peer: litep2p::PeerId = peer.into(); - Ok(peer == public_key.to_peer_id() && public_key.verify(message, signature)) + Ok(peer == identity && public_key.verify(message, signature)) } } diff --git a/substrate/client/network/src/protocol/notifications/behaviour.rs b/substrate/client/network/src/protocol/notifications/behaviour.rs index ec66a1d71f5bc..ef21f760d3932 100644 --- a/substrate/client/network/src/protocol/notifications/behaviour.rs +++ b/substrate/client/network/src/protocol/notifications/behaviour.rs @@ -644,7 +644,7 @@ impl Notifications { let peer_id = occ_entry.key().0; trace!( target: LOG_TARGET, - "PSM => Connect({}, {:?}): Will start to connect at until {:?}", + "PSM => Connect({}, {:?}): Will start to connect at {:?}", peer_id, set_id, timer_deadline, @@ -1037,7 +1037,7 @@ impl Notifications { if peerset_rejected { trace!( target: LOG_TARGET, - "Protocol accepted ({:?} {:?} {:?}) but Peerset had request disconnection, rejecting", + "Protocol accepted ({:?} {:?} {:?}) but Peerset had requested disconnection, rejecting", index, incoming.peer_id, incoming.set_id diff --git a/substrate/client/network/src/service.rs b/substrate/client/network/src/service.rs index 0e755a39161c0..19c7e8ee323b1 100644 --- a/substrate/client/network/src/service.rs +++ b/substrate/client/network/src/service.rs @@ -549,7 +549,7 @@ where // necessary. See .with_per_connection_event_buffer_size(24) .with_max_negotiating_inbound_streams(2048) - .with_idle_connection_timeout(Duration::from_secs(10)); + .with_idle_connection_timeout(network_config.idle_connection_timeout); Swarm::new(transport, behaviour, local_peer_id, config) }; diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index 3ae8c465e1b49..77abb56a4affd 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -670,17 +670,36 @@ where ToServiceCommand::BlocksProcessed(imported, count, results) => { self.strategy.on_blocks_processed(imported, count, results); }, - ToServiceCommand::JustificationImported(peer_id, hash, number, success) => { + ToServiceCommand::JustificationImported(peer_id, hash, number, import_result) => { + let success = + matches!(import_result, sc_consensus::JustificationImportResult::Success); self.strategy.on_justification_import(hash, number, success); - if !success { - log::info!( - target: LOG_TARGET, - "💔 Invalid justification provided by {peer_id} for #{hash}", - ); - self.network_service - .disconnect_peer(peer_id, self.block_announce_protocol_name.clone()); - self.network_service - .report_peer(peer_id, ReputationChange::new_fatal("Invalid justification")); + + match import_result { + sc_consensus::JustificationImportResult::OutdatedJustification => { + log::info!( + target: LOG_TARGET, + "💔 Outdated justification provided by {peer_id} for #{hash}", + ); + }, + sc_consensus::JustificationImportResult::Failure => { + log::info!( + target: LOG_TARGET, + "💔 Invalid justification provided by {peer_id} for #{hash}", + ); + self.network_service + .disconnect_peer(peer_id, self.block_announce_protocol_name.clone()); + self.network_service.report_peer( + peer_id, + ReputationChange::new_fatal("Invalid justification"), + ); + }, + sc_consensus::JustificationImportResult::Success => { + log::debug!( + target: LOG_TARGET, + "Justification for block #{hash} ({number}) imported from {peer_id} successfully", + ); + }, } }, ToServiceCommand::AnnounceBlock(hash, data) => self.announce_block(hash, data), diff --git a/substrate/client/network/sync/src/service/mock.rs b/substrate/client/network/sync/src/service/mock.rs index 300aa076515f8..1974e70227e3d 100644 --- a/substrate/client/network/sync/src/service/mock.rs +++ b/substrate/client/network/sync/src/service/mock.rs @@ -55,7 +55,7 @@ mockall::mock! { who: PeerId, hash: &B::Hash, number: NumberFor, - success: bool, + import_result: sc_consensus::JustificationImportResult, ); fn request_justification(&self, hash: &B::Hash, number: NumberFor); } diff --git a/substrate/client/network/sync/src/service/syncing_service.rs b/substrate/client/network/sync/src/service/syncing_service.rs index b56af2b9976a1..7c0c0b428ddb0 100644 --- a/substrate/client/network/sync/src/service/syncing_service.rs +++ b/substrate/client/network/sync/src/service/syncing_service.rs @@ -21,7 +21,9 @@ use crate::types::{ExtendedPeerInfo, SyncEvent, SyncEventStream, SyncStatus, Syn use futures::{channel::oneshot, Stream}; use sc_network_types::PeerId; -use sc_consensus::{BlockImportError, BlockImportStatus, JustificationSyncLink, Link}; +use sc_consensus::{ + BlockImportError, BlockImportStatus, JustificationImportResult, JustificationSyncLink, Link, +}; use sc_network::{NetworkBlock, NetworkSyncForkRequest}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; use sp_runtime::traits::{Block as BlockT, NumberFor}; @@ -44,7 +46,7 @@ pub enum ToServiceCommand { usize, Vec<(Result>, BlockImportError>, B::Hash)>, ), - JustificationImported(PeerId, B::Hash, NumberFor, bool), + JustificationImported(PeerId, B::Hash, NumberFor, JustificationImportResult), AnnounceBlock(B::Hash, Option>), NewBestBlockImported(B::Hash, NumberFor), EventStream(TracingUnboundedSender), @@ -192,11 +194,14 @@ impl Link for SyncingService { who: PeerId, hash: &B::Hash, number: NumberFor, - success: bool, + import_result: JustificationImportResult, ) { - let _ = self - .tx - .unbounded_send(ToServiceCommand::JustificationImported(who, *hash, number, success)); + let _ = self.tx.unbounded_send(ToServiceCommand::JustificationImported( + who, + *hash, + number, + import_result, + )); } fn request_justification(&self, hash: &B::Hash, number: NumberFor) { diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 74d94c30cd69b..b89b6e98dcc8e 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -258,6 +258,7 @@ where ClientConfig { offchain_worker_enabled: config.offchain_worker.enabled, offchain_indexing_api: config.offchain_worker.indexing_enabled, + wasmtime_precompiled: config.executor.wasmtime_precompiled.clone(), wasm_runtime_overrides: config.wasm_runtime_overrides.clone(), no_genesis: config.no_genesis(), wasm_runtime_substitutes, @@ -364,13 +365,18 @@ pub fn new_wasm_executor(config: &ExecutorConfiguration) -> Wa let strategy = config .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| HeapAllocStrategy::Static { extra_pages: p as _ }); - WasmExecutor::::builder() + let mut wasm_builder = WasmExecutor::::builder() .with_execution_method(config.wasm_method) .with_onchain_heap_alloc_strategy(strategy) .with_offchain_heap_alloc_strategy(strategy) .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) - .build() + .with_runtime_cache_size(config.runtime_cache_size); + + if let Some(ref wasmtime_precompiled_path) = config.wasmtime_precompiled { + wasm_builder = wasm_builder.with_wasmtime_precompiled_path(wasmtime_precompiled_path); + } + + wasm_builder.build() } /// Create an instance of default DB-backend backend. diff --git a/substrate/client/service/src/client/call_executor.rs b/substrate/client/service/src/client/call_executor.rs index 235b2a6a07ed1..c62fb1b2bd3c5 100644 --- a/substrate/client/service/src/client/call_executor.rs +++ b/substrate/client/service/src/client/call_executor.rs @@ -30,7 +30,7 @@ use sp_runtime::{ traits::{Block as BlockT, HashingFor}, }; use sp_state_machine::{backend::AsTrieBackend, OverlayedChanges, StateMachine, StorageProof}; -use std::{cell::RefCell, sync::Arc}; +use std::{cell::RefCell, path::PathBuf, sync::Arc}; /// Call executor that executes methods locally, querying all required /// data from local backend. @@ -38,6 +38,7 @@ pub struct LocalCallExecutor { backend: Arc, executor: E, code_provider: CodeProvider, + wasmtime_precompiled_path: Option, execution_extensions: Arc>, } @@ -59,6 +60,7 @@ where backend, executor, code_provider, + wasmtime_precompiled_path: client_config.wasmtime_precompiled, execution_extensions: Arc::new(execution_extensions), }) } @@ -73,6 +75,7 @@ where backend: self.backend.clone(), executor: self.executor.clone(), code_provider: self.code_provider.clone(), + wasmtime_precompiled_path: self.wasmtime_precompiled_path.clone(), execution_extensions: self.execution_extensions.clone(), } } diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index 5f55499b32f06..e9628eb400ad0 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -166,6 +166,8 @@ pub struct ClientConfig { pub wasm_runtime_substitutes: HashMap, Vec>, /// Enable recording of storage proofs during block import pub enable_import_proof_recording: bool, + /// Path where precompiled wasmtime modules exist. + pub wasmtime_precompiled: Option, } impl Default for ClientConfig { @@ -177,6 +179,7 @@ impl Default for ClientConfig { no_genesis: false, wasm_runtime_substitutes: HashMap::new(), enable_import_proof_recording: false, + wasmtime_precompiled: None, } } } diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index 74ae044bdc3f8..95ffa5048a85c 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -347,6 +347,10 @@ pub struct RpcConfiguration { pub struct ExecutorConfiguration { /// Wasm execution method. pub wasm_method: WasmExecutionMethod, + /// Directory where local WASM precompiled artifacts live. These wasm modules + /// take precedence over runtimes when the spec and wasm config matches. Set to `None` to + /// disable (default). + pub wasmtime_precompiled: Option, /// The size of the instances cache. /// /// The default value is 8. @@ -361,6 +365,7 @@ impl Default for ExecutorConfiguration { fn default() -> Self { Self { wasm_method: WasmExecutionMethod::default(), + wasmtime_precompiled: Default::default(), max_runtime_instances: 8, default_heap_pages: None, runtime_cache_size: 2, diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index ce9215a165d21..77b8c17a3c57b 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -259,7 +259,7 @@ fn create_out_dir() -> Result { } if !out_exists { - fs::create_dir(&out_dir) + fs::create_dir_all(&out_dir) .context(format!("Failed to create output directory: {})", out_dir.display(),))?; } diff --git a/substrate/primitives/blockchain/src/error.rs b/substrate/primitives/blockchain/src/error.rs index e8ac148d7511e..d1cf0aca2f350 100644 --- a/substrate/primitives/blockchain/src/error.rs +++ b/substrate/primitives/blockchain/src/error.rs @@ -102,6 +102,9 @@ pub enum Error { #[error("bad justification for header: {0}")] BadJustification(String), + #[error("outdated justification")] + OutdatedJustification, + #[error("This method is not currently available when running in light client mode")] NotAvailableOnLightClient, diff --git a/substrate/primitives/consensus/common/src/error.rs b/substrate/primitives/consensus/common/src/error.rs index fb8d0447fe3d6..5fa8984544a89 100644 --- a/substrate/primitives/consensus/common/src/error.rs +++ b/substrate/primitives/consensus/common/src/error.rs @@ -41,6 +41,9 @@ pub enum Error { /// Justification requirements not met. #[error("Invalid justification")] InvalidJustification, + /// The justification provided is outdated and corresponds to a previous set. + #[error("Import failed with outdated justification")] + OutdatedJustification, /// Error from the client while importing. #[error("Import failed: {0}")] ClientImport(String), diff --git a/substrate/primitives/consensus/grandpa/src/lib.rs b/substrate/primitives/consensus/grandpa/src/lib.rs index 3aa4dc2b4cfb3..6c973964cfdee 100644 --- a/substrate/primitives/consensus/grandpa/src/lib.rs +++ b/substrate/primitives/consensus/grandpa/src/lib.rs @@ -368,7 +368,8 @@ where &$equivocation.first.1, $equivocation.round_number, report.set_id, - ); + ) + .is_valid(); let valid_second = check_message_signature( &$message($equivocation.second.0), @@ -376,7 +377,8 @@ where &$equivocation.second.1, $equivocation.round_number, report.set_id, - ); + ) + .is_valid(); return valid_first && valid_second }; @@ -412,6 +414,27 @@ pub fn localized_payload_with_buffer( (message, round, set_id).encode_to(buf) } +/// Result of checking a message signature. +#[derive(Clone, Encode, Decode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum SignatureResult { + /// Valid signature. + Valid, + + /// Invalid signature. + Invalid, + + /// Valid signature, but the message was signed in the previous set. + OutdatedSet, +} + +impl SignatureResult { + /// Returns `true` if the signature is valid. + pub fn is_valid(&self) -> bool { + matches!(self, SignatureResult::Valid) + } +} + /// Check a message signature by encoding the message as a localized payload and /// verifying the provided signature using the expected authority id. pub fn check_message_signature( @@ -420,7 +443,7 @@ pub fn check_message_signature( signature: &AuthoritySignature, round: RoundNumber, set_id: SetId, -) -> bool +) -> SignatureResult where H: Encode, N: Encode, @@ -439,7 +462,7 @@ pub fn check_message_signature_with_buffer( round: RoundNumber, set_id: SetId, buf: &mut Vec, -) -> bool +) -> SignatureResult where H: Encode, N: Encode, @@ -448,15 +471,34 @@ where localized_payload_with_buffer(round, set_id, message, buf); - let valid = id.verify(&buf, signature); + if id.verify(&buf, signature) { + return SignatureResult::Valid; + } - if !valid { - let log_target = if cfg!(feature = "std") { CLIENT_LOG_TARGET } else { RUNTIME_LOG_TARGET }; + let log_target = if cfg!(feature = "std") { CLIENT_LOG_TARGET } else { RUNTIME_LOG_TARGET }; + log::debug!( + target: log_target, + "Bad signature on message from id={id:?} round={round:?} set_id={set_id:?}", + ); - log::debug!(target: log_target, "Bad signature on message from {:?}", id); + // Check if the signature is valid in the previous set. + if set_id == 0 { + return SignatureResult::Invalid; } - valid + let prev_set_id = set_id - 1; + localized_payload_with_buffer(round, prev_set_id, message, buf); + let valid = id.verify(&buf, signature); + log::debug!( + target: log_target, + "Previous set signature check for id={id:?} round={round:?} previous_set={prev_set_id:?} valid={valid:?}" + ); + + if valid { + SignatureResult::OutdatedSet + } else { + SignatureResult::Invalid + } } /// Localizes the message to the given set and round and signs the payload. diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index 484c34d7c3300..92ddda29d4a0a 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -25,7 +25,7 @@ harness = false [dependencies] bip39 = { workspace = true, default-features = false, features = ["alloc"] } bitflags = { workspace = true } -bounded-collections = { workspace = true } +bounded-collections = { workspace = true, features = ["scale-codec"] } bs58 = { optional = true, workspace = true } codec = { features = ["derive", "max-encoded-len"], workspace = true } dyn-clonable = { optional = true, workspace = true } @@ -70,9 +70,6 @@ secp256k1 = { features = ["alloc", "recovery"], optional = true, workspace = tru sha2 = { optional = true, workspace = true } w3f-bls = { optional = true, workspace = true } -# bandersnatch crypto -ark-vrf = { optional = true, workspace = true, features = ["bandersnatch", "ring"] } - [target.'cfg(not(substrate_runtime))'.dependencies] sp-externalities.workspace = true sp-externalities.default-features = false @@ -87,7 +84,6 @@ serde_json = { workspace = true, default-features = true } [features] default = ["std"] std = [ - "ark-vrf?/std", "bip39/rand", "bip39/std", "blake2/std", @@ -162,4 +158,4 @@ bls-experimental = ["sha2", "w3f-bls"] # This feature adds Bandersnatch crypto primitives. # It should not be used in production since the implementation and interface may still # be subject to significant changes. -bandersnatch-experimental = ["ark-vrf"] +bandersnatch-experimental = [] diff --git a/substrate/primitives/wasm-interface/src/lib.rs b/substrate/primitives/wasm-interface/src/lib.rs index f6948d8cb88e9..59ab2caf3cb95 100644 --- a/substrate/primitives/wasm-interface/src/lib.rs +++ b/substrate/primitives/wasm-interface/src/lib.rs @@ -46,6 +46,8 @@ if_wasmtime_is_enabled! { pub use anyhow; } +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); + /// Result type used by traits in this crate. pub type Result = core::result::Result; diff --git a/substrate/utils/frame/benchmarking-cli/src/overhead/command.rs b/substrate/utils/frame/benchmarking-cli/src/overhead/command.rs index ccf33daca55fe..fe68749fd7a3c 100644 --- a/substrate/utils/frame/benchmarking-cli/src/overhead/command.rs +++ b/substrate/utils/frame/benchmarking-cli/src/overhead/command.rs @@ -521,6 +521,7 @@ impl OverheadCmd { no_genesis: false, wasm_runtime_substitutes: Default::default(), enable_import_proof_recording: chain_type.requires_proof_recording(), + wasmtime_precompiled: Default::default(), }, )?); diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index aad6d0a222666..7aa46d71b184a 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -84,19 +84,22 @@ pub fn new_partial(config: &Configuration) -> Result .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); - let executor = ParachainExecutor::builder() + let mut wasm_builder = WasmExecutor::builder() .with_execution_method(config.executor.wasm_method) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) .with_max_runtime_instances(config.executor.max_runtime_instances) - .with_runtime_cache_size(config.executor.runtime_cache_size) - .build(); + .with_runtime_cache_size(config.executor.runtime_cache_size); + if let Some(ref wasmtime_precompiled_path) = config.executor.wasmtime_precompiled { + wasm_builder = wasm_builder.with_wasmtime_precompiled_path(wasmtime_precompiled_path); + } + let wasm = wasm_builder.build(); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts_record_import::( config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), - executor, + wasm, true, )?; let client = Arc::new(client); diff --git a/templates/solochain/node/src/cli.rs b/templates/solochain/node/src/cli.rs index c09905c7949e7..f87c889454f38 100644 --- a/templates/solochain/node/src/cli.rs +++ b/templates/solochain/node/src/cli.rs @@ -49,4 +49,7 @@ pub enum Subcommand { /// Db meta columns information. ChainInfo(sc_cli::ChainInfoCmd), + + /// Precompile the WASM runtime into native code + PrecompileWasm(sc_cli::PrecompileWasmCmd), } diff --git a/templates/solochain/node/src/command.rs b/templates/solochain/node/src/command.rs index 57e6726178569..7449400ca4acd 100644 --- a/templates/solochain/node/src/command.rs +++ b/templates/solochain/node/src/command.rs @@ -181,6 +181,14 @@ pub fn run() -> sc_cli::Result<()> { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run::(&config)) }, + Some(Subcommand::PrecompileWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { task_manager, backend, .. } = + service::new_partial(&config)?; + Ok((cmd.run(backend, config.chain_spec), task_manager)) + }) + }, None => { let runner = cli.create_runner(&cli.run)?; runner.run_node_until_exit(|config| async move {