diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb7879b2e..3f515bde7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Update NFT deposits according to RFC-45 ([polkadot-fellows/runtimes#237](https://github.com/polkadot-fellows/runtimes/pull/237)) - Add Kusama People Chain ([polkadot-fellows/runtimes#217](https://github.com/polkadot-fellows/runtimes/pull/217)) - Asset Conversion setup for Polkadot Asset Hub, and XCM Swap Weight Trader for both Asset Hubs ([polkadot-fellows/runtimes#218](https://github.com/polkadot-fellows/runtimes/pull/218)) +- Adds Snowbridge to Kusama and Polkadot ([polkadot-fellows/runtimes#130](https://github.com/polkadot-fellows/runtimes/pull/130)) ### Changed diff --git a/Cargo.lock b/Cargo.lock index 2755ef036e..d26eacaaba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,77 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[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", + "hex-literal", + "itoa", + "proptest", + "rand", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" +dependencies = [ + "alloy-rlp-derive", + "arrayvec 0.7.4", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[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", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.48", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d7107bed88e8f09f0ddcc3335622d87bfb6821f3e0c7473329fb1cfad5e015" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + [[package]] name = "always-assert" version = "0.1.3" @@ -224,8 +295,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f" dependencies = [ "ark-ec", - "ark-ff", - "ark-std", + "ark-ff 0.4.2", + "ark-std 0.4.0", ] [[package]] @@ -235,9 +306,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" dependencies = [ "ark-ec", - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", ] [[package]] @@ -246,10 +317,10 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" dependencies = [ - "ark-ff", + "ark-ff 0.4.2", "ark-poly", - "ark-serialize", - "ark-std", + "ark-serialize 0.4.2", + "ark-std 0.4.0", "derivative", "hashbrown 0.13.2", "itertools 0.10.5", @@ -257,16 +328,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", "derivative", "digest 0.10.7", "itertools 0.10.5", @@ -277,6 +366,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-asm" version = "0.4.2" @@ -287,6 +386,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-macros" version = "0.4.2" @@ -306,13 +417,23 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" dependencies = [ - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", "derivative", "hashbrown 0.13.2", ] +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + [[package]] name = "ark-serialize" version = "0.4.2" @@ -320,7 +441,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ "ark-serialize-derive", - "ark-std", + "ark-std 0.4.0", "digest 0.10.7", "num-bigint", ] @@ -336,6 +457,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + [[package]] name = "ark-std" version = "0.4.0" @@ -521,6 +652,7 @@ dependencies = [ "polkadot-runtime-constants", "primitive-types", "scale-info", + "snowbridge-router-primitives", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -647,6 +779,7 @@ dependencies = [ "polkadot-runtime-constants", "primitive-types", "scale-info", + "snowbridge-router-primitives", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -798,6 +931,17 @@ dependencies = [ "pin-project-lite 0.2.13", ] +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -911,6 +1055,21 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitcoin_hashes" version = "0.11.0" @@ -1068,6 +1227,7 @@ dependencies = [ "scale-info", "sp-std", "staging-xcm", + "system-parachains-constants", ] [[package]] @@ -1080,6 +1240,7 @@ dependencies = [ "scale-info", "sp-std", "staging-xcm", + "system-parachains-constants", ] [[package]] @@ -1108,9 +1269,11 @@ dependencies = [ "frame-support", "kusama-runtime-constants", "polkadot-runtime-constants", + "snowbridge-core", "sp-api", "sp-runtime", "sp-std", + "staging-xcm", "system-parachains-constants", ] @@ -1124,9 +1287,11 @@ dependencies = [ "frame-support", "kusama-runtime-constants", "polkadot-runtime-constants", + "snowbridge-core", "sp-api", "sp-runtime", "sp-std", + "staging-xcm", "system-parachains-constants", ] @@ -1310,12 +1475,30 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "bridge-hub-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e69c25ec3db0ef22ae409cc60f5c42364124264ce6640e17dbcfc0280e4b5dd9" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "pallet-message-queue", + "parity-scale-codec", + "scale-info", + "snowbridge-core", + "sp-core", + "sp-runtime", + "sp-std", + "staging-xcm", +] + [[package]] name = "bridge-hub-kusama-emulated-chain" version = "1.0.0" dependencies = [ + "bridge-hub-common", "bridge-hub-kusama-runtime", - "cumulus-primitives-core", "emulated-integration-tests-common", "frame-support", "parachains-common", @@ -1326,11 +1509,14 @@ dependencies = [ name = "bridge-hub-kusama-integration-tests" version = "1.0.0" dependencies = [ + "asset-hub-kusama-runtime", + "bp-bridge-hub-kusama", "bp-messages", "bridge-hub-kusama-runtime", "cumulus-pallet-xcmp-queue", "emulated-integration-tests-common", "frame-support", + "hex-literal", "integration-tests-helpers", "kusama-polkadot-system-emulated-network", "kusama-system-emulated-network", @@ -1341,9 +1527,19 @@ dependencies = [ "pallet-message-queue", "pallet-xcm", "parachains-common", + "parity-scale-codec", + "scale-info", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-pallet-inbound-queue-fixtures", + "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-system", + "snowbridge-router-primitives", + "sp-core", "sp-runtime", "staging-xcm", "staging-xcm-executor", + "system-parachains-constants", ] [[package]] @@ -1362,6 +1558,7 @@ dependencies = [ "bp-polkadot-core", "bp-relayers", "bp-runtime", + "bridge-hub-common", "bridge-hub-test-utils", "bridge-runtime-common", "cumulus-pallet-aura-ext", @@ -1402,6 +1599,7 @@ dependencies = [ "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "parachains-common", + "parachains-runtimes-test-utils", "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain-primitives", @@ -1409,6 +1607,17 @@ dependencies = [ "polkadot-runtime-constants", "scale-info", "serde", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-outbound-queue-runtime-api", + "snowbridge-pallet-ethereum-client", + "snowbridge-pallet-inbound-queue", + "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-system", + "snowbridge-router-primitives", + "snowbridge-runtime-common", + "snowbridge-runtime-test-common", + "snowbridge-system-runtime-api", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -1437,8 +1646,8 @@ dependencies = [ name = "bridge-hub-polkadot-emulated-chain" version = "1.0.0" dependencies = [ + "bridge-hub-common", "bridge-hub-polkadot-runtime", - "cumulus-primitives-core", "emulated-integration-tests-common", "frame-support", "parachains-common", @@ -1449,11 +1658,14 @@ dependencies = [ name = "bridge-hub-polkadot-integration-tests" version = "1.0.0" dependencies = [ + "asset-hub-polkadot-runtime", + "bp-bridge-hub-polkadot", "bp-messages", "bridge-hub-polkadot-runtime", "cumulus-pallet-xcmp-queue", "emulated-integration-tests-common", "frame-support", + "hex-literal", "integration-tests-helpers", "kusama-polkadot-system-emulated-network", "pallet-asset-conversion", @@ -1463,9 +1675,20 @@ dependencies = [ "pallet-message-queue", "pallet-xcm", "parachains-common", + "parity-scale-codec", + "polkadot-system-emulated-network", + "scale-info", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-pallet-inbound-queue-fixtures", + "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-system", + "snowbridge-router-primitives", + "sp-core", "sp-runtime", "staging-xcm", "staging-xcm-executor", + "system-parachains-constants", ] [[package]] @@ -1484,6 +1707,7 @@ dependencies = [ "bp-polkadot-core", "bp-relayers", "bp-runtime", + "bridge-hub-common", "bridge-hub-test-utils", "bridge-runtime-common", "cumulus-pallet-aura-ext", @@ -1524,6 +1748,7 @@ dependencies = [ "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "parachains-common", + "parachains-runtimes-test-utils", "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain-primitives", @@ -1531,6 +1756,17 @@ dependencies = [ "polkadot-runtime-constants", "scale-info", "serde", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-outbound-queue-runtime-api", + "snowbridge-pallet-ethereum-client", + "snowbridge-pallet-inbound-queue", + "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-system", + "snowbridge-router-primitives", + "snowbridge-runtime-common", + "snowbridge-runtime-test-common", + "snowbridge-system-runtime-api", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -2117,6 +2353,19 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "const-hex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbd12d49ab0eaf8193ba9175e45f56bbc2e4b27d57b8cfe62aa47942a46b9a9" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + [[package]] name = "const-oid" version = "0.9.5" @@ -2998,6 +3247,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + [[package]] name = "dyn-clonable" version = "0.9.0" @@ -3393,6 +3648,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ethabi-decode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d398648d65820a727d6a81e58b962f874473396a047e4c30bafe3240953417" +dependencies = [ + "ethereum-types", + "tiny-keccak", +] + [[package]] name = "ethbloom" version = "0.13.0" @@ -3401,8 +3666,10 @@ checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", "fixed-hash", + "impl-codec", "impl-rlp", "impl-serde", + "scale-info", "tiny-keccak", ] @@ -3414,9 +3681,11 @@ checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", "fixed-hash", + "impl-codec", "impl-rlp", "impl-serde", "primitive-types", + "scale-info", "uint", ] @@ -3499,6 +3768,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec 0.7.4", + "auto_impl", + "bytes", +] + [[package]] name = "fatality" version = "0.0.6" @@ -8190,6 +8470,12 @@ dependencies = [ "substrate-wasm-builder", ] +[[package]] +name = "parity-bytes" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b56e3a2420138bdb970f84dfb9c774aea80fa0e7371549eedec0d80c209c67" + [[package]] name = "parity-db" version = "0.4.13" @@ -8385,9 +8671,9 @@ dependencies = [ [[package]] name = "penpal-runtime" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c804f33306e47153a4bad6a9cd33bbd537fa4b5f9434d505c8a6fc321a0bb71a" +checksum = "ec559654c03e2c39b3b6921e4baddb34df06feaf37fac1432e277158233345b5" dependencies = [ "assets-common", "cumulus-pallet-aura-ext", @@ -8443,7 +8729,6 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", - "testnet-parachains-constants", ] [[package]] @@ -10139,6 +10424,26 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "proptest" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.4.1", + "lazy_static", + "num-traits", + "rand", + "rand_chacha 0.3.1", + "rand_xorshift", + "regex-syntax 0.8.2", + "rusty-fork", + "tempfile", + "unarray", +] + [[package]] name = "prost" version = "0.11.9" @@ -10365,6 +10670,15 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -10598,23 +10912,6 @@ dependencies = [ "librocksdb-sys", ] -[[package]] -name = "rococo-runtime-constants" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b45c21ccb0f8777512a65510c106aeee4b59682944b9a5cb31cd7b8ed4ccb47" -dependencies = [ - "frame-support", - "polkadot-primitives", - "polkadot-runtime-common", - "smallvec", - "sp-core", - "sp-runtime", - "sp-weights", - "staging-xcm", - "staging-xcm-builder", -] - [[package]] name = "route-recognizer" version = "0.3.1" @@ -10657,6 +10954,36 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ruint" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b1d9521f889713d1221270fdd63370feca7e5c71a18745343402fa86e4f04f" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -10684,6 +11011,15 @@ dependencies = [ "semver 0.9.0", ] +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.0" @@ -10791,10 +11127,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] -name = "rw-stream-sink" +name = "rusty-fork" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "rw-stream-sink" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" dependencies = [ "futures", "pin-project", @@ -12167,7 +12515,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", ] [[package]] @@ -12176,7 +12524,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", ] [[package]] @@ -12194,6 +12551,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "separator" version = "0.4.1" @@ -12209,6 +12575,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd31f59f6fe2b0c055371bb2f16d7f0aa7d8881676c04a55b1596d1a17cd10a4" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.14" @@ -12444,6 +12819,407 @@ dependencies = [ "subtle 2.5.0", ] +[[package]] +name = "snowbridge-amcl" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460a9ed63cdf03c1b9847e8a12a5f5ba19c4efd5869e4a737e05be25d7c427e5" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "snowbridge-beacon-primitives" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a73ef707257064bc4ecce8323cdb7c30e8ecd1ce74aa89a6e82e81fa8b9970" +dependencies = [ + "byte-slice-cast", + "frame-support", + "frame-system", + "hex", + "parity-scale-codec", + "rlp", + "scale-info", + "serde", + "snowbridge-ethereum", + "snowbridge-milagro-bls", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "ssz_rs", + "ssz_rs_derive", + "static_assertions", +] + +[[package]] +name = "snowbridge-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3615664be8525d6f23e42dce1c314abc7f961a7f862b74012444e16d5dde5c" +dependencies = [ + "ethabi-decode", + "frame-support", + "frame-system", + "hex-literal", + "parity-scale-codec", + "polkadot-parachain-primitives", + "scale-info", + "serde", + "snowbridge-beacon-primitives", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", +] + +[[package]] +name = "snowbridge-ethereum" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "315ad74f081734cb51f48bb10cd18a3f83cfec3c09a551bc936027708635e808" +dependencies = [ + "ethabi-decode", + "ethbloom", + "ethereum-types", + "hex-literal", + "parity-bytes", + "parity-scale-codec", + "rlp", + "rustc-hex", + "scale-info", + "serde", + "serde-big-array", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "snowbridge-milagro-bls" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "026aa8638f690a53e3f7676024b9e913b1cab0111d1b7b92669d40a188f9d7e6" +dependencies = [ + "hex", + "lazy_static", + "parity-scale-codec", + "rand", + "scale-info", + "snowbridge-amcl", + "zeroize", +] + +[[package]] +name = "snowbridge-outbound-queue-merkle-tree" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5382cf77aeb8a92a142751949cdea1f230b7864e1ea687352c28439faabcf69d" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "snowbridge-outbound-queue-runtime-api" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4933810488df1660cf60f53a7952ce1dff6aaef72d9fc740e1274e937136444e" +dependencies = [ + "frame-support", + "parity-scale-codec", + "snowbridge-core", + "snowbridge-outbound-queue-merkle-tree", + "sp-api", + "sp-core", + "sp-std", + "staging-xcm", +] + +[[package]] +name = "snowbridge-pallet-ethereum-client" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0fd437cd5db8b7d16cdd4da110d674d245532afa31df10108a11858a07844a6" +dependencies = [ + "bp-runtime", + "byte-slice-cast", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "log", + "pallet-timestamp", + "parity-scale-codec", + "rlp", + "scale-info", + "serde", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-ethereum", + "snowbridge-pallet-ethereum-client-fixtures", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "ssz_rs", + "ssz_rs_derive", + "static_assertions", +] + +[[package]] +name = "snowbridge-pallet-ethereum-client-fixtures" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3d5b4b7e17eaadbc52ac3fc9fbcce63dfc60c05a1b19604cd009d6595c1175e" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "snowbridge-beacon-primitives", + "snowbridge-core", + "sp-core", + "sp-std", +] + +[[package]] +name = "snowbridge-pallet-inbound-queue" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c1fbfd32dbba082a9312c02c3ef35de929741f78f48e88b7668909350ef9e2" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "log", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-ethereum", + "snowbridge-pallet-inbound-queue-fixtures", + "snowbridge-router-primitives", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + +[[package]] +name = "snowbridge-pallet-inbound-queue-fixtures" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d8fff63b0a64d1dd06571ad074e664594bd9392481cd3489edde36e1d2dcc96" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "snowbridge-beacon-primitives", + "snowbridge-core", + "sp-core", + "sp-std", +] + +[[package]] +name = "snowbridge-pallet-outbound-queue" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7498ba5bc2b15e1d032b3e44659cd117cad24eab457a40dc5e17acfaac0aff" +dependencies = [ + "bridge-hub-common", + "ethabi-decode", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "parity-scale-codec", + "scale-info", + "serde", + "snowbridge-core", + "snowbridge-outbound-queue-merkle-tree", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", +] + +[[package]] +name = "snowbridge-pallet-system" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6a410d9312bdcc3ba5944abb7d21f6b25065b32c7f8b39b9c7a4b0bd0dacfb4" +dependencies = [ + "ethabi-decode", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "snowbridge-core", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + +[[package]] +name = "snowbridge-router-primitives" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee5cc8e156f033971c5435676be92ab6f70a926b3497ca9c28c0dde9697b8da9" +dependencies = [ + "ethabi-decode", + "frame-support", + "frame-system", + "hex-literal", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "snowbridge-core", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + +[[package]] +name = "snowbridge-runtime-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d751007be1c9c68b075b0b1f9b02c25a02f458903a2c7893ba6e4231a00c24a" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "snowbridge-core", + "sp-arithmetic", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + +[[package]] +name = "snowbridge-runtime-test-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bce50af98162e76b5ae50a2beb215bcde2a199eb5ea579ba4752b91278e655b1" +dependencies = [ + "assets-common", + "cumulus-pallet-aura-ext", + "cumulus-pallet-parachain-system", + "cumulus-pallet-session-benchmarking", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-utility", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal", + "log", + "pallet-aura", + "pallet-authorship", + "pallet-balances", + "pallet-collator-selection", + "pallet-message-queue", + "pallet-multisig", + "pallet-session", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parachains-common", + "parachains-runtimes-test-utils", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-runtime-common", + "scale-info", + "serde", + "smallvec", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-outbound-queue-runtime-api", + "snowbridge-pallet-ethereum-client", + "snowbridge-pallet-ethereum-client-fixtures", + "snowbridge-pallet-inbound-queue", + "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-system", + "snowbridge-router-primitives", + "snowbridge-system-runtime-api", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-storage", + "sp-transaction-pool", + "sp-version", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + +[[package]] +name = "snowbridge-system-runtime-api" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5c3c4e6e87b76ae1f9639bbc1528e1ae4e5a8d6a16d02d8d48e56d4a89f4ca" +dependencies = [ + "parity-scale-codec", + "snowbridge-core", + "sp-api", + "sp-core", + "sp-std", + "staging-xcm", +] + [[package]] name = "socket2" version = "0.4.10" @@ -13325,6 +14101,29 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "ssz_rs" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057291e5631f280978fa9c8009390663ca4613359fc1318e36a8c24c392f6d1f" +dependencies = [ + "bitvec", + "num-bigint", + "sha2 0.9.9", + "ssz_rs_derive", +] + +[[package]] +name = "ssz_rs_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f07d54c4d01a1713eb363b55ba51595da15f6f1211435b71466460da022aa140" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -13769,6 +14568,18 @@ dependencies = [ "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", + "quote", + "syn 2.0.48", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -13866,21 +14677,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" -[[package]] -name = "testnet-parachains-constants" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ff23c25f39d64b930ab9d721643d0c780a66201c7f1da34fac6982752626bf5" -dependencies = [ - "frame-support", - "polkadot-core-primitives", - "rococo-runtime-constants", - "smallvec", - "sp-runtime", - "staging-xcm", - "westend-runtime-constants", -] - [[package]] name = "thiserror" version = "1.0.56" @@ -14440,6 +15236,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-bidi" version = "0.3.14" @@ -14557,8 +15359,8 @@ dependencies = [ "ark-bls12-377", "ark-bls12-381", "ark-ec", - "ark-ff", - "ark-serialize", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", "ark-serialize-derive", "arrayref", "constcat", @@ -14572,6 +15374,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.4.0" @@ -14976,23 +15787,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "westend-runtime-constants" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b080c193714605ce1033311d85035247adca170181cd68a3ad7e3ca87755a14" -dependencies = [ - "frame-support", - "polkadot-primitives", - "polkadot-runtime-common", - "smallvec", - "sp-core", - "sp-runtime", - "sp-weights", - "staging-xcm", - "staging-xcm-builder", -] - [[package]] name = "which" version = "4.4.2" diff --git a/docs/removing-migrations.md b/docs/removing-migrations.md index e3b0dc3a6f..43a3812c6a 100644 --- a/docs/removing-migrations.md +++ b/docs/removing-migrations.md @@ -22,4 +22,4 @@ For some chain runtime `spec_version: a_bcd_efg,` (e.g. `spec_version: 1_000_001 ## Automation -Currently, all of the above is done manually, but (at least parts of) it could be automated. Prerequisites can definitely be automated, so could the branches/patches dance. Code changes would easily be done manually and that's it. \ No newline at end of file +Currently, all of the above is done manually, but (at least parts of) it could be automated. Prerequisites can definitely be automated, so could the branches/patches dance. Code changes would easily be done manually and that's it. diff --git a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/Cargo.toml b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/Cargo.toml index 089dfe898c..83accaf328 100644 --- a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/Cargo.toml +++ b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/Cargo.toml @@ -15,7 +15,7 @@ frame-support = { version = "29.0.0" } # Cumulus parachains-common = { version = "8.0.0" } -cumulus-primitives-core = { version = "0.8.0" } +bridge-hub-common = { version = "0.1.0" } emulated-integration-tests-common = { version = "4.0.0" } # Runtimes diff --git a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/src/genesis.rs b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/src/genesis.rs index f2d00f7304..f835a319c6 100644 --- a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/src/genesis.rs +++ b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/src/genesis.rs @@ -22,7 +22,7 @@ use emulated_integration_tests_common::{ }; use parachains_common::Balance; -pub const ASSETHUB_PARA_ID: u32 = 1000; +pub const ASSET_HUB_PARA_ID: u32 = 1000; pub const PARA_ID: u32 = 1002; pub const ED: Balance = bridge_hub_kusama_runtime::ExistentialDeposit::get(); @@ -65,6 +65,11 @@ pub fn genesis() -> Storage { owner: Some(get_account_id_from_seed::(accounts::BOB)), ..Default::default() }, + ethereum_system: bridge_hub_kusama_runtime::EthereumSystemConfig { + para_id: PARA_ID.into(), + asset_hub_para_id: ASSET_HUB_PARA_ID.into(), + ..Default::default() + }, ..Default::default() }; diff --git a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/src/lib.rs b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/src/lib.rs index 1504f90852..038b6e7a44 100644 --- a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/src/lib.rs +++ b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama/src/lib.rs @@ -36,11 +36,14 @@ decl_test_parachains! { XcmpMessageHandler: bridge_hub_kusama_runtime::XcmpQueue, LocationToAccountId: bridge_hub_kusama_runtime::xcm_config::LocationToAccountId, ParachainInfo: bridge_hub_kusama_runtime::ParachainInfo, - MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + MessageOrigin: bridge_hub_common::AggregateMessageOrigin, }, pallets = { PolkadotXcm: bridge_hub_kusama_runtime::PolkadotXcm, Balances: bridge_hub_kusama_runtime::Balances, + EthereumSystem: bridge_hub_kusama_runtime::EthereumSystem, + EthereumInboundQueue: bridge_hub_kusama_runtime::EthereumInboundQueue, + EthereumOutboundQueue: bridge_hub_kusama_runtime::EthereumOutboundQueue, } }, } diff --git a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/Cargo.toml b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/Cargo.toml index b9ff11a527..da7e86c7a3 100644 --- a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/Cargo.toml +++ b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/Cargo.toml @@ -15,7 +15,7 @@ frame-support = { version = "29.0.0" } # Cumulus parachains-common = { version = "8.0.0" } -cumulus-primitives-core = { version = "0.8.0" } +bridge-hub-common = { version = "0.1.0" } emulated-integration-tests-common = { version = "4.0.0" } # Runtimes diff --git a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/src/genesis.rs b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/src/genesis.rs index c5d4ec1929..85b48a2661 100644 --- a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/src/genesis.rs +++ b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/src/genesis.rs @@ -22,6 +22,7 @@ use emulated_integration_tests_common::{ }; use parachains_common::Balance; +pub const ASSET_HUB_PARA_ID: u32 = 1000; pub const PARA_ID: u32 = 1002; pub const ED: Balance = bridge_hub_polkadot_runtime::ExistentialDeposit::get(); @@ -64,6 +65,11 @@ pub fn genesis() -> Storage { owner: Some(get_account_id_from_seed::(accounts::BOB)), ..Default::default() }, + ethereum_system: bridge_hub_polkadot_runtime::EthereumSystemConfig { + para_id: PARA_ID.into(), + asset_hub_para_id: ASSET_HUB_PARA_ID.into(), + ..Default::default() + }, ..Default::default() }; diff --git a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/src/lib.rs b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/src/lib.rs index c448eb7f98..cf50845853 100644 --- a/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/src/lib.rs +++ b/integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot/src/lib.rs @@ -36,11 +36,14 @@ decl_test_parachains! { XcmpMessageHandler: bridge_hub_polkadot_runtime::XcmpQueue, LocationToAccountId: bridge_hub_polkadot_runtime::xcm_config::LocationToAccountId, ParachainInfo: bridge_hub_polkadot_runtime::ParachainInfo, - MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + MessageOrigin: bridge_hub_common::AggregateMessageOrigin, }, pallets = { PolkadotXcm: bridge_hub_polkadot_runtime::PolkadotXcm, Balances: bridge_hub_polkadot_runtime::Balances, + EthereumSystem: bridge_hub_polkadot_runtime::EthereumSystem, + EthereumInboundQueue: bridge_hub_polkadot_runtime::EthereumInboundQueue, + EthereumOutboundQueue: bridge_hub_polkadot_runtime::EthereumOutboundQueue, } }, } diff --git a/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml b/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml index 4cf672b77c..2204d81dbf 100644 --- a/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml +++ b/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml @@ -17,7 +17,7 @@ frame-support = { version = "29.0.0" } parachains-common = { version = "8.0.0" } cumulus-primitives-core = { version = "0.8.0" } emulated-integration-tests-common = { version = "4.0.0" } -penpal-runtime = { version = "0.15.0", features = ["experimental"] } +penpal-runtime = { version = "0.15.1", features = ["experimental"] } # Runtimes kusama-emulated-chain = { path = "../../../relays/kusama" } diff --git a/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs b/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs index 30d6e7bfad..2a8001126c 100644 --- a/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs +++ b/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs @@ -15,7 +15,9 @@ mod genesis; pub use genesis::{genesis, ED, PARA_ID_A, PARA_ID_B}; -pub use penpal_runtime::xcm_config::{LocalTeleportableToAssetHub, XcmConfig}; +pub use penpal_runtime::xcm_config::{ + CustomizableAssetFromSystemAssetHub, LocalTeleportableToAssetHub, XcmConfig, +}; // Substrate use frame_support::traits::OnInitialize; diff --git a/integration-tests/emulated/networks/kusama-polkadot-system/src/lib.rs b/integration-tests/emulated/networks/kusama-polkadot-system/src/lib.rs index dbdb453718..247ace6b3e 100644 --- a/integration-tests/emulated/networks/kusama-polkadot-system/src/lib.rs +++ b/integration-tests/emulated/networks/kusama-polkadot-system/src/lib.rs @@ -26,7 +26,7 @@ use asset_hub_polkadot_emulated_chain::AssetHubPolkadot; use bridge_hub_kusama_emulated_chain::BridgeHubKusama; use bridge_hub_polkadot_emulated_chain::BridgeHubPolkadot; use kusama_emulated_chain::Kusama; -use penpal_emulated_chain::PenpalA; +use penpal_emulated_chain::{PenpalA, PenpalB}; use polkadot_emulated_chain::Polkadot; // Cumulus @@ -55,6 +55,7 @@ decl_test_networks! { parachains = vec![ AssetHubPolkadot, BridgeHubPolkadot, + PenpalB, ], bridge = PolkadotKusamaMockBridge }, diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/Cargo.toml b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/Cargo.toml index c68144892a..b4a9ae2cb7 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/Cargo.toml +++ b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/Cargo.toml @@ -8,8 +8,12 @@ description = "Bridge Hub Kusama runtime integration tests with xcm-emulator" publish = false [dependencies] +codec = { package = "parity-scale-codec", version = "3.6.9" } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +hex-literal = "0.4.1" # Substrate +sp-core = { version = "29.0.0" } sp-runtime = { version = "32.0.0" } frame-support = { version = "29.0.0" } pallet-balances = { version = "29.0.0" } @@ -32,7 +36,18 @@ bp-messages = { version = "0.8.0" } pallet-bridge-messages = { version = "0.8.0" } # Local +bp-bridge-hub-kusama = { path = "../../../../../system-parachains/bridge-hubs/bridge-hub-kusama/primitives"} bridge-hub-kusama-runtime = { path = "../../../../../system-parachains/bridge-hubs/bridge-hub-kusama" } +asset-hub-kusama-runtime = { path = "../../../../../system-parachains/asset-hubs/asset-hub-kusama" } integration-tests-helpers = { path = "../../../helpers" } kusama-polkadot-system-emulated-network = { path = "../../../networks/kusama-polkadot-system" } kusama-system-emulated-network = { path = "../../../networks/kusama-system" } +system-parachains-constants = { path = "../../../../../system-parachains/constants" } + +# Snowbridge +snowbridge-beacon-primitives = { version = "0.1.0" } +snowbridge-core = { version = "0.1.0" } +snowbridge-router-primitives = { version = "0.1.0" } +snowbridge-pallet-system = { version = "0.1.0" } +snowbridge-pallet-outbound-queue = { version = "0.1.0" } +snowbridge-pallet-inbound-queue-fixtures = { version = "0.9.0" } diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/mod.rs b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/mod.rs index 91a6b7b08f..05b1b3437c 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/mod.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/mod.rs @@ -18,6 +18,7 @@ use crate::*; mod asset_transfers; mod claim_assets; mod send_xcm; +mod snowbridge; mod teleport; pub(crate) fn asset_hub_polkadot_location() -> Location { diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/snowbridge.rs b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/snowbridge.rs new file mode 100644 index 0000000000..1e55bcc7bb --- /dev/null +++ b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/snowbridge.rs @@ -0,0 +1,732 @@ +// 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::*; +use asset_hub_kusama_runtime::xcm_config::bridging::to_ethereum::{ + BridgeHubEthereumBaseFee, EthereumNetwork, +}; +use bp_bridge_hub_kusama::snowbridge::CreateAssetCall; +use bridge_hub_kusama_runtime::{ + EthereumBeaconClient, EthereumGatewayAddress, EthereumInboundQueue, Runtime, RuntimeOrigin, +}; +use codec::{Decode, Encode}; +use emulated_integration_tests_common::xcm_emulator::ConvertLocation; +use frame_support::pallet_prelude::TypeInfo; +use hex_literal::hex; +use kusama_system_emulated_network::{ + penpal_emulated_chain::CustomizableAssetFromSystemAssetHub, + BridgeHubKusamaParaSender as BridgeHubKusamaSender, +}; +use snowbridge_beacon_primitives::CompactExecutionHeader; +use snowbridge_core::{ + gwei, + inbound::{Log, Message, Proof}, + meth, + outbound::OperatingMode, + Rewards, +}; +use snowbridge_pallet_inbound_queue_fixtures::{ + register_token_with_insufficient_fee::make_register_token_with_infufficient_fee_message, + InboundQueueFixture, +}; +use snowbridge_pallet_system::PricingParametersOf; +use snowbridge_router_primitives::inbound::{ + Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, +}; +use sp_core::{H160, H256}; +use sp_runtime::{DispatchError::Token, FixedU128, TokenError::FundsUnavailable}; +use system_parachains_constants::kusama::currency::UNITS; + +const INITIAL_FUND: u128 = 5_000_000_000 * KUSAMA_ED; +const CHAIN_ID: u64 = 1; +const TREASURY_ACCOUNT: [u8; 32] = + hex!("da5026b2e34b876343bd6becae58eb5af71ec727633bfb972641fa7a89ff4988"); +const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); +const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); +const GATEWAY_ADDRESS: [u8; 20] = hex!("EDa338E4dC46038493b885327842fD3E301CaB39"); + +#[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: InboundQueueFixture) -> DispatchResult { + EthereumBeaconClient::store_execution_header( + fixture.message.proof.block_hash, + fixture.execution_header, + 0, + H256::default(), + ); + + EthereumInboundQueue::submit( + RuntimeOrigin::signed(BridgeHubKusamaSender::get()), + fixture.message, + ) +} + +/// 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. + BridgeHubKusama::fund_para_sovereign(origin_para.into(), INITIAL_FUND); + + let sudo_origin = ::RuntimeOrigin::root(); + let destination = Kusama::child_location_of(BridgeHubKusama::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 { + require_weight_at_most: 3000000000.into(), + origin_kind: OriginKind::Xcm, + call: create_agent_call.encode().into(), + }, + ])); + + // Kusama Global Consensus + // Send XCM message from Relay Chain to Bridge Hub source Parachain + Kusama::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!( + Kusama, + vec![ + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + BridgeHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that a message was sent to Ethereum to create the agent + assert_expected_events!( + BridgeHubKusama, + 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. + BridgeHubKusama::fund_para_sovereign(origin_para.into(), INITIAL_FUND); + + let sudo_origin = ::RuntimeOrigin::root(); + let destination: VersionedLocation = + Kusama::child_location_of(BridgeHubKusama::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 { + require_weight_at_most: 3000000000.into(), + origin_kind: OriginKind::Xcm, + call: create_agent_call.encode().into(), + }, + ])); + + 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 { + require_weight_at_most: 3000000000.into(), + origin_kind: OriginKind::Xcm, + call: create_channel_call.encode().into(), + }, + ])); + + // Kusama Global Consensus + // Send XCM message from Relay Chain to Bridge Hub source Parachain + Kusama::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!( + Kusama, + vec![ + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + BridgeHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the Channel was created + assert_expected_events!( + BridgeHubKusama, + 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 AH sovereign account on BH so that it can pay execution fees. + BridgeHubKusama::fund_para_sovereign(AssetHubKusama::para_id().into(), INITIAL_FUND); + // Fund ethereum sovereign account on AssetHub. + AssetHubKusama::fund_accounts(vec![(ethereum_sovereign_account(), INITIAL_FUND)]); + + BridgeHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + // 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!( + BridgeHubKusama, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubKusama, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {}, + ] + ); + }); +} + +/// Tests sending a token to a 3rd party parachain, called PenPal. The token reserve is +/// still located on AssetHub. +#[test] +fn send_token_from_ethereum_to_penpal() { + let asset_hub_sovereign = BridgeHubKusama::sovereign_account_id_of(Location::new( + 1, + [Parachain(AssetHubKusama::para_id().into())], + )); + + // The Weth asset location, identified by the contract address on Ethereum + let weth_asset_location: Location = + (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); + // Converts the Weth asset location into an asset ID + let weth_asset_id: v3::Location = weth_asset_location.try_into().unwrap(); + + // Fund ethereum sovereign on AssetHub + AssetHubKusama::fund_accounts(vec![(ethereum_sovereign_account(), INITIAL_FUND)]); + + PenpalA::execute_with(|| { + // Set the trusted asset location from AH, in this case, Ethereum. + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]).encode(), + )], + )); + + // Create asset on the Penpal parachain. + assert_ok!(::ForeignAssets::create( + ::RuntimeOrigin::signed(PenpalASender::get()), + weth_asset_id, + asset_hub_sovereign.clone().into(), + 1000, + )); + + assert!(::ForeignAssets::asset_exists(weth_asset_id)); + }); + + AssetHubKusama::execute_with(|| { + assert_ok!(::ForeignAssets::force_create( + ::RuntimeOrigin::root(), + weth_asset_id, + asset_hub_sovereign.clone().into(), + true, + 1000, + )); + + assert!(::ForeignAssets::asset_exists( + weth_asset_id + )); + }); + + BridgeHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + type RuntimeOrigin = ::RuntimeOrigin; + + // Fund AssetHub sovereign account so it can pay execution fees for the asset transfer + assert_ok!(::Balances::force_set_balance( + RuntimeOrigin::root(), + asset_hub_sovereign.clone().into(), + INITIAL_FUND, + )); + + let message_id: H256 = [1; 32].into(); + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendToken { + token: WETH.into(), + destination: Destination::ForeignAccountId32 { + para_id: PenpalA::para_id().into(), + id: PenpalAReceiver::get().into(), + fee: 40_000_000_000, + }, + amount: 1_000_000, + fee: 40_000_000_000, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubKusama::para_id()).unwrap(); + + assert_expected_events!( + BridgeHubKusama, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the assets were issued on AssetHub + assert_expected_events!( + AssetHubKusama, + 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 registering of a token as an asset on AssetHub, and then subsequently sending +/// a token from Ethereum to AssetHub. +#[test] +fn send_token_from_ethereum_to_asset_hub() { + BridgeHubKusama::fund_para_sovereign(AssetHubKusama::para_id().into(), INITIAL_FUND); + // Fund ethereum sovereign account on AssetHub. + AssetHubKusama::fund_accounts(vec![(ethereum_sovereign_account(), INITIAL_FUND)]); + + BridgeHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + + // 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!( + BridgeHubKusama, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the token was received and issued as a foreign asset on AssetHub + assert_expected_events!( + AssetHubKusama, + 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() { + let assethub_sovereign = BridgeHubKusama::sovereign_account_id_of(Location::new( + 1, + [Parachain(AssetHubKusama::para_id().into())], + )); + + AssetHubKusama::force_default_xcm_version(Some(XCM_VERSION)); + BridgeHubKusama::force_default_xcm_version(Some(XCM_VERSION)); + AssetHubKusama::force_xcm_version( + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), + XCM_VERSION, + ); + + BridgeHubKusama::fund_accounts(vec![ + (assethub_sovereign.clone(), INITIAL_FUND), + (TREASURY_ACCOUNT.into(), INITIAL_FUND), + ]); + AssetHubKusama::fund_accounts(vec![ + (AssetHubPolkadotReceiver::get(), INITIAL_FUND), + (ethereum_sovereign_account(), INITIAL_FUND), + ]); + + const WETH_AMOUNT: u128 = 1_000_000_000; + let base_fee = 2_750_872_500_000u128; + + AssetHubKusama::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(BridgeHubEthereumBaseFee::key().to_vec(), base_fee.encode())], + )); + }); + + BridgeHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!( + ::EthereumSystem::set_pricing_parameters( + ::RuntimeOrigin::root(), + PricingParametersOf:: { + exchange_rate: FixedU128::from_rational(1, 75), + fee_per_gas: gwei(20), + rewards: Rewards { + local: (1 * UNITS / 100).into(), // 0.01 KSM + remote: meth(1), + }, + } + ) + ); + + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + + // 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!( + BridgeHubKusama, + 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!( + BridgeHubKusama, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + // Check that AssetHub has issued the foreign asset + assert_expected_events!( + AssetHubKusama, + 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::V4(Assets::from(assets)); + + let destination = VersionedLocation::V4(Location::new( + 2, + [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], + )); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + let free_balance_before = ::Balances::free_balance( + AssetHubKusamaReceiver::get(), + ); + // Send the Weth back to Ethereum + assert_ok!(::PolkadotXcm::reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubKusamaReceiver::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + )); + + let free_balance_after = ::Balances::free_balance( + AssetHubKusamaReceiver::get(), + ); + // Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender + let free_balance_diff = free_balance_before - free_balance_after; + assert!(free_balance_diff > base_fee); + }); + + BridgeHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubKusama, + vec![ + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued {..}) => {}, + ] + ); + let events = BridgeHubKusama::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 }) + if *who == TREASURY_ACCOUNT.into() && *amount == 169033333 + )), + "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 }) + if *who == assethub_sovereign && *amount == 502500000000, + )), + "AssetHub sovereign takes remote fee." + ); + }); +} + +#[test] +fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { + BridgeHubKusama::fund_para_sovereign(AssetHubKusama::para_id().into(), INITIAL_FUND); + + BridgeHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + + // Construct RegisterToken message and sent to inbound queue + let message = make_register_token_with_infufficient_fee_message(); + assert_ok!(send_inbound_message(message)); + + assert_expected_events!( + BridgeHubKusama, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubKusama::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubKusama, + vec![ + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {}, + ] + ); + }); +} + +#[test] +fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() { + // Insufficient fund + BridgeHubKusama::fund_para_sovereign(AssetHubKusama::para_id().into(), 1_000); + + BridgeHubKusama::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + + assert_err!(send_inbound_message(make_register_token_message()), Token(FundsUnavailable)); + }); +} + +/// Tests that the EthereumInboundQueue CreateAssetCall parameter on BridgeHub matches +/// the ForeignAssets::create call on AssetHub. +#[test] +fn asset_hub_foreign_assets_pallet_is_configured_correctly_in_bridge_hub() { + let assethub_sovereign = BridgeHubKusama::sovereign_account_id_of(Location::new( + 1, + [Parachain(AssetHubKusama::para_id().into())], + )); + + let call_create_foreign_assets = + ::RuntimeCall::ForeignAssets(pallet_assets::Call::< + ::Runtime, + pallet_assets::Instance2, + >::create { + id: v3::Location::default(), + min_balance: ASSET_MIN_BALANCE, + admin: assethub_sovereign.into(), + }) + .encode(); + + let bridge_hub_inbound_queue_assets_pallet_call_index = CreateAssetCall::get(); + + assert!( + call_create_foreign_assets.starts_with(&bridge_hub_inbound_queue_assets_pallet_call_index) + ); +} + +fn ethereum_sovereign_account() -> AccountId { + let origin_location = (Parent, Parent, EthereumNetwork::get()).into(); + GlobalConsensusEthereumConvertsFor::::convert_location(&origin_location).unwrap() +} + +fn make_register_token_message() -> InboundQueueFixture { + InboundQueueFixture { + execution_header: CompactExecutionHeader{ + parent_hash: hex!("d5de3dd02c96dbdc8aaa4db70a1e9fdab5ded5f4d52f18798acd56a3d37d1ad6").into(), + block_number: 772, + state_root: hex!("49cba2a79b23ad74cefe80c3a96699825d1cda0f75bfceb587c5549211c86245").into(), + receipts_root: hex!("ac9cf067acc72a958a0d7c572c7b66ba6e232f65bbbd09078d7c7123f87ede64").into(), + }, + message: Message { + event_log: Log { + address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), + ], + data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e0001000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000").into(), + }, + proof: Proof { + block_hash: hex!("392182a385b3a417e8ddea8b252953ee81e6ec0fb09d9056c96c89fbeb703a3f").into(), + tx_index: 0, + data: (vec![ + hex!("7b1f61b9714c080ef0be014e01657a15f45f0304b477beebc7ca5596c8033095").to_vec(), + ], vec![ + hex!("f9028e822080b9028802f90284018301d205b9010000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000080000000000000000000000000000004000000000080000000000000000000000000000000000010100000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000040004000000000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000200000000000010f90179f85894eda338e4dc46038493b885327842fd3e301cab39e1a0f78bb28d4b1d7da699e5c0bc2be29c2b04b5aab6aacf6298fe5304f9db9c6d7ea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7df9011c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a05f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0b8a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e0001000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000").to_vec(), + ]), + }, + }, + } +} + +fn make_send_token_message() -> InboundQueueFixture { + InboundQueueFixture { + execution_header: CompactExecutionHeader{ + parent_hash: hex!("920cecde45d428e3a77590b70f8533cf4c2c36917b8a7b74c915e7fa3dae7075").into(), + block_number: 1148, + state_root: hex!("bbc6ba0e9940d641afecbbaf3f97abd2b9ffaf2f6bd4879c4a71e659eca89978").into(), + receipts_root: hex!("717d6f476c17511fe96543b914cf08f19352567e10188f7f6c6c2f4528806c9c").into(), + }, + message: Message { + event_log: Log { + address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + hex!("c8eaf22f2cb07bac4679df0a660e7115ed87fcfd4e32ac269f6540265bbbd26f").into(), + ], + data: hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005f0001000000000000000187d1f7fdfee7f651fabc8bfcb6e086c278b77a7d008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000e40b5402000000000000000000000000").into(), + }, + proof: Proof { + block_hash: hex!("d3c155f123c3cbff22f3d7869283e02179edea9ffa7a5e9a4d8414c2a6b8991f").into(), + tx_index: 0, + data: (vec![ + hex!("9f3340b57eddc1f86de30776db57faeca80269a3dd459031741988dec240ce34").to_vec(), + ], vec![ + hex!("f90451822080b9044b02f90447018301bcb9b9010000800000000000000000000020000000000000000000004000000000000000000400000000000000000000001000000010000000000000000000000008000000200000000000000001000008000000000000000000000000000000008000080000000000200000000000000000000000000100000000000000000011000000000000020200000000000000000000000000003000000040080008000000000000000000040044000021000000002000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000200800000000000f9033cf89b9487d1f7fdfee7f651fabc8bfcb6e086c278b77a7df863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000090a987b944cb1dcce5564e5fdecd7a54d3de27fea000000000000000000000000057a2d4ff0c3866d96556884bf09fecdd7ccd530ca00000000000000000000000000000000000000000000000000de0b6b3a7640000f9015d94eda338e4dc46038493b885327842fd3e301cab39f884a024c5d2de620c6e25186ae16f6919eba93b6e2c1a33857cc419d9f3a00d6967e9a000000000000000000000000090a987b944cb1dcce5564e5fdecd7a54d3de27fea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7da000000000000000000000000000000000000000000000000000000000000003e8b8c000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000208eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48f9013c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a0c8eaf22f2cb07bac4679df0a660e7115ed87fcfd4e32ac269f6540265bbbd26fb8c000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005f0001000000000000000187d1f7fdfee7f651fabc8bfcb6e086c278b77a7d008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000e40b5402000000000000000000000000").to_vec(), + ]), + }, + }, + } +} diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/Cargo.toml b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/Cargo.toml index 0a44e29909..d7fa7a7aa8 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/Cargo.toml +++ b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/Cargo.toml @@ -8,8 +8,12 @@ description = "Bridge Hub Polkadot runtime integration tests with xcm-emulator" publish = false [dependencies] +codec = { package = "parity-scale-codec", version = "3.6.9" } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +hex-literal = "0.4.1" # Substrate +sp-core = { version = "29.0.0" } sp-runtime = { version = "32.0.0" } frame-support = { version = "29.0.0" } pallet-balances = { version = "29.0.0" } @@ -31,7 +35,19 @@ cumulus-pallet-xcmp-queue = { version = "0.8.0" } bp-messages = { version = "0.8.0" } pallet-bridge-messages = { version = "0.8.0" } -# Runtimes +# Local +bp-bridge-hub-polkadot = { path = "../../../../../system-parachains/bridge-hubs/bridge-hub-polkadot/primitives"} bridge-hub-polkadot-runtime = { path = "../../../../../system-parachains/bridge-hubs/bridge-hub-polkadot" } +asset-hub-polkadot-runtime = { path = "../../../../../system-parachains/asset-hubs/asset-hub-polkadot" } integration-tests-helpers = { path = "../../../helpers" } kusama-polkadot-system-emulated-network = { path = "../../../networks/kusama-polkadot-system" } +polkadot-system-emulated-network = { path = "../../../networks/polkadot-system" } +system-parachains-constants = { path = "../../../../../system-parachains/constants" } + +# Snowbridge +snowbridge-beacon-primitives = { version = "0.1.0" } +snowbridge-core = { version = "0.1.0" } +snowbridge-router-primitives = { version = "0.1.0" } +snowbridge-pallet-system = { version = "0.1.0" } +snowbridge-pallet-outbound-queue = { version = "0.1.0" } +snowbridge-pallet-inbound-queue-fixtures = { version = "0.9.0" } diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/lib.rs b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/lib.rs index e82ecd4410..4dec6f9570 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/lib.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/lib.rs @@ -53,7 +53,7 @@ pub use kusama_polkadot_system_emulated_network::{ genesis::ED as BRIDGE_HUB_POLKADOT_ED, BridgeHubPolkadotParaPallet as BridgeHubPolkadotPallet, }, - polkadot_emulated_chain::PolkadotRelayPallet as PolkadotPallet, + polkadot_emulated_chain::{genesis::ED as POLKADOT_ED, PolkadotRelayPallet as PolkadotPallet}, AssetHubKusamaPara as AssetHubKusama, AssetHubKusamaParaReceiver as AssetHubKusamaReceiver, AssetHubPolkadotPara as AssetHubPolkadot, AssetHubPolkadotParaReceiver as AssetHubPolkadotReceiver, @@ -62,6 +62,11 @@ pub use kusama_polkadot_system_emulated_network::{ BridgeHubPolkadotParaSender as BridgeHubPolkadotSender, PolkadotRelay as Polkadot, }; pub use parachains_common::{AccountId, Balance}; +pub use polkadot_system_emulated_network::{ + penpal_emulated_chain::PenpalBParaPallet as PenpalBPallet, + BridgeHubPolkadotParaReceiver as BridgeHubPolkadotReceiver, PenpalBPara as PenpalB, + PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender, +}; pub const ASSET_ID: u32 = 1; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/mod.rs b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/mod.rs index b0560761ee..9b9c1243cf 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/mod.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/mod.rs @@ -18,6 +18,7 @@ use crate::*; mod asset_transfers; mod claim_assets; mod send_xcm; +mod snowbridge; mod teleport; pub(crate) fn asset_hub_kusama_location() -> Location { diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs new file mode 100644 index 0000000000..3cbeadf3f7 --- /dev/null +++ b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs @@ -0,0 +1,736 @@ +// 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::*; +use asset_hub_polkadot_runtime::xcm_config::bridging::to_ethereum::{ + BridgeHubEthereumBaseFee, EthereumNetwork, +}; +use bp_bridge_hub_polkadot::snowbridge::CreateAssetCall; +use bridge_hub_polkadot_runtime::{ + EthereumBeaconClient, EthereumGatewayAddress, EthereumInboundQueue, Runtime, RuntimeOrigin, +}; +use codec::{Decode, Encode}; +use emulated_integration_tests_common::xcm_emulator::ConvertLocation; +use frame_support::pallet_prelude::TypeInfo; +use hex_literal::hex; +use polkadot_system_emulated_network::{ + penpal_emulated_chain::CustomizableAssetFromSystemAssetHub, + BridgeHubPolkadotParaSender as BridgeHubPolkadotSender, +}; +use snowbridge_beacon_primitives::CompactExecutionHeader; +use snowbridge_core::{ + gwei, + inbound::{Log, Message, Proof}, + meth, + outbound::OperatingMode, + Rewards, +}; +use snowbridge_pallet_inbound_queue_fixtures::{ + register_token_with_insufficient_fee::make_register_token_with_infufficient_fee_message, + InboundQueueFixture, +}; +use snowbridge_pallet_system::PricingParametersOf; +use snowbridge_router_primitives::inbound::{ + Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, +}; +use sp_core::{H160, H256}; +use sp_runtime::{DispatchError::Token, FixedU128, TokenError::FundsUnavailable}; +use system_parachains_constants::polkadot::currency::UNITS; + +const INITIAL_FUND: u128 = 5_000_000_000 * POLKADOT_ED; +const CHAIN_ID: u64 = 1; +const TREASURY_ACCOUNT: [u8; 32] = + hex!("af3e7da28608e13e4399cc7d14a57bdb154dde5f3d546f5f293994ef36ef7f11"); +const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); +const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); +const GATEWAY_ADDRESS: [u8; 20] = hex!("EDa338E4dC46038493b885327842fD3E301CaB39"); + +#[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: InboundQueueFixture) -> DispatchResult { + EthereumBeaconClient::store_execution_header( + fixture.message.proof.block_hash, + fixture.execution_header, + 0, + H256::default(), + ); + + EthereumInboundQueue::submit( + RuntimeOrigin::signed(BridgeHubPolkadotSender::get()), + fixture.message, + ) +} + +/// 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. + BridgeHubPolkadot::fund_para_sovereign(origin_para.into(), INITIAL_FUND); + + let sudo_origin = ::RuntimeOrigin::root(); + let destination = Polkadot::child_location_of(BridgeHubPolkadot::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 { + require_weight_at_most: 3000000000.into(), + origin_kind: OriginKind::Xcm, + call: create_agent_call.encode().into(), + }, + ])); + + // Polkadot Global Consensus + // Send XCM message from Relay Chain to Bridge Hub source Parachain + Polkadot::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!( + Polkadot, + vec![ + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that a message was sent to Ethereum to create the agent + assert_expected_events!( + BridgeHubPolkadot, + 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. + BridgeHubPolkadot::fund_para_sovereign(origin_para.into(), INITIAL_FUND); + + let sudo_origin = ::RuntimeOrigin::root(); + let destination: VersionedLocation = + Polkadot::child_location_of(BridgeHubPolkadot::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 { + require_weight_at_most: 3000000000.into(), + origin_kind: OriginKind::Xcm, + call: create_agent_call.encode().into(), + }, + ])); + + 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 { + require_weight_at_most: 3000000000.into(), + origin_kind: OriginKind::Xcm, + call: create_channel_call.encode().into(), + }, + ])); + + // Polkadot Global Consensus + // Send XCM message from Relay Chain to Bridge Hub source Parachain + Polkadot::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!( + Polkadot, + vec![ + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the Channel was created + assert_expected_events!( + BridgeHubPolkadot, + 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 AH sovereign account on BH so that it can pay execution fees. + BridgeHubPolkadot::fund_para_sovereign(AssetHubPolkadot::para_id().into(), INITIAL_FUND); + // Fund ethereum sovereign account on AssetHub. + AssetHubPolkadot::fund_accounts(vec![(ethereum_sovereign_account(), INITIAL_FUND)]); + + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + // 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!( + BridgeHubPolkadot, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubPolkadot, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {}, + ] + ); + }); +} + +/// Tests sending a token to a 3rd party parachain, called PenPal. The token reserve is +/// still located on AssetHub. +#[test] +fn send_token_from_ethereum_to_penpal() { + let asset_hub_sovereign = BridgeHubPolkadot::sovereign_account_id_of(Location::new( + 1, + [Parachain(AssetHubPolkadot::para_id().into())], + )); + + // The Weth asset location, identified by the contract address on Ethereum + let weth_asset_location: Location = + (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); + // Converts the Weth asset location into an asset ID + let weth_asset_id: v3::Location = weth_asset_location.try_into().unwrap(); + + // Fund ethereum sovereign on AssetHub + AssetHubPolkadot::fund_accounts(vec![(ethereum_sovereign_account(), INITIAL_FUND)]); + + // Create asset on the Penpal parachain. + PenpalB::execute_with(|| { + // Set the trusted asset location from AH, in this case, Ethereum. + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]).encode(), + )], + )); + + assert_ok!(::ForeignAssets::create( + ::RuntimeOrigin::signed(PenpalBSender::get()), + weth_asset_id, + asset_hub_sovereign.clone().into(), + 1000, + )); + + assert!(::ForeignAssets::asset_exists(weth_asset_id)); + }); + + AssetHubPolkadot::execute_with(|| { + assert_ok!(::ForeignAssets::force_create( + ::RuntimeOrigin::root(), + weth_asset_id, + asset_hub_sovereign.clone().into(), + true, + 1000, + )); + + assert!(::ForeignAssets::asset_exists( + weth_asset_id + )); + }); + + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + type RuntimeOrigin = ::RuntimeOrigin; + + // Fund AssetHub sovereign account so it can pay execution fees for the asset transfer + assert_ok!(::Balances::force_set_balance( + RuntimeOrigin::root(), + asset_hub_sovereign.clone().into(), + INITIAL_FUND, + )); + + let message_id: H256 = [1; 32].into(); + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendToken { + token: WETH.into(), + destination: Destination::ForeignAccountId32 { + para_id: PenpalB::para_id().into(), + id: PenpalBReceiver::get().into(), + fee: 40_000_000_000, + }, + amount: 1_000_000, + fee: 40_000_000_000, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id()).unwrap(); + + assert_expected_events!( + BridgeHubPolkadot, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the assets were issued on AssetHub + assert_expected_events!( + AssetHubPolkadot, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the assets were issued on PenPal + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + ] + ); + }); +} + +/// Tests the registering of a token as an asset on AssetHub, and then subsequently sending +/// a token from Ethereum to AssetHub. +#[test] +fn send_token_from_ethereum_to_asset_hub() { + BridgeHubPolkadot::fund_para_sovereign(AssetHubPolkadot::para_id().into(), INITIAL_FUND); + // Fund ethereum sovereign account on AssetHub. + AssetHubPolkadot::fund_accounts(vec![(ethereum_sovereign_account(), INITIAL_FUND)]); + + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + + // 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!( + BridgeHubPolkadot, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the token was received and issued as a foreign asset on AssetHub + assert_expected_events!( + AssetHubPolkadot, + 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() { + let assethub_sovereign = BridgeHubPolkadot::sovereign_account_id_of(Location::new( + 1, + [Parachain(AssetHubPolkadot::para_id().into())], + )); + + AssetHubPolkadot::force_default_xcm_version(Some(XCM_VERSION)); + BridgeHubPolkadot::force_default_xcm_version(Some(XCM_VERSION)); + AssetHubPolkadot::force_xcm_version( + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), + XCM_VERSION, + ); + + BridgeHubPolkadot::fund_accounts(vec![ + (assethub_sovereign.clone(), INITIAL_FUND), + (TREASURY_ACCOUNT.into(), INITIAL_FUND), + ]); + AssetHubPolkadot::fund_accounts(vec![ + (AssetHubPolkadotReceiver::get(), INITIAL_FUND), + (ethereum_sovereign_account(), INITIAL_FUND), + ]); + + const WETH_AMOUNT: u128 = 1_000_000_000; + let base_fee = 2_750_872_500_000u128; + + AssetHubPolkadot::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(BridgeHubEthereumBaseFee::key().to_vec(), base_fee.encode())], + )); + }); + + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!( + ::EthereumSystem::set_pricing_parameters( + ::RuntimeOrigin::root(), + PricingParametersOf:: { + exchange_rate: FixedU128::from_rational(1, 75), + fee_per_gas: gwei(20), + rewards: Rewards { + local: (1 * UNITS / 100).into(), // 0.01 DOT + remote: meth(1), + }, + } + ) + ); + + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + + // 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!( + BridgeHubPolkadot, + 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!( + BridgeHubPolkadot, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + // Check that AssetHub has issued the foreign asset + assert_expected_events!( + AssetHubPolkadot, + 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::V4(Assets::from(assets)); + + let destination = VersionedLocation::V4(Location::new( + 2, + [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], + )); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + let free_balance_before = + ::Balances::free_balance( + AssetHubPolkadotReceiver::get(), + ); + // Send the Weth back to Ethereum + assert_ok!( + ::PolkadotXcm::reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubPolkadotReceiver::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + ) + ); + + let free_balance_after = + ::Balances::free_balance( + AssetHubPolkadotReceiver::get(), + ); + // Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender + let free_balance_diff = free_balance_before - free_balance_after; + assert!(free_balance_diff > base_fee); + }); + + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubPolkadot, + vec![ + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued {..}) => {}, + ] + ); + let events = BridgeHubPolkadot::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 }) + if *who == TREASURY_ACCOUNT.into() && *amount == 50710000 + )), + "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 }) + if *who == assethub_sovereign && *amount == 5025000000, + )), + "AssetHub sovereign takes remote fee." + ); + }); +} + +#[test] +fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { + BridgeHubPolkadot::fund_para_sovereign(AssetHubPolkadot::para_id().into(), INITIAL_FUND); + + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + + // Construct RegisterToken message and sent to inbound queue + let message = make_register_token_with_infufficient_fee_message(); + assert_ok!(send_inbound_message(message)); + + assert_expected_events!( + BridgeHubPolkadot, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubPolkadot, + vec![ + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {}, + ] + ); + }); +} + +#[test] +fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() { + // Insufficient fund + BridgeHubPolkadot::fund_para_sovereign(AssetHubPolkadot::para_id().into(), 1_000); + + BridgeHubPolkadot::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], + )); + + assert_err!(send_inbound_message(make_register_token_message()), Token(FundsUnavailable)); + }); +} + +/// Tests that the EthereumInboundQueue CreateAssetCall parameter on BridgeHub matches +/// the ForeignAssets::create call on AssetHub. +#[test] +fn asset_hub_foreign_assets_pallet_is_configured_correctly_in_bridge_hub() { + let assethub_sovereign = BridgeHubPolkadot::sovereign_account_id_of(Location::new( + 1, + [Parachain(AssetHubPolkadot::para_id().into())], + )); + + let call_create_foreign_assets = + ::RuntimeCall::ForeignAssets(pallet_assets::Call::< + ::Runtime, + pallet_assets::Instance2, + >::create { + id: v3::Location::default(), + min_balance: ASSET_MIN_BALANCE, + admin: assethub_sovereign.into(), + }) + .encode(); + + let bridge_hub_inbound_queue_assets_pallet_call_index = CreateAssetCall::get(); + + assert!( + call_create_foreign_assets.starts_with(&bridge_hub_inbound_queue_assets_pallet_call_index) + ); +} + +fn ethereum_sovereign_account() -> AccountId { + let origin_location = (Parent, Parent, EthereumNetwork::get()).into(); + GlobalConsensusEthereumConvertsFor::::convert_location(&origin_location).unwrap() +} + +fn make_register_token_message() -> InboundQueueFixture { + InboundQueueFixture { + execution_header: CompactExecutionHeader{ + parent_hash: hex!("d5de3dd02c96dbdc8aaa4db70a1e9fdab5ded5f4d52f18798acd56a3d37d1ad6").into(), + block_number: 772, + state_root: hex!("49cba2a79b23ad74cefe80c3a96699825d1cda0f75bfceb587c5549211c86245").into(), + receipts_root: hex!("ac9cf067acc72a958a0d7c572c7b66ba6e232f65bbbd09078d7c7123f87ede64").into(), + }, + message: Message { + event_log: Log { + address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), + ], + data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e0001000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000").into(), + }, + proof: Proof { + block_hash: hex!("392182a385b3a417e8ddea8b252953ee81e6ec0fb09d9056c96c89fbeb703a3f").into(), + tx_index: 0, + data: (vec![ + hex!("7b1f61b9714c080ef0be014e01657a15f45f0304b477beebc7ca5596c8033095").to_vec(), + ], vec![ + hex!("f9028e822080b9028802f90284018301d205b9010000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000080000000000000000000000000000004000000000080000000000000000000000000000000000010100000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000040004000000000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000200000000000010f90179f85894eda338e4dc46038493b885327842fd3e301cab39e1a0f78bb28d4b1d7da699e5c0bc2be29c2b04b5aab6aacf6298fe5304f9db9c6d7ea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7df9011c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a05f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0b8a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e0001000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000").to_vec(), + ]), + }, + }, + } +} + +fn make_send_token_message() -> InboundQueueFixture { + InboundQueueFixture { + execution_header: CompactExecutionHeader{ + parent_hash: hex!("920cecde45d428e3a77590b70f8533cf4c2c36917b8a7b74c915e7fa3dae7075").into(), + block_number: 1148, + state_root: hex!("bbc6ba0e9940d641afecbbaf3f97abd2b9ffaf2f6bd4879c4a71e659eca89978").into(), + receipts_root: hex!("717d6f476c17511fe96543b914cf08f19352567e10188f7f6c6c2f4528806c9c").into(), + }, + message: Message { + event_log: Log { + address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + hex!("c8eaf22f2cb07bac4679df0a660e7115ed87fcfd4e32ac269f6540265bbbd26f").into(), + ], + data: hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005f0001000000000000000187d1f7fdfee7f651fabc8bfcb6e086c278b77a7d008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000e40b5402000000000000000000000000").into(), + }, + proof: Proof { + block_hash: hex!("d3c155f123c3cbff22f3d7869283e02179edea9ffa7a5e9a4d8414c2a6b8991f").into(), + tx_index: 0, + data: (vec![ + hex!("9f3340b57eddc1f86de30776db57faeca80269a3dd459031741988dec240ce34").to_vec(), + ], vec![ + hex!("f90451822080b9044b02f90447018301bcb9b9010000800000000000000000000020000000000000000000004000000000000000000400000000000000000000001000000010000000000000000000000008000000200000000000000001000008000000000000000000000000000000008000080000000000200000000000000000000000000100000000000000000011000000000000020200000000000000000000000000003000000040080008000000000000000000040044000021000000002000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000200800000000000f9033cf89b9487d1f7fdfee7f651fabc8bfcb6e086c278b77a7df863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000090a987b944cb1dcce5564e5fdecd7a54d3de27fea000000000000000000000000057a2d4ff0c3866d96556884bf09fecdd7ccd530ca00000000000000000000000000000000000000000000000000de0b6b3a7640000f9015d94eda338e4dc46038493b885327842fd3e301cab39f884a024c5d2de620c6e25186ae16f6919eba93b6e2c1a33857cc419d9f3a00d6967e9a000000000000000000000000090a987b944cb1dcce5564e5fdecd7a54d3de27fea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7da000000000000000000000000000000000000000000000000000000000000003e8b8c000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000208eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48f9013c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a0c8eaf22f2cb07bac4679df0a660e7115ed87fcfd4e32ac269f6540265bbbd26fb8c000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005f0001000000000000000187d1f7fdfee7f651fabc8bfcb6e086c278b77a7d008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000e40b5402000000000000000000000000").to_vec(), + ]), + }, + }, + } +} diff --git a/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml b/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml index ad921f0474..6c1bf63ddf 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml @@ -94,6 +94,7 @@ assets-common = { default-features = false , version = "0.8.0" } # Bridges pallet-xcm-bridge-hub-router = { default-features = false , version = "0.6.0" } +snowbridge-router-primitives = { default-features = false , version = "0.1.0" } [dev-dependencies] asset-test-utils = { version = "8.0.1" } @@ -142,6 +143,7 @@ runtime-benchmarks = [ "pallet-xcm/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -235,6 +237,7 @@ std = [ "polkadot-runtime-common/std", "polkadot-runtime-constants/std", "scale-info/std", + "snowbridge-router-primitives/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/system-parachains/asset-hubs/asset-hub-kusama/primitives/Cargo.toml b/system-parachains/asset-hubs/asset-hub-kusama/primitives/Cargo.toml index 102c205335..81aa14b93b 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/primitives/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-kusama/primitives/Cargo.toml @@ -11,6 +11,9 @@ license.workspace = true codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +# Local +system-parachains-constants = { path = "../../../constants", default-features = false } + # Bridge Dependencies bp-xcm-bridge-hub-router = { default-features = false , version = "0.7.0" } @@ -29,5 +32,6 @@ std = [ "frame-support/std", "scale-info/std", "sp-std/std", + "system-parachains-constants/std", "xcm/std" ] diff --git a/system-parachains/asset-hubs/asset-hub-kusama/primitives/src/lib.rs b/system-parachains/asset-hubs/asset-hub-kusama/primitives/src/lib.rs index cce3b111bf..3112b297dc 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/primitives/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/primitives/src/lib.rs @@ -24,6 +24,8 @@ use xcm::prelude::*; pub use bp_xcm_bridge_hub_router::XcmBridgeHubRouterCall; +use system_parachains_constants::kusama::currency::UNITS; + /// `AssetHubKusama` Runtime `Call` enum. /// /// The enum represents a subset of possible `Call`s we can send to `AssetHubKusama` chain. @@ -48,6 +50,9 @@ frame_support::parameter_types! { pub CongestedMessage: Xcm<()> = build_congestion_message(true).into(); /// Message that is sent to the sibling Kusama Asset Hub when the with-Polkadot bridge becomes uncongested. pub UncongestedMessage: Xcm<()> = build_congestion_message(false).into(); + + /// Should match the `AssetDeposit` of the `ForeignAssets` pallet on Asset Hub. + pub const CreateForeignAssetDeposit: u128 = UNITS / 10; } fn build_congestion_message(is_congested: bool) -> sp_std::vec::Vec> { diff --git a/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs b/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs index 41b793ab29..8070b827bf 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs @@ -30,7 +30,7 @@ pub mod xcm_config; use assets_common::{ foreign_creators::ForeignCreators, local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, - matching::FromSiblingParachain, + matching::{FromNetwork, FromSiblingParachain}, AssetIdForTrustBackedAssetsConvert, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; @@ -396,7 +396,14 @@ impl pallet_assets::Config for Runtime { type AssetIdParameter = xcm::v3::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< - (FromSiblingParachain, xcm::v3::Location>,), + ( + FromSiblingParachain, xcm::v3::Location>, + FromNetwork< + xcm_config::UniversalLocation, + xcm_config::bridging::to_ethereum::EthereumNetwork, + xcm::v3::Location, + >, + ), ForeignCreatorsSovereignAccountOf, AccountId, xcm::v3::Location, @@ -1757,4 +1764,12 @@ mod tests { let parachain_tbf = TransactionByteFee::get(); assert_eq!(relay_tbf / 10, parachain_tbf); } + + #[test] + fn create_foreign_asset_deposit_is_equal_to_asset_hub_foreign_asset_pallet_deposit() { + assert_eq!( + bp_asset_hub_kusama::CreateForeignAssetDeposit::get(), + ForeignAssetsAssetDeposit::get() + ); + } } diff --git a/system-parachains/asset-hubs/asset-hub-kusama/src/xcm_config.rs b/system-parachains/asset-hubs/asset-hub-kusama/src/xcm_config.rs index 0c0eff4b07..f6139a3061 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/src/xcm_config.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/src/xcm_config.rs @@ -21,7 +21,7 @@ use super::{ }; use crate::{ForeignAssets, ForeignAssetsInstance}; use assets_common::{ - matching::{FromSiblingParachain, IsForeignConcreteAsset}, + matching::{FromNetwork, FromSiblingParachain, IsForeignConcreteAsset}, TrustBackedAssetsAsLocation, }; use frame_support::{ @@ -41,6 +41,7 @@ use parachains_common::{ }, }; use polkadot_parachain_primitives::primitives::Sibling; +use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use system_parachains_constants::TREASURY_PALLET_ID; use xcm::latest::prelude::*; @@ -51,9 +52,10 @@ use xcm_builder::{ FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, - TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, - WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, + SignedToAccountId32, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::{ traits::{ConvertLocation, WithOriginFilter}, @@ -105,6 +107,9 @@ pub type LocationToAccountId = ( // Different global consensus parachain sovereign account. // (Used for over-bridge transfers and reserve processing) GlobalConsensusParachainConvertsFor, + // Ethereum contract sovereign account. + // (Used to get convert ethereum contract locations to sovereign account) + GlobalConsensusEthereumConvertsFor, ); /// Means for transacting the native currency on this chain. @@ -256,8 +261,9 @@ impl Contains for SafeCallFilter { match call { RuntimeCall::System(frame_system::Call::set_storage { items }) if items.iter().all(|(k, _)| { - k.eq(&bridging::XcmBridgeHubRouterBaseFee::key()) || - k.eq(&bridging::XcmBridgeHubRouterByteFee::key()) + k.eq(&bridging::XcmBridgeHubRouterByteFee::key()) || + k.eq(&bridging::XcmBridgeHubRouterBaseFee::key()) || + k.eq(&bridging::to_ethereum::BridgeHubEthereumBaseFee::key()) }) => return true, _ => (), @@ -529,7 +535,10 @@ impl xcm_executor::Config for XcmConfig { // as reserve locations (we trust the Bridge Hub to relay the message that a reserve is being // held). Asset Hub may _act_ as a reserve location for KSM and assets created // under `pallet-assets`. Users must use teleport where allowed (e.g. KSM with the Relay Chain). - type IsReserve = (bridging::to_polkadot::IsTrustedBridgedReserveLocationForConcreteAsset,); + type IsReserve = ( + bridging::to_polkadot::IsTrustedBridgedReserveLocationForConcreteAsset, + bridging::to_ethereum::IsTrustedBridgedReserveLocationForForeignAsset, + ); type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; type Barrier = Barrier; @@ -594,7 +603,8 @@ impl xcm_executor::Config for XcmConfig { XcmFeeToAccount, >; type MessageExporter = (); - type UniversalAliases = bridging::to_polkadot::UniversalAliases; + type UniversalAliases = + (bridging::to_polkadot::UniversalAliases, bridging::to_ethereum::UniversalAliases); type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; @@ -622,6 +632,13 @@ pub type XcmRouter = WithUniqueTopic<( // Router which wraps and sends xcm to BridgeHub to be delivered to the Polkadot // GlobalConsensus ToPolkadotXcmRouter, + // Router which wraps and sends xcm to BridgeHub to be delivered to the Ethereum + // GlobalConsensus + SovereignPaidRemoteExporter< + xcm_builder::NetworkExportTable, + XcmpQueue, + UniversalLocation, + >, )>; impl pallet_xcm::Config for Runtime { @@ -667,6 +684,7 @@ pub type ForeignCreatorsSovereignAccountOf = ( SiblingParachainConvertsVia, AccountId32Aliases, ParentIsPreset, + GlobalConsensusEthereumConvertsFor, ); /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. @@ -780,6 +798,57 @@ pub mod bridging { >; } + pub mod to_ethereum { + use super::*; + pub use bp_bridge_hub_kusama::snowbridge::EthereumNetwork; + use bp_bridge_hub_kusama::snowbridge::InboundQueuePalletInstance; + + parameter_types! { + /// User fee for transfers from Kusama to Ethereum. + /// The fee is set to max Balance to disable the bridge until a fee is set by + /// governance. + pub const DefaultBridgeHubEthereumBaseFee: Balance = Balance::MAX; + pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); + pub SiblingBridgeHubWithEthereumInboundQueueInstance: Location = Location::new( + 1, + [ + Parachain(SiblingBridgeHubParaId::get()), + PalletInstance(InboundQueuePalletInstance::get()), + ] + ); + + /// Set up exporters configuration. + /// `Option` represents static "base fee" which is used for total delivery fee calculation. + pub BridgeTable: sp_std::vec::Vec = sp_std::vec![ + NetworkExportTableItem::new( + EthereumNetwork::get(), + Some(sp_std::vec![Junctions::Here]), + SiblingBridgeHub::get(), + Some(( + XcmBridgeHubRouterFeeAssetId::get(), + BridgeHubEthereumBaseFee::get(), + ).into()) + ), + ]; + + /// Universal aliases + pub UniversalAliases: BTreeSet<(Location, Junction)> = BTreeSet::from_iter( + sp_std::vec![ + (SiblingBridgeHubWithEthereumInboundQueueInstance::get(), GlobalConsensus(EthereumNetwork::get())), + ] + ); + } + + pub type IsTrustedBridgedReserveLocationForForeignAsset = + matching::IsForeignConcreteAsset>; + + impl Contains<(Location, Junction)> for UniversalAliases { + fn contains(alias: &(Location, Junction)) -> bool { + UniversalAliases::get().contains(alias) + } + } + } + /// Benchmarks helper for bridging configuration. #[cfg(feature = "runtime-benchmarks")] pub struct BridgingBenchmarksHelper; diff --git a/system-parachains/asset-hubs/asset-hub-kusama/tests/snowbridge.rs b/system-parachains/asset-hubs/asset-hub-kusama/tests/snowbridge.rs new file mode 100644 index 0000000000..0d8e946e83 --- /dev/null +++ b/system-parachains/asset-hubs/asset-hub-kusama/tests/snowbridge.rs @@ -0,0 +1,75 @@ +// This file is part of Cumulus. + +// 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 asset_hub_kusama_runtime::xcm_config::bridging::{ + to_ethereum::{BridgeHubEthereumBaseFee, BridgeTable, EthereumNetwork}, + SiblingBridgeHub, XcmBridgeHubRouterFeeAssetId, +}; +use sp_core::H160; +use sp_std::prelude::*; +use xcm::prelude::*; +use xcm_builder::{ExporterFor, NetworkExportTable}; + +#[test] +fn network_export_table_works() { + sp_io::TestExternalities::default().execute_with(|| { + let test_data: Vec<(NetworkId, InteriorLocation, Option<(Location, Option)>)> = vec![ + // From Ethereum (from GlobalConsensus(Ethereum) is routed to BridgeHub, with a fee, + // matched. + ( + EthereumNetwork::get(), + Junctions::Here, + Some(( + SiblingBridgeHub::get().into(), + Some(Asset { + id: XcmBridgeHubRouterFeeAssetId::get(), + fun: Fungible(BridgeHubEthereumBaseFee::get()), + }), + )), + ), + // From Ethereum with a random parachain ID filter, not matched. + (EthereumNetwork::get(), [Parachain(4321)].into(), None), + // From Ethereum with a account ID added to the Ethereum Network, not matched. + ( + EthereumNetwork::get(), + [ + GlobalConsensus(EthereumNetwork::get()), + AccountKey20 { network: None, key: H160::random().into() }, + ] + .into(), + None, + ), + // From Ethereum with the Sepolia chain ID instead of Mainnet, not matched. + (NetworkId::Ethereum { chain_id: 11155111 }, Junctions::Here, None), + ]; + + for (network, remote_location, expected_result) in test_data { + assert_eq!( + NetworkExportTable::::exporter_for( + &network, + &remote_location, + &Xcm::default() + ), + expected_result, + "expected_result: {:?} not matched for network: {:?} and remote_location: {:?}", + expected_result, + network, + remote_location, + ) + } + }); +} diff --git a/system-parachains/asset-hubs/asset-hub-kusama/tests/tests.rs b/system-parachains/asset-hubs/asset-hub-kusama/tests/tests.rs index f0a8bdd79e..e0d387532c 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/tests/tests.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/tests/tests.rs @@ -706,3 +706,29 @@ fn treasury_pallet_account_not_none() { LocationToAccountId::convert_location(&RelayTreasuryLocation::get()).unwrap() ) } + +#[test] +fn change_xcm_bridge_hub_ethereum_base_fee_by_governance_works() { + asset_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + bridging::to_ethereum::BridgeHubEthereumBaseFee, + Balance, + >( + collator_session_keys(), + 1000, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + ( + bridging::to_ethereum::BridgeHubEthereumBaseFee::key().to_vec(), + bridging::to_ethereum::BridgeHubEthereumBaseFee::get(), + ) + }, + |old_value| { + if let Some(new_value) = old_value.checked_add(1) { + new_value + } else { + old_value.checked_sub(1).unwrap() + } + }, + ) +} diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml index 0e99e2b6fd..2a4bbcacf3 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml @@ -94,6 +94,7 @@ assets-common = { default-features = false , version = "0.8.0" } # Bridges pallet-xcm-bridge-hub-router = { default-features = false , version = "0.6.0" } +snowbridge-router-primitives = { default-features = false, version = "0.1.0" } [dev-dependencies] hex-literal = "0.4.1" @@ -133,6 +134,7 @@ runtime-benchmarks = [ "pallet-xcm/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -222,6 +224,7 @@ std = [ "polkadot-runtime-common/std", "polkadot-runtime-constants/std", "scale-info/std", + "snowbridge-router-primitives/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/primitives/Cargo.toml b/system-parachains/asset-hubs/asset-hub-polkadot/primitives/Cargo.toml index 8fd8267b69..a91d4846d8 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/primitives/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-polkadot/primitives/Cargo.toml @@ -11,6 +11,9 @@ license.workspace = true codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +# Local +system-parachains-constants = { path = "../../../constants", default-features = false } + # Bridge Dependencies bp-xcm-bridge-hub-router = { default-features = false , version = "0.7.0" } @@ -29,5 +32,6 @@ std = [ "frame-support/std", "scale-info/std", "sp-std/std", + "system-parachains-constants/std", "xcm/std" ] diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/primitives/src/lib.rs b/system-parachains/asset-hubs/asset-hub-polkadot/primitives/src/lib.rs index 023454b710..1573499291 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/primitives/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/primitives/src/lib.rs @@ -24,6 +24,8 @@ use xcm::prelude::*; pub use bp_xcm_bridge_hub_router::XcmBridgeHubRouterCall; +use system_parachains_constants::polkadot::currency::UNITS; + /// `AssetHubPolkadot` Runtime `Call` enum. /// /// The enum represents a subset of possible `Call`s we can send to `AssetHubPolkadot` chain. @@ -48,6 +50,9 @@ frame_support::parameter_types! { pub CongestedMessage: Xcm<()> = build_congestion_message(true).into(); /// Message that is sent to the sibling Kusama Asset Hub when the with-Polkadot bridge becomes uncongested. pub UncongestedMessage: Xcm<()> = build_congestion_message(false).into(); + + /// Should match the `AssetDeposit` of the `ForeignAssets` pallet on Asset Hub. + pub const CreateForeignAssetDeposit: u128 = 10 * UNITS; } fn build_congestion_message(is_congested: bool) -> sp_std::vec::Vec> { diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs index bd6b7fc47b..9b4ef06108 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs @@ -66,7 +66,7 @@ pub mod xcm_config; use assets_common::{ foreign_creators::ForeignCreators, local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, - matching::FromSiblingParachain, + matching::{FromNetwork, FromSiblingParachain}, AssetIdForTrustBackedAssetsConvert, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; @@ -326,7 +326,14 @@ impl pallet_assets::Config for Runtime { type AssetIdParameter = xcm::v3::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< - (FromSiblingParachain, xcm::v3::Location>,), + ( + FromSiblingParachain, xcm::v3::Location>, + FromNetwork< + xcm_config::UniversalLocation, + xcm_config::bridging::to_ethereum::EthereumNetwork, + xcm::v3::Location, + >, + ), ForeignCreatorsSovereignAccountOf, AccountId, xcm::v3::Location, @@ -1714,4 +1721,12 @@ mod tests { let parachain_tbf = TransactionByteFee::get(); assert_eq!(relay_tbf / 10, parachain_tbf); } + + #[test] + fn create_foreign_asset_deposit_is_equal_to_asset_hub_foreign_asset_pallet_deposit() { + assert_eq!( + bp_asset_hub_polkadot::CreateForeignAssetDeposit::get(), + ForeignAssetsAssetDeposit::get() + ); + } } diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/src/xcm_config.rs b/system-parachains/asset-hubs/asset-hub-polkadot/src/xcm_config.rs index a01d95ecca..e043c8b1a9 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/src/xcm_config.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/src/xcm_config.rs @@ -21,7 +21,7 @@ use super::{ }; use crate::ForeignAssetsInstance; use assets_common::{ - matching::{FromSiblingParachain, IsForeignConcreteAsset}, + matching::{FromNetwork, FromSiblingParachain, IsForeignConcreteAsset}, TrustBackedAssetsAsLocation, }; use frame_support::{ @@ -42,6 +42,7 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_constants::system_parachain; +use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use system_parachains_constants::TREASURY_PALLET_ID; use xcm::latest::prelude::*; @@ -52,9 +53,10 @@ use xcm_builder::{ FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, - TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, - WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, + SignedToAccountId32, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::{ traits::{ConvertLocation, WithOriginFilter}, @@ -104,6 +106,9 @@ pub type LocationToAccountId = ( // Different global consensus parachain sovereign account. // (Used for over-bridge transfers and reserve processing) GlobalConsensusParachainConvertsFor, + // Ethereum contract sovereign account. + // (Used to get convert ethereum contract locations to sovereign account) + GlobalConsensusEthereumConvertsFor, ); /// Means for transacting the native currency on this chain. @@ -287,8 +292,9 @@ impl Contains for SafeCallFilter { match call { RuntimeCall::System(frame_system::Call::set_storage { items }) if items.iter().all(|(k, _)| { - k.eq(&bridging::XcmBridgeHubRouterBaseFee::key()) || - k.eq(&bridging::XcmBridgeHubRouterByteFee::key()) + k.eq(&bridging::XcmBridgeHubRouterByteFee::key()) || + k.eq(&bridging::XcmBridgeHubRouterBaseFee::key()) || + k.eq(&bridging::to_ethereum::BridgeHubEthereumBaseFee::key()) }) => return true, _ => (), @@ -523,7 +529,10 @@ impl xcm_executor::Config for XcmConfig { // as reserve locations (we trust the Bridge Hub to relay the message that a reserve is being // held). Asset Hub may _act_ as a reserve location for DOT and assets created // under `pallet-assets`. Users must use teleport where allowed (e.g. DOT with the Relay Chain). - type IsReserve = (bridging::to_kusama::IsTrustedBridgedReserveLocationForConcreteAsset,); + type IsReserve = ( + bridging::to_kusama::IsTrustedBridgedReserveLocationForConcreteAsset, + bridging::to_ethereum::IsTrustedBridgedReserveLocationForForeignAsset, + ); type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; type Barrier = Barrier; @@ -588,7 +597,8 @@ impl xcm_executor::Config for XcmConfig { XcmFeeToAccount, >; type MessageExporter = (); - type UniversalAliases = bridging::to_kusama::UniversalAliases; + type UniversalAliases = + (bridging::to_kusama::UniversalAliases, bridging::to_ethereum::UniversalAliases); type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; @@ -616,6 +626,13 @@ pub type XcmRouter = WithUniqueTopic<( // Router which wraps and sends xcm to BridgeHub to be delivered to the Kusama // GlobalConsensus ToKusamaXcmRouter, + // Router which wraps and sends xcm to BridgeHub to be delivered to the Ethereum + // GlobalConsensus + SovereignPaidRemoteExporter< + xcm_builder::NetworkExportTable, + XcmpQueue, + UniversalLocation, + >, )>; impl pallet_xcm::Config for Runtime { @@ -661,6 +678,7 @@ pub type ForeignCreatorsSovereignAccountOf = ( SiblingParachainConvertsVia, AccountId32Aliases, ParentIsPreset, + GlobalConsensusEthereumConvertsFor, ); /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. @@ -774,6 +792,57 @@ pub mod bridging { >; } + pub mod to_ethereum { + use super::*; + pub use bp_bridge_hub_polkadot::snowbridge::EthereumNetwork; + use bp_bridge_hub_polkadot::snowbridge::InboundQueuePalletInstance; + + parameter_types! { + /// User fee for transfers from Polkadot to Ethereum. + /// The fee is set to max Balance to disable the bridge until a fee is set by + /// governance. + pub const DefaultBridgeHubEthereumBaseFee: Balance = Balance::MAX; + pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); + pub SiblingBridgeHubWithEthereumInboundQueueInstance: Location = Location::new( + 1, + [ + Parachain(SiblingBridgeHubParaId::get()), + PalletInstance(InboundQueuePalletInstance::get()), + ] + ); + + /// Set up exporters configuration. + /// `Option` represents static "base fee" which is used for total delivery fee calculation. + pub BridgeTable: sp_std::vec::Vec = sp_std::vec![ + NetworkExportTableItem::new( + EthereumNetwork::get(), + Some(sp_std::vec![Junctions::Here]), + SiblingBridgeHub::get(), + Some(( + XcmBridgeHubRouterFeeAssetId::get(), + BridgeHubEthereumBaseFee::get(), + ).into()) + ), + ]; + + /// Universal aliases + pub UniversalAliases: BTreeSet<(Location, Junction)> = BTreeSet::from_iter( + sp_std::vec![ + (SiblingBridgeHubWithEthereumInboundQueueInstance::get(), GlobalConsensus(EthereumNetwork::get())), + ] + ); + } + + pub type IsTrustedBridgedReserveLocationForForeignAsset = + matching::IsForeignConcreteAsset>; + + impl Contains<(Location, Junction)> for UniversalAliases { + fn contains(alias: &(Location, Junction)) -> bool { + UniversalAliases::get().contains(alias) + } + } + } + /// Benchmarks helper for bridging configuration. #[cfg(feature = "runtime-benchmarks")] pub struct BridgingBenchmarksHelper; diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/tests/snowbridge.rs b/system-parachains/asset-hubs/asset-hub-polkadot/tests/snowbridge.rs new file mode 100644 index 0000000000..77f7054726 --- /dev/null +++ b/system-parachains/asset-hubs/asset-hub-polkadot/tests/snowbridge.rs @@ -0,0 +1,75 @@ +// This file is part of Cumulus. + +// 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 asset_hub_polkadot_runtime::xcm_config::bridging::{ + to_ethereum::{BridgeHubEthereumBaseFee, BridgeTable, EthereumNetwork}, + SiblingBridgeHub, XcmBridgeHubRouterFeeAssetId, +}; +use sp_core::H160; +use sp_std::prelude::*; +use xcm::prelude::*; +use xcm_builder::{ExporterFor, NetworkExportTable}; + +#[test] +fn network_export_table_works() { + sp_io::TestExternalities::default().execute_with(|| { + let test_data: Vec<(NetworkId, InteriorLocation, Option<(Location, Option)>)> = vec![ + // From Ethereum (from GlobalConsensus(Ethereum) is routed to BridgeHub, with a fee, + // matched. + ( + EthereumNetwork::get(), + Junctions::Here, + Some(( + SiblingBridgeHub::get().into(), + Some(Asset { + id: XcmBridgeHubRouterFeeAssetId::get(), + fun: Fungible(BridgeHubEthereumBaseFee::get()), + }), + )), + ), + // From Ethereum with a random parachain ID filter, not matched. + (EthereumNetwork::get(), [Parachain(4321)].into(), None), + // From Ethereum with a account ID added to the Ethereum Network, not matched. + ( + EthereumNetwork::get(), + [ + GlobalConsensus(EthereumNetwork::get()), + AccountKey20 { network: None, key: H160::random().into() }, + ] + .into(), + None, + ), + // From Ethereum with the Sepolia chain ID instead of Mainnet, not matched. + (NetworkId::Ethereum { chain_id: 11155111 }, Junctions::Here, None), + ]; + + for (network, remote_location, expected_result) in test_data { + assert_eq!( + NetworkExportTable::::exporter_for( + &network, + &remote_location, + &Xcm::default() + ), + expected_result, + "expected_result: {:?} not matched for network: {:?} and remote_location: {:?}", + expected_result, + network, + remote_location, + ) + } + }); +} diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/tests/tests.rs b/system-parachains/asset-hubs/asset-hub-polkadot/tests/tests.rs index 946179403c..b561355149 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/tests/tests.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/tests/tests.rs @@ -704,3 +704,29 @@ fn treasury_pallet_account_not_none() { LocationToAccountId::convert_location(&RelayTreasuryLocation::get()).unwrap() ) } + +#[test] +fn change_xcm_bridge_hub_ethereum_base_fee_by_governance_works() { + asset_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + bridging::to_ethereum::BridgeHubEthereumBaseFee, + Balance, + >( + collator_session_keys(), + 1000, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + ( + bridging::to_ethereum::BridgeHubEthereumBaseFee::key().to_vec(), + bridging::to_ethereum::BridgeHubEthereumBaseFee::get(), + ) + }, + |old_value| { + if let Some(new_value) = old_value.checked_add(1) { + new_value + } else { + old_value.checked_sub(1).unwrap() + } + }, + ) +} diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml index acb1582175..02ced15d47 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml @@ -93,6 +93,7 @@ bp-relayers = { default-features = false , version = "0.8.0" } bp-runtime = { default-features = false , version = "0.8.0" } bp-kusama = { default-features = false , version = "0.6.0" } bp-polkadot = { default-features = false , version = "0.6.0" } +bridge-hub-common = { default-features = false , version = "0.1.0" } bridge-runtime-common = { default-features = false , version = "0.8.0" } pallet-bridge-grandpa = { default-features = false , version = "0.8.0" } pallet-bridge-messages = { default-features = false , version = "0.8.0" } @@ -100,11 +101,25 @@ pallet-bridge-parachains = { default-features = false , version = "0.8.0" } pallet-bridge-relayers = { default-features = false , version = "0.8.0" } pallet-xcm-bridge-hub = { default-features = false , version = "0.3.0" } +# Ethereum Bridge (Snowbridge) +snowbridge-beacon-primitives = { default-features = false , version = "0.1.0" } +snowbridge-pallet-system = { default-features = false , version = "0.1.0" } +snowbridge-system-runtime-api = { default-features = false , version = "0.1.0" } +snowbridge-core = { default-features = false , version = "0.1.0" } +snowbridge-pallet-ethereum-client = { default-features = false , version = "0.1.0" } +snowbridge-pallet-inbound-queue = { default-features = false , version = "0.1.0" } +snowbridge-pallet-outbound-queue = { default-features = false , version = "0.1.0" } +snowbridge-outbound-queue-runtime-api = { default-features = false , version = "0.1.0" } +snowbridge-router-primitives = { default-features = false , version = "0.1.0" } +snowbridge-runtime-common = { default-features = false, version = "0.1.0" } + [dev-dependencies] bridge-hub-test-utils = { version = "0.8.0" } bridge-runtime-common = { version = "0.8.0", features = ["integrity-test"] } sp-keyring = { version = "32.0.0" } static_assertions = { version = "1.1.0" } +snowbridge-runtime-test-common = { version = "0.1.0" } +parachains-runtimes-test-utils = { version = "8.0.0" } [features] default = [ "std" ] @@ -121,6 +136,7 @@ std = [ "bp-runtime/std", "bp-kusama/std", "bp-polkadot/std", + "bridge-hub-common/std", "bridge-runtime-common/std", "codec/std", "cumulus-pallet-aura-ext/std", @@ -167,6 +183,16 @@ std = [ "polkadot-runtime-constants/std", "scale-info/std", "serde", + "snowbridge-beacon-primitives/std", + "snowbridge-core/std", + "snowbridge-pallet-ethereum-client/std", + "snowbridge-pallet-inbound-queue/std", + "snowbridge-outbound-queue-runtime-api/std", + "snowbridge-pallet-outbound-queue/std", + "snowbridge-router-primitives/std", + "snowbridge-runtime-common/std", + "snowbridge-system-runtime-api/std", + "snowbridge-pallet-system/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", @@ -189,6 +215,7 @@ std = [ ] runtime-benchmarks = [ + "bridge-hub-common/runtime-benchmarks", "bridge-runtime-common/runtime-benchmarks", "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", @@ -215,9 +242,18 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", + "snowbridge-pallet-ethereum-client/runtime-benchmarks", + "snowbridge-pallet-inbound-queue/runtime-benchmarks", + "snowbridge-pallet-outbound-queue/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", + "snowbridge-runtime-common/runtime-benchmarks", + "snowbridge-runtime-test-common/runtime-benchmarks", + "snowbridge-pallet-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", ] try-runtime = [ @@ -248,6 +284,10 @@ try-runtime = [ "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", + "snowbridge-pallet-ethereum-client/try-runtime", + "snowbridge-pallet-inbound-queue/try-runtime", + "snowbridge-pallet-outbound-queue/try-runtime", + "snowbridge-pallet-system/try-runtime", "sp-runtime/try-runtime", ] diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/primitives/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-kusama/primitives/Cargo.toml index ab1deb9797..fbef882772 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/primitives/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/primitives/Cargo.toml @@ -18,6 +18,7 @@ system-parachains-constants = { path = "../../../constants", default-features = bp-bridge-hub-cumulus = { default-features = false , version = "0.8.0" } bp-runtime = { default-features = false , version = "0.8.0" } bp-messages = { default-features = false , version = "0.8.0" } +snowbridge-core = { default-features = false , version = "0.1.0" } # Substrate Based Dependencies frame-support = { default-features = false, version = "29.0.0" } @@ -25,6 +26,9 @@ sp-api = { default-features = false, version = "27.0.0" } sp-runtime = { default-features = false, version = "32.0.0" } sp-std = { default-features = false , version = "14.0.0" } +# Polkadot +xcm = { package = "staging-xcm", default-features = false, version = "8.0.1" } + [features] default = [ "std" ] std = [ @@ -34,8 +38,10 @@ std = [ "frame-support/std", "kusama-runtime-constants/std", "polkadot-runtime-constants/std", + "snowbridge-core/std", "sp-api/std", "sp-runtime/std", "sp-std/std", "system-parachains-constants/std", + "xcm/std", ] diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/primitives/src/lib.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/primitives/src/lib.rs index a01e306c82..4b2b294ac5 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/primitives/src/lib.rs +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/primitives/src/lib.rs @@ -158,6 +158,43 @@ fn convert_from_udot_to_uksm(price_in_udot: Balance) -> Balance { FixedU128::DIV } +pub mod snowbridge { + use crate::Balance; + use frame_support::parameter_types; + use snowbridge_core::{PricingParameters, Rewards, U256}; + use sp_runtime::FixedU128; + use xcm::latest::NetworkId; + + parameter_types! { + /// Should match the `ForeignAssets::create` index on Asset Hub. + pub const CreateAssetCall: [u8;2] = [53, 0]; + /// The pallet index of the Ethereum inbound queue pallet in the Bridge Hub runtime. + pub const InboundQueuePalletInstance: u8 = 80; + /// Default pricing parameters used to calculate bridging fees. Initialized to unit values, + /// as it is intended that these parameters should be updated with more + /// accurate values prior to bridge activation. This can be performed + /// using the `EthereumSystem::set_pricing_parameters` governance extrinsic. + pub Parameters: PricingParameters = PricingParameters { + // ETH/DOT exchange rate + exchange_rate: FixedU128::from_rational(1, 1), + // Ether fee per gas unit + fee_per_gas: U256::one(), + // Relayer rewards + rewards: Rewards { + // Reward for submitting a message to BridgeHub + local: 1, + // Reward for submitting a message to the Gateway contract on Ethereum + remote: U256::one(), + } + }; + /// Network and location for the Ethereum chain. On Kusama, the Ethereum chain bridged + /// to is the Ethereum Main network, with chain ID 1. + /// + /// + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 1 }; + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_ethereum_config.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_ethereum_config.rs new file mode 100644 index 0000000000..dc940f797c --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_ethereum_config.rs @@ -0,0 +1,27 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +use crate::{xcm_config::UniversalLocation, Runtime}; +pub use bp_bridge_hub_kusama::snowbridge::EthereumNetwork; +use snowbridge_router_primitives::outbound::EthereumBlobExporter; + +/// Exports message to the Ethereum Gateway contract. +pub type SnowbridgeExporter = EthereumBlobExporter< + UniversalLocation, + EthereumNetwork, + snowbridge_pallet_outbound_queue::Pallet, + snowbridge_core::AgentIdOf, +>; diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs index 0fb464ac36..9c835b7f83 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs @@ -22,17 +22,25 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +pub mod bridge_to_ethereum_config; pub mod bridge_to_polkadot_config; mod weights; pub mod xcm_config; +use bp_bridge_hub_kusama::snowbridge::{CreateAssetCall, InboundQueuePalletInstance, Parameters}; +use bridge_hub_common::message_queue::{ + AggregateMessageOrigin, NarrowOriginToSibling, ParaIdToSibling, +}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::ParaId; +use snowbridge_beacon_primitives::{Fork, ForkVersions}; +use snowbridge_core::{outbound::Message, AgentId, AllowSiblingsOnly}; +use snowbridge_router_primitives::inbound::MessageToXcm; use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT}, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, Keccak256}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; @@ -60,20 +68,23 @@ use frame_system::{ use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; -use xcm_config::{FellowshipLocation, GovernanceLocation, XcmOriginToTransactDispatchOrigin}; +use xcm_config::{ + FellowshipLocation, GovernanceLocation, TreasuryAccount, XcmOriginToTransactDispatchOrigin, +}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; +use kusama_runtime_constants::system_parachain::{ASSET_HUB_ID, BRIDGE_HUB_ID}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use parachains_common::{ - impls::DealWithFees, message_queue::*, AccountId, Balance, BlockNumber, Hash, Header, Nonce, - Signature, + impls::DealWithFees, AccountId, Balance, BlockNumber, Hash, Header, Nonce, Signature, }; pub use system_parachains_constants::SLOT_DURATION; + use system_parachains_constants::{ kusama::{consensus::*, currency::*, fee::WeightToFee}, AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, @@ -126,6 +137,11 @@ pub type UncheckedExtrinsic = pub type Migrations = ( // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + snowbridge_pallet_system::migration::v0::InitializeOnUpgrade< + Runtime, + ConstU32, + ConstU32, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); @@ -319,21 +335,31 @@ type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< impl parachain_info::Config for Runtime {} parameter_types! { - pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; + /// Amount of weight that can be spent per block to service messages. This was increased + /// from 35% to 60% of the max block weight to accommodate the Ethereum beacon light client + /// extrinsics. The `force_checkpoint` and `submit` extrinsics (for submit, optionally) includes + /// the sync committee's pubkeys (512 x 48 bytes). + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(60) * RuntimeBlockWeights::get().max_block; } impl pallet_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_message_queue::WeightInfo; - #[cfg(feature = "runtime-benchmarks")] - type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< - cumulus_primitives_core::AggregateMessageOrigin, - >; - #[cfg(not(feature = "runtime-benchmarks"))] - type MessageProcessor = xcm_builder::ProcessXcmMessage< - AggregateMessageOrigin, - xcm_executor::XcmExecutor, - RuntimeCall, + // Use the NoopMessageProcessor exclusively for benchmarks, not for tests with the + // runtime-benchmarks feature as tests require the BridgeHubMessageRouter to process messages. + // The "test" feature flag doesn't work, hence the reliance on the "std" feature, which is + // enabled during tests. + #[cfg(all(not(feature = "std"), feature = "runtime-benchmarks"))] + type MessageProcessor = + pallet_message_queue::mock_helpers::NoopMessageProcessor; + #[cfg(not(all(not(feature = "std"), feature = "runtime-benchmarks")))] + type MessageProcessor = bridge_hub_common::BridgeHubMessageRouter< + xcm_builder::ProcessXcmMessage< + AggregateMessageOrigin, + xcm_executor::XcmExecutor, + RuntimeCall, + >, + EthereumOutboundQueue, >; type Size = u32; // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: @@ -473,6 +499,169 @@ impl pallet_utility::Config for Runtime { type WeightInfo = weights::pallet_utility::WeightInfo; } +// Ethereum Bridge +parameter_types! { + // The gateway address is set by governance. + pub storage EthereumGatewayAddress: H160 = H160::zero(); +} + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmark_helpers { + use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; + use codec::Encode; + use snowbridge_beacon_primitives::CompactExecutionHeader; + 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(block_hash: H256, header: CompactExecutionHeader) { + EthereumBeaconClient::store_execution_header(block_hash, header, 0, H256::default()) + } + } + + pub struct DoNothingRouter; + impl SendXcm for DoNothingRouter { + type Ticket = Xcm<()>; + + fn validate( + _dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + Ok((xcm.clone().unwrap(), Assets::new())) + } + fn deliver(xcm: Xcm<()>) -> Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } + } + + impl snowbridge_pallet_system::BenchmarkHelper for () { + fn make_xcm_origin(location: Location) -> RuntimeOrigin { + RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) + } + } +} + +impl snowbridge_pallet_inbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Verifier = snowbridge_pallet_ethereum_client::Pallet; + type Token = Balances; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = xcm_config::XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = benchmark_helpers::DoNothingRouter; + type ChannelLookup = EthereumSystem; + type GatewayAddress = EthereumGatewayAddress; + #[cfg(feature = "runtime-benchmarks")] + type Helper = Runtime; + type MessageConverter = MessageToXcm< + CreateAssetCall, + bp_asset_hub_kusama::CreateForeignAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + >; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type MaxMessageSize = ConstU32<2048>; + type WeightInfo = weights::snowbridge_pallet_inbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type AssetTransactor = ::AssetTransactor; +} + +impl snowbridge_pallet_outbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Hashing = Keccak256; + type MessageQueue = MessageQueue; + type Decimals = ConstU8<12>; + type MaxMessagePayloadSize = ConstU32<2048>; + type MaxMessagesPerBlock = ConstU32<32>; + type GasMeter = snowbridge_core::outbound::ConstantGasMeter; + type Balance = Balance; + type WeightToFee = WeightToFee; + type WeightInfo = weights::snowbridge_pallet_outbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type Channels = EthereumSystem; +} + +#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 0, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 0, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 0, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 0, + } + }; +} + +#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 74240, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 144896, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 194048, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 269568, + }, + }; +} + +parameter_types! { + pub const MaxExecutionHeadersToKeep: u32 = 8192 * 20; +} + +impl snowbridge_pallet_ethereum_client::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + type MaxExecutionHeadersToKeep = MaxExecutionHeadersToKeep; + type WeightInfo = weights::snowbridge_pallet_ethereum_client::WeightInfo; +} + +impl snowbridge_pallet_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OutboundQueue = EthereumOutboundQueue; + type SiblingOrigin = EnsureXcm; + type AgentIdOf = snowbridge_core::AgentIdOf; + type TreasuryAccount = TreasuryAccount; + type Token = Balances; + type WeightInfo = weights::snowbridge_pallet_system::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type DefaultPricingParameters = Parameters; + type InboundDeliveryCost = EthereumInboundQueue; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -515,6 +704,12 @@ construct_runtime!( BridgePolkadotMessages: pallet_bridge_messages:: = 53, XcmOverBridgeHubPolkadot: pallet_xcm_bridge_hub:: = 54, + // Ethereum bridge pallets. + EthereumInboundQueue: snowbridge_pallet_inbound_queue = 80, + EthereumOutboundQueue: snowbridge_pallet_outbound_queue = 81, + EthereumBeaconClient: snowbridge_pallet_ethereum_client = 82, + EthereumSystem: snowbridge_pallet_system = 83, + // Message Queue. Importantly, it is registered after Snowbridge pallets // so that messages are processed after the `on_initialize` hooks of bridging pallets. MessageQueue: pallet_message_queue = 175, @@ -546,6 +741,11 @@ mod benches { [pallet_bridge_grandpa, PolkadotFinality] [pallet_bridge_parachains, PolkadotParachains] [pallet_bridge_messages, PolkadotMessages] + // Ethereum Bridge + [snowbridge_pallet_inbound_queue, EthereumInboundQueue] + [snowbridge_pallet_outbound_queue, EthereumOutboundQueue] + [snowbridge_pallet_system, EthereumSystem] + [snowbridge_pallet_ethereum_client, EthereumBeaconClient] ); } @@ -756,6 +956,22 @@ impl_runtime_apis! { } } + impl snowbridge_outbound_queue_runtime_api::OutboundQueueApi for Runtime { + fn prove_message(leaf_index: u64) -> Option { + snowbridge_pallet_outbound_queue::api::prove_message::(leaf_index) + } + + fn calculate_fee(message: Message) -> Option { + snowbridge_pallet_outbound_queue::api::calculate_fee::(message) + } + } + + impl snowbridge_system_runtime_api::ControlApi for Runtime { + fn agent_id(location: VersionedLocation) -> Option { + snowbridge_pallet_system::api::agent_id::(location) + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { @@ -1146,6 +1362,14 @@ cumulus_pallet_parachain_system::register_validate_block! { mod tests { use super::*; + #[test] + fn bridge_hub_inbound_queue_pallet_index_is_correct() { + assert_eq!( + InboundQueuePalletInstance::get(), + ::index() as u8 + ); + } + #[test] fn test_transasction_byte_fee_is_one_tenth_of_relay() { let relay_tbf = kusama_runtime_constants::fee::TRANSACTION_BYTE_FEE; diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/mod.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/mod.rs index 163b47d14b..8fbbc73d18 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/mod.rs +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/mod.rs @@ -43,6 +43,10 @@ pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; pub mod rocksdb_weights; +pub mod snowbridge_pallet_ethereum_client; +pub mod snowbridge_pallet_inbound_queue; +pub mod snowbridge_pallet_outbound_queue; +pub mod snowbridge_pallet_system; pub mod xcm; pub use block_weights::constants::BlockExecutionWeight; diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_ethereum_client.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_ethereum_client.rs new file mode 100644 index 0000000000..0d5f29c6ff --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_ethereum_client.rs @@ -0,0 +1,151 @@ +// 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. + +//! Autogenerated weights for `snowbridge_pallet_ethereum_client` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-06-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-172-31-8-124`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --base-path +// /mnt/scratch/benchmark +// --chain=bridge-hub-rococo-dev +// --pallet=snowbridge_ethereum_beacon_client +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --steps +// 50 +// --repeat +// 20 +// --output +// ./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_ethereum_beacon_client.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_pallet_ethereum_client`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_ethereum_client::WeightInfo for WeightInfo { + /// Storage: EthereumBeaconClient FinalizedBeaconStateIndex (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconStateIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconStateMapping (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconStateMapping (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient NextSyncCommittee (r:0 w:1) + /// Proof: EthereumBeaconClient NextSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient InitialCheckpointRoot (r:0 w:1) + /// Proof: EthereumBeaconClient InitialCheckpointRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ValidatorsRoot (r:0 w:1) + /// Proof: EthereumBeaconClient ValidatorsRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestFinalizedBlockRoot (r:0 w:1) + /// Proof: EthereumBeaconClient LatestFinalizedBlockRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient CurrentSyncCommittee (r:0 w:1) + /// Proof: EthereumBeaconClient CurrentSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestExecutionState (r:0 w:1) + /// Proof: EthereumBeaconClient LatestExecutionState (max_values: Some(1), max_size: Some(80), added: 575, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconState (r:0 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconState (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + fn force_checkpoint() -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3501` + // Minimum execution time: 97_185_781_000 picoseconds. + Weight::from_parts(97_263_571_000, 0) + .saturating_add(Weight::from_parts(0, 3501)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(9)) + } + /// Storage: EthereumBeaconClient LatestFinalizedBlockRoot (r:1 w:1) + /// Proof: EthereumBeaconClient LatestFinalizedBlockRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconState (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconState (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestExecutionState (r:1 w:0) + /// Proof: EthereumBeaconClient LatestExecutionState (max_values: Some(1), max_size: Some(80), added: 575, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient NextSyncCommittee (r:1 w:0) + /// Proof: EthereumBeaconClient NextSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient CurrentSyncCommittee (r:1 w:0) + /// Proof: EthereumBeaconClient CurrentSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ValidatorsRoot (r:1 w:0) + /// Proof: EthereumBeaconClient ValidatorsRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconStateIndex (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconStateIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconStateMapping (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconStateMapping (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + fn submit() -> Weight { + // Proof Size summary in bytes: + // Measured: `92753` + // Estimated: `93857` + // Minimum execution time: 25_999_968_000 picoseconds. + Weight::from_parts(26_051_019_000, 0) + .saturating_add(Weight::from_parts(0, 93857)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: EthereumBeaconClient LatestFinalizedBlockRoot (r:1 w:0) + /// Proof: EthereumBeaconClient LatestFinalizedBlockRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconState (r:1 w:0) + /// Proof: EthereumBeaconClient FinalizedBeaconState (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestExecutionState (r:1 w:0) + /// Proof: EthereumBeaconClient LatestExecutionState (max_values: Some(1), max_size: Some(80), added: 575, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient NextSyncCommittee (r:1 w:1) + /// Proof: EthereumBeaconClient NextSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient CurrentSyncCommittee (r:1 w:0) + /// Proof: EthereumBeaconClient CurrentSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ValidatorsRoot (r:1 w:0) + /// Proof: EthereumBeaconClient ValidatorsRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn submit_with_sync_committee() -> Weight { + // Proof Size summary in bytes: + // Measured: `92717` + // Estimated: `93857` + // Minimum execution time: 122_354_917_000 picoseconds. + Weight::from_parts(122_461_312_000, 0) + .saturating_add(Weight::from_parts(0, 93857)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: EthereumBeaconClient LatestFinalizedBlockRoot (r:1 w:0) + /// Proof: EthereumBeaconClient LatestFinalizedBlockRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconState (r:1 w:0) + /// Proof: EthereumBeaconClient FinalizedBeaconState (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestExecutionState (r:1 w:1) + /// Proof: EthereumBeaconClient LatestExecutionState (max_values: Some(1), max_size: Some(80), added: 575, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ExecutionHeaderIndex (r:1 w:1) + /// Proof: EthereumBeaconClient ExecutionHeaderIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ExecutionHeaderMapping (r:1 w:1) + /// Proof: EthereumBeaconClient ExecutionHeaderMapping (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ExecutionHeaders (r:0 w:1) + /// Proof: EthereumBeaconClient ExecutionHeaders (max_values: None, max_size: Some(136), added: 2611, mode: MaxEncodedLen) + fn submit_execution_header() -> Weight { + // Proof Size summary in bytes: + // Measured: `386` + // Estimated: `3537` + // Minimum execution time: 108_761_000 picoseconds. + Weight::from_parts(113_158_000, 0) + .saturating_add(Weight::from_parts(0, 3537)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_inbound_queue.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_inbound_queue.rs new file mode 100644 index 0000000000..faf404f90c --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_inbound_queue.rs @@ -0,0 +1,69 @@ +// 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. + +//! Autogenerated weights for `snowbridge_pallet_inbound_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-09-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `macbook pro 14 m2`, CPU: `m2-arm64` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --chain=bridge-hub-rococo-dev +// --pallet=snowbridge_inbound_queue +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --steps +// 50 +// --repeat +// 20 +// --output +// ./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_inbound_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_pallet_inbound_queue`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_inbound_queue::WeightInfo for WeightInfo { + /// Storage: EthereumInboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumInboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ExecutionHeaders (r:1 w:0) + /// Proof: EthereumBeaconClient ExecutionHeaders (max_values: None, max_size: Some(136), added: 2611, mode: MaxEncodedLen) + /// Storage: EthereumInboundQueue Nonce (r:1 w:1) + /// Proof: EthereumInboundQueue Nonce (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn submit() -> Weight { + // Proof Size summary in bytes: + // Measured: `457` + // Estimated: `3601` + // Minimum execution time: 69_000_000 picoseconds. + Weight::from_parts(70_000_000, 0) + .saturating_add(Weight::from_parts(0, 3601)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_outbound_queue.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_outbound_queue.rs new file mode 100644 index 0000000000..8adcef076e --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_outbound_queue.rs @@ -0,0 +1,87 @@ +// 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. + +//! Autogenerated weights for `snowbridge_outbound_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-20, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `192.168.1.13`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// ../target/release/polkadot-parachain +// benchmark +// pallet +// --chain=bridge-hub-rococo-dev +// --pallet=snowbridge_outbound_queue +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --output +// ../parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_outbound_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_outbound_queue`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_outbound_queue::WeightInfo for WeightInfo { + /// Storage: EthereumOutboundQueue MessageLeaves (r:1 w:1) + /// Proof Skipped: EthereumOutboundQueue MessageLeaves (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: EthereumOutboundQueue PendingHighPriorityMessageCount (r:1 w:1) + /// Proof: EthereumOutboundQueue PendingHighPriorityMessageCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue Nonce (r:1 w:1) + /// Proof: EthereumOutboundQueue Nonce (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue Messages (r:1 w:1) + /// Proof Skipped: EthereumOutboundQueue Messages (max_values: Some(1), max_size: None, mode: Measured) + fn do_process_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3485` + // Minimum execution time: 39_000_000 picoseconds. + Weight::from_parts(39_000_000, 3485) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: EthereumOutboundQueue MessageLeaves (r:1 w:0) + /// Proof Skipped: EthereumOutboundQueue MessageLeaves (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Digest (r:1 w:1) + /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) + fn commit() -> Weight { + // Proof Size summary in bytes: + // Measured: `1094` + // Estimated: `2579` + // Minimum execution time: 28_000_000 picoseconds. + Weight::from_parts(28_000_000, 2579) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + + fn commit_single() -> Weight { + // Proof Size summary in bytes: + // Measured: `1094` + // Estimated: `2579` + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(9_000_000, 1586) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_system.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_system.rs new file mode 100644 index 0000000000..c6c188e323 --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/weights/snowbridge_pallet_system.rs @@ -0,0 +1,256 @@ +// 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. + +//! Autogenerated weights for `snowbridge_system` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-09, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `crake.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --chain +// bridge-hub-rococo-dev +// --pallet=snowbridge_pallet_system +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --output +// parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_system`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_system::WeightInfo for WeightInfo { + /// 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 47_000_000 picoseconds. + Weight::from_parts(47_000_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: EthereumSystem Agents (r:1 w:1) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 create_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `187` + // Estimated: `6196` + // Minimum execution time: 87_000_000 picoseconds. + Weight::from_parts(87_000_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(6)) + } + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: EthereumSystem Agents (r:1 w:0) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: EthereumSystem Channels (r:1 w:1) + /// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen) + /// 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn create_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `602` + // Estimated: `69050` + // Minimum execution time: 84_000_000 picoseconds. + Weight::from_parts(84_000_000, 0) + .saturating_add(Weight::from_parts(0, 69050)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// 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 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(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: 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 set_operating_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 30_000_000 picoseconds. + Weight::from_parts(30_000_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) + .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 transfer_native_from_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `6044` + // Minimum execution time: 43_000_000 picoseconds. + Weight::from_parts(43_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 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: 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 set_token_transfer_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 31_000_000 picoseconds. + Weight::from_parts(42_000_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 set_pricing_parameters() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 31_000_000 picoseconds. + Weight::from_parts(42_000_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 828904565b..ab2e43e3a4 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -15,12 +15,13 @@ // along with Cumulus. If not, see . use super::{ + bridge_to_ethereum_config::EthereumNetwork, bridge_to_polkadot_config::{ DeliveryRewardInBalance, RequiredStakeForStakeAndSlash, ToBridgeHubPolkadotHaulBlobExporter, }, - AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, - PriceForParentDelivery, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, - XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, EthereumGatewayAddress, ParachainInfo, + ParachainSystem, PolkadotXcm, PriceForParentDelivery, Runtime, RuntimeCall, RuntimeEvent, + RuntimeOrigin, WeightToFee, XcmpQueue, }; use frame_support::{ parameter_types, @@ -36,21 +37,23 @@ use parachains_common::{ }, }; use polkadot_parachain_primitives::primitives::Sibling; +use snowbridge_runtime_common::XcmExportFeeToSibling; use sp_runtime::traits::AccountIdConversion; +use sp_std::marker::PhantomData; use system_parachains_constants::TREASURY_PALLET_ID; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, - FungibleAdapter, HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, + FungibleAdapter, HandleFee, HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, XcmFeeToAccount, + XcmFeeToAccount, }; use xcm_executor::{ - traits::{ConvertLocation, WithOriginFilter}, + traits::{ConvertLocation, FeeManager, FeeReason, FeeReason::Export, WithOriginFilter}, XcmExecutor, }; @@ -154,7 +157,8 @@ impl Contains for SafeCallFilter { RuntimeCall::System(frame_system::Call::set_storage { items }) if items.iter().all(|(k, _)| { k.eq(&DeliveryRewardInBalance::key()) || - k.eq(&RequiredStakeForStakeAndSlash::key()) + k.eq(&RequiredStakeForStakeAndSlash::key()) || + k.eq(&EthereumGatewayAddress::key()) }) => return true, _ => (), @@ -199,7 +203,22 @@ impl Contains for SafeCallFilter { RuntimeCall::BridgePolkadotMessages(pallet_bridge_messages::Call::< Runtime, crate::bridge_to_polkadot_config::WithBridgeHubPolkadotMessagesInstance, - >::set_operating_mode { .. }) + >::set_operating_mode { .. }) | + RuntimeCall::EthereumBeaconClient( + snowbridge_pallet_ethereum_client::Call::force_checkpoint { .. } | + snowbridge_pallet_ethereum_client::Call::set_operating_mode { .. }, + ) | RuntimeCall::EthereumInboundQueue( + snowbridge_pallet_inbound_queue::Call::set_operating_mode { .. }, + ) | RuntimeCall::EthereumOutboundQueue( + snowbridge_pallet_outbound_queue::Call::set_operating_mode { .. }, + ) | RuntimeCall::EthereumSystem( + snowbridge_pallet_system::Call::upgrade { .. } | + snowbridge_pallet_system::Call::set_operating_mode { .. } | + snowbridge_pallet_system::Call::set_pricing_parameters { .. } | + snowbridge_pallet_system::Call::force_update_channel { .. } | + snowbridge_pallet_system::Call::force_transfer_native_from_agent { .. } | + snowbridge_pallet_system::Call::set_token_transfer_fees { .. }, + ) ) } } @@ -272,11 +291,22 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); - type FeeManager = XcmFeeManagerFromComponents< + type FeeManager = XcmFeeManagerFromComponentsBridgeHub< WaivedLocations, - XcmFeeToAccount, + ( + XcmExportFeeToSibling< + bp_kusama::Balance, + AccountId, + KsmRelayLocation, + EthereumNetwork, + Self::AssetTransactor, + crate::EthereumOutboundQueue, + >, + XcmFeeToAccount, + ), >; - type MessageExporter = ToBridgeHubPolkadotHaulBlobExporter; + type MessageExporter = + (ToBridgeHubPolkadotHaulBlobExporter, crate::bridge_to_ethereum_config::SnowbridgeExporter); type UniversalAliases = Nothing; type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; @@ -335,6 +365,30 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } +/// A `FeeManager` implementation that forces fees for any message delivered to Ethereum. +/// Otherwise, it permits the specified `WaivedLocations` to not pay for fees and uses the provided +/// `HandleFee` implementation. +pub struct XcmFeeManagerFromComponentsBridgeHub( + PhantomData<(WaivedLocations, HandleFee)>, +); +impl, FeeHandler: HandleFee> FeeManager + for XcmFeeManagerFromComponentsBridgeHub +{ + fn is_waived(origin: Option<&Location>, fee_reason: FeeReason) -> bool { + let Some(loc) = origin else { return false }; + if let Export { network, destination: Here } = fee_reason { + if network == EthereumNetwork::get() { + return false + } + } + WaivedLocations::contains(loc) + } + + fn handle_fee(fee: Assets, context: Option<&XcmContext>, reason: FeeReason) { + FeeHandler::handle_fee(fee, context, reason); + } +} + #[test] fn treasury_pallet_account_not_none() { assert_eq!( diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/tests/snowbridge.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/tests/snowbridge.rs new file mode 100644 index 0000000000..160ccbb27c --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/tests/snowbridge.rs @@ -0,0 +1,505 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +#![cfg(test)] + +use bp_polkadot_core::Signature; +use bridge_hub_kusama_runtime::{ + bridge_to_ethereum_config::EthereumNetwork, + bridge_to_polkadot_config::RefundBridgeHubPolkadotMessages, + xcm_config::{XcmConfig, XcmFeeManagerFromComponentsBridgeHub}, + BridgeRejectObsoleteHeadersAndMessages, EthereumGatewayAddress, Executive, + MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra, + UncheckedExtrinsic, +}; +use bridge_hub_test_utils::ValidatorIdOf; +use codec::{Decode, Encode}; +use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; +use frame_support::{ + assert_err, assert_ok, parameter_types, + traits::{Contains, OnFinalize, OnInitialize}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use kusama_runtime_constants::currency::UNITS; +use parachains_common::{AccountId, AuraId, Balance}; +pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works; +use parachains_runtimes_test_utils::{ + AccountIdOf, CollatorSessionKeys, ExtBuilder, XcmReceivedFrom, +}; +use snowbridge_core::{gwei, meth, ChannelId, ParaId, Rewards}; +use snowbridge_pallet_ethereum_client::WeightInfo; +use snowbridge_pallet_system::{PricingParametersOf, WeightInfo as EthereumSystemWeightInfo}; +use snowbridge_runtime_test_common::initial_fund; +use sp_core::H160; +use sp_keyring::AccountKeyring::Alice; +use sp_runtime::{ + generic::{Era, SignedPayload}, + traits::Header, + AccountId32, FixedU128, Saturating, +}; +use xcm::{latest::prelude::*, v3::Error}; +use xcm_builder::HandleFee; +use xcm_executor::{ + traits::{FeeManager, FeeReason}, + XcmExecutor, +}; + +type RuntimeHelper = + parachains_runtimes_test_utils::RuntimeHelper; + +parameter_types! { + pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; +} + +fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys { + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(Alice), + AccountId::from(Alice), + SessionKeys { aura: AuraId::from(Alice.public()) }, + ) +} + +#[test] +pub fn transfer_token_to_ethereum_works() { + send_transfer_token_message_success::( + collator_session_keys(), + 1013, + 1000, + H160::random(), + H160::random(), + DefaultBridgeHubEthereumBaseFee::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::EthereumOutboundQueue(event)) => Some(event), + _ => None, + } + }), + ) +} + +#[test] +pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { + snowbridge_runtime_test_common::send_unpaid_transfer_token_message::( + collator_session_keys(), + 1013, + 1000, + H160::random(), + H160::random(), + ) +} + +#[test] +pub fn transfer_token_to_ethereum_fee_not_enough() { + send_transfer_token_message_failure::( + collator_session_keys(), + 1013, + 1000, + DefaultBridgeHubEthereumBaseFee::get() + 1_000_000_000, + H160::random(), + H160::random(), + // fee not enough + 1_000_000_000, + Box::new(|call| RuntimeCall::EthereumSystem(call).encode()), + NotHoldingFees, + ) +} + +#[test] +pub fn transfer_token_to_ethereum_insufficient_fund() { + send_transfer_token_message_failure::( + collator_session_keys(), + 1013, + 1000, + 1_000_000_000, + H160::random(), + H160::random(), + DefaultBridgeHubEthereumBaseFee::get(), + Box::new(|call| RuntimeCall::EthereumSystem(call).encode()), + FailedToTransactAsset("Funds are unavailable"), + ) +} + +#[test] +fn change_ethereum_gateway_by_governance_works() { + change_storage_constant_by_governance_works::( + collator_session_keys(), + bp_bridge_hub_kusama::BRIDGE_HUB_KUSAMA_PARACHAIN_ID, + Box::new(|call| RuntimeCall::System(call).encode()), + || (EthereumGatewayAddress::key().to_vec(), EthereumGatewayAddress::get()), + |_| [1; 20].into(), + ) +} + +/// Fee is not waived when origin is none. +#[test] +fn test_xcm_fee_manager_from_components_bh_origin_none() { + assert!(!TestXcmFeeManager::is_waived(None, FeeReason::ChargeFees)); +} + +/// Fee is not waived when origin is not in waived location. +#[test] +fn test_xcm_fee_manager_from_components_bh_origin_not_in_waived_locations() { + assert!(!TestXcmFeeManager::is_waived( + Some(&Location::new(1, [Parachain(1)])), + FeeReason::DepositReserveAsset + )); +} + +/// Fee is waived when origin is in waived location. +#[test] +fn test_xcm_fee_manager_from_components_bh_origin_in_waived_locations() { + assert!(TestXcmFeeManager::is_waived( + Some(&Location::new(1, [Parachain(2)])), + FeeReason::DepositReserveAsset + )); +} + +/// Fee is waived when origin is in waived location with Export message, but not to Ethereum. +#[test] +fn test_xcm_fee_manager_from_components_bh_origin_in_waived_locations_with_export_to_polkadot_reason( +) { + assert!(TestXcmFeeManager::is_waived( + Some(&Location::new(1, [Parachain(2)])), + FeeReason::Export { network: Polkadot, destination: Here } + )); +} + +/// Fee is not waived when origin is in waived location but exported to Ethereum. +#[test] +fn test_xcm_fee_manager_from_components_bh_in_waived_locations_with_export_to_ethereum_reason() { + assert!(!TestXcmFeeManager::is_waived( + Some(&Location::new(1, [Parachain(1)])), + FeeReason::Export { network: EthereumNetwork::get(), destination: Here } + )); +} + +struct MockWaivedLocations; +impl Contains for MockWaivedLocations { + fn contains(loc: &Location) -> bool { + loc == &Location::new(1, [Parachain(2)]) + } +} + +struct MockFeeHandler; +impl HandleFee for MockFeeHandler { + fn handle_fee(fee: Assets, _context: Option<&XcmContext>, _reason: FeeReason) -> Assets { + fee + } +} + +type TestXcmFeeManager = XcmFeeManagerFromComponentsBridgeHub; + +#[allow(clippy::too_many_arguments)] +pub fn send_transfer_token_message_failure( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + assethub_parachain_id: u32, + initial_amount: u128, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, + system_call_encode: Box) -> Vec>, + expected_error: Error, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config, + XcmConfig: xcm_executor::Config, + ValidatorIdOf: From>, + <::Token as frame_support::traits::fungible::Inspect<::AccountId>>::Balance: From +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + assert_ok!(>::initialize( + runtime_para_id.into(), + assethub_parachain_id.into(), + )); + + let require_weight_at_most = + ::WeightInfo::set_pricing_parameters(); + + let set_pricing_parameters_call = system_call_encode(snowbridge_pallet_system::Call::< + Runtime, + >::set_pricing_parameters { + params: { + PricingParametersOf:: { + exchange_rate: FixedU128::from_rational(1, 75), + fee_per_gas: gwei(20), + rewards: Rewards { + local: (1 * UNITS / 100).into(), // 0.01 KSM + remote: meth(1), + }, + } + }, + }); + + assert_ok!(RuntimeHelper::::execute_as_governance( + set_pricing_parameters_call, + require_weight_at_most + ) + .ensure_complete()); + + // fund asset hub sovereign account enough so it can pay fees + initial_fund::(assethub_parachain_id, initial_amount); + + let outcome = send_transfer_token_message::( + assethub_parachain_id, + weth_contract_address, + destination_address, + fee_amount, + ); + assert_err!(outcome.ensure_complete(), expected_error); + }); +} + +#[test] +fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() { + let max_message_queue_weight = MessageQueueServiceWeight::get(); + let force_checkpoint = + ::WeightInfo::force_checkpoint(); + let submit_checkpoint = + ::WeightInfo::submit(); + max_message_queue_weight.all_gt(force_checkpoint); + max_message_queue_weight.all_gt(submit_checkpoint); +} + +#[test] +fn ethereum_client_consensus_extrinsics_work() { + snowbridge_runtime_test_common::ethereum_extrinsic( + collator_session_keys(), + 1013, + construct_and_apply_extrinsic, + ); +} + +#[test] +fn ethereum_to_polkadot_message_extrinsics_work() { + snowbridge_runtime_test_common::ethereum_to_polkadot_message_extrinsics_work( + collator_session_keys(), + 1013, + construct_and_apply_extrinsic, + ); +} + +#[test] +fn ethereum_outbound_queue_processes_messages_before_message_queue_works() { + // TODO: add test after dependencies are upgraded to >= 1.8 +} + +fn construct_extrinsic( + sender: sp_keyring::AccountKeyring, + call: RuntimeCall, +) -> UncheckedExtrinsic { + let account_id = AccountId32::from(sender.public()); + let extra: SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account_id).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + BridgeRejectObsoleteHeadersAndMessages, + (RefundBridgeHubPolkadotMessages::default()), + ); + let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); + let signature = payload.using_encoded(|e| sender.sign(e)); + UncheckedExtrinsic::new_signed( + call, + account_id.into(), + Signature::Sr25519(signature.clone()), + extra, + ) +} + +fn construct_and_apply_extrinsic( + origin: sp_keyring::AccountKeyring, + call: RuntimeCall, +) -> sp_runtime::DispatchOutcome { + let xt = construct_extrinsic(origin, call); + let r = Executive::apply_extrinsic(xt); + r.unwrap() +} + +// TODO remove when Ethereum network ID has been extracted as a param +pub fn send_transfer_token_message( + assethub_parachain_id: u32, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, +) -> Outcome +where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config, + XcmConfig: xcm_executor::Config, +{ + let assethub_parachain_location = Location::new(1, Parachain(assethub_parachain_id)); + let asset = Asset { + id: AssetId(Location::new( + 0, + [AccountKey20 { network: None, key: weth_contract_address.into() }], + )), + fun: Fungible(1000000000), + }; + let assets = vec![asset.clone()]; + + let inner_xcm = Xcm(vec![ + WithdrawAsset(Assets::from(assets.clone())), + ClearOrigin, + BuyExecution { fees: asset, weight_limit: Unlimited }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { network: None, key: destination_address.into() }], + ), + }, + SetTopic([0; 32]), + ]); + + let fee = + Asset { id: AssetId(Location { parents: 1, interior: Here }), fun: Fungible(fee_amount) }; + + // prepare transfer token message + let xcm = Xcm(vec![ + WithdrawAsset(Assets::from(vec![fee.clone()])), + BuyExecution { fees: fee, weight_limit: Unlimited }, + ExportMessage { network: Ethereum { chain_id: 1 }, destination: Here, xcm: inner_xcm }, + ]); + + // execute XCM + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + assethub_parachain_location, + xcm, + &mut hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + Weight::zero(), + ) +} + +pub fn send_transfer_token_message_success( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + assethub_parachain_id: u32, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, + snowbridge_pallet_outbound_queue: Box< + dyn Fn(Vec) -> Option>, + >, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + pallet_message_queue::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config, + XcmConfig: xcm_executor::Config, + ValidatorIdOf: From>, + ::AccountId: From + AsRef<[u8]>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + >::initialize( + runtime_para_id.into(), + assethub_parachain_id.into(), + ) + .unwrap(); + + // fund asset hub sovereign account enough so it can pay fees + initial_fund::(assethub_parachain_id, 5_000_000_000_000); + + let outcome = send_transfer_token_message::( + assethub_parachain_id, + weth_contract_address, + destination_address, + fee_amount, + ); + + assert_ok!(outcome.ensure_complete()); + + // check events + let mut events = >::events() + .into_iter() + .filter_map(|e| snowbridge_pallet_outbound_queue(e.event.encode())); + assert!(events.any(|e| matches!( + e, + snowbridge_pallet_outbound_queue::Event::MessageQueued { .. } + ))); + + let block_number = >::block_number(); + let next_block_number = >::block_number() + .saturating_add(BlockNumberFor::::from(1u32)); + + // finish current block + >::on_finalize(block_number); + >::on_finalize(block_number); + >::on_finalize(block_number); + + // start next block + >::set_block_number(next_block_number); + >::on_initialize(next_block_number); + >::on_initialize(next_block_number); + >::on_initialize(next_block_number); + + // finish next block + >::on_finalize(next_block_number); + >::on_finalize(next_block_number); + let included_head = >::finalize(); + + let origin: ParaId = assethub_parachain_id.into(); + let channel_id: ChannelId = origin.into(); + + let nonce = snowbridge_pallet_outbound_queue::Nonce::::try_get(channel_id); + assert_ok!(nonce); + assert_eq!(nonce.unwrap(), 1); + + let digest = included_head.digest(); + + let digest_items = digest.logs(); + assert!(digest_items.len() == 1 && digest_items[0].as_other().is_some()); + }); +} diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml index 7cc7f49f18..2533ea4ed3 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml @@ -93,6 +93,7 @@ bp-relayers = { default-features = false , version = "0.8.0" } bp-runtime = { default-features = false , version = "0.8.0" } bp-kusama = { default-features = false , version = "0.6.0" } bp-polkadot = { default-features = false , version = "0.6.0" } +bridge-hub-common = { default-features = false , version = "0.1.0" } bridge-runtime-common = { default-features = false , version = "0.8.0" } pallet-bridge-grandpa = { default-features = false , version = "0.8.0" } pallet-bridge-messages = { default-features = false , version = "0.8.0" } @@ -100,11 +101,25 @@ pallet-bridge-parachains = { default-features = false , version = "0.8.0" } pallet-bridge-relayers = { default-features = false , version = "0.8.0" } pallet-xcm-bridge-hub = { default-features = false , version = "0.3.0" } +# Ethereum Bridge (Snowbridge) +snowbridge-beacon-primitives = { default-features = false , version = "0.1.0" } +snowbridge-pallet-system = { default-features = false , version = "0.1.0" } +snowbridge-system-runtime-api = { default-features = false , version = "0.1.0" } +snowbridge-core = { default-features = false , version = "0.1.0" } +snowbridge-pallet-ethereum-client = { default-features = false , version = "0.1.0" } +snowbridge-pallet-inbound-queue = { default-features = false , version = "0.1.0" } +snowbridge-pallet-outbound-queue = { default-features = false , version = "0.1.0" } +snowbridge-outbound-queue-runtime-api = { default-features = false , version = "0.1.0" } +snowbridge-router-primitives = { default-features = false , version = "0.1.0" } +snowbridge-runtime-common = { default-features = false, version = "0.1.0" } + [dev-dependencies] bridge-hub-test-utils = { version = "0.8.0" } bridge-runtime-common = { version = "0.8.0", features = ["integrity-test"] } sp-keyring = { version = "32.0.0" } static_assertions = { version = "1.1.0" } +snowbridge-runtime-test-common = { version = "0.1.0" } +parachains-runtimes-test-utils = { version = "8.0.0" } [features] default = [ "std" ] @@ -121,6 +136,7 @@ std = [ "bp-runtime/std", "bp-kusama/std", "bp-polkadot/std", + "bridge-hub-common/std", "bridge-runtime-common/std", "codec/std", "cumulus-pallet-aura-ext/std", @@ -167,6 +183,16 @@ std = [ "polkadot-runtime-constants/std", "scale-info/std", "serde", + "snowbridge-beacon-primitives/std", + "snowbridge-core/std", + "snowbridge-pallet-ethereum-client/std", + "snowbridge-pallet-inbound-queue/std", + "snowbridge-outbound-queue-runtime-api/std", + "snowbridge-pallet-outbound-queue/std", + "snowbridge-router-primitives/std", + "snowbridge-runtime-common/std", + "snowbridge-system-runtime-api/std", + "snowbridge-pallet-system/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", @@ -189,6 +215,7 @@ std = [ ] runtime-benchmarks = [ + "bridge-hub-common/runtime-benchmarks", "bridge-runtime-common/runtime-benchmarks", "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", @@ -212,11 +239,21 @@ runtime-benchmarks = [ "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm-bridge-hub/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", + "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", + "snowbridge-pallet-ethereum-client/runtime-benchmarks", + "snowbridge-pallet-inbound-queue/runtime-benchmarks", + "snowbridge-pallet-outbound-queue/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", + "snowbridge-runtime-common/runtime-benchmarks", + "snowbridge-runtime-test-common/runtime-benchmarks", + "snowbridge-pallet-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", ] try-runtime = [ @@ -247,6 +284,10 @@ try-runtime = [ "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", + "snowbridge-pallet-ethereum-client/try-runtime", + "snowbridge-pallet-inbound-queue/try-runtime", + "snowbridge-pallet-outbound-queue/try-runtime", + "snowbridge-pallet-system/try-runtime", "sp-runtime/try-runtime", ] diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/primitives/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-polkadot/primitives/Cargo.toml index faaa3dace9..6787337771 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/primitives/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/primitives/Cargo.toml @@ -18,6 +18,7 @@ system-parachains-constants = { path = "../../../constants", default-features = bp-bridge-hub-cumulus = { default-features = false , version = "0.8.0" } bp-runtime = { default-features = false , version = "0.8.0" } bp-messages = { default-features = false , version = "0.8.0" } +snowbridge-core = { default-features = false , version = "0.1.0" } # Substrate Based Dependencies frame-support = { default-features = false, version = "29.0.0" } @@ -25,6 +26,9 @@ sp-api = { default-features = false, version = "27.0.0" } sp-runtime = { default-features = false, version = "32.0.0" } sp-std = { default-features = false , version = "14.0.0" } +# Polkadot +xcm = { package = "staging-xcm", default-features = false, version = "8.0.1" } + [features] default = [ "std" ] std = [ @@ -38,4 +42,5 @@ std = [ "sp-runtime/std", "sp-std/std", "system-parachains-constants/std", + "xcm/std", ] diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/primitives/src/lib.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/primitives/src/lib.rs index 8952d3827a..94e392174f 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/primitives/src/lib.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/primitives/src/lib.rs @@ -149,6 +149,43 @@ fn convert_from_uksm_to_udot(price_in_uksm: Balance) -> Balance { FixedU128::DIV } +pub mod snowbridge { + use crate::Balance; + use frame_support::parameter_types; + use snowbridge_core::{PricingParameters, Rewards, U256}; + use sp_runtime::FixedU128; + use xcm::latest::NetworkId; + + parameter_types! { + /// Should match the `ForeignAssets::create` index on Asset Hub. + pub const CreateAssetCall: [u8;2] = [53, 0]; + /// The pallet index of the Ethereum inbound queue pallet in the Bridge Hub runtime. + pub const InboundQueuePalletInstance: u8 = 80; + /// Default pricing parameters used to calculate bridging fees. Initialized to unit values, + /// as it is intended that these parameters should be updated with more + /// accurate values prior to bridge activation. This can be performed + /// using the `EthereumSystem::set_pricing_parameters` governance extrinsic. + pub Parameters: PricingParameters = PricingParameters { + // ETH/DOT exchange rate + exchange_rate: FixedU128::from_rational(1, 1), + // Ether fee per gas unit + fee_per_gas: U256::one(), + // Relayer rewards + rewards: Rewards { + // Reward for submitting a message to BridgeHub + local: 1, + // Reward for submitting a message to the Gateway contract on Ethereum + remote: U256::one(), + } + }; + /// Network and location for the Ethereum chain. On Polkadot, the Ethereum chain bridged + /// to is the Ethereum Main network, with chain ID 1. + /// + /// + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 1 }; + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_ethereum_config.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_ethereum_config.rs new file mode 100644 index 0000000000..5803366ef1 --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_ethereum_config.rs @@ -0,0 +1,27 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +use crate::{xcm_config::UniversalLocation, Runtime}; +pub use bp_bridge_hub_polkadot::snowbridge::EthereumNetwork; +use snowbridge_router_primitives::outbound::EthereumBlobExporter; + +/// Exports message to the Ethereum Gateway contract. +pub type SnowbridgeExporter = EthereumBlobExporter< + UniversalLocation, + EthereumNetwork, + snowbridge_pallet_outbound_queue::Pallet, + snowbridge_core::AgentIdOf, +>; diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs index b79af8f02c..3b60af9b18 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs @@ -22,17 +22,25 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +pub mod bridge_to_ethereum_config; pub mod bridge_to_kusama_config; mod weights; pub mod xcm_config; +use bp_bridge_hub_kusama::snowbridge::{CreateAssetCall, InboundQueuePalletInstance, Parameters}; +use bridge_hub_common::message_queue::{ + AggregateMessageOrigin, NarrowOriginToSibling, ParaIdToSibling, +}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::ParaId; +use snowbridge_beacon_primitives::{Fork, ForkVersions}; +use snowbridge_core::{outbound::Message, AgentId, AllowSiblingsOnly}; +use snowbridge_router_primitives::inbound::MessageToXcm; use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT}, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, Keccak256}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; @@ -60,21 +68,24 @@ use frame_system::{ use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; -use xcm_config::{FellowshipLocation, GovernanceLocation, XcmOriginToTransactDispatchOrigin}; +use xcm_config::{ + FellowshipLocation, GovernanceLocation, TreasuryAccount, XcmOriginToTransactDispatchOrigin, +}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; // Polkadot imports use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; +use polkadot_runtime_constants::system_parachain::{ASSET_HUB_ID, BRIDGE_HUB_ID}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use parachains_common::{ - impls::DealWithFees, message_queue::*, AccountId, Balance, BlockNumber, Hash, Header, Nonce, - Signature, + impls::DealWithFees, AccountId, Balance, BlockNumber, Hash, Header, Nonce, Signature, }; pub use system_parachains_constants::SLOT_DURATION; + use system_parachains_constants::{ polkadot::{consensus::*, currency::*, fee::WeightToFee}, AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, @@ -127,6 +138,11 @@ pub type UncheckedExtrinsic = pub type Migrations = ( // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + snowbridge_pallet_system::migration::v0::InitializeOnUpgrade< + Runtime, + ConstU32, + ConstU32, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); @@ -320,21 +336,31 @@ type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< impl parachain_info::Config for Runtime {} parameter_types! { - pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; + /// Amount of weight that can be spent per block to service messages. This was increased + /// from 35% to 60% of the max block weight to accommodate the Ethereum beacon light client + /// extrinsics. The `force_checkpoint` and `submit` extrinsics (for submit, optionally) includes + /// the sync committee's pubkeys (512 x 48 bytes). + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(60) * RuntimeBlockWeights::get().max_block; } impl pallet_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_message_queue::WeightInfo; - #[cfg(feature = "runtime-benchmarks")] - type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< - cumulus_primitives_core::AggregateMessageOrigin, - >; - #[cfg(not(feature = "runtime-benchmarks"))] - type MessageProcessor = xcm_builder::ProcessXcmMessage< - AggregateMessageOrigin, - xcm_executor::XcmExecutor, - RuntimeCall, + // Use the NoopMessageProcessor exclusively for benchmarks, not for tests with the + // runtime-benchmarks feature as tests require the BridgeHubMessageRouter to process messages. + // The "test" feature flag doesn't work, hence the reliance on the "std" feature, which is + // enabled during tests. + #[cfg(all(not(feature = "std"), feature = "runtime-benchmarks"))] + type MessageProcessor = + pallet_message_queue::mock_helpers::NoopMessageProcessor; + #[cfg(not(all(not(feature = "std"), feature = "runtime-benchmarks")))] + type MessageProcessor = bridge_hub_common::BridgeHubMessageRouter< + xcm_builder::ProcessXcmMessage< + AggregateMessageOrigin, + xcm_executor::XcmExecutor, + RuntimeCall, + >, + EthereumOutboundQueue, >; type Size = u32; // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: @@ -474,6 +500,169 @@ impl pallet_utility::Config for Runtime { type WeightInfo = weights::pallet_utility::WeightInfo; } +// Ethereum Bridge +parameter_types! { + // The gateway address is set by governance. + pub storage EthereumGatewayAddress: H160 = H160::zero(); +} + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmark_helpers { + use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; + use codec::Encode; + use snowbridge_beacon_primitives::CompactExecutionHeader; + 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(block_hash: H256, header: CompactExecutionHeader) { + EthereumBeaconClient::store_execution_header(block_hash, header, 0, H256::default()) + } + } + + pub struct DoNothingRouter; + impl SendXcm for DoNothingRouter { + type Ticket = Xcm<()>; + + fn validate( + _dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + Ok((xcm.clone().unwrap(), Assets::new())) + } + fn deliver(xcm: Xcm<()>) -> Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } + } + + impl snowbridge_pallet_system::BenchmarkHelper for () { + fn make_xcm_origin(location: Location) -> RuntimeOrigin { + RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) + } + } +} + +impl snowbridge_pallet_inbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Verifier = snowbridge_pallet_ethereum_client::Pallet; + type Token = Balances; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = xcm_config::XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = benchmark_helpers::DoNothingRouter; + type ChannelLookup = EthereumSystem; + type GatewayAddress = EthereumGatewayAddress; + #[cfg(feature = "runtime-benchmarks")] + type Helper = Runtime; + type MessageConverter = MessageToXcm< + CreateAssetCall, + bp_asset_hub_polkadot::CreateForeignAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + >; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type MaxMessageSize = ConstU32<2048>; + type WeightInfo = weights::snowbridge_pallet_inbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type AssetTransactor = ::AssetTransactor; +} + +impl snowbridge_pallet_outbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Hashing = Keccak256; + type MessageQueue = MessageQueue; + type Decimals = ConstU8<10>; + type MaxMessagePayloadSize = ConstU32<2048>; + type MaxMessagesPerBlock = ConstU32<32>; + type GasMeter = snowbridge_core::outbound::ConstantGasMeter; + type Balance = Balance; + type WeightToFee = WeightToFee; + type WeightInfo = weights::snowbridge_pallet_outbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type Channels = EthereumSystem; +} + +#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 0, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 0, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 0, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 0, + } + }; +} + +#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 74240, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 144896, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 194048, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 269568, + }, + }; +} + +parameter_types! { + pub const MaxExecutionHeadersToKeep: u32 = 8192 * 20; +} + +impl snowbridge_pallet_ethereum_client::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + type MaxExecutionHeadersToKeep = MaxExecutionHeadersToKeep; + type WeightInfo = weights::snowbridge_pallet_ethereum_client::WeightInfo; +} + +impl snowbridge_pallet_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OutboundQueue = EthereumOutboundQueue; + type SiblingOrigin = EnsureXcm; + type AgentIdOf = snowbridge_core::AgentIdOf; + type TreasuryAccount = TreasuryAccount; + type Token = Balances; + type WeightInfo = weights::snowbridge_pallet_system::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type DefaultPricingParameters = Parameters; + type InboundDeliveryCost = EthereumInboundQueue; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -516,6 +705,12 @@ construct_runtime!( BridgeKusamaMessages: pallet_bridge_messages:: = 53, XcmOverBridgeHubKusama: pallet_xcm_bridge_hub:: = 54, + // Ethereum bridge pallets. + EthereumInboundQueue: snowbridge_pallet_inbound_queue = 80, + EthereumOutboundQueue: snowbridge_pallet_outbound_queue = 81, + EthereumBeaconClient: snowbridge_pallet_ethereum_client = 82, + EthereumSystem: snowbridge_pallet_system = 83, + // Message Queue. Importantly, it is registered after Snowbridge pallets // so that messages are processed after the `on_initialize` hooks of bridging pallets. MessageQueue: pallet_message_queue = 175, @@ -547,6 +742,11 @@ mod benches { [pallet_bridge_grandpa, KusamaFinality] [pallet_bridge_parachains, KusamaParachains] [pallet_bridge_messages, KusamaMessages] + // Ethereum Bridge + [snowbridge_pallet_inbound_queue, EthereumInboundQueue] + [snowbridge_pallet_outbound_queue, EthereumOutboundQueue] + [snowbridge_pallet_system, EthereumSystem] + [snowbridge_pallet_ethereum_client, EthereumBeaconClient] ); } @@ -757,6 +957,22 @@ impl_runtime_apis! { } } + impl snowbridge_outbound_queue_runtime_api::OutboundQueueApi for Runtime { + fn prove_message(leaf_index: u64) -> Option { + snowbridge_pallet_outbound_queue::api::prove_message::(leaf_index) + } + + fn calculate_fee(message: Message) -> Option { + snowbridge_pallet_outbound_queue::api::calculate_fee::(message) + } + } + + impl snowbridge_system_runtime_api::ControlApi for Runtime { + fn agent_id(location: VersionedLocation) -> Option { + snowbridge_pallet_system::api::agent_id::(location) + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { @@ -1147,6 +1363,14 @@ cumulus_pallet_parachain_system::register_validate_block! { mod tests { use super::*; + #[test] + fn bridge_hub_inbound_queue_pallet_index_is_correct() { + assert_eq!( + InboundQueuePalletInstance::get(), + ::index() as u8 + ); + } + #[test] fn test_transasction_byte_fee_is_one_tenth_of_relay() { let relay_tbf = polkadot_runtime_constants::fee::TRANSACTION_BYTE_FEE; diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/mod.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/mod.rs index e56ad7c2cc..467a41fd8a 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/mod.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/mod.rs @@ -43,6 +43,10 @@ pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; pub mod rocksdb_weights; +pub mod snowbridge_pallet_ethereum_client; +pub mod snowbridge_pallet_inbound_queue; +pub mod snowbridge_pallet_outbound_queue; +pub mod snowbridge_pallet_system; pub mod xcm; pub use block_weights::constants::BlockExecutionWeight; diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_ethereum_client.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_ethereum_client.rs new file mode 100644 index 0000000000..0d5f29c6ff --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_ethereum_client.rs @@ -0,0 +1,151 @@ +// 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. + +//! Autogenerated weights for `snowbridge_pallet_ethereum_client` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-06-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ip-172-31-8-124`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --base-path +// /mnt/scratch/benchmark +// --chain=bridge-hub-rococo-dev +// --pallet=snowbridge_ethereum_beacon_client +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --steps +// 50 +// --repeat +// 20 +// --output +// ./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_ethereum_beacon_client.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_pallet_ethereum_client`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_ethereum_client::WeightInfo for WeightInfo { + /// Storage: EthereumBeaconClient FinalizedBeaconStateIndex (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconStateIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconStateMapping (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconStateMapping (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient NextSyncCommittee (r:0 w:1) + /// Proof: EthereumBeaconClient NextSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient InitialCheckpointRoot (r:0 w:1) + /// Proof: EthereumBeaconClient InitialCheckpointRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ValidatorsRoot (r:0 w:1) + /// Proof: EthereumBeaconClient ValidatorsRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestFinalizedBlockRoot (r:0 w:1) + /// Proof: EthereumBeaconClient LatestFinalizedBlockRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient CurrentSyncCommittee (r:0 w:1) + /// Proof: EthereumBeaconClient CurrentSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestExecutionState (r:0 w:1) + /// Proof: EthereumBeaconClient LatestExecutionState (max_values: Some(1), max_size: Some(80), added: 575, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconState (r:0 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconState (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + fn force_checkpoint() -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3501` + // Minimum execution time: 97_185_781_000 picoseconds. + Weight::from_parts(97_263_571_000, 0) + .saturating_add(Weight::from_parts(0, 3501)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(9)) + } + /// Storage: EthereumBeaconClient LatestFinalizedBlockRoot (r:1 w:1) + /// Proof: EthereumBeaconClient LatestFinalizedBlockRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconState (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconState (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestExecutionState (r:1 w:0) + /// Proof: EthereumBeaconClient LatestExecutionState (max_values: Some(1), max_size: Some(80), added: 575, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient NextSyncCommittee (r:1 w:0) + /// Proof: EthereumBeaconClient NextSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient CurrentSyncCommittee (r:1 w:0) + /// Proof: EthereumBeaconClient CurrentSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ValidatorsRoot (r:1 w:0) + /// Proof: EthereumBeaconClient ValidatorsRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconStateIndex (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconStateIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconStateMapping (r:1 w:1) + /// Proof: EthereumBeaconClient FinalizedBeaconStateMapping (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + fn submit() -> Weight { + // Proof Size summary in bytes: + // Measured: `92753` + // Estimated: `93857` + // Minimum execution time: 25_999_968_000 picoseconds. + Weight::from_parts(26_051_019_000, 0) + .saturating_add(Weight::from_parts(0, 93857)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: EthereumBeaconClient LatestFinalizedBlockRoot (r:1 w:0) + /// Proof: EthereumBeaconClient LatestFinalizedBlockRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconState (r:1 w:0) + /// Proof: EthereumBeaconClient FinalizedBeaconState (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestExecutionState (r:1 w:0) + /// Proof: EthereumBeaconClient LatestExecutionState (max_values: Some(1), max_size: Some(80), added: 575, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient NextSyncCommittee (r:1 w:1) + /// Proof: EthereumBeaconClient NextSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient CurrentSyncCommittee (r:1 w:0) + /// Proof: EthereumBeaconClient CurrentSyncCommittee (max_values: Some(1), max_size: Some(92372), added: 92867, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ValidatorsRoot (r:1 w:0) + /// Proof: EthereumBeaconClient ValidatorsRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn submit_with_sync_committee() -> Weight { + // Proof Size summary in bytes: + // Measured: `92717` + // Estimated: `93857` + // Minimum execution time: 122_354_917_000 picoseconds. + Weight::from_parts(122_461_312_000, 0) + .saturating_add(Weight::from_parts(0, 93857)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: EthereumBeaconClient LatestFinalizedBlockRoot (r:1 w:0) + /// Proof: EthereumBeaconClient LatestFinalizedBlockRoot (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient FinalizedBeaconState (r:1 w:0) + /// Proof: EthereumBeaconClient FinalizedBeaconState (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient LatestExecutionState (r:1 w:1) + /// Proof: EthereumBeaconClient LatestExecutionState (max_values: Some(1), max_size: Some(80), added: 575, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ExecutionHeaderIndex (r:1 w:1) + /// Proof: EthereumBeaconClient ExecutionHeaderIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ExecutionHeaderMapping (r:1 w:1) + /// Proof: EthereumBeaconClient ExecutionHeaderMapping (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ExecutionHeaders (r:0 w:1) + /// Proof: EthereumBeaconClient ExecutionHeaders (max_values: None, max_size: Some(136), added: 2611, mode: MaxEncodedLen) + fn submit_execution_header() -> Weight { + // Proof Size summary in bytes: + // Measured: `386` + // Estimated: `3537` + // Minimum execution time: 108_761_000 picoseconds. + Weight::from_parts(113_158_000, 0) + .saturating_add(Weight::from_parts(0, 3537)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_inbound_queue.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_inbound_queue.rs new file mode 100644 index 0000000000..faf404f90c --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_inbound_queue.rs @@ -0,0 +1,69 @@ +// 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. + +//! Autogenerated weights for `snowbridge_pallet_inbound_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-09-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `macbook pro 14 m2`, CPU: `m2-arm64` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --chain=bridge-hub-rococo-dev +// --pallet=snowbridge_inbound_queue +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --steps +// 50 +// --repeat +// 20 +// --output +// ./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_inbound_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_pallet_inbound_queue`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_inbound_queue::WeightInfo for WeightInfo { + /// Storage: EthereumInboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumInboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: EthereumBeaconClient ExecutionHeaders (r:1 w:0) + /// Proof: EthereumBeaconClient ExecutionHeaders (max_values: None, max_size: Some(136), added: 2611, mode: MaxEncodedLen) + /// Storage: EthereumInboundQueue Nonce (r:1 w:1) + /// Proof: EthereumInboundQueue Nonce (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn submit() -> Weight { + // Proof Size summary in bytes: + // Measured: `457` + // Estimated: `3601` + // Minimum execution time: 69_000_000 picoseconds. + Weight::from_parts(70_000_000, 0) + .saturating_add(Weight::from_parts(0, 3601)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_outbound_queue.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_outbound_queue.rs new file mode 100644 index 0000000000..8adcef076e --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_outbound_queue.rs @@ -0,0 +1,87 @@ +// 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. + +//! Autogenerated weights for `snowbridge_outbound_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-20, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `192.168.1.13`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// ../target/release/polkadot-parachain +// benchmark +// pallet +// --chain=bridge-hub-rococo-dev +// --pallet=snowbridge_outbound_queue +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --output +// ../parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_outbound_queue.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_outbound_queue`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_outbound_queue::WeightInfo for WeightInfo { + /// Storage: EthereumOutboundQueue MessageLeaves (r:1 w:1) + /// Proof Skipped: EthereumOutboundQueue MessageLeaves (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: EthereumOutboundQueue PendingHighPriorityMessageCount (r:1 w:1) + /// Proof: EthereumOutboundQueue PendingHighPriorityMessageCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue Nonce (r:1 w:1) + /// Proof: EthereumOutboundQueue Nonce (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue Messages (r:1 w:1) + /// Proof Skipped: EthereumOutboundQueue Messages (max_values: Some(1), max_size: None, mode: Measured) + fn do_process_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3485` + // Minimum execution time: 39_000_000 picoseconds. + Weight::from_parts(39_000_000, 3485) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: EthereumOutboundQueue MessageLeaves (r:1 w:0) + /// Proof Skipped: EthereumOutboundQueue MessageLeaves (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Digest (r:1 w:1) + /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) + fn commit() -> Weight { + // Proof Size summary in bytes: + // Measured: `1094` + // Estimated: `2579` + // Minimum execution time: 28_000_000 picoseconds. + Weight::from_parts(28_000_000, 2579) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + + fn commit_single() -> Weight { + // Proof Size summary in bytes: + // Measured: `1094` + // Estimated: `2579` + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(9_000_000, 1586) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_system.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_system.rs new file mode 100644 index 0000000000..c6c188e323 --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_system.rs @@ -0,0 +1,256 @@ +// 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. + +//! Autogenerated weights for `snowbridge_system` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-09, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `crake.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --chain +// bridge-hub-rococo-dev +// --pallet=snowbridge_pallet_system +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --output +// parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `snowbridge_system`. +pub struct WeightInfo(PhantomData); +impl snowbridge_pallet_system::WeightInfo for WeightInfo { + /// 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 47_000_000 picoseconds. + Weight::from_parts(47_000_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: EthereumSystem Agents (r:1 w:1) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 create_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `187` + // Estimated: `6196` + // Minimum execution time: 87_000_000 picoseconds. + Weight::from_parts(87_000_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(6)) + } + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: EthereumSystem Agents (r:1 w:0) + /// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) + /// Storage: EthereumSystem Channels (r:1 w:1) + /// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen) + /// 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:1 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn create_channel() -> Weight { + // Proof Size summary in bytes: + // Measured: `602` + // Estimated: `69050` + // Minimum execution time: 84_000_000 picoseconds. + Weight::from_parts(84_000_000, 0) + .saturating_add(Weight::from_parts(0, 69050)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// 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 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(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: 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 set_operating_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 30_000_000 picoseconds. + Weight::from_parts(30_000_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) + .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 transfer_native_from_agent() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `6044` + // Minimum execution time: 43_000_000 picoseconds. + Weight::from_parts(43_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 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: 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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 set_token_transfer_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 31_000_000 picoseconds. + Weight::from_parts(42_000_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::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) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// 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 set_pricing_parameters() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 31_000_000 picoseconds. + Weight::from_parts(42_000_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs index c28e8b89ab..9be371b2aa 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs @@ -15,12 +15,13 @@ // along with Cumulus. If not, see . use super::{ + bridge_to_ethereum_config::EthereumNetwork, bridge_to_kusama_config::{ DeliveryRewardInBalance, RequiredStakeForStakeAndSlash, ToBridgeHubKusamaHaulBlobExporter, }, - AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, - PriceForParentDelivery, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, - XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, EthereumGatewayAddress, ParachainInfo, + ParachainSystem, PolkadotXcm, PriceForParentDelivery, Runtime, RuntimeCall, RuntimeEvent, + RuntimeOrigin, WeightToFee, XcmpQueue, }; use frame_support::{ parameter_types, @@ -37,21 +38,23 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_constants::system_parachain; +use snowbridge_runtime_common::XcmExportFeeToSibling; use sp_runtime::traits::AccountIdConversion; +use sp_std::marker::PhantomData; use system_parachains_constants::TREASURY_PALLET_ID; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, - FungibleAdapter, HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, + FungibleAdapter, HandleFee, HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, XcmFeeToAccount, + XcmFeeToAccount, }; use xcm_executor::{ - traits::{ConvertLocation, WithOriginFilter}, + traits::{ConvertLocation, FeeManager, FeeReason, FeeReason::Export, WithOriginFilter}, XcmExecutor, }; @@ -171,7 +174,8 @@ impl Contains for SafeCallFilter { RuntimeCall::System(frame_system::Call::set_storage { items }) if items.iter().all(|(k, _)| { k.eq(&DeliveryRewardInBalance::key()) || - k.eq(&RequiredStakeForStakeAndSlash::key()) + k.eq(&RequiredStakeForStakeAndSlash::key()) || + k.eq(&EthereumGatewayAddress::key()) }) => return true, _ => (), @@ -216,7 +220,22 @@ impl Contains for SafeCallFilter { RuntimeCall::BridgeKusamaMessages(pallet_bridge_messages::Call::< Runtime, crate::bridge_to_kusama_config::WithBridgeHubKusamaMessagesInstance, - >::set_operating_mode { .. }) + >::set_operating_mode { .. }) | + RuntimeCall::EthereumBeaconClient( + snowbridge_pallet_ethereum_client::Call::force_checkpoint { .. } | + snowbridge_pallet_ethereum_client::Call::set_operating_mode { .. }, + ) | RuntimeCall::EthereumInboundQueue( + snowbridge_pallet_inbound_queue::Call::set_operating_mode { .. }, + ) | RuntimeCall::EthereumOutboundQueue( + snowbridge_pallet_outbound_queue::Call::set_operating_mode { .. }, + ) | RuntimeCall::EthereumSystem( + snowbridge_pallet_system::Call::upgrade { .. } | + snowbridge_pallet_system::Call::set_operating_mode { .. } | + snowbridge_pallet_system::Call::set_pricing_parameters { .. } | + snowbridge_pallet_system::Call::force_update_channel { .. } | + snowbridge_pallet_system::Call::force_transfer_native_from_agent { .. } | + snowbridge_pallet_system::Call::set_token_transfer_fees { .. }, + ) ) } } @@ -291,11 +310,22 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); - type FeeManager = XcmFeeManagerFromComponents< + type FeeManager = XcmFeeManagerFromComponentsBridgeHub< WaivedLocations, - XcmFeeToAccount, + ( + XcmExportFeeToSibling< + bp_polkadot::Balance, + AccountId, + DotRelayLocation, + EthereumNetwork, + Self::AssetTransactor, + crate::EthereumOutboundQueue, + >, + XcmFeeToAccount, + ), >; - type MessageExporter = ToBridgeHubKusamaHaulBlobExporter; + type MessageExporter = + (ToBridgeHubKusamaHaulBlobExporter, crate::bridge_to_ethereum_config::SnowbridgeExporter); type UniversalAliases = Nothing; type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; @@ -353,3 +383,27 @@ impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; } + +/// A `FeeManager` implementation that forces fees for any message delivered to Ethereum. +/// Otherwise, it permits the specified `WaivedLocations` to not pay for fees and uses the provided +/// `HandleFee` implementation. +pub struct XcmFeeManagerFromComponentsBridgeHub( + PhantomData<(WaivedLocations, HandleFee)>, +); +impl, FeeHandler: HandleFee> FeeManager + for XcmFeeManagerFromComponentsBridgeHub +{ + fn is_waived(origin: Option<&Location>, fee_reason: FeeReason) -> bool { + let Some(loc) = origin else { return false }; + if let Export { network, destination: Here } = fee_reason { + if network == EthereumNetwork::get() { + return false + } + } + WaivedLocations::contains(loc) + } + + fn handle_fee(fee: Assets, context: Option<&XcmContext>, reason: FeeReason) { + FeeHandler::handle_fee(fee, context, reason); + } +} diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs new file mode 100644 index 0000000000..29f8ad5ad4 --- /dev/null +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs @@ -0,0 +1,505 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +#![cfg(test)] + +use bp_polkadot_core::Signature; +use bridge_hub_polkadot_runtime::{ + bridge_to_ethereum_config::EthereumNetwork, + bridge_to_kusama_config::RefundBridgeHubKusamaMessages, + xcm_config::{XcmConfig, XcmFeeManagerFromComponentsBridgeHub}, + BridgeRejectObsoleteHeadersAndMessages, EthereumGatewayAddress, Executive, + MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra, + UncheckedExtrinsic, +}; +use bridge_hub_test_utils::ValidatorIdOf; +use codec::{Decode, Encode}; +use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; +use frame_support::{ + assert_err, assert_ok, parameter_types, + traits::{Contains, OnFinalize, OnInitialize}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use parachains_common::{AccountId, AuraId, Balance}; +pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works; +use parachains_runtimes_test_utils::{ + AccountIdOf, CollatorSessionKeys, ExtBuilder, XcmReceivedFrom, +}; +use polkadot_runtime_constants::currency::UNITS; +use snowbridge_core::{gwei, meth, ChannelId, ParaId, Rewards}; +use snowbridge_pallet_ethereum_client::WeightInfo; +use snowbridge_pallet_system::{PricingParametersOf, WeightInfo as EthereumSystemWeightInfo}; +use snowbridge_runtime_test_common::initial_fund; +use sp_core::H160; +use sp_keyring::AccountKeyring::Alice; +use sp_runtime::{ + generic::{Era, SignedPayload}, + traits::Header, + AccountId32, FixedU128, Saturating, +}; +use xcm::{latest::prelude::*, v3::Error}; +use xcm_builder::HandleFee; +use xcm_executor::{ + traits::{FeeManager, FeeReason}, + XcmExecutor, +}; + +type RuntimeHelper = + parachains_runtimes_test_utils::RuntimeHelper; + +parameter_types! { + pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; +} + +fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys { + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(Alice), + AccountId::from(Alice), + SessionKeys { aura: AuraId::from(Alice.public()) }, + ) +} + +#[test] +pub fn transfer_token_to_ethereum_works() { + send_transfer_token_message_success::( + collator_session_keys(), + 1013, + 1000, + H160::random(), + H160::random(), + DefaultBridgeHubEthereumBaseFee::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::EthereumOutboundQueue(event)) => Some(event), + _ => None, + } + }), + ) +} + +#[test] +pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { + snowbridge_runtime_test_common::send_unpaid_transfer_token_message::( + collator_session_keys(), + 1013, + 1000, + H160::random(), + H160::random(), + ) +} + +#[test] +pub fn transfer_token_to_ethereum_fee_not_enough() { + send_transfer_token_message_failure::( + collator_session_keys(), + 1013, + 1000, + DefaultBridgeHubEthereumBaseFee::get() + 1_000_000_000, + H160::random(), + H160::random(), + // fee not enough + 1_000_000_000, + Box::new(|call| RuntimeCall::EthereumSystem(call).encode()), + NotHoldingFees, + ) +} + +#[test] +pub fn transfer_token_to_ethereum_insufficient_fund() { + send_transfer_token_message_failure::( + collator_session_keys(), + 1013, + 1000, + 1_000_000_000, + H160::random(), + H160::random(), + DefaultBridgeHubEthereumBaseFee::get(), + Box::new(|call| RuntimeCall::EthereumSystem(call).encode()), + FailedToTransactAsset("Funds are unavailable"), + ) +} + +#[test] +fn change_ethereum_gateway_by_governance_works() { + change_storage_constant_by_governance_works::( + collator_session_keys(), + bp_bridge_hub_polkadot::BRIDGE_HUB_POLKADOT_PARACHAIN_ID, + Box::new(|call| RuntimeCall::System(call).encode()), + || (EthereumGatewayAddress::key().to_vec(), EthereumGatewayAddress::get()), + |_| [1; 20].into(), + ) +} + +/// Fee is not waived when origin is none. +#[test] +fn test_xcm_fee_manager_from_components_bh_origin_none() { + assert!(!TestXcmFeeManager::is_waived(None, FeeReason::ChargeFees)); +} + +/// Fee is not waived when origin is not in waived location. +#[test] +fn test_xcm_fee_manager_from_components_bh_origin_not_in_waived_locations() { + assert!(!TestXcmFeeManager::is_waived( + Some(&Location::new(1, [Parachain(1)])), + FeeReason::DepositReserveAsset + )); +} + +/// Fee is waived when origin is in waived location. +#[test] +fn test_xcm_fee_manager_from_components_bh_origin_in_waived_locations() { + assert!(TestXcmFeeManager::is_waived( + Some(&Location::new(1, [Parachain(2)])), + FeeReason::DepositReserveAsset + )); +} + +/// Fee is waived when origin is in waived location with Export message, but not to Ethereum. +#[test] +fn test_xcm_fee_manager_from_components_bh_origin_in_waived_locations_with_export_to_polkadot_reason( +) { + assert!(TestXcmFeeManager::is_waived( + Some(&Location::new(1, [Parachain(2)])), + FeeReason::Export { network: Polkadot, destination: Here } + )); +} + +/// Fee is not waived when origin is in waived location but exported to Ethereum. +#[test] +fn test_xcm_fee_manager_from_components_bh_in_waived_locations_with_export_to_ethereum_reason() { + assert!(!TestXcmFeeManager::is_waived( + Some(&Location::new(1, [Parachain(1)])), + FeeReason::Export { network: EthereumNetwork::get(), destination: Here } + )); +} + +struct MockWaivedLocations; +impl Contains for MockWaivedLocations { + fn contains(loc: &Location) -> bool { + loc == &Location::new(1, [Parachain(2)]) + } +} + +struct MockFeeHandler; +impl HandleFee for MockFeeHandler { + fn handle_fee(fee: Assets, _context: Option<&XcmContext>, _reason: FeeReason) -> Assets { + fee + } +} + +type TestXcmFeeManager = XcmFeeManagerFromComponentsBridgeHub; + +#[allow(clippy::too_many_arguments)] +pub fn send_transfer_token_message_failure( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + assethub_parachain_id: u32, + initial_amount: u128, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, + system_call_encode: Box) -> Vec>, + expected_error: Error, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config, + XcmConfig: xcm_executor::Config, + ValidatorIdOf: From>, + <::Token as frame_support::traits::fungible::Inspect<::AccountId>>::Balance: From +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + assert_ok!(>::initialize( + runtime_para_id.into(), + assethub_parachain_id.into(), + )); + + let require_weight_at_most = + ::WeightInfo::set_pricing_parameters(); + + let set_pricing_parameters_call = system_call_encode(snowbridge_pallet_system::Call::< + Runtime, + >::set_pricing_parameters { + params: { + PricingParametersOf:: { + exchange_rate: FixedU128::from_rational(1, 75), + fee_per_gas: gwei(20), + rewards: Rewards { + local: (1 * UNITS / 100).into(), // 0.01 DOT + remote: meth(1), + }, + } + }, + }); + + assert_ok!(RuntimeHelper::::execute_as_governance( + set_pricing_parameters_call, + require_weight_at_most + ) + .ensure_complete()); + + // fund asset hub sovereign account enough so it can pay fees + initial_fund::(assethub_parachain_id, initial_amount); + + let outcome = send_transfer_token_message::( + assethub_parachain_id, + weth_contract_address, + destination_address, + fee_amount, + ); + assert_err!(outcome.ensure_complete(), expected_error); + }); +} + +#[test] +fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() { + let max_message_queue_weight = MessageQueueServiceWeight::get(); + let force_checkpoint = + ::WeightInfo::force_checkpoint(); + let submit_checkpoint = + ::WeightInfo::submit(); + max_message_queue_weight.all_gt(force_checkpoint); + max_message_queue_weight.all_gt(submit_checkpoint); +} + +#[test] +fn ethereum_client_consensus_extrinsics_work() { + snowbridge_runtime_test_common::ethereum_extrinsic( + collator_session_keys(), + 1013, + construct_and_apply_extrinsic, + ); +} + +#[test] +fn ethereum_to_polkadot_message_extrinsics_work() { + snowbridge_runtime_test_common::ethereum_to_polkadot_message_extrinsics_work( + collator_session_keys(), + 1013, + construct_and_apply_extrinsic, + ); +} + +#[test] +fn ethereum_outbound_queue_processes_messages_before_message_queue_works() { + // TODO: add test after dependencies are upgraded to >= 1.8 +} + +fn construct_extrinsic( + sender: sp_keyring::AccountKeyring, + call: RuntimeCall, +) -> UncheckedExtrinsic { + let account_id = AccountId32::from(sender.public()); + let extra: SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account_id).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + BridgeRejectObsoleteHeadersAndMessages, + (RefundBridgeHubKusamaMessages::default()), + ); + let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); + let signature = payload.using_encoded(|e| sender.sign(e)); + UncheckedExtrinsic::new_signed( + call, + account_id.into(), + Signature::Sr25519(signature.clone()), + extra, + ) +} + +fn construct_and_apply_extrinsic( + origin: sp_keyring::AccountKeyring, + call: RuntimeCall, +) -> sp_runtime::DispatchOutcome { + let xt = construct_extrinsic(origin, call); + let r = Executive::apply_extrinsic(xt); + r.unwrap() +} + +// TODO remove when Ethereum network ID has been extracted as a param +pub fn send_transfer_token_message( + assethub_parachain_id: u32, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, +) -> Outcome +where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config, + XcmConfig: xcm_executor::Config, +{ + let assethub_parachain_location = Location::new(1, Parachain(assethub_parachain_id)); + let asset = Asset { + id: AssetId(Location::new( + 0, + [AccountKey20 { network: None, key: weth_contract_address.into() }], + )), + fun: Fungible(1000000000), + }; + let assets = vec![asset.clone()]; + + let inner_xcm = Xcm(vec![ + WithdrawAsset(Assets::from(assets.clone())), + ClearOrigin, + BuyExecution { fees: asset, weight_limit: Unlimited }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { network: None, key: destination_address.into() }], + ), + }, + SetTopic([0; 32]), + ]); + + let fee = + Asset { id: AssetId(Location { parents: 1, interior: Here }), fun: Fungible(fee_amount) }; + + // prepare transfer token message + let xcm = Xcm(vec![ + WithdrawAsset(Assets::from(vec![fee.clone()])), + BuyExecution { fees: fee, weight_limit: Unlimited }, + ExportMessage { network: Ethereum { chain_id: 1 }, destination: Here, xcm: inner_xcm }, + ]); + + // execute XCM + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + assethub_parachain_location, + xcm, + &mut hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + Weight::zero(), + ) +} + +pub fn send_transfer_token_message_success( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + assethub_parachain_id: u32, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, + snowbridge_pallet_outbound_queue: Box< + dyn Fn(Vec) -> Option>, + >, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + pallet_message_queue::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config, + XcmConfig: xcm_executor::Config, + ValidatorIdOf: From>, + ::AccountId: From + AsRef<[u8]>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + >::initialize( + runtime_para_id.into(), + assethub_parachain_id.into(), + ) + .unwrap(); + + // fund asset hub sovereign account enough so it can pay fees + initial_fund::(assethub_parachain_id, 5_000_000_000_000); + + let outcome = send_transfer_token_message::( + assethub_parachain_id, + weth_contract_address, + destination_address, + fee_amount, + ); + + assert_ok!(outcome.ensure_complete()); + + // check events + let mut events = >::events() + .into_iter() + .filter_map(|e| snowbridge_pallet_outbound_queue(e.event.encode())); + assert!(events.any(|e| matches!( + e, + snowbridge_pallet_outbound_queue::Event::MessageQueued { .. } + ))); + + let block_number = >::block_number(); + let next_block_number = >::block_number() + .saturating_add(BlockNumberFor::::from(1u32)); + + // finish current block + >::on_finalize(block_number); + >::on_finalize(block_number); + >::on_finalize(block_number); + + // start next block + >::set_block_number(next_block_number); + >::on_initialize(next_block_number); + >::on_initialize(next_block_number); + >::on_initialize(next_block_number); + + // finish next block + >::on_finalize(next_block_number); + >::on_finalize(next_block_number); + let included_head = >::finalize(); + + let origin: ParaId = assethub_parachain_id.into(); + let channel_id: ChannelId = origin.into(); + + let nonce = snowbridge_pallet_outbound_queue::Nonce::::try_get(channel_id); + assert_ok!(nonce); + assert_eq!(nonce.unwrap(), 1); + + let digest = included_head.digest(); + + let digest_items = digest.logs(); + assert!(digest_items.len() == 1 && digest_items[0].as_other().is_some()); + }); +}