diff --git a/Cargo.lock b/Cargo.lock index e57c9951b..2ca6eaf8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.3", "once_cell", "version_check", ] @@ -58,15 +58,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "0a03e93e97a28fbc9f42fbc5ba0886a3c67eb637b476dbee711f80a6ffe8223d" [[package]] name = "approx" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +checksum = "072df7202e63b127ab55acfe16ce97013d5b97bf160489336d3f1840fd78e99e" dependencies = [ "num-traits", ] @@ -106,9 +106,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-trait" -version = "0.1.56" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" dependencies = [ "proc-macro2", "quote", @@ -117,15 +117,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "backtrace" -version = "0.3.65" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" dependencies = [ "addr2line", "cc", @@ -209,16 +209,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.4", ] [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.4", ] [[package]] @@ -394,6 +394,19 @@ dependencies = [ "sp-version", ] +[[package]] +name = "bp-parachains" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", +] + [[package]] name = "bp-polkadot" version = "0.1.0" @@ -450,11 +463,13 @@ name = "bp-runtime" version = "0.1.0" dependencies = [ "frame-support", + "frame-system", "hash-db", "hex-literal", "num-traits", "parity-scale-codec", "scale-info", + "serde", "sp-core", "sp-io", "sp-runtime", @@ -483,15 +498,19 @@ version = "0.1.0" dependencies = [ "bp-message-dispatch", "bp-messages", + "bp-parachains", + "bp-polkadot-core", "bp-runtime", "ed25519-dalek", "frame-support", "frame-system", "hash-db", + "log", "pallet-balances", "pallet-bridge-dispatch", "pallet-bridge-grandpa", "pallet-bridge-messages", + "pallet-bridge-parachains", "pallet-transaction-payment", "parity-scale-codec", "scale-info", @@ -506,15 +525,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" [[package]] name = "byte-slice-cast" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" +checksum = "1d30c751592b77c499e7bce34d99d67c2c11bdc0574e9a488ddade14150a4698" [[package]] name = "byte-tools" @@ -536,9 +555,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" [[package]] name = "cfg-if" @@ -572,9 +591,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" dependencies = [ "libc", ] @@ -591,7 +610,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.4", "rand_core 0.6.3", "subtle", "zeroize", @@ -603,7 +622,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.4", "typenum", ] @@ -613,7 +632,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.4", "subtle", ] @@ -623,7 +642,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.4", "subtle", ] @@ -688,7 +707,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.4", ] [[package]] @@ -697,7 +716,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.0", "crypto-common", "subtle", ] @@ -731,9 +750,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.6" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140206b78fb2bc3edbcfc9b5ccbd0b30699cfe8d348b8b31b330e47df5291a5a" +checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" [[package]] name = "ecdsa" @@ -749,9 +768,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "1.5.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" +checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" dependencies = [ "signature", ] @@ -766,7 +785,7 @@ dependencies = [ "ed25519", "rand 0.7.3", "serde", - "sha2 0.9.9", + "sha2 0.9.8", "zeroize", ] @@ -786,7 +805,7 @@ dependencies = [ "crypto-bigint", "der", "ff", - "generic-array 0.14.5", + "generic-array 0.14.4", "group", "rand_core 0.6.3", "sec1", @@ -835,9 +854,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "ff" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" +checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e" dependencies = [ "rand_core 0.6.3", "subtle", @@ -874,7 +893,7 @@ dependencies = [ [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "frame-support", "frame-system", @@ -908,7 +927,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "bitflags", "frame-metadata", @@ -938,7 +957,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "Inflector", "frame-support-procedural-tools", @@ -950,7 +969,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -962,7 +981,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "proc-macro2", "quote", @@ -972,7 +991,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "frame-support", "log", @@ -1099,9 +1118,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", "version_check", @@ -1122,13 +1141,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.10.0+wasi-snapshot-preview1", ] [[package]] @@ -1174,9 +1193,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" dependencies = [ "ahash", ] @@ -1229,7 +1248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.5", + "generic-array 0.14.4", "hmac 0.8.1", ] @@ -1288,15 +1307,15 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" dependencies = [ "wasm-bindgen", ] @@ -1315,9 +1334,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.2" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" [[package]] name = "lazy_static" @@ -1327,15 +1346,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" [[package]] name = "libm" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" [[package]] name = "libsecp256k1" @@ -1352,7 +1371,7 @@ dependencies = [ "libsecp256k1-gen-genmult", "rand 0.8.5", "serde", - "sha2 0.9.9", + "sha2 0.9.8", "typenum", ] @@ -1397,11 +1416,10 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" dependencies = [ - "autocfg", "scopeguard", ] @@ -1416,9 +1434,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.7.7" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84e6fe5655adc6ce00787cf7dcaf8dc4f998a0565d23eafc207a8b08ca3349a" +checksum = "32613e41de4c47ab04970c348ca7ae7382cf116625755af070b008a15516a889" dependencies = [ "hashbrown 0.11.2", ] @@ -1443,9 +1461,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memory-db" @@ -1454,7 +1472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6566c70c1016f525ced45d7b7f97730a2bafb037c788211d0c186ef5b2189f0a" dependencies = [ "hash-db", - "hashbrown 0.12.1", + "hashbrown 0.12.0", "parity-util-mem", ] @@ -1478,11 +1496,12 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", + "autocfg", ] [[package]] @@ -1495,7 +1514,7 @@ dependencies = [ "matrixmultiply", "nalgebra-macros", "num-complex", - "num-rational 0.4.1", + "num-rational 0.4.0", "num-traits", "rand 0.8.5", "rand_distr", @@ -1533,9 +1552,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" dependencies = [ "num-traits", ] @@ -1552,9 +1571,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg", "num-traits", @@ -1574,9 +1593,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" dependencies = [ "autocfg", "num-integer", @@ -1585,9 +1604,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", "libm", @@ -1595,9 +1614,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ "hermit-abi", "libc", @@ -1605,18 +1624,18 @@ dependencies = [ [[package]] name = "object" -version = "0.28.4" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "opaque-debug" @@ -1633,7 +1652,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "frame-benchmarking", "frame-support", @@ -1694,6 +1713,7 @@ dependencies = [ "bp-message-dispatch", "bp-messages", "bp-runtime", + "bp-test-utils", "frame-benchmarking", "frame-support", "frame-system", @@ -1714,9 +1734,11 @@ name = "pallet-bridge-parachains" version = "0.1.0" dependencies = [ "bp-header-chain", + "bp-parachains", "bp-polkadot-core", "bp-runtime", "bp-test-utils", + "frame-benchmarking", "frame-support", "frame-system", "log", @@ -1755,7 +1777,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "frame-benchmarking", "frame-support", @@ -1772,7 +1794,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "frame-support", "frame-system", @@ -1794,6 +1816,7 @@ dependencies = [ "arrayvec 0.7.2", "bitvec", "byte-slice-cast", + "bytes", "impl-trait-for-tuples", "parity-scale-codec-derive", "serde", @@ -1819,7 +1842,7 @@ checksum = "c32561d248d352148124f036cac253a644685a21dc9fea383eb4907d7bd35a8f" dependencies = [ "cfg-if", "ethereum-types", - "hashbrown 0.12.1", + "hashbrown 0.12.0", "impl-trait-for-tuples", "lru", "parity-util-mem-derive", @@ -1848,9 +1871,9 @@ checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" dependencies = [ "lock_api", "parking_lot_core", @@ -1858,9 +1881,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" dependencies = [ "cfg-if", "libc", @@ -1871,9 +1894,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.7" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" [[package]] name = "pbkdf2" @@ -1895,9 +1918,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" [[package]] name = "pin-utils" @@ -1907,9 +1930,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] name = "primitive-types" @@ -1937,18 +1960,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] @@ -2019,14 +2042,14 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.3", ] [[package]] name = "rand_distr" -version = "0.4.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +checksum = "964d548f8e7d12e102ef183a0de7e98180c9f8729f555897a857b96e48122d2f" dependencies = [ "num-traits", "rand 0.8.5", @@ -2058,27 +2081,27 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ "bitflags", ] [[package]] name = "ref-cast" -version = "1.0.7" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685d58625b6c2b83e4cc88a27c4bf65adb7b6b16dbdc413e515c9405b47432ab" +checksum = "300f2a835d808734ee295d45007adacb9ebb29dd3ae2424acfa17930cae541da" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.7" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a043824e29c94169374ac5183ac0ed43f5724dc4556b19568007486bd840fa1f" +checksum = "4c38e3aecd2b21cb3959637b883bb3714bc7e43f0268b9a29d3743ee3e55cdd2" dependencies = [ "proc-macro2", "quote", @@ -2087,9 +2110,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.6" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", @@ -2107,9 +2130,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rfc6979" @@ -2152,15 +2175,15 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "scale-info" -version = "2.1.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c46be926081c9f4dd5dd9b6f1d3e3229f2360bc6502dd8836f84a93b7c75e99a" +checksum = "8980cafbe98a7ee7a9cc16b32ebce542c77883f512d83fbf2ddc8f6a85ea74c9" dependencies = [ "bitvec", "cfg-if", @@ -2172,9 +2195,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.1.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e334bb10a245e28e5fd755cabcafd96cfcd167c99ae63a46924ca8d8703a3c" +checksum = "4260c630e8a8a33429d1688eff2f163f24c65a4e1b1578ef6b565061336e4b6f" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2213,25 +2236,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" dependencies = [ "der", - "generic-array 0.14.5", + "generic-array 0.14.4", "subtle", "zeroize", ] [[package]] name = "secp256k1" -version = "0.21.3" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c42e6f1735c5f00f51e43e28d6634141f2bcad10931b2609ddd74a86d751260" +checksum = "b7649a0b3ffb32636e60c7ce0d70511eda9c52c658cd0634e194d5a19943aeff" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" +checksum = "7058dc8eaf3f2810d7828680320acda0b25a288f6d288e19278e249bbf74226b" dependencies = [ "cc", ] @@ -2247,18 +2270,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.137" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -2267,11 +2290,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ - "itoa 1.0.2", + "itoa 1.0.1", "ryu", "serde", ] @@ -2290,9 +2313,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.9" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ "block-buffer 0.9.0", "cfg-if", @@ -2355,20 +2378,20 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "smallvec" -version = "1.8.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "hash-db", "log", @@ -2385,7 +2408,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "blake2", "proc-macro-crate", @@ -2397,7 +2420,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "parity-scale-codec", "scale-info", @@ -2410,7 +2433,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "integer-sqrt", "num-traits", @@ -2425,7 +2448,7 @@ dependencies = [ [[package]] name = "sp-core" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "base58", "bitflags", @@ -2471,7 +2494,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "blake2", "byteorder", @@ -2485,7 +2508,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "proc-macro2", "quote", @@ -2496,7 +2519,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "proc-macro2", "quote", @@ -2506,7 +2529,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "environmental", "parity-scale-codec", @@ -2517,7 +2540,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "finality-grandpa", "log", @@ -2535,7 +2558,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -2549,8 +2572,9 @@ dependencies = [ [[package]] name = "sp-io" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ + "bytes", "futures", "hash-db", "libsecp256k1", @@ -2574,7 +2598,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "async-trait", "futures", @@ -2590,7 +2614,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "backtrace", "lazy_static", @@ -2600,7 +2624,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "either", "hash256-std-hasher", @@ -2622,8 +2646,9 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ + "bytes", "impl-trait-for-tuples", "parity-scale-codec", "primitive-types", @@ -2639,7 +2664,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "Inflector", "proc-macro-crate", @@ -2651,7 +2676,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "parity-scale-codec", "scale-info", @@ -2662,7 +2687,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "hash-db", "log", @@ -2684,12 +2709,12 @@ dependencies = [ [[package]] name = "sp-std" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" [[package]] name = "sp-storage" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "impl-serde", "parity-scale-codec", @@ -2702,7 +2727,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "async-trait", "futures-timer", @@ -2718,7 +2743,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "parity-scale-codec", "sp-std", @@ -2730,7 +2755,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "hash-db", "memory-db", @@ -2746,7 +2771,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "impl-serde", "parity-scale-codec", @@ -2763,7 +2788,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -2774,7 +2799,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#ee3eb8f2448cc1bb978c5d1564febd351c128bb0" +source = "git+https://github.com/paritytech/substrate?branch=master#d09f5d92677c460bbb8b9b3e084bd9f3c1ffaf4c" dependencies = [ "impl-trait-for-tuples", "log", @@ -2785,9 +2810,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.23.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ef98aedad3dc52e10995e7ed15f1279e11d4da35795f5dac7305742d0feb66" +checksum = "a039906277e0d8db996cd9d1ef19278c10209d994ecfc1025ced16342873a17c" dependencies = [ "Inflector", "num-format", @@ -2826,7 +2851,7 @@ dependencies = [ "hmac 0.11.0", "pbkdf2 0.8.0", "schnorrkel", - "sha2 0.9.9", + "sha2 0.9.8", "zeroize", ] @@ -2838,9 +2863,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -2867,18 +2892,18 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -2906,7 +2931,7 @@ dependencies = [ "pbkdf2 0.4.0", "rand 0.7.3", "rustc-hash", - "sha2 0.9.9", + "sha2 0.9.8", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -2924,9 +2949,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" dependencies = [ "tinyvec_macros", ] @@ -2939,18 +2964,18 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "toml" -version = "0.5.9" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ "serde", ] [[package]] name = "tracing" -version = "0.1.35" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" dependencies = [ "cfg-if", "pin-project-lite", @@ -2960,9 +2985,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.21" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" dependencies = [ "proc-macro2", "quote", @@ -2971,9 +2996,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" dependencies = [ "once_cell", "valuable", @@ -2992,9 +3017,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.3" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" dependencies = [ "serde", "tracing-core", @@ -3029,7 +3054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d32d034c0d3db64b43c31de38e945f15b40cd4ca6d2dcfc26d4798ce8de4ab83" dependencies = [ "hash-db", - "hashbrown 0.12.1", + "hashbrown 0.12.0", "log", "rustc-hex", "smallvec", @@ -3064,15 +3089,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.15.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" [[package]] name = "uint" -version = "0.9.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" +checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" dependencies = [ "byteorder", "crunchy", @@ -3082,24 +3107,24 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] name = "unicode-normalization" -version = "0.1.20" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" dependencies = [ "tinyvec", ] [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "valuable" @@ -3109,9 +3134,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "wasi" @@ -3121,15 +3146,15 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3137,9 +3162,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" dependencies = [ "bumpalo", "lazy_static", @@ -3152,9 +3177,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3162,9 +3187,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ "proc-macro2", "quote", @@ -3175,9 +3200,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" [[package]] name = "wasmi" @@ -3227,9 +3252,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", @@ -3240,33 +3265,33 @@ dependencies = [ [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" [[package]] name = "wyz" @@ -3279,9 +3304,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.5" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94693807d016b2f2d2e14420eb3bfcca689311ff775dcf113d74ea624b7cdf07" +checksum = "7eb5728b8afd3f280a869ce1d4c554ffaed35f45c231fc41bfbd0381bef50317" dependencies = [ "zeroize_derive", ] diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index 2e46a84ae..a683ed963 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -10,6 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } ed25519-dalek = { version = "1.0", default-features = false, optional = true } +log = { version = "0.4.14", default-features = false } hash-db = { version = "0.15.2", default-features = false } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } @@ -17,10 +18,13 @@ scale-info = { version = "2.0.1", default-features = false, features = ["derive" bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false } +bp-parachains = { path = "../../primitives/parachains", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } pallet-bridge-dispatch = { path = "../../modules/dispatch", default-features = false } pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../modules/messages", default-features = false } +pallet-bridge-parachains = { path = "../../modules/parachains", default-features = false } # Substrate dependencies @@ -41,6 +45,8 @@ default = ["std"] std = [ "bp-message-dispatch/std", "bp-messages/std", + "bp-parachains/std", + "bp-polkadot-core/std", "bp-runtime/std", "codec/std", "frame-support/std", @@ -49,6 +55,7 @@ std = [ "pallet-bridge-dispatch/std", "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", "pallet-transaction-payment/std", "scale-info/std", "sp-api/std", diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs index ae7efb4a4..88e1e6d9c 100644 --- a/bin/runtime-common/src/lib.rs +++ b/bin/runtime-common/src/lib.rs @@ -18,5 +18,174 @@ #![cfg_attr(not(feature = "std"), no_std)] +use bp_runtime::FilterCall; +use sp_runtime::transaction_validity::TransactionValidity; + pub mod messages; pub mod messages_benchmarking; +pub mod messages_extension; +pub mod parachains_benchmarking; + +/// A duplication of the `FilterCall` trait. +/// +/// We need this trait in order to be able to implement it for the messages pallet, +/// since the implementation is done outside of the pallet crate. +pub trait BridgeRuntimeFilterCall { + /// Checks if a runtime call is valid. + fn validate(call: &Call) -> TransactionValidity; +} + +impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet +where + pallet_bridge_grandpa::Pallet: FilterCall, +{ + fn validate(call: &Call) -> TransactionValidity { + as FilterCall>::validate(call) + } +} + +impl BridgeRuntimeFilterCall for pallet_bridge_parachains::Pallet +where + pallet_bridge_parachains::Pallet: FilterCall, +{ + fn validate(call: &Call) -> TransactionValidity { + as FilterCall>::validate(call) + } +} + +/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension. +/// +/// ## Example +/// +/// ```nocompile +/// generate_bridge_reject_obsolete_headers_and_messages!{ +/// Call, AccountId +/// BridgeRialtoGrandpa, BridgeWestendGrandpa, +/// BridgeRialtoParachains +/// } +/// ``` +/// +/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged +/// headers and messages. Without that extension, even honest relayers may lose their funds if +/// there are multiple relays running and submitting the same information. +#[macro_export] +macro_rules! generate_bridge_reject_obsolete_headers_and_messages { + ($call:ty, $account_id:ty, $($filter_call:ty),*) => { + #[derive(Clone, codec::Decode, codec::Encode, Eq, PartialEq, frame_support::RuntimeDebug, scale_info::TypeInfo)] + pub struct BridgeRejectObsoleteHeadersAndMessages; + impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { + const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; + type AccountId = $account_id; + type Call = $call; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> sp_std::result::Result< + (), + sp_runtime::transaction_validity::TransactionValidityError, + > { + Ok(()) + } + + fn validate( + &self, + _who: &Self::AccountId, + call: &Self::Call, + _info: &sp_runtime::traits::DispatchInfoOf, + _len: usize, + ) -> sp_runtime::transaction_validity::TransactionValidity { + let valid = sp_runtime::transaction_validity::ValidTransaction::default(); + $( + let valid = valid + .combine_with(<$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?); + )* + Ok(valid) + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + info: &sp_runtime::traits::DispatchInfoOf, + len: usize, + ) -> Result { + self.validate(who, call, info, len).map(drop) + } + } + }; +} + +#[cfg(test)] +mod tests { + use crate::BridgeRuntimeFilterCall; + use frame_support::{assert_err, assert_ok}; + use sp_runtime::{ + traits::SignedExtension, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, + }; + + pub struct MockCall { + data: u32, + } + + impl sp_runtime::traits::Dispatchable for MockCall { + type Config = (); + type Info = (); + type Origin = (); + type PostInfo = (); + + fn dispatch( + self, + _origin: Self::Origin, + ) -> sp_runtime::DispatchResultWithInfo { + unimplemented!() + } + } + + struct FirstFilterCall; + impl BridgeRuntimeFilterCall for FirstFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 1 { + return InvalidTransaction::Custom(1).into(); + } + + Ok(ValidTransaction { priority: 1, ..Default::default() }) + } + } + + struct SecondFilterCall; + impl BridgeRuntimeFilterCall for SecondFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 2 { + return InvalidTransaction::Custom(2).into(); + } + + Ok(ValidTransaction { priority: 2, ..Default::default() }) + } + } + + #[test] + fn test() { + generate_bridge_reject_obsolete_headers_and_messages!( + MockCall, + (), + FirstFilterCall, + SecondFilterCall + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0), + InvalidTransaction::Custom(1) + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0), + InvalidTransaction::Custom(2) + ); + + assert_ok!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0), + ValidTransaction { priority: 3, ..Default::default() } + ) + } +} diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs index 6be8fc6e8..d78e10278 100644 --- a/bin/runtime-common/src/messages.rs +++ b/bin/runtime-common/src/messages.rs @@ -26,20 +26,24 @@ use bp_messages::{ target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, }; +use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaId}; use bp_runtime::{ messages::{DispatchFeePayment, MessageDispatchResult}, ChainId, Size, StorageProofChecker, }; -use codec::{Decode, DecodeLimit, Encode}; +use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; use frame_support::{ - traits::{Currency, ExistenceRequirement}, + traits::{Currency, ExistenceRequirement, Get}, weights::{Weight, WeightToFee}, RuntimeDebug, }; use hash_db::Hasher; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, Saturating, Zero}, + traits::{ + AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, Header as HeaderT, Saturating, + Zero, + }, FixedPointNumber, FixedPointOperand, FixedU128, }; use sp_std::{ @@ -79,7 +83,7 @@ pub trait ChainWithMessages { /// Hash used in the chain. type Hash: Decode; /// Accound id on the chain. - type AccountId: Encode + Decode; + type AccountId: Encode + Decode + MaxEncodedLen; /// Public key of the chain account that may be used to verify signatures. type Signer: Encode + Decode; /// Signature type used on the chain. @@ -109,12 +113,54 @@ pub struct MessageTransaction { pub size: u32, } +/// Helper trait for estimating the size and weight of a single message delivery confirmation +/// transaction. +pub trait ConfirmationTransactionEstimation { + // Estimate size and weight of single message delivery confirmation transaction. + fn estimate_delivery_confirmation_transaction() -> MessageTransaction; +} + +/// Default implementation for `ConfirmationTransactionEstimation`. +pub struct BasicConfirmationTransactionEstimation< + AccountId: MaxEncodedLen, + const MAX_CONFIRMATION_TX_WEIGHT: Weight, + const EXTRA_STORAGE_PROOF_SIZE: u32, + const TX_EXTRA_BYTES: u32, +>(PhantomData); + +impl< + AccountId: MaxEncodedLen, + const MAX_CONFIRMATION_TX_WEIGHT: Weight, + const EXTRA_STORAGE_PROOF_SIZE: u32, + const TX_EXTRA_BYTES: u32, + > ConfirmationTransactionEstimation + for BasicConfirmationTransactionEstimation< + AccountId, + MAX_CONFIRMATION_TX_WEIGHT, + EXTRA_STORAGE_PROOF_SIZE, + TX_EXTRA_BYTES, + > +{ + fn estimate_delivery_confirmation_transaction() -> MessageTransaction { + let inbound_data_size = InboundLaneData::::encoded_size_hint_u32(1, 1); + MessageTransaction { + dispatch_weight: MAX_CONFIRMATION_TX_WEIGHT, + size: inbound_data_size + .saturating_add(EXTRA_STORAGE_PROOF_SIZE) + .saturating_add(TX_EXTRA_BYTES), + } + } +} + /// This chain that has `pallet-bridge-messages` and `dispatch` modules. pub trait ThisChainWithMessages: ChainWithMessages { /// Call origin on the chain. type Origin; /// Call type on the chain. type Call: Encode + Decode; + /// Helper for estimating the size and weight of a single message delivery confirmation + /// transaction at this chain. + type ConfirmationTransactionEstimation: ConfirmationTransactionEstimation>; /// Do we accept message sent by given origin to given lane? fn is_message_accepted(origin: &Self::Origin, lane: &LaneId) -> bool; @@ -125,7 +171,9 @@ pub trait ThisChainWithMessages: ChainWithMessages { fn maximal_pending_messages_at_outbound_lane() -> MessageNonce; /// Estimate size and weight of single message delivery confirmation transaction at This chain. - fn estimate_delivery_confirmation_transaction() -> MessageTransaction>; + fn estimate_delivery_confirmation_transaction() -> MessageTransaction> { + Self::ConfirmationTransactionEstimation::estimate_delivery_confirmation_transaction() + } /// Returns minimal transaction fee that must be paid for given transaction at This chain. fn transaction_payment(transaction: MessageTransaction>) -> BalanceOf; @@ -225,6 +273,15 @@ pub mod source { BridgedChainOpaqueCall, >; + /// Maximal size of outbound message payload. + pub struct FromThisChainMaximalOutboundPayloadSize(PhantomData); + + impl Get for FromThisChainMaximalOutboundPayloadSize { + fn get() -> u32 { + maximal_message_size::() + } + } + /// Messages delivery proof from bridged chain: /// /// - hash of finalized header; @@ -241,7 +298,7 @@ pub mod source { } impl Size for FromBridgedChainMessagesDeliveryProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { u32::try_from( self.storage_proof.iter().fold(0usize, |sum, node| sum.saturating_add(node.len())), ) @@ -437,6 +494,9 @@ pub mod source { } /// Verify proof of This -> Bridged chain messages delivery. + /// + /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged + /// parachains, please use the `verify_messages_delivery_proof_from_parachain`. pub fn verify_messages_delivery_proof( proof: FromBridgedChainMessagesDeliveryProof>>, ) -> Result, &'static str> @@ -453,23 +513,70 @@ pub mod source { pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( bridged_header_hash.into(), StorageProof::new(storage_proof), - |storage| { - // Messages delivery proof is just proof of single storage key read => any error - // is fatal. - let storage_inbound_lane_data_key = - bp_messages::storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane); - let raw_inbound_lane_data = storage - .read_value(storage_inbound_lane_data_key.0.as_ref()) - .map_err(|_| "Failed to read inbound lane state from storage proof")? - .ok_or("Inbound lane state is missing from the messages proof")?; - let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..]) - .map_err(|_| "Failed to decode inbound lane state from the proof")?; - - Ok((lane, inbound_lane_data)) - }, + |storage| do_verify_messages_delivery_proof::< + B, + bp_runtime::HasherOf< + >::BridgedChain, + >, + >(lane, storage), ) .map_err(<&'static str>::from)? } + + /// Verify proof of This -> Bridged chain messages delivery. + /// + /// This function is used when Bridged chain is using parachain finality. For Bridged + /// chains with direct GRANDPA finality, please use the `verify_messages_delivery_proof`. + /// + /// This function currently only supports parachains, which are using header type that + /// implements `sp_runtime::traits::Header` trait. + pub fn verify_messages_delivery_proof_from_parachain< + B, + BridgedHeader, + ThisRuntime, + ParachainsInstance: 'static, + >( + bridged_parachain: ParaId, + proof: FromBridgedChainMessagesDeliveryProof>>, + ) -> Result, &'static str> + where + B: MessageBridge, + B::BridgedChain: ChainWithMessages, + BridgedHeader: HeaderT>>, + ThisRuntime: pallet_bridge_parachains::Config, + { + let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = + proof; + pallet_bridge_parachains::Pallet::::parse_finalized_storage_proof( + bridged_parachain, + bridged_header_hash, + StorageProof::new(storage_proof), + |para_head| BridgedHeader::decode(&mut ¶_head.0[..]).ok().map(|h| *h.state_root()), + |storage| do_verify_messages_delivery_proof::(lane, storage), + ) + .map_err(<&'static str>::from)? + } + + /// The essense of This -> Bridged chain messages delivery proof verification. + fn do_verify_messages_delivery_proof( + lane: LaneId, + storage: bp_runtime::StorageProofChecker, + ) -> Result, &'static str> { + // Messages delivery proof is just proof of single storage key read => any error + // is fatal. + let storage_inbound_lane_data_key = bp_messages::storage_keys::inbound_lane_data_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &lane, + ); + let raw_inbound_lane_data = storage + .read_value(storage_inbound_lane_data_key.0.as_ref()) + .map_err(|_| "Failed to read inbound lane state from storage proof")? + .ok_or("Inbound lane state is missing from the messages proof")?; + let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..]) + .map_err(|_| "Failed to decode inbound lane state from the proof")?; + + Ok((lane, inbound_lane_data)) + } } /// Sub-module that is declaring types required for processing Bridged -> This chain messages. @@ -503,6 +610,7 @@ pub mod target { pub bridged_header_hash: BridgedHeaderHash, /// A storage trie proof of messages being delivered. pub storage_proof: RawStorageProof, + /// Messages in this proof are sent over this lane. pub lane: LaneId, /// Nonce of the first message being delivered. pub nonces_start: MessageNonce, @@ -511,7 +619,7 @@ pub mod target { } impl Size for FromBridgedChainMessagesProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { u32::try_from( self.storage_proof.iter().fold(0usize, |sum, node| sum.saturating_add(node.len())), ) @@ -568,7 +676,7 @@ pub mod target { type DispatchPayload = FromBridgedChainMessagePayload; fn dispatch_weight( - message: &DispatchMessage>>, + message: &mut DispatchMessage>>, ) -> frame_support::weights::Weight { message.data.payload.as_ref().map(|payload| payload.weight).unwrap_or(0) } @@ -641,6 +749,9 @@ pub mod target { /// Verify proof of Bridged -> This chain messages. /// + /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged + /// parachains, please use the `verify_messages_proof_from_parachain`. + /// /// The `messages_count` argument verification (sane limits) is supposed to be made /// outside of this function. This function only verifies that the proof declares exactly /// `messages_count` messages. @@ -675,7 +786,55 @@ pub mod target { .map_err(Into::into) } - #[derive(Debug, PartialEq)] + /// Verify proof of Bridged -> This chain messages. + /// + /// This function is used when Bridged chain is using parachain finality. For Bridged + /// chains with direct GRANDPA finality, please use the `verify_messages_proof`. + /// + /// The `messages_count` argument verification (sane limits) is supposed to be made + /// outside of this function. This function only verifies that the proof declares exactly + /// `messages_count` messages. + /// + /// This function currently only supports parachains, which are using header type that + /// implements `sp_runtime::traits::Header` trait. + pub fn verify_messages_proof_from_parachain< + B, + BridgedHeader, + ThisRuntime, + ParachainsInstance: 'static, + >( + bridged_parachain: ParaId, + proof: FromBridgedChainMessagesProof>>, + messages_count: u32, + ) -> Result>>>, &'static str> + where + B: MessageBridge, + B::BridgedChain: ChainWithMessages, + BridgedHeader: HeaderT>>, + ThisRuntime: pallet_bridge_parachains::Config, + { + verify_messages_proof_with_parser::( + proof, + messages_count, + |bridged_header_hash, bridged_storage_proof| { + pallet_bridge_parachains::Pallet::::parse_finalized_storage_proof( + bridged_parachain, + bridged_header_hash, + StorageProof::new(bridged_storage_proof), + |para_head| BridgedHeader::decode(&mut ¶_head.0[..]).ok().map(|h| *h.state_root()), + |storage_adapter| storage_adapter, + ) + .map(|storage| StorageProofCheckerAdapter::<_, B> { + storage, + _dummy: Default::default(), + }) + .map_err(|err| MessageProofError::Custom(err.into())) + }, + ) + .map_err(Into::into) + } + + #[derive(Debug, PartialEq, Eq)] pub(crate) enum MessageProofError { Empty, MessagesCountMismatch, @@ -872,13 +1031,13 @@ mod tests { } } - #[derive(Debug, PartialEq, Decode, Encode, Clone)] + #[derive(Debug, PartialEq, Eq, Decode, Encode, Clone, MaxEncodedLen)] struct ThisChainAccountId(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Decode, Encode)] struct ThisChainSigner(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Decode, Encode)] struct ThisChainSignature(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Decode, Encode)] enum ThisChainCall { #[codec(index = 42)] Transfer, @@ -898,13 +1057,13 @@ mod tests { } } - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Decode, Encode, MaxEncodedLen)] struct BridgedChainAccountId(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Decode, Encode)] struct BridgedChainSigner(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Decode, Encode)] struct BridgedChainSignature(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Decode, Encode)] enum BridgedChainCall {} #[derive(Clone, Debug)] struct BridgedChainOrigin; @@ -921,7 +1080,7 @@ mod tests { macro_rules! impl_wrapped_balance { ($name:ident) => { - #[derive(Debug, PartialEq, Decode, Encode, Clone, Copy)] + #[derive(Debug, PartialEq, Eq, Decode, Encode, Clone, Copy)] struct $name(u32); impl From for $name { @@ -996,6 +1155,12 @@ mod tests { impl ThisChainWithMessages for ThisChain { type Call = ThisChainCall; + type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< + ::AccountId, + { DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT }, + 0, + 0, + >; type Origin = ThisChainOrigin; fn is_message_accepted(_send_origin: &Self::Origin, lane: &LaneId) -> bool { @@ -1006,13 +1171,6 @@ mod tests { MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE } - fn estimate_delivery_confirmation_transaction() -> MessageTransaction> { - MessageTransaction { - dispatch_weight: DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT, - size: 0, - } - } - fn transaction_payment(transaction: MessageTransaction>) -> BalanceOf { ThisChainBalance( transaction.dispatch_weight as u32 * THIS_CHAIN_WEIGHT_TO_BALANCE_RATE as u32, @@ -1057,6 +1215,12 @@ mod tests { impl ThisChainWithMessages for BridgedChain { type Call = BridgedChainCall; + type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< + ::AccountId, + 0, + 0, + 0, + >; type Origin = BridgedChainOrigin; fn is_message_accepted(_send_origin: &Self::Origin, _lane: &LaneId) -> bool { @@ -1067,10 +1231,6 @@ mod tests { unreachable!() } - fn estimate_delivery_confirmation_transaction() -> MessageTransaction> { - unreachable!() - } - fn transaction_payment( _transaction: MessageTransaction>, ) -> BalanceOf { diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/bin/runtime-common/src/messages_benchmarking.rs index 8e3e6e3fe..ae07ca2fa 100644 --- a/bin/runtime-common/src/messages_benchmarking.rs +++ b/bin/runtime-common/src/messages_benchmarking.rs @@ -27,7 +27,7 @@ use crate::messages::{ }; use bp_messages::{storage_keys, MessageData, MessageKey, MessagePayload}; -use bp_runtime::{messages::DispatchFeePayment, ChainId}; +use bp_runtime::StorageProofSize; use codec::Encode; use ed25519_dalek::{PublicKey, SecretKey, Signer, KEYPAIR_LENGTH, SECRET_KEY_LENGTH}; use frame_support::{ @@ -35,7 +35,7 @@ use frame_support::{ weights::{GetDispatchInfo, Weight}, }; use pallet_bridge_messages::benchmarking::{ - MessageDeliveryProofParams, MessageParams, MessageProofParams, ProofSize, + MessageDeliveryProofParams, MessageParams, MessageProofParams, }; use sp_core::Hasher; use sp_runtime::traits::{Header, IdentifyAccount, MaybeSerializeDeserialize, Zero}; @@ -99,7 +99,7 @@ where R: frame_system::Config>> + pallet_balances::Config>> + pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain
, + R::BridgedChain: bp_runtime::Chain>, Header = BH>, B: MessageBridge, BI: 'static, FI: 'static, @@ -115,9 +115,8 @@ where + From + IdentifyAccount>>, { - // we'll be dispatching the same call at This chain - let remark = match params.size { - ProofSize::Minimal(ref size) => vec![0u8; *size as _], + let message_payload = match params.size { + StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], _ => vec![], }; let call: CallOf> = frame_system::Call::remark { remark }.into(); @@ -164,7 +163,7 @@ where // finally - prepare storage proof and update environment let (state_root, storage_proof) = prepare_messages_storage_proof::(¶ms, message_payload); - let bridged_header_hash = insert_bridged_chain_header::(state_root); + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); ( FromBridgedChainMessagesProof { @@ -188,7 +187,7 @@ pub fn prepare_message_delivery_proof( ) -> FromBridgedChainMessagesDeliveryProof>> where R: pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain
, + R::BridgedChain: bp_runtime::Chain>, Header = BH>, FI: 'static, B: MessageBridge, BH: Header>>, @@ -216,7 +215,7 @@ where let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); // finally insert header with given state root to our storage - let bridged_header_hash = insert_bridged_chain_header::(root); + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(root); FromBridgedChainMessagesDeliveryProof { bridged_header_hash: bridged_header_hash.into(), @@ -288,28 +287,26 @@ where (root, storage_proof) } -/// Insert Bridged chain header with given state root into storage of GRANDPA pallet at This chain. -fn insert_bridged_chain_header( - state_root: HashOf>, -) -> HashOf> +/// Insert header to the bridge GRANDPA pallet. +pub(crate) fn insert_header_to_grandpa_pallet( + state_root: bp_runtime::HashOf, +) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) where - R: pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain
, - FI: 'static, - B: MessageBridge, - BH: Header>>, - HashOf>: Default, + R: pallet_bridge_grandpa::Config, + GI: 'static, + R::BridgedChain: bp_runtime::Chain, { - let bridged_header = BH::new( - Zero::zero(), + let bridged_block_number = Zero::zero(); + let bridged_header = bp_runtime::HeaderOf::::new( + bridged_block_number, Default::default(), state_root, Default::default(), Default::default(), ); let bridged_header_hash = bridged_header.hash(); - pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); - bridged_header_hash + pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); + (bridged_block_number, bridged_header_hash) } /// Generate ed25519 signature to be used in @@ -347,11 +344,15 @@ fn ed25519_sign( } /// Populate trie with dummy keys+values until trie has at least given size. -fn grow_trie(mut root: H::Out, mdb: &mut MemoryDB, trie_size: ProofSize) -> H::Out { +pub fn grow_trie( + mut root: H::Out, + mdb: &mut MemoryDB, + trie_size: StorageProofSize, +) -> H::Out { let (iterations, leaf_size, minimal_trie_size) = match trie_size { - ProofSize::Minimal(_) => return root, - ProofSize::HasLargeLeaf(size) => (1, size, size), - ProofSize::HasExtraNodes(size) => (8, 1, size), + StorageProofSize::Minimal(_) => return root, + StorageProofSize::HasLargeLeaf(size) => (1, size, size), + StorageProofSize::HasExtraNodes(size) => (8, 1, size), }; let mut key_index = 0; diff --git a/bin/runtime-common/src/messages_extension.rs b/bin/runtime-common/src/messages_extension.rs new file mode 100644 index 000000000..c4a97087f --- /dev/null +++ b/bin/runtime-common/src/messages_extension.rs @@ -0,0 +1,96 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + }, + BridgeRuntimeFilterCall, +}; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use pallet_bridge_messages::{Config, Pallet}; +use sp_runtime::transaction_validity::TransactionValidity; + +/// Validate messages in order to avoid "mining" messages delivery and delivery confirmation +/// transactions, that are delivering outdated messages/confirmations. Without this validation, +/// even honest relayers may lose their funds if there are multiple relays running and submitting +/// the same messages/confirmations. +impl< + BridgedHeaderHash, + SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< + >::InboundMessageFee, + MessagesProof = FromBridgedChainMessagesProof, + >, + TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< + >::OutboundPayload, + ::AccountId, + MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof, + >, + Call: IsSubType, T>>, + T: frame_system::Config + + Config, + I: 'static, + > BridgeRuntimeFilterCall for Pallet +{ + fn validate(call: &Call) -> TransactionValidity { + match call.is_sub_type() { + Some(pallet_bridge_messages::Call::::receive_messages_proof { + ref proof, + .. + }) => { + let inbound_lane_data = + pallet_bridge_messages::InboundLanes::::get(&proof.lane); + if proof.nonces_end <= inbound_lane_data.last_delivered_nonce() { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages delivery transaction: \ + lane {:?}, bundled {:?}, best {:?}", + proof.lane, + proof.nonces_end, + inbound_lane_data.last_delivered_nonce(), + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into(); + } + }, + Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { + ref proof, + ref relayers_state, + .. + }) => { + let latest_delivered_nonce = relayers_state.last_delivered_nonce; + + let outbound_lane_data = + pallet_bridge_messages::OutboundLanes::::get(&proof.lane); + if latest_delivered_nonce <= outbound_lane_data.latest_received_nonce { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages confirmation transaction: \ + lane {:?}, bundled {:?}, best {:?}", + proof.lane, + latest_delivered_nonce, + outbound_lane_data.latest_received_nonce, + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into(); + } + }, + _ => {}, + } + + Ok(sp_runtime::transaction_validity::ValidTransaction::default()) + } +} diff --git a/bin/runtime-common/src/parachains_benchmarking.rs b/bin/runtime-common/src/parachains_benchmarking.rs new file mode 100644 index 000000000..97a1cd3ee --- /dev/null +++ b/bin/runtime-common/src/parachains_benchmarking.rs @@ -0,0 +1,82 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to run benchmarks of parachains finality module. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::messages_benchmarking::{grow_trie, insert_header_to_grandpa_pallet}; + +use bp_parachains::parachain_head_storage_key_at_source; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::StorageProofSize; +use codec::Encode; +use frame_support::traits::Get; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use sp_std::prelude::*; +use sp_trie::{record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut}; + +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +pub fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + size: StorageProofSize, +) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) +where + R: pallet_bridge_parachains::Config + + pallet_bridge_grandpa::Config, + PI: 'static, + >::BridgedChain: + bp_runtime::Chain, +{ + let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]); + + // insert all heads to the trie + let mut parachain_heads = Vec::with_capacity(parachains.len()); + let mut storage_keys = Vec::with_capacity(parachains.len()); + let mut state_root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = TrieDBMutV1::::new(&mut mdb, &mut state_root); + + // insert parachain heads + for parachain in parachains { + let storage_key = + parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain); + trie.insert(&storage_key.0, ¶chain_head.encode()) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + parachain_heads.push((*parachain, parachain_head.hash())) + } + } + state_root = grow_trie(state_root, &mut mdb, size); + + // generate heads storage proof + let mut proof_recorder = Recorder::::new(); + record_all_keys::, _>(&mdb, &state_root, &mut proof_recorder) + .map_err(|_| "record_all_keys has failed") + .expect("record_all_keys should not fail in benchmarks"); + let proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + + let (relay_block_number, relay_block_hash) = + insert_header_to_grandpa_pallet::(state_root); + + (relay_block_number, relay_block_hash, ParaHeadsProof(proof), parachain_heads) +} diff --git a/modules/fee-market/src/tests.rs b/modules/fee-market/src/tests.rs index 4b5fd1509..ac79a5040 100644 --- a/modules/fee-market/src/tests.rs +++ b/modules/fee-market/src/tests.rs @@ -154,7 +154,7 @@ pub struct TestPayload { pub extra: Vec, } impl Size for TestPayload { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 16 + self.extra.len() as u32 } } @@ -169,7 +169,7 @@ pub struct TestMessagesProof { pub result: Result, } impl Size for TestMessagesProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 0 } } @@ -178,7 +178,7 @@ impl Size for TestMessagesProof { #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo)] pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData), ()>); impl Size for TestMessagesDeliveryProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 0 } } @@ -368,7 +368,7 @@ pub struct TestMessageDispatch; impl MessageDispatch for TestMessageDispatch { type DispatchPayload = TestPayload; - fn dispatch_weight(message: &DispatchMessage) -> Weight { + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { match message.data.payload.as_ref() { Ok(payload) => payload.declared_weight, Err(_) => 0, @@ -410,6 +410,9 @@ frame_support::parameter_types! { pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; } +/// Maximal outbound payload size. +pub const MAX_OUTBOUND_PAYLOAD_SIZE: u32 = 4096; + impl pallet_bridge_messages::Config for Test { type AccountIdConverter = AccountIdConverter; type BridgedChainId = TestBridgedChainId; @@ -421,6 +424,7 @@ impl pallet_bridge_messages::Config for Test { type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaximalOutboundPayloadSize = frame_support::traits::ConstU32; type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment; type MessageDispatch = TestMessageDispatch; type OnDeliveryConfirmed = FeeMarketMessageConfirmedHandler; @@ -686,6 +690,7 @@ fn test_call_relayer_cancel_registration_works() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -766,6 +771,7 @@ fn receive_messages_delivery_proof() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -814,7 +820,7 @@ fn test_callback_no_order_created_when_fee_market_not_ready() { Messages::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 200), DispatchError::Module(ModuleError { index: 4, - error: [2, 0, 0, 0], + error: [3, 0, 0, 0], message: Some("MessageRejectedByLaneVerifier") }) ); @@ -867,6 +873,7 @@ fn test_payment_cal_rewards_normally_single_message() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -932,6 +939,7 @@ fn test_payment_cal_rewards_normally_multi_message() { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, )); @@ -983,6 +991,7 @@ fn test_payment_cal_rewards_when_order_confirmed_in_second_slot() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1037,6 +1046,7 @@ fn test_payment_cal_rewards_when_order_confirmed_in_third_slot() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1087,6 +1097,7 @@ fn test_payment_cal_reward_with_duplicated_delivery_proof() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1103,6 +1114,7 @@ fn test_payment_cal_reward_with_duplicated_delivery_proof() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1150,6 +1162,7 @@ fn test_payment_with_slash_and_reduce_order_capacity() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1191,6 +1204,7 @@ fn test_payment_slash_with_protect() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1232,6 +1246,7 @@ fn test_payment_slash_event() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1299,6 +1314,7 @@ fn test_payment_with_multiple_message_out_of_deadline() { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, )); @@ -1341,6 +1357,7 @@ fn test_clean_order_state_at_the_end_of_block() { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 4, + last_delivered_nonce: 4, ..Default::default() }, )); @@ -1371,7 +1388,7 @@ fn test_fee_verification_when_send_message() { Messages::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 200), DispatchError::Module(ModuleError { index: 4, - error: [2, 0, 0, 0], + error: [3, 0, 0, 0], message: Some("MessageRejectedByLaneVerifier") }) ); @@ -1382,7 +1399,7 @@ fn test_fee_verification_when_send_message() { Messages::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 49), DispatchError::Module(ModuleError { index: 4, - error: [2, 0, 0, 0], + error: [3, 0, 0, 0], message: Some("MessageRejectedByLaneVerifier") }) ); @@ -1453,6 +1470,7 @@ fn test_relayer_update_order_capacity() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 3, + last_delivered_nonce: 3, ..Default::default() }, )); diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs index 46e1e41a8..f93798536 100644 --- a/modules/grandpa/src/benchmarking.rs +++ b/modules/grandpa/src/benchmarking.rs @@ -41,6 +41,7 @@ use crate::*; +use bp_runtime::BasicOperatingMode; use bp_test_utils::{ accounts, make_justification_for_header, JustificationGeneratorParams, TEST_GRANDPA_ROUND, TEST_GRANDPA_SET_ID, @@ -62,6 +63,16 @@ const MAX_VOTE_ANCESTRIES: u32 = 1000; // number of validators. const MAX_VALIDATOR_SET_SIZE: u32 = 1024; +// `1..MAX_VALIDATOR_SET_SIZE` and `1..MAX_VOTE_ANCESTRIES` are too large && benchmarks are +// running for almost 40m (steps=50, repeat=20) on a decent laptop, which is too much. Since +// we're building linear function here, let's just select some limited subrange for benchmarking. +const VALIDATOR_SET_SIZE_RANGE_BEGIN: u32 = MAX_VALIDATOR_SET_SIZE / 20; +const VALIDATOR_SET_SIZE_RANGE_END: u32 = + VALIDATOR_SET_SIZE_RANGE_BEGIN + VALIDATOR_SET_SIZE_RANGE_BEGIN; +const MAX_VOTE_ANCESTRIES_RANGE_BEGIN: u32 = MAX_VOTE_ANCESTRIES / 20; +const MAX_VOTE_ANCESTRIES_RANGE_END: u32 = + MAX_VOTE_ANCESTRIES_RANGE_BEGIN + MAX_VOTE_ANCESTRIES_RANGE_BEGIN; + /// Returns number of first header to be imported. /// /// Since we bootstrap the pallet with `HeadersToKeep` already imported headers, @@ -84,7 +95,7 @@ fn prepare_benchmark_data, I: 'static>( header: Box::new(bp_test_utils::test_header(Zero::zero())), authority_list, set_id: TEST_GRANDPA_SET_ID, - is_halted: false, + operating_mode: BasicOperatingMode::Normal, }; bootstrap_bridge::(init_data); @@ -106,8 +117,8 @@ benchmarks_instance_pallet! { // This is the "gold standard" benchmark for this extrinsic, and it's what should be used to // annotate the weight in the pallet. submit_finality_proof { - let p in 1..MAX_VALIDATOR_SET_SIZE; - let v in 1..MAX_VOTE_ANCESTRIES; + let p in VALIDATOR_SET_SIZE_RANGE_BEGIN..VALIDATOR_SET_SIZE_RANGE_END; + let v in MAX_VOTE_ANCESTRIES_RANGE_BEGIN..MAX_VOTE_ANCESTRIES_RANGE_END; let caller: T::AccountId = whitelisted_caller(); let (header, justification) = prepare_benchmark_data::(p, v); }: submit_finality_proof(RawOrigin::Signed(caller), Box::new(header), justification) @@ -115,7 +126,7 @@ benchmarks_instance_pallet! { let header: BridgedHeader = bp_test_utils::test_header(header_number::()); let expected_hash = header.hash(); - assert_eq!(>::get(), expected_hash); + assert_eq!(>::get().unwrap().1, expected_hash); assert!(>::contains_key(expected_hash)); } } diff --git a/modules/grandpa/src/extension.rs b/modules/grandpa/src/extension.rs new file mode 100644 index 000000000..3f5e66292 --- /dev/null +++ b/modules/grandpa/src/extension.rs @@ -0,0 +1,115 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{Config, Pallet}; +use bp_runtime::FilterCall; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use sp_runtime::{ + traits::Header, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, +}; + +/// Validate Grandpa headers in order to avoid "mining" transactions that provide outdated +/// bridged chain headers. Without this validation, even honest relayers may lose their funds +/// if there are multiple relays running and submitting the same information. +impl< + Call: IsSubType, T>>, + T: frame_system::Config + Config, + I: 'static, + > FilterCall for Pallet +{ + fn validate(call: &::Call) -> TransactionValidity { + let bundled_block_number = match call.is_sub_type() { + Some(crate::Call::::submit_finality_proof { ref finality_target, .. }) => + *finality_target.number(), + _ => return Ok(ValidTransaction::default()), + }; + + let best_finalized = crate::BestFinalized::::get(); + let best_finalized_number = match best_finalized { + Some((best_finalized_number, _)) => best_finalized_number, + None => return InvalidTransaction::Call.into(), + }; + + if best_finalized_number >= bundled_block_number { + log::trace!( + target: crate::LOG_TARGET, + "Rejecting obsolete bridged header: bundled {:?}, best {:?}", + bundled_block_number, + best_finalized_number, + ); + + return InvalidTransaction::Stale.into(); + } + + Ok(ValidTransaction::default()) + } +} + +#[cfg(test)] +mod tests { + use super::FilterCall; + use crate::{ + mock::{run_test, test_header, Call, TestNumber, TestRuntime}, + BestFinalized, + }; + use bp_test_utils::make_default_justification; + + fn validate_block_submit(num: TestNumber) -> bool { + crate::Pallet::::validate(&Call::Grandpa( + crate::Call::::submit_finality_proof { + finality_target: Box::new(test_header(num)), + justification: make_default_justification(&test_header(num)), + }, + )) + .is_ok() + } + + fn sync_to_header_10() { + let header10_hash = sp_core::H256::default(); + BestFinalized::::put((10, header10_hash)); + } + + #[test] + fn extension_rejects_obsolete_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(5)); + }); + } + + #[test] + fn extension_rejects_same_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(10)); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_header_10(); + assert!(validate_block_submit(15)); + }); + } +} diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 9a10c8376..f9600243e 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -37,18 +37,19 @@ #![allow(clippy::large_enum_variant)] use bp_header_chain::{justification::GrandpaJustification, InitializationData}; -use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf}; +use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf, OwnedBridgeModule}; use finality_grandpa::voter_set::VoterSet; use frame_support::{ensure, fail}; -use frame_system::{ensure_signed, RawOrigin}; +use frame_system::ensure_signed; use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; -use sp_runtime::traits::{BadOrigin, Header as HeaderT, Zero}; +use sp_runtime::traits::{Header as HeaderT, Zero}; use sp_std::{boxed::Box, convert::TryInto}; +mod extension; #[cfg(test)] mod mock; -/// Pallet containing weights for this pallet. +/// Module, containing weights for this pallet. pub mod weights; #[cfg(feature = "runtime-benchmarks")] @@ -58,6 +59,9 @@ pub mod benchmarking; pub use pallet::*; pub use weights::WeightInfo; +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-grandpa"; + /// Block number of the bridged chain. pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; /// Block hash of the bridged chain. @@ -70,6 +74,7 @@ pub type BridgedHeader = HeaderOf<>::BridgedChain>; #[frame_support::pallet] pub mod pallet { use super::*; + use bp_runtime::BasicOperatingMode; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -92,6 +97,8 @@ pub mod pallet { /// The setting is there to prevent growing the on-chain state indefinitely. Note /// the setting does not relate to block numbers - we will simply keep as much items /// in the storage, so it doesn't guarantee any fixed timeframe for finality headers. + /// + /// Incautious change of this constant may lead to orphan entries in the runtime storage. #[pallet::constant] type HeadersToKeep: Get; @@ -114,6 +121,14 @@ pub mod pallet { } } + impl, I: 'static> OwnedBridgeModule for Pallet { + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + type OwnerStorage = PalletOwner; + + const LOG_TARGET: &'static str = LOG_TARGET; + } + #[pallet::call] impl, I: 'static> Pallet { /// Verify a target header is finalized according to the given finality proof. @@ -132,19 +147,26 @@ pub mod pallet { finality_target: Box>, justification: GrandpaJustification>, ) -> DispatchResultWithPostInfo { - ensure_operational::()?; + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; let _ = ensure_signed(origin)?; ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); let (hash, number) = (finality_target.hash(), finality_target.number()); - log::trace!(target: "runtime::bridge-grandpa", "Going to try and finalize header {:?}", finality_target); + log::trace!( + target: LOG_TARGET, + "Going to try and finalize header {:?}", + finality_target + ); - let best_finalized = match >::get(>::get()) { + let best_finalized = BestFinalized::::get(); + let best_finalized = + best_finalized.and_then(|(_, hash)| ImportedHeaders::::get(hash)); + let best_finalized = match best_finalized { Some(best_finalized) => best_finalized, None => { log::error!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Cannot finalize header {:?} because pallet is not yet initialized", finality_target, ); @@ -165,7 +187,11 @@ pub mod pallet { try_enact_authority_change::(&finality_target, set_id)?; >::mutate(|count| *count += 1); insert_header::(*finality_target, hash); - log::info!(target: "runtime::bridge-grandpa", "Successfully imported finalized header with hash {:?}!", hash); + log::info!( + target: LOG_TARGET, + "Successfully imported finalized header with hash {:?}!", + hash + ); // mandatory header is a header that changes authorities set. The pallet can't go // further without importing this header. So every bridge MUST import mandatory headers. @@ -192,14 +218,14 @@ pub mod pallet { origin: OriginFor, init_data: super::InitializationData>, ) -> DispatchResultWithPostInfo { - ensure_owner_or_root::(origin)?; + Self::ensure_owner_or_root(origin)?; let init_allowed = !>::exists(); ensure!(init_allowed, >::AlreadyInitialized); initialize_bridge::(init_data.clone()); log::info!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Pallet has been initialized with the following parameters: {:?}", init_data ); @@ -211,43 +237,19 @@ pub mod pallet { /// /// May only be called either by root, or by `PalletOwner`. #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_owner( - origin: OriginFor, - new_owner: Option, - ) -> DispatchResultWithPostInfo { - ensure_owner_or_root::(origin)?; - match new_owner { - Some(new_owner) => { - PalletOwner::::put(&new_owner); - log::info!(target: "runtime::bridge-grandpa", "Setting pallet Owner to: {:?}", new_owner); - }, - None => { - PalletOwner::::kill(); - log::info!(target: "runtime::bridge-grandpa", "Removed Owner of pallet."); - }, - } - - Ok(().into()) + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) } /// Halt or resume all pallet operations. /// /// May only be called either by root, or by `PalletOwner`. #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_operational( + pub fn set_operating_mode( origin: OriginFor, - operational: bool, - ) -> DispatchResultWithPostInfo { - ensure_owner_or_root::(origin)?; - >::put(!operational); - - if operational { - log::info!(target: "runtime::bridge-grandpa", "Resuming pallet operations."); - } else { - log::warn!(target: "runtime::bridge-grandpa", "Stopping pallet operations."); - } - - Ok(().into()) + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) } } @@ -270,7 +272,7 @@ pub mod pallet { /// Hash of the best finalized header. #[pallet::storage] pub type BestFinalized, I: 'static = ()> = - StorageValue<_, BridgedBlockHash, ValueQuery>; + StorageValue<_, (BridgedBlockNumber, BridgedBlockHash), OptionQuery>; /// A ring buffer of imported hashes. Ordered by the insertion time. #[pallet::storage] @@ -302,9 +304,12 @@ pub mod pallet { pub type PalletOwner, I: 'static = ()> = StorageValue<_, T::AccountId, OptionQuery>; - /// If true, all pallet transactions are failed immediately. + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. #[pallet::storage] - pub(super) type IsHalted, I: 'static = ()> = StorageValue<_, bool, ValueQuery>; + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig, I: 'static = ()> { @@ -333,7 +338,7 @@ pub mod pallet { } else { // Since the bridge hasn't been initialized we shouldn't allow anyone to perform // transactions. - >::put(true); + >::put(BasicOperatingMode::Halted); } } } @@ -358,10 +363,10 @@ pub mod pallet { NotInitialized, /// The pallet has already been initialized. AlreadyInitialized, - /// All pallet operations are halted. - Halted, /// The storage proof doesn't contains storage root. So it is invalid for given header. StorageRootMismatch, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), } /// Check the given header for a GRANDPA scheduled authority set change. If a change @@ -399,7 +404,7 @@ pub mod pallet { change_enacted = true; log::info!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Transitioned from authority set {} to {}! New authorities are: {:?}", current_set_id, current_set_id + 1, @@ -436,7 +441,7 @@ pub mod pallet { ) .map_err(|e| { log::error!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Received invalid justification for {:?}: {:?}", hash, e, @@ -455,14 +460,14 @@ pub mod pallet { ) { let index = >::get(); let pruning = >::try_get(index); - >::put(hash); + >::put((*header.number(), hash)); >::insert(hash, header); >::insert(index, hash); // Update ring buffer pointer and remove old header. >::put((index + 1) % T::HeadersToKeep::get()); if let Ok(hash) = pruning { - log::debug!(target: "runtime::bridge-grandpa", "Pruning old header: {:?}.", hash); + log::debug!(target: LOG_TARGET, "Pruning old header: {:?}.", hash); >::remove(hash); } } @@ -472,7 +477,8 @@ pub mod pallet { pub(crate) fn initialize_bridge, I: 'static>( init_params: super::InitializationData>, ) { - let super::InitializationData { header, authority_list, set_id, is_halted } = init_params; + let super::InitializationData { header, authority_list, set_id, operating_mode } = + init_params; let initial_hash = header.hash(); >::put(initial_hash); @@ -482,7 +488,7 @@ pub mod pallet { let authority_set = bp_header_chain::AuthoritySet::new(authority_list, set_id); >::put(authority_set); - >::put(is_halted); + >::put(operating_mode); } #[cfg(feature = "runtime-benchmarks")] @@ -507,26 +513,6 @@ pub mod pallet { insert_header::(header, hash); } } - - /// Ensure that the origin is either root, or `PalletOwner`. - fn ensure_owner_or_root, I: 'static>(origin: T::Origin) -> Result<(), BadOrigin> { - match origin.into() { - Ok(RawOrigin::Root) => Ok(()), - Ok(RawOrigin::Signed(ref signer)) - if Some(signer) == >::get().as_ref() => - Ok(()), - _ => Err(BadOrigin), - } - } - - /// Ensure that the pallet is in operational mode (not halted). - fn ensure_operational, I: 'static>() -> Result<(), Error> { - if >::get() { - Err(>::Halted) - } else { - Ok(()) - } - } } impl, I: 'static> Pallet { @@ -534,17 +520,9 @@ impl, I: 'static> Pallet { /// /// Returns a dummy header if there is no best header. This can only happen /// if the pallet has not been initialized yet. - pub fn best_finalized() -> BridgedHeader { - let hash = >::get(); - >::get(hash).unwrap_or_else(|| { - >::new( - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ) - }) + pub fn best_finalized() -> Option> { + let (_, hash) = >::get()?; + >::get(hash) } /// Check if a particular header is known to the bridge pallet. @@ -613,7 +591,7 @@ pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader authority_list: sp_std::vec::Vec::new(), /* we don't verify any proofs in external * benchmarks */ set_id: 0, - is_halted: false, + operating_mode: bp_runtime::BasicOperatingMode::Normal, }); } @@ -621,9 +599,10 @@ pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader mod tests { use super::*; use crate::mock::{run_test, test_header, Origin, TestHeader, TestNumber, TestRuntime}; + use bp_runtime::BasicOperatingMode; use bp_test_utils::{ - authority_list, make_default_justification, make_justification_for_header, - JustificationGeneratorParams, ALICE, BOB, + authority_list, generate_owned_bridge_module_tests, make_default_justification, + make_justification_for_header, JustificationGeneratorParams, ALICE, BOB, }; use codec::Encode; use frame_support::{ @@ -648,7 +627,7 @@ mod tests { header: Box::new(genesis), authority_list: authority_list(), set_id: 1, - is_halted: false, + operating_mode: BasicOperatingMode::Normal, }; Pallet::::initialize(origin, init_data.clone()).map(|_| init_data) @@ -711,21 +690,18 @@ mod tests { #[test] fn init_storage_entries_are_correctly_initialized() { run_test(|| { - assert_eq!( - BestFinalized::::get(), - BridgedBlockHash::::default() - ); - assert_eq!(Pallet::::best_finalized(), test_header(0)); + assert_eq!(BestFinalized::::get(), None,); + assert_eq!(Pallet::::best_finalized(), None); let init_data = init_with_origin(Origin::root()).unwrap(); assert!(>::contains_key(init_data.header.hash())); - assert_eq!(BestFinalized::::get(), init_data.header.hash()); + assert_eq!(BestFinalized::::get().unwrap().1, init_data.header.hash()); assert_eq!( CurrentAuthoritySet::::get().authorities, init_data.authority_list ); - assert!(!IsHalted::::get()); + assert_eq!(PalletOperatingMode::::get(), BasicOperatingMode::Normal); }) } @@ -740,73 +716,24 @@ mod tests { }) } - #[test] - fn pallet_owner_may_change_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_owner(Origin::root(), Some(1))); - assert_noop!( - Pallet::::set_operational(Origin::signed(2), false), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operational(Origin::root(), false)); - - assert_ok!(Pallet::::set_owner(Origin::signed(1), None)); - assert_noop!( - Pallet::::set_operational(Origin::signed(1), true), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operational(Origin::signed(2), true), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operational(Origin::root(), true)); - }); - } - - #[test] - fn pallet_may_be_halted_by_root() { - run_test(|| { - assert_ok!(Pallet::::set_operational(Origin::root(), false)); - assert_ok!(Pallet::::set_operational(Origin::root(), true)); - }); - } - - #[test] - fn pallet_may_be_halted_by_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_operational(Origin::signed(2), false)); - assert_ok!(Pallet::::set_operational(Origin::signed(2), true)); - - assert_noop!( - Pallet::::set_operational(Origin::signed(1), false), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operational(Origin::signed(1), true), - DispatchError::BadOrigin, - ); - - assert_ok!(Pallet::::set_operational(Origin::signed(2), false)); - assert_noop!( - Pallet::::set_operational(Origin::signed(1), true), - DispatchError::BadOrigin, - ); - }); - } - #[test] fn pallet_rejects_transactions_if_halted() { run_test(|| { initialize_substrate_bridge(); - assert_ok!(Pallet::::set_operational(Origin::root(), false)); - assert_noop!(submit_finality_proof(1), Error::::Halted); + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + BasicOperatingMode::Halted + )); + assert_noop!( + submit_finality_proof(1), + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted) + ); - assert_ok!(Pallet::::set_operational(Origin::root(), true)); + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + BasicOperatingMode::Normal + )); assert_ok!(submit_finality_proof(1)); }) } @@ -831,7 +758,7 @@ mod tests { ); let header = test_header(1); - assert_eq!(>::get(), header.hash()); + assert_eq!(>::get().unwrap().1, header.hash()); assert!(>::contains_key(header.hash())); }) } @@ -888,7 +815,7 @@ mod tests { header: Box::new(genesis), authority_list: invalid_authority_list, set_id: 1, - is_halted: false, + operating_mode: BasicOperatingMode::Normal, }; assert_ok!(Pallet::::initialize(Origin::root(), init_data)); @@ -948,7 +875,7 @@ mod tests { ); // Make sure that our header is the best finalized - assert_eq!(>::get(), header.hash()); + assert_eq!(>::get().unwrap().1, header.hash()); assert!(>::contains_key(header.hash())); // Make sure that the authority set actually changed upon importing our header @@ -1032,7 +959,7 @@ mod tests { header.set_state_root(state_root); let hash = header.hash(); - >::put(hash); + >::put((2, hash)); >::insert(hash, header); assert_ok!( @@ -1128,7 +1055,7 @@ mod tests { run_test(|| { initialize_substrate_bridge(); assert_ok!(submit_finality_proof(1)); - let first_header = Pallet::::best_finalized(); + let first_header = Pallet::::best_finalized().unwrap(); next_block(); assert_ok!(submit_finality_proof(2)); @@ -1152,13 +1079,15 @@ mod tests { #[test] fn storage_keys_computed_properly() { assert_eq!( - IsHalted::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::is_halted_key("Grandpa").0, + PalletOperatingMode::::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0, ); assert_eq!( BestFinalized::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::best_finalized_hash_key("Grandpa").0, + bp_header_chain::storage_keys::best_finalized_key("Grandpa").0, ); } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); } diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs index 3db37666e..1fa855594 100644 --- a/modules/grandpa/src/mock.rs +++ b/modules/grandpa/src/mock.rs @@ -42,7 +42,7 @@ construct_runtime! { UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Grandpa: grandpa::{Pallet}, + Grandpa: grandpa::{Pallet, Call}, } } diff --git a/modules/grandpa/src/weights.rs b/modules/grandpa/src/weights.rs index 2c4660160..d17fee6ce 100644 --- a/modules/grandpa/src/weights.rs +++ b/modules/grandpa/src/weights.rs @@ -17,14 +17,15 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-12-28, STEPS: 50, REPEAT: 20 +//! DATE: 2022-07-06, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled -//! CHAIN: Some("dev"), DB CACHE: 128 +//! CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/millau-bridge-node // benchmark +// pallet // --chain=dev // --steps=50 // --repeat=20 @@ -55,9 +56,9 @@ pub trait WeightInfo { pub struct MillauWeight(PhantomData); impl WeightInfo for MillauWeight { fn submit_finality_proof(p: u32, v: u32) -> Weight { - (115_651_000 as Weight) - .saturating_add((61_465_000 as Weight).saturating_mul(p as Weight)) - .saturating_add((3_438_000 as Weight).saturating_mul(v as Weight)) + (55_070_000 as Weight) + .saturating_add((39_678_000 as Weight).saturating_mul(p as Weight)) + .saturating_add((1_540_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } @@ -66,9 +67,9 @@ impl WeightInfo for MillauWeight { // For backwards compatibility and tests impl WeightInfo for () { fn submit_finality_proof(p: u32, v: u32) -> Weight { - (115_651_000 as Weight) - .saturating_add((61_465_000 as Weight).saturating_mul(p as Weight)) - .saturating_add((3_438_000 as Weight).saturating_mul(v as Weight)) + (55_070_000 as Weight) + .saturating_add((39_678_000 as Weight).saturating_mul(p as Weight)) + .saturating_add((1_540_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml index 804f323f1..56eacfe7d 100644 --- a/modules/messages/Cargo.toml +++ b/modules/messages/Cargo.toml @@ -32,6 +32,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [dev-dependencies] sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +bp-test-utils = { path = "../../primitives/test-utils" } [features] default = ["std"] diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs index 46a8150d0..f068de18f 100644 --- a/modules/messages/src/benchmarking.rs +++ b/modules/messages/src/benchmarking.rs @@ -19,14 +19,15 @@ use crate::{ inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, outbound_lane::ReceivalConfirmationResult, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, Call, + OutboundLanes, OutboundMessages, }; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, DeliveredMessages, - InboundLaneData, LaneId, MessageData, MessageNonce, OutboundLaneData, UnrewardedRelayer, - UnrewardedRelayersState, + InboundLaneData, LaneId, MessageData, MessageKey, MessageNonce, OutboundLaneData, + UnrewardedRelayer, UnrewardedRelayersState, }; -use bp_runtime::messages::DispatchFeePayment; +use bp_runtime::{messages::DispatchFeePayment, StorageProofSize}; use frame_benchmarking::{account, benchmarks_instance_pallet}; use frame_support::{traits::Get, weights::Weight}; use frame_system::RawOrigin; @@ -37,20 +38,6 @@ const SEED: u32 = 0; /// Pallet we're benchmarking here. pub struct Pallet, I: 'static>(crate::Pallet); -/// Proof size requirements. -#[derive(Clone, Copy, Debug)] -pub enum ProofSize { - /// The proof is expected to be minimal. If value size may be changed, then it is expected to - /// have given size. - Minimal(u32), - /// The proof is expected to have at least given size and grow by increasing number of trie - /// nodes included in the proof. - HasExtraNodes(u32), - /// The proof is expected to have at least given size and grow by increasing value that is - /// stored in the trie. - HasLargeLeaf(u32), -} - /// Benchmark-specific message parameters. #[derive(Debug)] pub struct MessageParams { @@ -70,7 +57,7 @@ pub struct MessageProofParams { /// If `Some`, the proof needs to include this outbound lane data. pub outbound_lane_data: Option, /// Proof size requirements. - pub size: ProofSize, + pub size: StorageProofSize, /// Where the fee for dispatching message is paid? pub dispatch_fee_payment: DispatchFeePayment, } @@ -83,7 +70,7 @@ pub struct MessageDeliveryProofParams { /// The proof needs to include this inbound lane data. pub inbound_lane_data: InboundLaneData, /// Proof size requirements. - pub size: ProofSize, + pub size: StorageProofSize, } /// Trait that must be implemented by runtime. @@ -258,7 +245,10 @@ benchmarks_instance_pallet! { send_regular_message_with_payload::(vec![42u8; T::maximal_message_size() as _]); }: increase_message_fee(RawOrigin::Signed(sender.clone()), lane_id, nonce, additional_fee) verify { - assert_eq!(T::account_balance(&sender), 0.into()); + assert_eq!( + OutboundMessages::::get(MessageKey { lane_id, nonce }).unwrap().fee, + T::message_fee() + additional_fee, + ); } // Benchmark `increase_message_fee` with following conditions: @@ -279,7 +269,10 @@ benchmarks_instance_pallet! { send_regular_message_with_payload::(vec![42u8; i as _]); }: increase_message_fee(RawOrigin::Signed(sender.clone()), lane_id, nonce, additional_fee) verify { - assert_eq!(T::account_balance(&sender), 0.into()); + assert_eq!( + OutboundMessages::::get(MessageKey { lane_id, nonce }).unwrap().fee, + T::message_fee() + additional_fee, + ); } // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: @@ -302,7 +295,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -337,7 +330,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=22, outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) @@ -376,7 +369,7 @@ benchmarks_instance_pallet! { latest_received_nonce: 20, latest_generated_nonce: 21, }), - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -408,7 +401,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::HasExtraNodes(1024), + size: StorageProofSize::HasExtraNodes(1024), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -443,7 +436,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::HasExtraNodes(16 * 1024), + size: StorageProofSize::HasExtraNodes(16 * 1024), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -477,7 +470,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtSourceChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -507,6 +500,7 @@ benchmarks_instance_pallet! { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 1, total_messages: 1, + last_delivered_nonce: 1, }; let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), @@ -517,14 +511,11 @@ benchmarks_instance_pallet! { }].into_iter().collect(), last_confirmed_nonce: 0, }, - size: ProofSize::Minimal(0), + size: StorageProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { - assert_eq!( - T::account_balance(&relayer_id), - relayer_balance + T::message_fee(), - ); + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); } // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: @@ -548,6 +539,7 @@ benchmarks_instance_pallet! { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 2, total_messages: 2, + last_delivered_nonce: 2, }; let mut delivered_messages = DeliveredMessages::new(1, true); delivered_messages.note_dispatched_message(true); @@ -560,11 +552,11 @@ benchmarks_instance_pallet! { }].into_iter().collect(), last_confirmed_nonce: 0, }, - size: ProofSize::Minimal(0), + size: StorageProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { - ensure_relayer_rewarded::(&relayer_id, &relayer_balance); + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); } // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: @@ -590,6 +582,7 @@ benchmarks_instance_pallet! { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, total_messages: 2, + last_delivered_nonce: 2, }; let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), @@ -606,12 +599,11 @@ benchmarks_instance_pallet! { ].into_iter().collect(), last_confirmed_nonce: 0, }, - size: ProofSize::Minimal(0), + size: StorageProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state) verify { - ensure_relayer_rewarded::(&relayer1_id, &relayer1_balance); - ensure_relayer_rewarded::(&relayer2_id, &relayer2_balance); + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); } } @@ -653,16 +645,3 @@ fn receive_messages, I: 'static>(nonce: MessageNonce) { last_confirmed_nonce: 0, }); } - -fn ensure_relayer_rewarded, I: 'static>( - relayer_id: &T::AccountId, - old_balance: &T::OutboundMessageFee, -) { - let new_balance = T::account_balance(relayer_id); - assert!( - new_balance > *old_balance, - "Relayer haven't received reward for relaying message: old balance = {:?}, new balance = {:?}", - old_balance, - new_balance, - ); -} diff --git a/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs index 924691251..dd6d21651 100644 --- a/modules/messages/src/inbound_lane.rs +++ b/modules/messages/src/inbound_lane.rs @@ -16,13 +16,17 @@ //! Everything about incoming messages receival. +use crate::Config; + use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData, UnrewardedRelayer, }; use bp_runtime::messages::MessageDispatchResult; -use frame_support::RuntimeDebug; +use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use frame_support::{traits::Get, RuntimeDebug}; +use scale_info::{Type, TypeInfo}; use sp_std::prelude::PartialEq; /// Inbound lane storage. @@ -44,6 +48,68 @@ pub trait InboundLaneStorage { fn set_data(&mut self, data: InboundLaneData); } +/// Inbound lane data wrapper that implements `MaxEncodedLen`. +/// +/// We have already had `MaxEncodedLen`-like functionality before, but its usage has +/// been localized and we haven't been passing bounds (maximal count of unrewarded relayer entries, +/// maximal count of unconfirmed messages) everywhere. This wrapper allows us to avoid passing +/// these generic bounds all over the code. +/// +/// The encoding of this type matches encoding of the corresponding `MessageData`. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)] +pub struct StoredInboundLaneData, I: 'static>(pub InboundLaneData); + +impl, I: 'static> sp_std::ops::Deref for StoredInboundLaneData { + type Target = InboundLaneData; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl, I: 'static> sp_std::ops::DerefMut for StoredInboundLaneData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl, I: 'static> Default for StoredInboundLaneData { + fn default() -> Self { + StoredInboundLaneData(Default::default()) + } +} + +impl, I: 'static> From> + for InboundLaneData +{ + fn from(data: StoredInboundLaneData) -> Self { + data.0 + } +} + +impl, I: 'static> EncodeLike> + for InboundLaneData +{ +} + +impl, I: 'static> TypeInfo for StoredInboundLaneData { + type Identity = Self; + + fn type_info() -> Type { + InboundLaneData::::type_info() + } +} + +impl, I: 'static> MaxEncodedLen for StoredInboundLaneData { + fn max_encoded_len() -> usize { + InboundLaneData::::encoded_size_hint( + T::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize, + T::MaxUnconfirmedMessagesAtInboundLane::get() as usize, + ) + .unwrap_or(usize::MAX) + } +} + /// Result of single message receival. #[derive(RuntimeDebug, PartialEq, Eq)] pub enum ReceivalResult { @@ -338,7 +404,7 @@ mod tests { run_test(|| { let mut lane = inbound_lane::(TEST_LANE_ID); let max_nonce = - ::MaxUnrewardedRelayerEntriesAtInboundLane::get(); + ::MaxUnrewardedRelayerEntriesAtInboundLane::get(); for current_nonce in 1..max_nonce + 1 { assert_eq!( lane.receive_message::( @@ -377,8 +443,7 @@ mod tests { fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() { run_test(|| { let mut lane = inbound_lane::(TEST_LANE_ID); - let max_nonce = - ::MaxUnconfirmedMessagesAtInboundLane::get(); + let max_nonce = ::MaxUnconfirmedMessagesAtInboundLane::get(); for current_nonce in 1..=max_nonce { assert_eq!( lane.receive_message::( diff --git a/modules/messages/src/instant_payments.rs b/modules/messages/src/instant_payments.rs deleted file mode 100644 index fca54dfd1..000000000 --- a/modules/messages/src/instant_payments.rs +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -//! Implementation of `MessageDeliveryAndDispatchPayment` trait on top of `Currency` trait. -//! -//! The payment is first transferred to a special `relayers-fund` account and only transferred -//! to the actual relayer in case confirmation is received. - -use crate::OutboundMessages; - -use bp_messages::{ - source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards, SenderOrigin}, - LaneId, MessageKey, MessageNonce, UnrewardedRelayer, -}; -use codec::Encode; -use frame_support::traits::{Currency as CurrencyT, ExistenceRequirement, Get}; -use num_traits::{SaturatingAdd, Zero}; -use sp_runtime::traits::Saturating; -use sp_std::{collections::vec_deque::VecDeque, fmt::Debug, ops::RangeInclusive}; - -/// Error that occurs when message fee is non-zero, but payer is not defined. -const NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE: &str = - "Non-zero message fee can't be paid by "; - -/// Instant message payments made in given currency. -/// -/// The balance is initially reserved in a special `relayers-fund` account, and transferred -/// to the relayer when message delivery is confirmed. -/// -/// Additionally, confirmation transaction submitter (`confirmation_relayer`) is reimbursed -/// with the confirmation rewards (part of message fee, reserved to pay for delivery confirmation). -/// -/// NOTE The `relayers-fund` account must always exist i.e. be over Existential Deposit (ED; the -/// pallet enforces that) to make sure that even if the message cost is below ED it is still paid -/// to the relayer account. -/// NOTE It's within relayer's interest to keep their balance above ED as well, to make sure they -/// can receive the payment. -pub struct InstantCurrencyPayments { - _phantom: sp_std::marker::PhantomData<(T, I, Currency, GetConfirmationFee)>, -} - -impl - MessageDeliveryAndDispatchPayment - for InstantCurrencyPayments -where - T: frame_system::Config + crate::Config, - I: 'static, - T::Origin: SenderOrigin, - Currency: CurrencyT, - Currency::Balance: From, - GetConfirmationFee: Get, -{ - type Error = &'static str; - - fn pay_delivery_and_dispatch_fee( - submitter: &T::Origin, - fee: &Currency::Balance, - relayer_fund_account: &T::AccountId, - ) -> Result<(), Self::Error> { - let submitter_account = match submitter.linked_account() { - Some(submitter_account) => submitter_account, - None if !fee.is_zero() => { - // if we'll accept some message that has declared that the `fee` has been paid but - // it isn't actually paid, then it'll lead to problems with delivery confirmation - // payments (see `pay_relayer_rewards` && `confirmation_relayer` in particular) - return Err(NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE); - }, - None => { - // message lane verifier has accepted the message before, so this message - // is unpaid **by design** - // => let's just do nothing - return Ok(()); - }, - }; - - if !frame_system::Pallet::::account_exists(relayer_fund_account) { - return Err("The relayer fund account must exist for the message lanes pallet to work correctly."); - } - - Currency::transfer( - &submitter_account, - relayer_fund_account, - *fee, - // it's fine for the submitter to go below Existential Deposit and die. - ExistenceRequirement::AllowDeath, - ) - .map_err(Into::into) - } - - fn pay_relayers_rewards( - lane_id: LaneId, - messages_relayers: VecDeque>, - confirmation_relayer: &T::AccountId, - received_range: &RangeInclusive, - relayer_fund_account: &T::AccountId, - ) { - let relayers_rewards = - cal_relayers_rewards::(lane_id, messages_relayers, received_range); - if !relayers_rewards.is_empty() { - pay_relayers_rewards::( - confirmation_relayer, - relayers_rewards, - relayer_fund_account, - GetConfirmationFee::get(), - ); - } - } -} - -/// Calculate the relayers rewards -pub(crate) fn cal_relayers_rewards( - lane_id: LaneId, - messages_relayers: VecDeque>, - received_range: &RangeInclusive, -) -> RelayersRewards -where - T: frame_system::Config + crate::Config, - I: 'static, -{ - // remember to reward relayers that have delivered messages - // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain - let mut relayers_rewards: RelayersRewards<_, T::OutboundMessageFee> = RelayersRewards::new(); - for entry in messages_relayers { - let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start()); - let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end()); - - // loop won't proceed if current entry is ahead of received range (begin > end). - // this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain - let mut relayer_reward = relayers_rewards.entry(entry.relayer).or_default(); - for nonce in nonce_begin..nonce_end + 1 { - let message_data = OutboundMessages::::get(MessageKey { lane_id, nonce }) - .expect("message was just confirmed; we never prune unconfirmed messages; qed"); - relayer_reward.reward = relayer_reward.reward.saturating_add(&message_data.fee); - relayer_reward.messages += 1; - } - } - relayers_rewards -} - -/// Pay rewards to given relayers, optionally rewarding confirmation relayer. -fn pay_relayers_rewards( - confirmation_relayer: &AccountId, - relayers_rewards: RelayersRewards, - relayer_fund_account: &AccountId, - confirmation_fee: Currency::Balance, -) where - AccountId: Debug + Encode + PartialEq, - Currency: CurrencyT, - Currency::Balance: From, -{ - // reward every relayer except `confirmation_relayer` - let mut confirmation_relayer_reward = Currency::Balance::zero(); - for (relayer, reward) in relayers_rewards { - let mut relayer_reward = reward.reward; - - if relayer != *confirmation_relayer { - // If delivery confirmation is submitted by other relayer, let's deduct confirmation fee - // from relayer reward. - // - // If confirmation fee has been increased (or if it was the only component of message - // fee), then messages relayer may receive zero reward. - let mut confirmation_reward = confirmation_fee.saturating_mul(reward.messages.into()); - if confirmation_reward > relayer_reward { - confirmation_reward = relayer_reward; - } - relayer_reward = relayer_reward.saturating_sub(confirmation_reward); - confirmation_relayer_reward = - confirmation_relayer_reward.saturating_add(confirmation_reward); - } else { - // If delivery confirmation is submitted by this relayer, let's add confirmation fee - // from other relayers to this relayer reward. - confirmation_relayer_reward = confirmation_relayer_reward.saturating_add(reward.reward); - continue; - } - - pay_relayer_reward::(relayer_fund_account, &relayer, relayer_reward); - } - - // finally - pay reward to confirmation relayer - pay_relayer_reward::( - relayer_fund_account, - confirmation_relayer, - confirmation_relayer_reward, - ); -} - -/// Transfer funds from relayers fund account to given relayer. -fn pay_relayer_reward( - relayer_fund_account: &AccountId, - relayer_account: &AccountId, - reward: Currency::Balance, -) where - AccountId: Debug, - Currency: CurrencyT, -{ - if reward.is_zero() { - return; - } - - let pay_result = Currency::transfer( - relayer_fund_account, - relayer_account, - reward, - // the relayer fund account must stay above ED (needs to be pre-funded) - ExistenceRequirement::KeepAlive, - ); - - match pay_result { - Ok(_) => log::trace!( - target: "runtime::bridge-messages", - "Rewarded relayer {:?} with {:?}", - relayer_account, - reward, - ), - Err(error) => log::trace!( - target: "runtime::bridge-messages", - "Failed to pay relayer {:?} reward {:?}: {:?}", - relayer_account, - reward, - error, - ), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::{ - run_test, AccountId as TestAccountId, Balance as TestBalance, Origin, TestRuntime, - }; - use bp_messages::source_chain::RelayerRewards; - - type Balances = pallet_balances::Pallet; - - const RELAYER_1: TestAccountId = 1; - const RELAYER_2: TestAccountId = 2; - const RELAYER_3: TestAccountId = 3; - const RELAYERS_FUND_ACCOUNT: TestAccountId = crate::mock::ENDOWED_ACCOUNT; - - fn relayers_rewards() -> RelayersRewards { - vec![ - (RELAYER_1, RelayerRewards { reward: 100, messages: 2 }), - (RELAYER_2, RelayerRewards { reward: 100, messages: 3 }), - ] - .into_iter() - .collect() - } - - #[test] - fn pay_delivery_and_dispatch_fee_fails_on_non_zero_fee_and_unknown_payer() { - frame_support::parameter_types! { - const GetConfirmationFee: TestBalance = 0; - }; - - run_test(|| { - let result = InstantCurrencyPayments::< - TestRuntime, - (), - Balances, - GetConfirmationFee, - >::pay_delivery_and_dispatch_fee( - &Origin::root(), - &100, - &RELAYERS_FUND_ACCOUNT, - ); - assert_eq!(result, Err(NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE)); - }); - } - - #[test] - fn pay_delivery_and_dispatch_succeeds_on_zero_fee_and_unknown_payer() { - frame_support::parameter_types! { - const GetConfirmationFee: TestBalance = 0; - }; - - run_test(|| { - let result = InstantCurrencyPayments::< - TestRuntime, - (), - Balances, - GetConfirmationFee, - >::pay_delivery_and_dispatch_fee( - &Origin::root(), - &0, - &RELAYERS_FUND_ACCOUNT, - ); - assert!(result.is_ok()); - }); - } - - #[test] - fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { - run_test(|| { - pay_relayers_rewards::( - &RELAYER_2, - relayers_rewards(), - &RELAYERS_FUND_ACCOUNT, - 10, - ); - - assert_eq!(Balances::free_balance(&RELAYER_1), 80); - assert_eq!(Balances::free_balance(&RELAYER_2), 120); - }); - } - - #[test] - fn confirmation_relayer_is_rewarded_if_it_has_not_delivered_any_delivered_messages() { - run_test(|| { - pay_relayers_rewards::( - &RELAYER_3, - relayers_rewards(), - &RELAYERS_FUND_ACCOUNT, - 10, - ); - - assert_eq!(Balances::free_balance(&RELAYER_1), 80); - assert_eq!(Balances::free_balance(&RELAYER_2), 70); - assert_eq!(Balances::free_balance(&RELAYER_3), 50); - }); - } - - #[test] - fn only_confirmation_relayer_is_rewarded_if_confirmation_fee_has_significantly_increased() { - run_test(|| { - pay_relayers_rewards::( - &RELAYER_3, - relayers_rewards(), - &RELAYERS_FUND_ACCOUNT, - 1000, - ); - - assert_eq!(Balances::free_balance(&RELAYER_1), 0); - assert_eq!(Balances::free_balance(&RELAYER_2), 0); - assert_eq!(Balances::free_balance(&RELAYER_3), 200); - }); - } -} diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 298312c0d..e87c70640 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -37,6 +37,8 @@ // Generated by `decl_event!` #![allow(clippy::unused_unit)] +pub use inbound_lane::StoredInboundLaneData; +pub use outbound_lane::StoredMessageData; pub use weights::WeightInfo; pub use weights_ext::{ ensure_able_to_receive_confirmation, ensure_able_to_receive_message, @@ -56,28 +58,26 @@ use bp_messages::{ target_chain::{ DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, }, - total_unrewarded_messages, DeliveredMessages, InboundLaneData, LaneId, MessageData, MessageKey, - MessageNonce, OperatingMode, OutboundLaneData, Parameter as MessagesParameter, - UnrewardedRelayersState, + total_unrewarded_messages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, + MessageData, MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, + OutboundMessageDetails, Parameter as MessagesParameter, UnrewardedRelayersState, }; -use bp_runtime::{ChainId, Size}; -use codec::{Decode, Encode}; +use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, Size}; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ - fail, + ensure, fail, traits::Get, weights::{Pays, PostDispatchInfo}, }; -use frame_system::RawOrigin; use num_traits::{SaturatingAdd, Zero}; use sp_core::H256; -use sp_runtime::traits::{BadOrigin, Convert}; +use sp_runtime::traits::Convert; use sp_std::{cell::RefCell, cmp::PartialOrd, marker::PhantomData, prelude::*}; mod inbound_lane; mod outbound_lane; mod weights_ext; -pub mod instant_payments; pub mod weights; #[cfg(feature = "runtime-benchmarks")] @@ -88,6 +88,9 @@ mod mock; pub use pallet::*; +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-messages"; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -140,6 +143,9 @@ pub mod pallet { /// these messages are from different lanes. type MaxUnconfirmedMessagesAtInboundLane: Get; + /// Maximal size of the outbound payload. + #[pallet::constant] + type MaximalOutboundPayloadSize: Get; /// Payload type of outbound messages. This payload is dispatched on the bridged chain. type OutboundPayload: Parameter + Size; /// Message fee type of outbound messages. This fee is paid on this chain. @@ -149,15 +155,16 @@ pub mod pallet { + Parameter + SaturatingAdd + Zero - + Copy; + + Copy + + MaxEncodedLen; /// Payload type of inbound messages. This payload is dispatched on this chain. type InboundPayload: Decode; /// Message fee type of inbound messages. This fee is paid on the bridged chain. - type InboundMessageFee: Decode; + type InboundMessageFee: Decode + Zero; /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the /// bridged chain. - type InboundRelayer: Parameter; + type InboundRelayer: Parameter + MaxEncodedLen; /// A type which can be turned into an AccountId from a 256-bit hash. /// @@ -211,9 +218,16 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] - #[pallet::without_storage_info] pub struct Pallet(PhantomData<(T, I)>); + impl, I: 'static> OwnedBridgeModule for Pallet { + type OperatingMode = MessagesOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + type OwnerStorage = PalletOwner; + + const LOG_TARGET: &'static str = LOG_TARGET; + } + #[pallet::call] impl, I: 'static> Pallet { /// Change `PalletOwner`. @@ -221,18 +235,7 @@ pub mod pallet { /// May only be called either by root, or by `PalletOwner`. #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { - ensure_owner_or_root::(origin)?; - match new_owner { - Some(new_owner) => { - PalletOwner::::put(&new_owner); - log::info!(target: "runtime::bridge-messages", "Setting pallet Owner to: {:?}", new_owner); - }, - None => { - PalletOwner::::kill(); - log::info!(target: "runtime::bridge-messages", "Removed Owner of pallet."); - }, - } - Ok(()) + >::set_owner(origin, new_owner) } /// Halt or resume all/some pallet operations. @@ -241,16 +244,9 @@ pub mod pallet { #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_operating_mode( origin: OriginFor, - operating_mode: OperatingMode, + operating_mode: MessagesOperatingMode, ) -> DispatchResult { - ensure_owner_or_root::(origin)?; - PalletOperatingMode::::put(operating_mode); - log::info!( - target: "runtime::bridge-messages", - "Setting messages pallet operating mode to {:?}.", - operating_mode, - ); - Ok(()) + >::set_operating_mode(origin, operating_mode) } /// Update pallet parameter. @@ -264,9 +260,9 @@ pub mod pallet { origin: OriginFor, parameter: T::Parameter, ) -> DispatchResult { - ensure_owner_or_root::(origin)?; + Self::ensure_owner_or_root(origin)?; parameter.save(); - Self::deposit_event(Event::ParameterUpdated(parameter)); + Self::deposit_event(Event::ParameterUpdated { parameter }); Ok(()) } @@ -294,7 +290,7 @@ pub mod pallet { nonce: MessageNonce, additional_fee: T::OutboundMessageFee, ) -> DispatchResultWithPostInfo { - ensure_not_halted::()?; + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; // if someone tries to pay for already-delivered message, we're rejecting this intention // (otherwise this additional fee will be locked forever in relayers fund) // @@ -318,7 +314,7 @@ pub mod pallet { ) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Submitter can't pay additional fee {:?} for the message {:?}/{:?} to {:?}: {:?}", additional_fee, lane_id, @@ -365,7 +361,7 @@ pub mod pallet { messages_count: u32, dispatch_weight: Weight, ) -> DispatchResultWithPostInfo { - ensure_not_halted::()?; + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; let relayer_id_at_this_chain = ensure_signed(origin)?; // reject transactions that are declaring too many messages @@ -398,11 +394,7 @@ pub mod pallet { T::InboundPayload, >(proof, messages_count) .map_err(|err| { - log::trace!( - target: "runtime::bridge-messages", - "Rejecting invalid messages proof: {:?}", - err, - ); + log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); Error::::InvalidMessagesProof })?; @@ -418,7 +410,7 @@ pub mod pallet { let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Received lane {:?} state update: latest_confirmed_nonce={}", lane_id, updated_latest_confirmed_nonce, @@ -426,16 +418,16 @@ pub mod pallet { } } - for message in lane_data.messages { + for mut message in lane_data.messages { debug_assert_eq!(message.key.lane_id, lane_id); // ensure that relayer has declared enough weight for dispatching next message // on this lane. We can't dispatch lane messages out-of-order, so if declared // weight is not enough, let's move to next lane - let dispatch_weight = T::MessageDispatch::dispatch_weight(&message); + let dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); if dispatch_weight > dispatch_weight_left { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", lane_id, dispatch_weight, @@ -488,7 +480,7 @@ pub mod pallet { } log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Received messages: total={}, valid={}. Weight used: {}/{}", total_messages, valid_messages, @@ -510,7 +502,7 @@ pub mod pallet { proof: MessagesDeliveryProofOf, relayers_state: UnrewardedRelayersState, ) -> DispatchResultWithPostInfo { - ensure_not_halted::()?; + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; // why do we need to know the weight of this (`receive_messages_delivery_proof`) call? // Because we may want to return some funds for messages that are not processed by the @@ -534,7 +526,7 @@ pub mod pallet { let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Rejecting invalid messages delivery proof: {:?}", err, ); @@ -552,6 +544,12 @@ pub mod pallet { == relayers_state.unrewarded_relayer_entries, Error::::InvalidUnrewardedRelayersState ); + // the `last_delivered_nonce` field may also be used by the signed extension. Even + // though providing wrong value isn't critical, let's also check it here. + ensure!( + lane_data.last_delivered_nonce() == relayers_state.last_delivered_nonce, + Error::::InvalidUnrewardedRelayersState + ); // mark messages as delivered let mut lane = outbound_lane::(lane_id); @@ -568,7 +566,7 @@ pub mod pallet { to_confirm_messages_count, ) => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Messages delivery proof contains too many messages to confirm: {} vs declared {}", to_confirm_messages_count, relayers_state.total_messages, @@ -578,7 +576,7 @@ pub mod pallet { }, error => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Messages delivery proof contains invalid unrewarded relayers vec: {:?}", error, ); @@ -597,7 +595,7 @@ pub mod pallet { Some(difference) if difference == 0 => (), Some(difference) => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnDeliveryConfirmed callback has spent less weight than expected. Refunding: \ {} - {} = {}", preliminary_callback_overhead, @@ -612,7 +610,7 @@ pub mod pallet { "T::OnDeliveryConfirmed callback consumed too much weight." ); log::error!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnDeliveryConfirmed callback has spent more weight that it is allowed to: \ {} vs {}", preliminary_callback_overhead, @@ -623,7 +621,10 @@ pub mod pallet { // emit 'delivered' event let received_range = confirmed_messages.begin..=confirmed_messages.end; - Self::deposit_event(Event::MessagesDelivered(lane_id, confirmed_messages)); + Self::deposit_event(Event::MessagesDelivered { + lane_id, + messages: confirmed_messages, + }); // if some new messages have been confirmed, reward relayers let relayer_fund_account = @@ -638,7 +639,7 @@ pub mod pallet { } log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Received messages delivery proof up to (and including) {} at lane {:?}", last_delivered_nonce, lane_id, @@ -652,17 +653,19 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { /// Pallet parameter has been updated. - ParameterUpdated(T::Parameter), + ParameterUpdated { parameter: T::Parameter }, /// Message has been accepted and is waiting to be delivered. - MessageAccepted(LaneId, MessageNonce), + MessageAccepted { lane_id: LaneId, nonce: MessageNonce }, /// Messages in the inclusive range have been delivered to the bridged chain. - MessagesDelivered(LaneId, DeliveredMessages), + MessagesDelivered { lane_id: LaneId, messages: DeliveredMessages }, } #[pallet::error] pub enum Error { - /// All pallet operations are halted. - Halted, + /// Pallet is not in Normal operating mode. + NotOperatingNormally, + /// The message is too large to be sent over the bridge. + MessageIsTooLarge, /// Message has been treated as invalid by chain verifier. MessageRejectedByChainVerifier, /// Message has been treated as invalid by lane verifier. @@ -687,6 +690,8 @@ pub mod pallet { /// The number of actually confirmed messages is going to be larger than the number of /// messages in the proof. This may mean that this or bridged chain storage is corrupted. TryingToConfirmMoreMessagesThanExpected, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), } /// Optional pallet owner. @@ -705,12 +710,12 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn operating_mode)] pub type PalletOperatingMode, I: 'static = ()> = - StorageValue<_, OperatingMode, ValueQuery>; + StorageValue<_, MessagesOperatingMode, ValueQuery>; /// Map of lane id => inbound lane data. #[pallet::storage] pub type InboundLanes, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, LaneId, InboundLaneData, ValueQuery>; + StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; /// Map of lane id => outbound lane data. #[pallet::storage] @@ -720,12 +725,12 @@ pub mod pallet { /// All queued outbound messages. #[pallet::storage] pub type OutboundMessages, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, MessageKey, MessageData>; + StorageMap<_, Blake2_128Concat, MessageKey, StoredMessageData>; #[pallet::genesis_config] pub struct GenesisConfig, I: 'static = ()> { /// Initial pallet operating mode. - pub operating_mode: OperatingMode, + pub operating_mode: MessagesOperatingMode, /// Initial pallet owner. pub owner: Option, /// Dummy marker. @@ -759,7 +764,23 @@ pub mod pallet { lane: LaneId, nonce: MessageNonce, ) -> Option> { - OutboundMessages::::get(MessageKey { lane_id: lane, nonce }) + OutboundMessages::::get(MessageKey { lane_id: lane, nonce }).map(Into::into) + } + + /// Prepare data, related to given inbound message. + pub fn inbound_message_data( + lane: LaneId, + payload: MessagePayload, + outbound_details: OutboundMessageDetails, + ) -> InboundMessageDetails { + let mut dispatch_message = DispatchMessage { + key: MessageKey { lane_id: lane, nonce: outbound_details.nonce }, + data: MessageData { payload, fee: outbound_details.delivery_and_dispatch_fee } + .into(), + }; + InboundMessageDetails { + dispatch_weight: T::MessageDispatch::dispatch_weight(&mut dispatch_message), + } } } } @@ -810,13 +831,19 @@ fn send_message, I: 'static>( > { ensure_normal_operating_mode::()?; + // the most lightweigh check is the message size check + ensure!( + payload.size() < T::MaximalOutboundPayloadSize::get(), + Error::::MessageIsTooLarge, + ); + // initially, actual (post-dispatch) weight is equal to pre-dispatch weight let mut actual_weight = T::WeightInfo::send_message_weight(&payload, T::DbWeight::get()); // let's first check if message can be delivered to target chain T::TargetHeaderChain::verify_message(&payload).map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Message to lane {:?} is rejected by target chain: {:?}", lane_id, err, @@ -836,7 +863,7 @@ fn send_message, I: 'static>( ) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Message to lane {:?} is rejected by lane verifier: {:?}", lane_id, err, @@ -853,7 +880,7 @@ fn send_message, I: 'static>( ) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Message to lane {:?} is rejected because submitter is unable to pay fee {:?}: {:?}", lane_id, delivery_and_dispatch_fee, @@ -879,7 +906,7 @@ fn send_message, I: 'static>( Some(difference) if difference == 0 => (), Some(difference) => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnMessageAccepted callback has spent less weight than expected. Refunding: \ {} - {} = {}", single_message_callback_overhead, @@ -891,7 +918,7 @@ fn send_message, I: 'static>( None => { debug_assert!(false, "T::OnMessageAccepted callback consumed too much weight."); log::error!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnMessageAccepted callback has spent more weight that it is allowed to: \ {} vs {}", single_message_callback_overhead, @@ -910,45 +937,27 @@ fn send_message, I: 'static>( } log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Accepted message {} to lane {:?}. Message size: {:?}", nonce, lane_id, encoded_payload_len, ); - Pallet::::deposit_event(Event::MessageAccepted(lane_id, nonce)); + Pallet::::deposit_event(Event::MessageAccepted { lane_id, nonce }); Ok(SendMessageArtifacts { nonce, weight: actual_weight }) } -/// Ensure that the origin is either root, or `PalletOwner`. -fn ensure_owner_or_root, I: 'static>(origin: T::Origin) -> Result<(), BadOrigin> { - match origin.into() { - Ok(RawOrigin::Root) => Ok(()), - Ok(RawOrigin::Signed(ref signer)) - if Some(signer) == Pallet::::module_owner().as_ref() => - Ok(()), - _ => Err(BadOrigin), - } -} - /// Ensure that the pallet is in normal operational mode. fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error> { - if PalletOperatingMode::::get() != OperatingMode::Normal { - Err(Error::::Halted) - } else { - Ok(()) + if PalletOperatingMode::::get() + == MessagesOperatingMode::Basic(BasicOperatingMode::Normal) + { + return Ok(()); } -} -/// Ensure that the pallet is not halted. -fn ensure_not_halted, I: 'static>() -> Result<(), Error> { - if PalletOperatingMode::::get() == OperatingMode::Halted { - Err(Error::::Halted) - } else { - Ok(()) - } + Err(Error::::NotOperatingNormally) } /// Creates new inbound lane object, backed by runtime storage. @@ -1003,7 +1012,8 @@ impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage< match self.cached_data.clone().into_inner() { Some(data) => data, None => { - let data = InboundLanes::::get(&self.lane_id); + let data: InboundLaneData = + InboundLanes::::get(&self.lane_id).into(); *self.cached_data.try_borrow_mut().expect( "we're in the single-threaded environment;\ we have no recursive borrows; qed", @@ -1018,7 +1028,7 @@ impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage< "we're in the single-threaded environment;\ we have no recursive borrows; qed", ) = Some(data.clone()); - InboundLanes::::insert(&self.lane_id, data) + InboundLanes::::insert(&self.lane_id, StoredInboundLaneData::(data)) } } @@ -1046,6 +1056,7 @@ impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorag #[cfg(test)] fn message(&self, nonce: &MessageNonce) -> Option> { OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) + .map(Into::into) } fn save_message( @@ -1092,10 +1103,12 @@ mod tests { message, message_payload, run_test, unrewarded_relayer, Event as TestEvent, Origin, TestMessageDeliveryAndDispatchPayment, TestMessagesDeliveryProof, TestMessagesParameter, TestMessagesProof, TestOnDeliveryConfirmed1, TestOnDeliveryConfirmed2, - TestOnMessageAccepted, TestRuntime, TokenConversionRate, PAYLOAD_REJECTED_BY_TARGET_CHAIN, - REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, + TestOnMessageAccepted, TestRuntime, TokenConversionRate, MAX_OUTBOUND_PAYLOAD_SIZE, + PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, + TEST_RELAYER_B, }; use bp_messages::{UnrewardedRelayer, UnrewardedRelayersState}; + use bp_test_utils::generate_owned_bridge_module_tests; use frame_support::{ assert_noop, assert_ok, storage::generator::{StorageMap, StorageValue}, @@ -1112,7 +1125,9 @@ mod tests { fn inbound_unrewarded_relayers_state( lane: bp_messages::LaneId, ) -> bp_messages::UnrewardedRelayersState { - let relayers = InboundLanes::::get(&lane).relayers; + let inbound_lane_data = InboundLanes::::get(&lane).0; + let last_delivered_nonce = inbound_lane_data.last_delivered_nonce(); + let relayers = inbound_lane_data.relayers; bp_messages::UnrewardedRelayersState { unrewarded_relayer_entries: relayers.len() as _, messages_in_oldest_entry: relayers @@ -1120,6 +1135,7 @@ mod tests { .map(|entry| 1 + entry.messages.end - entry.messages.begin) .unwrap_or(0), total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + last_delivered_nonce, } } @@ -1143,7 +1159,10 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(Event::MessageAccepted(TEST_LANE_ID, message_nonce)), + event: TestEvent::Messages(Event::MessageAccepted { + lane_id: TEST_LANE_ID, + nonce: message_nonce + }), topics: vec![], }], ); @@ -1178,6 +1197,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1186,94 +1206,15 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(Event::MessagesDelivered( - TEST_LANE_ID, - DeliveredMessages::new(1, true), - )), + event: TestEvent::Messages(Event::MessagesDelivered { + lane_id: TEST_LANE_ID, + messages: DeliveredMessages::new(1, true), + }), topics: vec![], }], ); } - #[test] - fn pallet_owner_may_change_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_owner(Origin::root(), Some(1))); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(2), OperatingMode::Halted), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - OperatingMode::Halted - )); - - assert_ok!(Pallet::::set_owner(Origin::signed(1), None)); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(1), OperatingMode::Normal), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(2), OperatingMode::Normal), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - OperatingMode::Normal - )); - }); - } - - #[test] - fn pallet_may_be_halted_by_root() { - run_test(|| { - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - OperatingMode::Halted - )); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - OperatingMode::Normal - )); - }); - } - - #[test] - fn pallet_may_be_halted_by_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - OperatingMode::Halted - )); - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - OperatingMode::Normal - )); - - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(1), OperatingMode::Halted), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(1), OperatingMode::Normal), - DispatchError::BadOrigin, - ); - - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - OperatingMode::Halted - )); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(1), OperatingMode::Normal), - DispatchError::BadOrigin, - ); - }); - } - #[test] fn pallet_parameter_may_be_updated_by_root() { run_test(|| { @@ -1290,7 +1231,7 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(Event::ParameterUpdated(parameter)), + event: TestEvent::Messages(Event::ParameterUpdated { parameter }), topics: vec![], }], ); @@ -1314,7 +1255,7 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(Event::ParameterUpdated(parameter)), + event: TestEvent::Messages(Event::ParameterUpdated { parameter }), topics: vec![], }], ); @@ -1376,7 +1317,9 @@ mod tests { // send message first to be able to check that delivery_proof fails later send_regular_message(); - PalletOperatingMode::::put(OperatingMode::Halted); + PalletOperatingMode::::put(MessagesOperatingMode::Basic( + BasicOperatingMode::Halted, + )); assert_noop!( Pallet::::send_message( @@ -1385,12 +1328,12 @@ mod tests { REGULAR_PAYLOAD, REGULAR_PAYLOAD.declared_weight, ), - Error::::Halted, + Error::::NotOperatingNormally, ); assert_noop!( Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1,), - Error::::Halted, + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), ); assert_noop!( @@ -1401,7 +1344,7 @@ mod tests { 1, REGULAR_PAYLOAD.declared_weight, ), - Error::::Halted, + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), ); assert_noop!( @@ -1420,9 +1363,10 @@ mod tests { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 1, total_messages: 1, + last_delivered_nonce: 1, }, ), - Error::::Halted, + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), ); }); } @@ -1433,7 +1377,9 @@ mod tests { // send message first to be able to check that delivery_proof fails later send_regular_message(); - PalletOperatingMode::::put(OperatingMode::RejectingOutboundMessages); + PalletOperatingMode::::put( + MessagesOperatingMode::RejectingOutboundMessages, + ); assert_noop!( Pallet::::send_message( @@ -1442,7 +1388,7 @@ mod tests { REGULAR_PAYLOAD, REGULAR_PAYLOAD.declared_weight, ), - Error::::Halted, + Error::::NotOperatingNormally, ); assert_ok!(Pallet::::increase_message_fee( @@ -1475,6 +1421,7 @@ mod tests { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 1, total_messages: 1, + last_delivered_nonce: 1, }, )); }); @@ -1487,6 +1434,25 @@ mod tests { }); } + #[test] + fn send_message_rejects_too_large_message() { + run_test(|| { + let mut message_payload = message_payload(1, 0); + // the payload isn't simply extra, so it'll definitely overflow + // `MAX_OUTBOUND_PAYLOAD_SIZE` if we add `MAX_OUTBOUND_PAYLOAD_SIZE` bytes to extra + message_payload.extra.extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]); + assert_noop!( + Pallet::::send_message( + Origin::signed(1), + TEST_LANE_ID, + message_payload, + 0, + ), + Error::::MessageIsTooLarge, + ); + }) + } + #[test] fn chain_verifier_rejects_invalid_message_in_send_message() { run_test(|| { @@ -1546,7 +1512,7 @@ mod tests { REGULAR_PAYLOAD.declared_weight, )); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1); + assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); }); } @@ -1572,6 +1538,7 @@ mod tests { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, total_messages: 2, + last_delivered_nonce: 10, }, ); @@ -1590,7 +1557,7 @@ mod tests { )); assert_eq!( - InboundLanes::::get(TEST_LANE_ID), + InboundLanes::::get(TEST_LANE_ID).0, InboundLaneData { last_confirmed_nonce: 9, relayers: vec![ @@ -1607,6 +1574,7 @@ mod tests { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, total_messages: 2, + last_delivered_nonce: 11, }, ); }); @@ -1702,6 +1670,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1727,6 +1696,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, )); @@ -1771,6 +1741,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, ), @@ -1796,6 +1767,33 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 1, + last_delivered_nonce: 2, + ..Default::default() + }, + ), + Error::::InvalidUnrewardedRelayersState, + ); + + // when last delivered nonce is invalid + assert_noop!( + Pallet::::receive_messages_delivery_proof( + Origin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B) + ] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + total_messages: 2, + last_delivered_nonce: 8, ..Default::default() }, ), @@ -2032,6 +2030,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, )); @@ -2042,6 +2041,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 3, ..Default::default() }, )); @@ -2069,6 +2069,7 @@ mod tests { let relayers_state = UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 3, + last_delivered_nonce: 3, ..Default::default() }; let pre_dispatch_weight = @@ -2143,7 +2144,7 @@ mod tests { TEST_LANE_ID, InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, ))), - UnrewardedRelayersState::default(), + UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() }, ), Error::::TryingToConfirmMoreMessagesThanExpected, ); @@ -2155,8 +2156,8 @@ mod tests { run_test(|| { let mut small_payload = message_payload(0, 100); let mut large_payload = message_payload(1, 100); - small_payload.extra = vec![1; 100]; - large_payload.extra = vec![2; 16_384]; + small_payload.extra = vec![1; MAX_OUTBOUND_PAYLOAD_SIZE as usize / 10]; + large_payload.extra = vec![2; MAX_OUTBOUND_PAYLOAD_SIZE as usize / 5]; assert_ok!(Pallet::::send_message( Origin::signed(1), @@ -2222,6 +2223,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: max_messages_to_prune, + last_delivered_nonce: max_messages_to_prune, ..Default::default() }, )); @@ -2302,4 +2304,30 @@ mod tests { bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, ); } + + #[test] + fn inbound_message_details_works() { + run_test(|| { + assert_eq!( + Pallet::::inbound_message_data( + TEST_LANE_ID, + REGULAR_PAYLOAD.encode(), + OutboundMessageDetails { + nonce: 0, + dispatch_weight: 0, + size: 0, + delivery_and_dispatch_fee: 0, + dispatch_fee_payment: + bp_runtime::messages::DispatchFeePayment::AtTargetChain, + }, + ), + InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight }, + ); + }); + } + + generate_owned_bridge_module_tests!( + MessagesOperatingMode::Basic(BasicOperatingMode::Normal), + MessagesOperatingMode::Basic(BasicOperatingMode::Halted) + ); } diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index 8ab089c7c..9bbc180a8 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -149,7 +149,7 @@ parameter_types! { pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16; pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 32; pub storage TokenConversionRate: FixedU128 = 1.into(); - pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; + pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; } #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] @@ -177,6 +177,7 @@ impl Config for TestRuntime { type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaximalOutboundPayloadSize = frame_support::traits::ConstU32; type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment; type MessageDispatch = TestMessageDispatch; type OnDeliveryConfirmed = (TestOnDeliveryConfirmed1, TestOnDeliveryConfirmed2); @@ -200,11 +201,14 @@ impl SenderOrigin for Origin { } impl Size for TestPayload { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 16 + self.extra.len() as u32 } } +/// Maximal outbound payload size. +pub const MAX_OUTBOUND_PAYLOAD_SIZE: u32 = 4096; + /// Account that has balance to use in tests. pub const ENDOWED_ACCOUNT: AccountId = 0xDEAD; @@ -239,7 +243,7 @@ pub struct TestMessagesProof { } impl Size for TestMessagesProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 0 } } @@ -266,7 +270,7 @@ impl From>, ()>> for TestMessagesProof { pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData), ()>); impl Size for TestMessagesDeliveryProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 0 } } @@ -482,7 +486,7 @@ pub struct TestMessageDispatch; impl MessageDispatch for TestMessageDispatch { type DispatchPayload = TestPayload; - fn dispatch_weight(message: &DispatchMessage) -> Weight { + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { match message.data.payload.as_ref() { Ok(payload) => payload.declared_weight, Err(_) => 0, diff --git a/modules/messages/src/outbound_lane.rs b/modules/messages/src/outbound_lane.rs index df5bb88ee..40d211f97 100644 --- a/modules/messages/src/outbound_lane.rs +++ b/modules/messages/src/outbound_lane.rs @@ -16,12 +16,16 @@ //! Everything about outgoing messages sending. +use crate::Config; + use bitvec::prelude::*; use bp_messages::{ DeliveredMessages, DispatchResultsBitVec, LaneId, MessageData, MessageNonce, OutboundLaneData, UnrewardedRelayer, }; -use frame_support::RuntimeDebug; +use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use frame_support::{traits::Get, RuntimeDebug}; +use scale_info::{Type, TypeInfo}; use sp_std::collections::vec_deque::VecDeque; /// Outbound lane storage. @@ -44,6 +48,58 @@ pub trait OutboundLaneStorage { fn remove_message(&mut self, nonce: &MessageNonce); } +/// Outbound message data wrapper that implements `MaxEncodedLen`. +/// +/// We have already had `MaxEncodedLen`-like functionality before, but its usage has +/// been localized and we haven't been passing it everywhere. This wrapper allows us +/// to avoid passing these generic bounds all over the code. +/// +/// The encoding of this type matches encoding of the corresponding `MessageData`. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)] +pub struct StoredMessageData, I: 'static>(pub MessageData); + +impl, I: 'static> sp_std::ops::Deref for StoredMessageData { + type Target = MessageData; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl, I: 'static> sp_std::ops::DerefMut for StoredMessageData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl, I: 'static> From> + for MessageData +{ + fn from(data: StoredMessageData) -> Self { + data.0 + } +} + +impl, I: 'static> TypeInfo for StoredMessageData { + type Identity = Self; + + fn type_info() -> Type { + MessageData::::type_info() + } +} + +impl, I: 'static> EncodeLike> + for MessageData +{ +} + +impl, I: 'static> MaxEncodedLen for StoredMessageData { + fn max_encoded_len() -> usize { + T::OutboundMessageFee::max_encoded_len() + .saturating_add(T::MaximalOutboundPayloadSize::get() as usize) + } +} + /// Result of messages receival confirmation. #[derive(RuntimeDebug, PartialEq, Eq)] pub enum ReceivalConfirmationResult { diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs index b6256eec9..916000ef3 100644 --- a/modules/messages/src/weights.rs +++ b/modules/messages/src/weights.rs @@ -17,14 +17,15 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-12-28, STEPS: 50, REPEAT: 20 +//! DATE: 2022-07-07, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled -//! CHAIN: Some("dev"), DB CACHE: 128 +//! CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/millau-bridge-node // benchmark +// pallet // --chain=dev // --steps=50 // --repeat=20 @@ -68,175 +69,175 @@ pub trait WeightInfo { pub struct MillauWeight(PhantomData); impl WeightInfo for MillauWeight { fn send_minimal_message_worst_case() -> Weight { - (117_480_000 as Weight) - .saturating_add(T::DbWeight::get().reads(7 as Weight)) - .saturating_add(T::DbWeight::get().writes(12 as Weight)) + (37_948_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(10 as Weight)) } fn send_1_kb_message_worst_case() -> Weight { - (128_391_000 as Weight) - .saturating_add(T::DbWeight::get().reads(7 as Weight)) - .saturating_add(T::DbWeight::get().writes(12 as Weight)) + (39_158_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(10 as Weight)) } fn send_16_kb_message_worst_case() -> Weight { - (149_149_000 as Weight) - .saturating_add(T::DbWeight::get().reads(7 as Weight)) - .saturating_add(T::DbWeight::get().writes(12 as Weight)) + (48_698_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(10 as Weight)) } fn maximal_increase_message_fee() -> Weight { - (6_015_058_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + (2_919_864_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn increase_message_fee(i: u32) -> Weight { (0 as Weight) - .saturating_add((2_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + .saturating_add((1_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_single_message_proof() -> Weight { - (179_892_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + (25_992_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn receive_two_messages_proof() -> Weight { - (291_793_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + (37_016_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - (192_191_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + (31_589_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn receive_single_message_proof_1_kb() -> Weight { - (202_104_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + (25_962_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_single_message_proof_16_kb() -> Weight { - (357_144_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + (74_385_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_single_prepaid_message_proof() -> Weight { - (122_648_000 as Weight) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (26_159_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn receive_delivery_proof_for_single_message() -> Weight { - (107_631_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + (27_590_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - (113_885_000 as Weight) - .saturating_add(T::DbWeight::get().reads(7 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + (27_354_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - (155_151_000 as Weight) - .saturating_add(T::DbWeight::get().reads(8 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + (27_652_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } } // For backwards compatibility and tests impl WeightInfo for () { fn send_minimal_message_worst_case() -> Weight { - (117_480_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(7 as Weight)) - .saturating_add(RocksDbWeight::get().writes(12 as Weight)) + (37_948_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(10 as Weight)) } fn send_1_kb_message_worst_case() -> Weight { - (128_391_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(7 as Weight)) - .saturating_add(RocksDbWeight::get().writes(12 as Weight)) + (39_158_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(10 as Weight)) } fn send_16_kb_message_worst_case() -> Weight { - (149_149_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(7 as Weight)) - .saturating_add(RocksDbWeight::get().writes(12 as Weight)) + (48_698_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(10 as Weight)) } fn maximal_increase_message_fee() -> Weight { - (6_015_058_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (2_919_864_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn increase_message_fee(i: u32) -> Weight { (0 as Weight) - .saturating_add((2_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + .saturating_add((1_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_single_message_proof() -> Weight { - (179_892_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (25_992_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn receive_two_messages_proof() -> Weight { - (291_793_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (37_016_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - (192_191_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (31_589_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn receive_single_message_proof_1_kb() -> Weight { - (202_104_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (25_962_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_single_message_proof_16_kb() -> Weight { - (357_144_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (74_385_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_single_prepaid_message_proof() -> Weight { - (122_648_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (26_159_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn receive_delivery_proof_for_single_message() -> Weight { - (107_631_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (27_590_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - (113_885_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(7 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (27_354_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - (155_151_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(8 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + (27_652_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } } diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs index 5699ec5b2..4b8eb544d 100644 --- a/modules/messages/src/weights_ext.rs +++ b/modules/messages/src/weights_ext.rs @@ -79,10 +79,12 @@ pub fn ensure_weights_are_correct( // verify `receive_messages_delivery_proof` weight components assert_ne!(W::receive_messages_delivery_proof_overhead(), 0); - assert_ne!(W::receive_messages_delivery_proof_messages_overhead(1), 0); - assert_ne!(W::receive_messages_delivery_proof_relayers_overhead(1), 0); assert_ne!(W::storage_proof_size_overhead(1), 0); + // `receive_messages_delivery_proof_messages_overhead` and + // `receive_messages_delivery_proof_relayers_overhead` may return zero if rewards are not paid + // during confirmations delivery, so we're not checking it here + // verify that the hardcoded value covers `receive_messages_delivery_proof` weight let actual_messages_delivery_confirmation_tx_weight = W::receive_messages_delivery_proof_weight( &PreComputedSize(W::expected_extra_storage_proof_size() as usize), @@ -199,7 +201,7 @@ pub trait WeightInfoExt: WeightInfo { /// Weight of message send extrinsic. fn send_message_weight(message: &impl Size, db_weight: RuntimeDbWeight) -> Weight { let transaction_overhead = Self::send_message_overhead(); - let message_size_overhead = Self::send_message_size_overhead(message.size_hint()); + let message_size_overhead = Self::send_message_size_overhead(message.size()); let call_back_overhead = Self::single_message_callback_overhead(db_weight); transaction_overhead @@ -225,7 +227,7 @@ pub trait WeightInfoExt: WeightInfo { let expected_proof_size = EXPECTED_DEFAULT_MESSAGE_LENGTH .saturating_mul(messages_count.saturating_sub(1)) .saturating_add(Self::expected_extra_storage_proof_size()); - let actual_proof_size = proof.size_hint(); + let actual_proof_size = proof.size(); let proof_size_overhead = Self::storage_proof_size_overhead( actual_proof_size.saturating_sub(expected_proof_size), ); @@ -253,7 +255,7 @@ pub trait WeightInfoExt: WeightInfo { // proof size overhead weight let expected_proof_size = Self::expected_extra_storage_proof_size(); - let actual_proof_size = proof.size_hint(); + let actual_proof_size = proof.size(); let proof_size_overhead = Self::storage_proof_size_overhead( actual_proof_size.saturating_sub(expected_proof_size), ); diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml index 6ed9552e5..8636c7a40 100644 --- a/modules/parachains/Cargo.toml +++ b/modules/parachains/Cargo.toml @@ -13,12 +13,14 @@ serde = { version = "1.0.101", optional = true } # Bridge Dependencies +bp-parachains = { path = "../../primitives/parachains", default-features = false } bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } pallet-bridge-grandpa = { path = "../grandpa", default-features = false } # Substrate Dependencies +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -34,6 +36,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } [features] default = ["std"] std = [ + "bp-parachains/std", "bp-polkadot-core/std", "bp-runtime/std", "codec/std", @@ -48,3 +51,6 @@ std = [ "sp-std/std", "sp-trie/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] diff --git a/modules/parachains/src/benchmarking.rs b/modules/parachains/src/benchmarking.rs new file mode 100644 index 000000000..aba296dfc --- /dev/null +++ b/modules/parachains/src/benchmarking.rs @@ -0,0 +1,106 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachains finality pallet benchmarking. + +use crate::{ + weights_ext::DEFAULT_PARACHAIN_HEAD_SIZE, Call, RelayBlockHash, RelayBlockHasher, + RelayBlockNumber, +}; + +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use bp_runtime::StorageProofSize; +use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_system::RawOrigin; +use sp_std::prelude::*; + +/// Pallet we're benchmarking here. +pub struct Pallet, I: 'static>(crate::Pallet); + +/// Trait that must be implemented by runtime to benchmark the parachains finality pallet. +pub trait Config: crate::Config { + /// Generate parachain heads proof and prepare environment for verifying this proof. + fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + proof_size: StorageProofSize, + ) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>); +} + +benchmarks_instance_pallet! { + where_clause { + where + >::BridgedChain: + bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, + } + + // Benchmark `submit_parachain_heads` extrinsic with different number of parachains. + submit_parachain_heads_with_n_parachains { + let p in 1..1024; + + let sender = account("sender", 0, 0); + let parachains = (1..=p).map(ParaId).collect::>(); + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::Minimal(0), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 1kb proof size. + submit_parachain_heads_with_1kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![ParaId(1)]; + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasExtraNodes(1024), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 16kb proof size. + submit_parachain_heads_with_16kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![ParaId(1)]; + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasExtraNodes(16 * 1024), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } +} diff --git a/modules/parachains/src/extension.rs b/modules/parachains/src/extension.rs new file mode 100644 index 000000000..18e718da0 --- /dev/null +++ b/modules/parachains/src/extension.rs @@ -0,0 +1,156 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{Config, Pallet, RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use bp_runtime::FilterCall; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use sp_runtime::transaction_validity::{TransactionValidity, ValidTransaction}; + +/// Validate parachain heads in order to avoid "mining" transactions that provide +/// outdated bridged parachain heads. Without this validation, even honest relayers +/// may lose their funds if there are multiple relays running and submitting the +/// same information. +/// +/// This validation only works with transactions that are updating single parachain +/// head. We can't use unbounded validation - it may take too long and either break +/// block production, or "eat" significant portion of block production time literally +/// for nothing. In addition, the single-parachain-head-per-transaction is how the +/// pallet will be used in our environment. +impl< + Call: IsSubType, T>>, + T: frame_system::Config + Config, + I: 'static, + > FilterCall for Pallet +where + >::BridgedChain: + bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, +{ + fn validate(call: &Call) -> TransactionValidity { + let (updated_at_relay_block_number, parachains) = match call.is_sub_type() { + Some(crate::Call::::submit_parachain_heads { + ref at_relay_block, + ref parachains, + .. + }) => (at_relay_block.0, parachains), + _ => return Ok(ValidTransaction::default()), + }; + let (parachain, parachain_head_hash) = match parachains.as_slice() { + &[(parachain, parachain_head_hash)] => (parachain, parachain_head_hash), + _ => return Ok(ValidTransaction::default()), + }; + + let maybe_stored_best_head = crate::BestParaHeads::::get(parachain); + Self::validate_updated_parachain_head( + parachain, + &maybe_stored_best_head, + updated_at_relay_block_number, + parachain_head_hash, + "Rejecting obsolete parachain-head transaction", + ) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + extension::FilterCall, + mock::{run_test, Call, TestRuntime}, + BestParaHead, BestParaHeads, RelayBlockNumber, + }; + use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; + + fn validate_submit_parachain_heads( + num: RelayBlockNumber, + parachains: Vec<(ParaId, ParaHash)>, + ) -> bool { + crate::Pallet::::validate(&Call::Parachains( + crate::Call::::submit_parachain_heads { + at_relay_block: (num, Default::default()), + parachains, + parachain_heads_proof: ParaHeadsProof(Vec::new()), + }, + )) + .is_ok() + } + + fn sync_to_relay_header_10() { + BestParaHeads::::insert( + ParaId(1), + BestParaHead { + at_relay_block_number: 10, + head_hash: [1u8; 32].into(), + next_imported_hash_position: 0, + }, + ); + } + + #[test] + fn extension_rejects_header_from_the_obsolete_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(5, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_the_same_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(10, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_new_relay_block_with_same_hash() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(20, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_if_more_than_one_parachain_is_submitted() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5, but another + // parachain head is also supplied => tx is accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads( + 5, + vec![(ParaId(1), [1u8; 32].into()), (ParaId(2), [1u8; 32].into())] + )); + }); + } +} diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index 4d3066fb6..55f16129f 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -23,9 +23,14 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaId, ParachainHeadsProof}; +pub use weights::WeightInfo; +pub use weights_ext::WeightInfoExt; + +use bp_parachains::parachain_head_storage_key_at_source; +use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::StorageProofError; use codec::{Decode, Encode}; -use frame_support::RuntimeDebug; +use frame_support::{traits::Contains, weights::PostDispatchInfo, RuntimeDebug}; use scale_info::TypeInfo; use sp_runtime::traits::Header as HeaderT; use sp_std::vec::Vec; @@ -33,9 +38,19 @@ use sp_std::vec::Vec; // Re-export in crate namespace for `construct_runtime!`. pub use pallet::*; +pub mod weights; +pub mod weights_ext; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +mod extension; #[cfg(test)] mod mock; +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-parachains"; + /// Block hash of the bridged relay chain. pub type RelayBlockHash = bp_polkadot_core::Hash; /// Block number of the bridged relay chain. @@ -54,18 +69,41 @@ pub struct BestParaHead { pub next_imported_hash_position: u32, } +/// Artifacts of the parachains head update. +struct UpdateParachainHeadArtifacts { + /// New best head of the parachain. + pub best_head: BestParaHead, + /// If `true`, some old parachain head has been pruned during update. + pub prune_happened: bool, +} + #[frame_support::pallet] pub mod pallet { use super::*; + use bp_parachains::ImportedParaHeadsKeyProvider; + use bp_runtime::{BasicOperatingMode, OwnedBridgeModule, StorageDoubleMapKeyProvider}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + /// Weight info of the given parachains pallet. + pub type WeightInfoOf = >::WeightInfo; + #[pallet::error] pub enum Error { - /// Relay chain block is unknown to us. + /// Relay chain block hash is unknown to us. UnknownRelayChainBlock, + /// The number of stored relay block is different from what the relayer has provided. + InvalidRelayChainBlockNumber, /// Invalid storage proof has been passed. InvalidStorageProof, + /// Given parachain head is unknown. + UnknownParaHead, + /// The storage proof doesn't contains storage root. So it is invalid for given header. + StorageRootMismatch, + /// Failed to extract state root from given parachain head. + FailedToExtractStateRoot, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), } #[pallet::config] @@ -73,21 +111,54 @@ pub mod pallet { pub trait Config: pallet_bridge_grandpa::Config { - /// Instance of bridges GRANDPA pallet that this pallet is linked to. + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfoExt; + + /// Instance of bridges GRANDPA pallet (within this runtime) that this pallet is linked to. /// /// The GRANDPA pallet instance must be configured to import headers of relay chain that /// we're interested in. type BridgesGrandpaPalletInstance: 'static; + /// Name of the `paras` pallet in the `construct_runtime!()` call at the bridged chain. + #[pallet::constant] + type ParasPalletName: Get<&'static str>; + + /// Set of parachains that are tracked by this pallet. + /// + /// The set may be extended easily, without requiring any runtime upgrades. Removing tracked + /// parachain requires special handling - pruning existing heads and cleaning related data + /// structures. + type TrackedParachains: Contains; + /// Maximal number of single parachain heads to keep in the storage. /// /// The setting is there to prevent growing the on-chain state indefinitely. Note /// the setting does not relate to parachain block numbers - we will simply keep as much /// items in the storage, so it doesn't guarantee any fixed timeframe for heads. + /// + /// Incautious change of this constant may lead to orphan entries in the runtime storage. #[pallet::constant] type HeadsToKeep: Get; } + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume them. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). + #[pallet::storage] + pub type PalletOwner, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. + #[pallet::storage] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + /// Best parachain heads. #[pallet::storage] pub type BestParaHeads, I: 'static = ()> = @@ -95,8 +166,14 @@ pub mod pallet { /// Parachain heads which have been imported into the pallet. #[pallet::storage] - pub type ImportedParaHeads, I: 'static = ()> = - StorageDoubleMap<_, Blake2_128Concat, ParaId, Blake2_128Concat, ParaHash, ParaHead>; + pub type ImportedParaHeads, I: 'static = ()> = StorageDoubleMap< + _, + ::Hasher1, + ::Key1, + ::Hasher2, + ::Key2, + ::Value, + >; /// A ring buffer of imported parachain head hashes. Ordered by the insertion time. #[pallet::storage] @@ -108,6 +185,14 @@ pub mod pallet { #[pallet::without_storage_info] pub struct Pallet(PhantomData<(T, I)>); + impl, I: 'static> OwnedBridgeModule for Pallet { + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + type OwnerStorage = PalletOwner; + + const LOG_TARGET: &'static str = LOG_TARGET; + } + #[pallet::call] impl, I: 'static> Pallet where @@ -124,123 +209,240 @@ pub mod pallet { /// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain. /// The proof is supposed to be crafted at the `relay_header_hash` that must already be /// imported by corresponding GRANDPA pallet at this chain. - #[pallet::weight(0)] // TODO: https://github.com/paritytech/parity-bridges-common/issues/1391 + #[pallet::weight(WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + parachain_heads_proof, + parachains.len() as _, + ))] pub fn submit_parachain_heads( _origin: OriginFor, - relay_block_hash: RelayBlockHash, - parachains: Vec, - parachain_heads_proof: ParachainHeadsProof, - ) -> DispatchResult { + at_relay_block: (RelayBlockNumber, RelayBlockHash), + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; // we'll need relay chain header to verify that parachains heads are always increasing. + let (relay_block_number, relay_block_hash) = at_relay_block; let relay_block = pallet_bridge_grandpa::ImportedHeaders::< T, T::BridgesGrandpaPalletInstance, >::get(relay_block_hash) .ok_or(Error::::UnknownRelayChainBlock)?; - let relay_block_number = *relay_block.number(); + ensure!( + *relay_block.number() == relay_block_number, + Error::::InvalidRelayChainBlockNumber, + ); // now parse storage proof and read parachain heads + let mut actual_weight = WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + ¶chain_heads_proof, + parachains.len() as _, + ); pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( relay_block_hash, - sp_trie::StorageProof::new(parachain_heads_proof), + sp_trie::StorageProof::new(parachain_heads_proof.0), move |storage| { - for parachain in parachains { - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1393 + for (parachain, parachain_head_hash) in parachains { + // if we're not tracking this parachain, we'll just ignore its head proof here + if !T::TrackedParachains::contains(¶chain) { + log::trace!( + target: LOG_TARGET, + "The head of parachain {:?} has been provided, but it is not tracked by the pallet", + parachain, + ); + continue; + } + let parachain_head = match Pallet::::read_parachain_head(&storage, parachain) { - Some(parachain_head) => parachain_head, - None => { + Ok(Some(parachain_head)) => parachain_head, + Ok(None) => { log::trace!( - target: "runtime::bridge-parachains", - "The head of parachain {:?} has been declared, but is missing from the proof", + target: LOG_TARGET, + "The head of parachain {:?} is None. {}", parachain, + if BestParaHeads::::contains_key(¶chain) { + "Looks like it is not yet registered at the source relay chain" + } else { + "Looks like it has been deregistered from the source relay chain" + }, ); continue; - } + }, + Err(e) => { + log::trace!( + target: LOG_TARGET, + "The read of head of parachain {:?} has failed: {:?}", + parachain, + e, + ); + continue; + }, }; - let _: Result<_, ()> = BestParaHeads::::try_mutate(parachain, |stored_best_head| { - *stored_best_head = Some(Pallet::::update_parachain_head( + // if relayer has specified invalid parachain head hash, ignore the head + // (this isn't strictly necessary, but better safe than sorry) + let actual_parachain_head_hash = parachain_head.hash(); + if parachain_head_hash != actual_parachain_head_hash { + log::trace!( + target: LOG_TARGET, + "The submitter has specified invalid parachain {:?} head hash: {:?} vs {:?}", + parachain, + parachain_head_hash, + actual_parachain_head_hash, + ); + continue; + } + + let prune_happened: Result<_, ()> = BestParaHeads::::try_mutate(parachain, |stored_best_head| { + let artifacts = Pallet::::update_parachain_head( parachain, stored_best_head.take(), relay_block_number, parachain_head, - )?); - Ok(()) + parachain_head_hash, + )?; + *stored_best_head = Some(artifacts.best_head); + Ok(artifacts.prune_happened) }); + + if matches!(prune_happened, Err(_) | Ok(false)) { + actual_weight = actual_weight + .saturating_sub(WeightInfoOf::::parachain_head_pruning_weight(T::DbWeight::get())); + } } }, ) .map_err(|_| Error::::InvalidStorageProof)?; - // TODO: there may be parachains we are not interested in - so we only need to accept - // intersection of `parachains-interesting-to-us` and `parachains` - // https://github.com/paritytech/parity-bridges-common/issues/1392 + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + } - // TODO: if some parachain is no more interesting to us, we should start pruning its - // heads - // https://github.com/paritytech/parity-bridges-common/issues/1392 + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } - Ok(()) + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) } } impl, I: 'static> Pallet { + /// Get best finalized header of the given parachain. + pub fn best_parachain_head(parachain: ParaId) -> Option { + let best_para_head_hash = BestParaHeads::::get(parachain)?.head_hash; + ImportedParaHeads::::get(parachain, best_para_head_hash) + } + + /// Get parachain head with given hash. + pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option { + ImportedParaHeads::::get(parachain, hash) + } + + /// Verify that the passed storage proof is valid, given it is crafted using + /// known finalized header. If the proof is valid, then the `parse` callback + /// is called and the function returns its result. + pub fn parse_finalized_storage_proof( + parachain: ParaId, + hash: ParaHash, + storage_proof: sp_trie::StorageProof, + decode_state_root: impl FnOnce(ParaHead) -> Option, + parse: impl FnOnce(bp_runtime::StorageProofChecker) -> R, + ) -> Result { + let para_head = + Self::parachain_head(parachain, hash).ok_or(Error::::UnknownParaHead)?; + let state_root = + decode_state_root(para_head).ok_or(Error::::FailedToExtractStateRoot)?; + let storage_proof_checker = + bp_runtime::StorageProofChecker::new(state_root, storage_proof) + .map_err(|_| Error::::StorageRootMismatch)?; + + Ok(parse(storage_proof_checker)) + } + /// Read parachain head from storage proof. fn read_parachain_head( storage: &bp_runtime::StorageProofChecker, parachain: ParaId, - ) -> Option { - let parachain_head_key = storage_keys::parachain_head_key(parachain); - let parachain_head = storage.read_value(parachain_head_key.0.as_ref()).ok()??; - let parachain_head = ParaHead::decode(&mut ¶chain_head[..]).ok()?; - Some(parachain_head) + ) -> Result, StorageProofError> { + let parachain_head_key = + parachain_head_storage_key_at_source(T::ParasPalletName::get(), parachain); + storage.read_and_decode_value(parachain_head_key.0.as_ref()) + } + + /// Check if para head has been already updated at better relay chain block. + /// Without this check, we may import heads in random order. + pub fn validate_updated_parachain_head( + parachain: ParaId, + maybe_stored_best_head: &Option, + updated_at_relay_block_number: RelayBlockNumber, + updated_head_hash: ParaHash, + err_log_prefix: &str, + ) -> TransactionValidity { + let stored_best_head = match maybe_stored_best_head { + Some(stored_best_head) => stored_best_head, + None => return Ok(ValidTransaction::default()), + }; + + if stored_best_head.at_relay_block_number >= updated_at_relay_block_number { + log::trace!( + target: LOG_TARGET, + "{}. The parachain head for {:?} was already updated at better relay chain block {} >= {}.", + err_log_prefix, + parachain, + stored_best_head.at_relay_block_number, + updated_at_relay_block_number + ); + return InvalidTransaction::Stale.into(); + } + + if stored_best_head.head_hash == updated_head_hash { + log::trace!( + target: LOG_TARGET, + "{}. The parachain head hash for {:?} was already updated to {} at block {} < {}.", + err_log_prefix, + parachain, + updated_head_hash, + stored_best_head.at_relay_block_number, + updated_at_relay_block_number + ); + return InvalidTransaction::Stale.into(); + } + + Ok(ValidTransaction::default()) } /// Try to update parachain head. - fn update_parachain_head( + pub(super) fn update_parachain_head( parachain: ParaId, stored_best_head: Option, updated_at_relay_block_number: RelayBlockNumber, updated_head: ParaHead, - ) -> Result { + updated_head_hash: ParaHash, + ) -> Result { // check if head has been already updated at better relay chain block. Without this // check, we may import heads in random order - let updated_head_hash = updated_head.hash(); - let next_imported_hash_position = match stored_best_head { - Some(stored_best_head) - if stored_best_head.at_relay_block_number <= updated_at_relay_block_number => - { - // check if this head has already been imported before - if updated_head_hash == stored_best_head.head_hash { - log::trace!( - target: "runtime::bridge-parachains", - "The head of parachain {:?} can't be updated to {}, because it has been already updated\ - to the same value at previous relay chain block: {} < {}", - parachain, - updated_head_hash, - stored_best_head.at_relay_block_number, - updated_at_relay_block_number, - ); - return Err(()); - } - - stored_best_head.next_imported_hash_position - }, - None => 0, - Some(stored_best_head) => { - log::trace!( - target: "runtime::bridge-parachains", - "The head of parachain {:?} can't be updated to {}, because it has been already updated\ - to {} at better relay chain block: {} > {}", - parachain, - updated_head_hash, - stored_best_head.head_hash, - stored_best_head.at_relay_block_number, - updated_at_relay_block_number, - ); - return Err(()); - }, - }; + Self::validate_updated_parachain_head( + parachain, + &stored_best_head, + updated_at_relay_block_number, + updated_head_hash, + "The parachain head can't be updated", + ) + .map_err(|_| ())?; + let next_imported_hash_position = stored_best_head + .map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position); // insert updated best parachain head let head_hash_to_prune = @@ -257,11 +459,18 @@ pub mod pallet { updated_head_hash, ); ImportedParaHeads::::insert(parachain, updated_head_hash, updated_head); + log::trace!( + target: LOG_TARGET, + "Updated head of parachain {:?} to {}", + parachain, + updated_head_hash, + ); // remove old head + let prune_happened = head_hash_to_prune.is_ok(); if let Ok(head_hash_to_prune) = head_hash_to_prune { log::trace!( - target: "runtime::bridge-parachains", + target: LOG_TARGET, "Pruning old head of parachain {:?}: {}", parachain, head_hash_to_prune, @@ -269,30 +478,62 @@ pub mod pallet { ImportedParaHeads::::remove(parachain, head_hash_to_prune); } - Ok(updated_best_para_head) + Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) } } -} -pub mod storage_keys { - use super::*; - use bp_runtime::storage_map_final_key; - use frame_support::Twox64Concat; - use sp_core::storage::StorageKey; + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Initial pallet operating mode. + pub operating_mode: BasicOperatingMode, + /// Initial pallet owner. + pub owner: Option, + /// Dummy marker. + pub phantom: sp_std::marker::PhantomData, + } - /// Storage key of the parachain head in the runtime storage of relay chain. - pub fn parachain_head_key(parachain: ParaId) -> StorageKey { - storage_map_final_key::("Paras", "Heads", ¶chain.encode()) + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + operating_mode: Default::default(), + owner: Default::default(), + phantom: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + PalletOperatingMode::::put(&self.operating_mode); + if let Some(ref owner) = self.owner { + PalletOwner::::put(owner); + } + } } } #[cfg(test)] mod tests { use super::*; - use crate::mock::{run_test, test_relay_header, Origin, TestRuntime}; + use crate::mock::{ + run_test, test_relay_header, Origin, TestRuntime, PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID, + }; - use bp_test_utils::{authority_list, make_default_justification}; - use frame_support::{assert_noop, assert_ok, traits::OnInitialize}; + use bp_parachains::ImportedParaHeadsKeyProvider; + use bp_runtime::{BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider}; + use bp_test_utils::{ + authority_list, generate_owned_bridge_module_tests, make_default_justification, + }; + use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchResultWithPostInfo, + storage::generator::{StorageDoubleMap, StorageMap}, + traits::{Get, OnInitialize}, + weights::Weight, + }; + use sp_runtime::DispatchError; use sp_trie::{ record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut, }; @@ -306,7 +547,7 @@ mod tests { header: Box::new(test_relay_header(0, state_root)), authority_list: authority_list(), set_id: 1, - is_halted: false, + operating_mode: BasicOperatingMode::Normal, }, ) .unwrap(); @@ -329,17 +570,20 @@ mod tests { } fn prepare_parachain_heads_proof( - heads: Vec<(ParaId, ParaHead)>, - ) -> (RelayBlockHash, ParachainHeadsProof) { + heads: Vec<(u32, ParaHead)>, + ) -> (RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) { + let mut parachains = Vec::with_capacity(heads.len()); let mut root = Default::default(); let mut mdb = MemoryDB::default(); { let mut trie = TrieDBMutV1::::new(&mut mdb, &mut root); for (parachain, head) in heads { - let storage_key = storage_keys::parachain_head_key(parachain); + let storage_key = + parachain_head_storage_key_at_source(PARAS_PALLET_NAME, ParaId(parachain)); trie.insert(&storage_key.0, &head.encode()) .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in tests"); + parachains.push((ParaId(parachain), head.hash())); } } @@ -350,7 +594,7 @@ mod tests { .expect("record_all_keys should not fail in benchmarks"); let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); - (root, storage_proof) + (root, ParaHeadsProof(storage_proof), parachains) } fn initial_best_head(parachain: u32) -> BestParaHead { @@ -372,30 +616,70 @@ mod tests { fn import_parachain_1_head( relay_chain_block: RelayBlockNumber, relay_state_root: RelayBlockHash, - proof: ParachainHeadsProof, - ) -> sp_runtime::DispatchResult { + parachains: Vec<(ParaId, ParaHash)>, + proof: ParaHeadsProof, + ) -> DispatchResultWithPostInfo { Pallet::::submit_parachain_heads( Origin::signed(1), - test_relay_header(relay_chain_block, relay_state_root).hash(), - vec![ParaId(1)], + (relay_chain_block, test_relay_header(relay_chain_block, relay_state_root).hash()), + parachains, proof, ) } + fn weight_of_import_parachain_1_head(proof: &ParaHeadsProof, prune_expected: bool) -> Weight { + let db_weight = ::DbWeight::get(); + WeightInfoOf::::submit_parachain_heads_weight(db_weight, proof, 1) + .saturating_sub(if prune_expected { + 0 + } else { + WeightInfoOf::::parachain_head_pruning_weight(db_weight) + }) + } + + #[test] + fn submit_parachain_heads_checks_operating_mode() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + + run_test(|| { + initialize(state_root); + + // `submit_parachain_heads()` should fail when the pallet is halted. + PalletOperatingMode::::put(BasicOperatingMode::Halted); + assert_noop!( + Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains.clone(), + proof.clone(), + ), + Error::::BridgeModule(OwnedBridgeModuleError::Halted) + ); + + // `submit_parachain_heads()` should succeed now that the pallet is resumed. + PalletOperatingMode::::put(BasicOperatingMode::Normal); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ),); + }); + } + #[test] fn imports_initial_parachain_heads() { - let (state_root, proof) = prepare_parachain_heads_proof(vec![ - (ParaId(1), head_data(1, 0)), - (ParaId(3), head_data(3, 10)), - ]); + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0)), (3, head_data(3, 10))]); run_test(|| { initialize(state_root); // we're trying to update heads of parachains 1, 2 and 3 assert_ok!(Pallet::::submit_parachain_heads( Origin::signed(1), - test_relay_header(0, state_root).hash(), - vec![ParaId(1), ParaId(2), ParaId(3)], + (0, test_relay_header(0, state_root).hash()), + parachains, proof, ),); @@ -428,14 +712,14 @@ mod tests { #[test] fn imports_parachain_heads_is_able_to_progress() { - let (state_root_5, proof_5) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 5))]); - let (state_root_10, proof_10) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 10))]); + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10, parachains_10) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); run_test(|| { // start with relay block #0 and import head#5 of parachain#1 initialize(state_root_5); - assert_ok!(import_parachain_1_head(0, state_root_5, proof_5)); + assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); assert_eq!( BestParaHeads::::get(ParaId(1)), Some(BestParaHead { @@ -455,7 +739,7 @@ mod tests { // import head#10 of parachain#1 at relay block #1 proceed(1, state_root_10); - assert_ok!(import_parachain_1_head(1, state_root_10, proof_10)); + assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); assert_eq!( BestParaHeads::::get(ParaId(1)), Some(BestParaHead { @@ -475,36 +759,74 @@ mod tests { }); } + #[test] + fn ignores_untracked_parachain() { + let (state_root, proof, parachains) = prepare_parachain_heads_proof(vec![ + (1, head_data(1, 5)), + (UNTRACKED_PARACHAIN_ID, head_data(1, 5)), + (2, head_data(1, 5)), + ]); + run_test(|| { + // start with relay block #0 and try to import head#5 of parachain#1 and untracked + // parachain + initialize(state_root); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + )); + assert_eq!( + BestParaHeads::::get(ParaId(1)), + Some(BestParaHead { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash(), + next_imported_hash_position: 1, + }) + ); + assert_eq!(BestParaHeads::::get(ParaId(UNTRACKED_PARACHAIN_ID)), None,); + assert_eq!( + BestParaHeads::::get(ParaId(2)), + Some(BestParaHead { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash(), + next_imported_hash_position: 1, + }) + ); + }); + } + #[test] fn does_nothing_when_already_imported_this_head_at_previous_relay_header() { - let (state_root, proof) = prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 0))]); + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); run_test(|| { // import head#0 of parachain#1 at relay block#0 initialize(state_root); - assert_ok!(import_parachain_1_head(0, state_root, proof.clone())); + assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone())); assert_eq!(BestParaHeads::::get(ParaId(1)), Some(initial_best_head(1))); // try to import head#0 of parachain#1 at relay block#1 // => call succeeds, but nothing is changed proceed(1, state_root); - assert_ok!(import_parachain_1_head(1, state_root, proof)); + assert_ok!(import_parachain_1_head(1, state_root, parachains, proof)); assert_eq!(BestParaHeads::::get(ParaId(1)), Some(initial_best_head(1))); }); } #[test] fn does_nothing_when_already_imported_head_at_better_relay_header() { - let (state_root_5, proof_5) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 5))]); - let (state_root_10, proof_10) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 10))]); + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10, parachains_10) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); run_test(|| { // start with relay block #0 initialize(state_root_5); // head#10 of parachain#1 at relay block#1 proceed(1, state_root_10); - assert_ok!(import_parachain_1_head(1, state_root_10, proof_10)); + assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); assert_eq!( BestParaHeads::::get(ParaId(1)), Some(BestParaHead { @@ -514,9 +836,9 @@ mod tests { }) ); - // now try to import head#1 at relay block#0 + // now try to import head#5 at relay block#0 // => nothing is changed, because better head has already been imported - assert_ok!(import_parachain_1_head(0, state_root_5, proof_5)); + assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); assert_eq!( BestParaHeads::::get(ParaId(1)), Some(BestParaHead { @@ -535,14 +857,18 @@ mod tests { // import exactly `HeadsToKeep` headers for i in 0..heads_to_keep { - let (state_root, proof) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, i))]); + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, i))]); if i == 0 { initialize(state_root); } else { proceed(i, state_root); } - assert_ok!(import_parachain_1_head(i, state_root, proof)); + + let expected_weight = weight_of_import_parachain_1_head(&proof, false); + let result = import_parachain_1_head(i, state_root, parachains, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); } // nothing is pruned yet @@ -552,10 +878,13 @@ mod tests { } // import next relay chain header and next parachain head - let (state_root, proof) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, heads_to_keep))]); + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, heads_to_keep))]); proceed(heads_to_keep, state_root); - assert_ok!(import_parachain_1_head(heads_to_keep, state_root, proof)); + let expected_weight = weight_of_import_parachain_1_head(&proof, true); + let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); // and the head#0 is pruned assert!( @@ -570,14 +899,15 @@ mod tests { #[test] fn fails_on_unknown_relay_chain_block() { - let (state_root, proof) = prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 5))]); + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); run_test(|| { // start with relay block #0 initialize(state_root); // try to import head#5 of parachain#1 at unknown relay chain block #1 assert_noop!( - import_parachain_1_head(1, state_root, proof), + import_parachain_1_head(1, state_root, parachains, proof), Error::::UnknownRelayChainBlock ); }); @@ -585,17 +915,93 @@ mod tests { #[test] fn fails_on_invalid_storage_proof() { - let (_state_root, proof) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 5))]); + let (_state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); run_test(|| { // start with relay block #0 initialize(Default::default()); // try to import head#5 of parachain#1 at relay chain block #0 assert_noop!( - import_parachain_1_head(0, Default::default(), proof), + import_parachain_1_head(0, Default::default(), parachains, proof), Error::::InvalidStorageProof ); }); } + + #[test] + fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() { + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10_at_20, proof_10_at_20, parachains_10_at_20) = + prepare_parachain_heads_proof(vec![(2, head_data(2, 10))]); + let (state_root_10_at_30, proof_10_at_30, parachains_10_at_30) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + run_test(|| { + // we've already imported head#5 of parachain#1 at relay block#10 + initialize(state_root_5); + import_parachain_1_head(0, state_root_5, parachains_5, proof_5).expect("ok"); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(head_data(1, 5)) + ); + + // then if someone is pretending to provide updated head#10 of parachain#1 at relay + // block#20, but fails to do that + // + // => we'll leave previous value + proceed(20, state_root_10_at_20); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (20, test_relay_header(20, state_root_10_at_20).hash()), + parachains_10_at_20, + proof_10_at_20, + ),); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(head_data(1, 5)) + ); + + // then if someone is pretending to provide updated head#10 of parachain#1 at relay + // block#30, and actualy provides it + // + // => we'll update value + proceed(30, state_root_10_at_30); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (30, test_relay_header(30, state_root_10_at_30).hash()), + parachains_10_at_30, + proof_10_at_30, + ),); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(head_data(1, 10)) + ); + }); + } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + BestParaHeads::::storage_map_final_key(ParaId(42)).to_vec(), + bp_parachains::best_parachain_head_hash_storage_key_at_target("Parachains", ParaId(42)) + .0, + ); + + assert_eq!( + ImportedParaHeads::::storage_double_map_final_key( + ParaId(42), + ParaHash::from([21u8; 32]) + ) + .to_vec(), + ImportedParaHeadsKeyProvider::final_key( + "Parachains", + &ParaId(42), + &ParaHash::from([21u8; 32]) + ) + .0, + ); + } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); } diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index 141a1e49e..2341c829c 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -14,8 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +use bp_polkadot_core::parachains::ParaId; use bp_runtime::Chain; -use frame_support::{construct_runtime, parameter_types, weights::Weight}; +use frame_support::{construct_runtime, parameter_types, traits::IsInVec, weights::Weight}; use sp_runtime::{ testing::{Header, H256}, traits::{BlakeTwo256, Header as HeaderT, IdentityLookup}, @@ -33,6 +34,9 @@ pub type RelayBlockHeader = type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +pub const PARAS_PALLET_NAME: &str = "Paras"; +pub const UNTRACKED_PARACHAIN_ID: u32 = 10; + construct_runtime! { pub enum TestRuntime where Block = Block, @@ -42,7 +46,7 @@ construct_runtime! { System: frame_system::{Pallet, Call, Config, Storage, Event}, Grandpa1: pallet_bridge_grandpa::::{Pallet}, Grandpa2: pallet_bridge_grandpa::::{Pallet}, - Parachains: pallet_bridge_parachains::{Pallet}, + Parachains: pallet_bridge_parachains::{Call, Pallet}, } } @@ -103,11 +107,16 @@ impl pallet_bridge_grandpa::Config for TestRun parameter_types! { pub const HeadsToKeep: u32 = 4; + pub const ParasPalletName: &'static str = PARAS_PALLET_NAME; + pub GetTenFirstParachains: Vec = (0..10).map(ParaId).collect(); } impl pallet_bridge_parachains::Config for TestRuntime { type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; type HeadsToKeep = HeadsToKeep; + type ParasPalletName = ParasPalletName; + type TrackedParachains = IsInVec; + type WeightInfo = (); } #[derive(Debug)] diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs new file mode 100644 index 000000000..34cc23195 --- /dev/null +++ b/modules/parachains/src/weights.rs @@ -0,0 +1,102 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for `pallet_bridge_parachains` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-07-06, STEPS: 50, REPEAT: 20 +//! LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled +//! CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_parachains +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/parachains/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for `pallet_bridge_parachains`. +pub trait WeightInfo { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight; + fn submit_parachain_heads_with_1kb_proof() -> Weight; + fn submit_parachain_heads_with_16kb_proof() -> Weight; +} + +/// Weights for `pallet_bridge_parachains` using the Millau node and recommended hardware. +pub struct MillauWeight(PhantomData); +impl WeightInfo for MillauWeight { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + (0 as Weight) + .saturating_add((18_706_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(p as Weight))) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(p as Weight))) + } + + fn submit_parachain_heads_with_1kb_proof() -> Weight { + (27_549_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + + fn submit_parachain_heads_with_16kb_proof() -> Weight { + (80_792_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + (0 as Weight) + .saturating_add((18_706_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(p as Weight))) + .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(p as Weight))) + } + + fn submit_parachain_heads_with_1kb_proof() -> Weight { + (27_549_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + + fn submit_parachain_heads_with_16kb_proof() -> Weight { + (80_792_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } +} diff --git a/modules/parachains/src/weights_ext.rs b/modules/parachains/src/weights_ext.rs new file mode 100644 index 000000000..38c001d74 --- /dev/null +++ b/modules/parachains/src/weights_ext.rs @@ -0,0 +1,97 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Weight-related utilities. + +use crate::weights::{MillauWeight, WeightInfo}; + +use bp_runtime::Size; +use frame_support::weights::{RuntimeDbWeight, Weight}; + +/// Size of the regular parachain head. +/// +/// It's not that we are expecting all parachain heads to share the same size or that we would +/// reject all heads that have larger/lesser size. It is about head size that we use in benchmarks. +/// Relayer would need to pay additional fee for extra bytes. +/// +/// 384 is a bit larger (1.3 times) than the size of the randomly chosen Polkadot block. +pub const DEFAULT_PARACHAIN_HEAD_SIZE: u32 = 384; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// the Rialto chain. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Extended weight info. +pub trait WeightInfoExt: WeightInfo { + /// Storage proof overhead, that is included in every storage proof. + /// + /// The relayer would pay some extra fee for additional proof bytes, since they mean + /// more hashing operations. + fn expected_extra_storage_proof_size() -> u32; + + /// Weight of the parachain heads delivery extrinsic. + fn submit_parachain_heads_weight( + db_weight: RuntimeDbWeight, + proof: &impl Size, + parachains_count: u32, + ) -> Weight { + // weight of the `submit_parachain_heads` with exactly `parachains_count` parachain + // heads of the default size (`DEFAULT_PARACHAIN_HEAD_SIZE`) + let base_weight = Self::submit_parachain_heads_with_n_parachains(parachains_count); + + // overhead because of extra storage proof bytes + let expected_proof_size = parachains_count + .saturating_mul(DEFAULT_PARACHAIN_HEAD_SIZE) + .saturating_add(Self::expected_extra_storage_proof_size()); + let actual_proof_size = proof.size(); + let proof_size_overhead = Self::storage_proof_size_overhead( + actual_proof_size.saturating_sub(expected_proof_size), + ); + + // potential pruning weight (refunded if hasn't happened) + let pruning_weight = (parachains_count as Weight) + .saturating_mul(Self::parachain_head_pruning_weight(db_weight)); + + base_weight.saturating_add(proof_size_overhead).saturating_add(pruning_weight) + } + + /// Returns weight of single parachain head pruning. + fn parachain_head_pruning_weight(db_weight: RuntimeDbWeight) -> Weight { + // it's just one write operation, we don't want any benchmarks for that + db_weight.writes(1) + } + + /// Returns weight that needs to be accounted when storage proof of given size is received. + fn storage_proof_size_overhead(extra_proof_bytes: u32) -> Weight { + let extra_proof_bytes_in_bytes = extra_proof_bytes as Weight; + let extra_byte_weight = (Self::submit_parachain_heads_with_16kb_proof() + - Self::submit_parachain_heads_with_1kb_proof()) + / (15 * 1024); + extra_proof_bytes_in_bytes.saturating_mul(extra_byte_weight) + } +} + +impl WeightInfoExt for () { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} + +impl WeightInfoExt for MillauWeight { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} diff --git a/primitives/header-chain/src/justification.rs b/primitives/header-chain/src/justification.rs index bc9ab0046..7dfb72272 100644 --- a/primitives/header-chain/src/justification.rs +++ b/primitives/header-chain/src/justification.rs @@ -53,7 +53,7 @@ impl crate::FinalityProof for GrandpaJustification { } /// Justification verification error. -#[derive(RuntimeDebug, PartialEq)] +#[derive(Eq, RuntimeDebug, PartialEq)] pub enum Error { /// Failed to decode justification. JustificationDecode, diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs index 28949f28d..8ac797262 100644 --- a/primitives/header-chain/src/lib.rs +++ b/primitives/header-chain/src/lib.rs @@ -19,6 +19,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +use bp_runtime::BasicOperatingMode; use codec::{Codec, Decode, Encode, EncodeLike}; use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; use scale_info::TypeInfo; @@ -38,7 +39,7 @@ pub trait Parameter: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} /// A GRANDPA Authority List and ID. -#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone, TypeInfo)] +#[derive(Default, Encode, Eq, Decode, RuntimeDebug, PartialEq, Clone, TypeInfo)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct AuthoritySet { /// List of GRANDPA authorities for the current round. @@ -66,8 +67,8 @@ pub struct InitializationData { pub authority_list: AuthorityList, /// The ID of the initial authority set. pub set_id: SetId, - /// Should the pallet block transaction immediately after initialization. - pub is_halted: bool, + /// Pallet operating mode. + pub operating_mode: BasicOperatingMode, } /// base trait for verifying transaction inclusion proofs. @@ -88,7 +89,7 @@ pub trait InclusionProofVerifier { /// A trait for pallets which want to keep track of finalized headers from a bridged chain. pub trait HeaderChain { /// Get the best finalized header known to the header chain. - fn best_finalized() -> H; + fn best_finalized() -> Option; /// Get the best authority set known to the header chain. fn authority_set() -> AuthoritySet; @@ -98,8 +99,8 @@ pub trait HeaderChain { } impl HeaderChain for () { - fn best_finalized() -> H { - H::default() + fn best_finalized() -> Option { + None } fn authority_set() -> AuthoritySet { diff --git a/primitives/header-chain/src/storage_keys.rs b/primitives/header-chain/src/storage_keys.rs index e123703ee..bb642b181 100644 --- a/primitives/header-chain/src/storage_keys.rs +++ b/primitives/header-chain/src/storage_keys.rs @@ -17,25 +17,25 @@ //! Storage keys of bridge GRANDPA pallet. /// Name of the `IsHalted` storage value. -pub const IS_HALTED_VALUE_NAME: &str = "IsHalted"; +pub const PALLET_OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; /// Name of the `BestFinalized` storage value. pub const BEST_FINALIZED_VALUE_NAME: &str = "BestFinalized"; use sp_core::storage::StorageKey; -/// Storage key of the `IsHalted` flag in the runtime storage. -pub fn is_halted_key(pallet_prefix: &str) -> StorageKey { +/// Storage key of the `PalletOperatingMode` variable in the runtime storage. +pub fn pallet_operating_mode_key(pallet_prefix: &str) -> StorageKey { StorageKey( bp_runtime::storage_value_final_key( pallet_prefix.as_bytes(), - IS_HALTED_VALUE_NAME.as_bytes(), + PALLET_OPERATING_MODE_VALUE_NAME.as_bytes(), ) .to_vec(), ) } -/// Storage key of the best finalized header hash value in the runtime storage. -pub fn best_finalized_hash_key(pallet_prefix: &str) -> StorageKey { +/// Storage key of the best finalized header number and hash value in the runtime storage. +pub fn best_finalized_key(pallet_prefix: &str) -> StorageKey { StorageKey( bp_runtime::storage_value_final_key( pallet_prefix.as_bytes(), @@ -51,23 +51,23 @@ mod tests { use hex_literal::hex; #[test] - fn is_halted_key_computed_properly() { + fn pallet_operating_mode_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // compatibility with previous pallet. - let storage_key = is_halted_key("BridgeGrandpa").0; + let storage_key = pallet_operating_mode_key("BridgeGrandpa").0; assert_eq!( storage_key, - hex!("0b06f475eddb98cf933a12262e0388de9611a984bbd04e2fd39f97bbc006115f").to_vec(), + hex!("0b06f475eddb98cf933a12262e0388de0f4cf0917788d791142ff6c1f216e7b3").to_vec(), "Unexpected storage key: {}", hex::encode(&storage_key), ); } #[test] - fn best_finalized_hash_key_computed_properly() { + fn best_finalized_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // compatibility with previous pallet. - let storage_key = best_finalized_hash_key("BridgeGrandpa").0; + let storage_key = best_finalized_key("BridgeGrandpa").0; assert_eq!( storage_key, hex!("0b06f475eddb98cf933a12262e0388dea4ebafdd473c549fdb24c5c991c5591c").to_vec(), diff --git a/primitives/message-dispatch/src/lib.rs b/primitives/message-dispatch/src/lib.rs index 7401dd468..b017bb9f7 100644 --- a/primitives/message-dispatch/src/lib.rs +++ b/primitives/message-dispatch/src/lib.rs @@ -147,7 +147,7 @@ pub struct MessagePayload< impl Size for MessagePayload> { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { self.call.len() as _ } } diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs index b4daf0a8e..f6f90a71f 100644 --- a/primitives/messages/src/lib.rs +++ b/primitives/messages/src/lib.rs @@ -21,8 +21,8 @@ #![allow(clippy::too_many_arguments)] use bitvec::prelude::*; -use bp_runtime::messages::DispatchFeePayment; -use codec::{Decode, Encode}; +use bp_runtime::{messages::DispatchFeePayment, BasicOperatingMode, OperatingMode}; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::RuntimeDebug; use scale_info::TypeInfo; use sp_std::{collections::vec_deque::VecDeque, prelude::*}; @@ -35,11 +35,11 @@ pub mod target_chain; pub use frame_support::weights::Weight; /// Messages pallet operating mode. -#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -pub enum OperatingMode { - /// Normal mode, when all operations are allowed. - Normal, +pub enum MessagesOperatingMode { + /// Basic operating mode (Normal/Halted) + Basic(BasicOperatingMode), /// The pallet is not accepting outbound messages. Inbound messages and receiving proofs /// are still accepted. /// @@ -48,13 +48,20 @@ pub enum OperatingMode { /// queued messages to the bridged chain. Once upgrade is completed, the mode may be switched /// back to `Normal`. RejectingOutboundMessages, - /// The pallet is halted. All operations (except operating mode change) are prohibited. - Halted, } -impl Default for OperatingMode { +impl Default for MessagesOperatingMode { fn default() -> Self { - OperatingMode::Normal + MessagesOperatingMode::Basic(BasicOperatingMode::Normal) + } +} + +impl OperatingMode for MessagesOperatingMode { + fn is_halted(&self) -> bool { + match self { + Self::Basic(operating_mode) => operating_mode.is_halted(), + _ => false, + } } } @@ -81,7 +88,7 @@ pub type BridgeMessageId = (LaneId, MessageNonce); pub type MessagePayload = Vec; /// Message key (unique message identifier) as it is stored in the storage. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct MessageKey { /// ID of the message lane. pub lane_id: LaneId, @@ -150,13 +157,13 @@ impl InboundLaneData { /// Returns approximate size of the struct, given a number of entries in the `relayers` set and /// size of each entry. /// - /// Returns `None` if size overflows `u32` limits. - pub fn encoded_size_hint( - relayer_id_encoded_size: u32, - relayers_entries: u32, - messages_count: u32, - ) -> Option { - let message_nonce_size = 8; + /// Returns `None` if size overflows `usize` limits. + pub fn encoded_size_hint(relayers_entries: usize, messages_count: usize) -> Option + where + RelayerId: MaxEncodedLen, + { + let message_nonce_size = MessageNonce::max_encoded_len(); + let relayer_id_encoded_size = RelayerId::max_encoded_len(); let relayers_entry_size = relayer_id_encoded_size.checked_add(2 * message_nonce_size)?; let relayers_size = relayers_entries.checked_mul(relayers_entry_size)?; let dispatch_results_per_byte = 8; @@ -167,18 +174,34 @@ impl InboundLaneData { .and_then(|result| result.checked_add(dispatch_result_size)) } + /// Returns the approximate size of the struct as u32, given a number of entries in the + /// `relayers` set and the size of each entry. + /// + /// Returns `u32::MAX` if size overflows `u32` limits. + pub fn encoded_size_hint_u32(relayers_entries: usize, messages_count: usize) -> u32 + where + RelayerId: MaxEncodedLen, + { + Self::encoded_size_hint(relayers_entries, messages_count) + .and_then(|x| u32::try_from(x).ok()) + .unwrap_or(u32::MAX) + } + /// Nonce of the last message that has been delivered to this (target) chain. pub fn last_delivered_nonce(&self) -> MessageNonce { self.relayers.back().map(|entry| entry.messages.end).unwrap_or(self.last_confirmed_nonce) } } -/// Message details, returned by runtime APIs. +/// Outbound message details, returned by runtime APIs. #[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] -pub struct MessageDetails { +pub struct OutboundMessageDetails { /// Nonce assigned to the message. pub nonce: MessageNonce, - /// Message dispatch weight, declared by the submitter. + /// Message dispatch weight. + /// + /// Depending on messages pallet configuration, it may be declared by the message submitter, + /// computed automatically or just be zero if dispatch fee is paid at the target chain. pub dispatch_weight: Weight, /// Size of the encoded message. pub size: u32, @@ -188,6 +211,18 @@ pub struct MessageDetails { pub dispatch_fee_payment: DispatchFeePayment, } +/// Inbound message details, returned by runtime APIs. +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] +pub struct InboundMessageDetails { + /// Computed message dispatch weight. + /// + /// Runtime API guarantees that it will match the value, returned by + /// `target_chain::MessageDispatch::dispatch_weight`. This means that if the runtime + /// has failed to decode the message, it will be zero - that's because `undecodable` + /// message cannot be dispatched. + pub dispatch_weight: Weight, +} + /// Bit vector of message dispatch results. pub type DispatchResultsBitVec = BitVec; @@ -271,10 +306,15 @@ pub struct UnrewardedRelayersState { pub messages_in_oldest_entry: MessageNonce, /// Total number of messages in the relayers vector. pub total_messages: MessageNonce, + /// Nonce of the latest message that has been delivered to the target chain. + /// + /// This corresponds to the result of the `InboundLaneData::last_delivered_nonce` call + /// at the bridged chain. + pub last_delivered_nonce: MessageNonce, } /// Outbound lane data. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] pub struct OutboundLaneData { /// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated /// message if all sent messages are already pruned. @@ -348,11 +388,8 @@ mod tests { (13u8, 128u8), ]; for (relayer_entries, messages_count) in test_cases { - let expected_size = InboundLaneData::::encoded_size_hint( - 1, - relayer_entries as _, - messages_count as _, - ); + let expected_size = + InboundLaneData::::encoded_size_hint(relayer_entries as _, messages_count as _); let actual_size = InboundLaneData { relayers: (1u8..=relayer_entries) .map(|i| { diff --git a/primitives/messages/src/source_chain.rs b/primitives/messages/src/source_chain.rs index 2f0c43790..b86a6d386 100644 --- a/primitives/messages/src/source_chain.rs +++ b/primitives/messages/src/source_chain.rs @@ -150,7 +150,7 @@ pub trait MessageDeliveryAndDispatchPayment { } /// Send message artifacts. -#[derive(RuntimeDebug, PartialEq)] +#[derive(Eq, RuntimeDebug, PartialEq)] pub struct SendMessageArtifacts { /// Nonce of the message. pub nonce: MessageNonce, @@ -175,7 +175,7 @@ pub trait MessagesBridge { } /// Bridge that does nothing when message is being sent. -#[derive(RuntimeDebug, PartialEq)] +#[derive(Eq, RuntimeDebug, PartialEq)] pub struct NoopMessagesBridge; impl diff --git a/primitives/messages/src/target_chain.rs b/primitives/messages/src/target_chain.rs index 685a4e9a8..9f009eb2e 100644 --- a/primitives/messages/src/target_chain.rs +++ b/primitives/messages/src/target_chain.rs @@ -93,9 +93,10 @@ pub trait MessageDispatch { /// Estimate dispatch weight. /// - /// This function must: (1) be instant and (2) return correct upper bound - /// of dispatch weight. - fn dispatch_weight(message: &DispatchMessage) -> Weight; + /// This function must return correct upper bound of dispatch weight. The return value + /// of this function is expected to match return value of the corresponding + /// `FromInboundLaneApi::message_details().dispatch_weight` call. + fn dispatch_weight(message: &mut DispatchMessage) -> Weight; /// Checking in message receiving step before dispatch /// @@ -165,7 +166,7 @@ impl SourceHeaderChain for ForbidInboundMessages { impl MessageDispatch for ForbidInboundMessages { type DispatchPayload = (); - fn dispatch_weight(_message: &DispatchMessage) -> Weight { + fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { Weight::MAX } diff --git a/primitives/parachains/Cargo.toml b/primitives/parachains/Cargo.toml new file mode 100644 index 000000000..a27dd0336 --- /dev/null +++ b/primitives/parachains/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "bp-parachains" +description = "Primitives of parachains module." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } + +# Bridge dependencies + +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "scale-info/std", + "serde", + "sp-core/std", +] diff --git a/primitives/parachains/src/lib.rs b/primitives/parachains/src/lib.rs new file mode 100644 index 000000000..be1dcb179 --- /dev/null +++ b/primitives/parachains/src/lib.rs @@ -0,0 +1,83 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of parachains module. + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_polkadot_core::{ + parachains::{ParaHash, ParaHead, ParaId}, + BlockNumber as RelayBlockNumber, +}; +use bp_runtime::StorageDoubleMapKeyProvider; +use codec::{Decode, Encode}; +use frame_support::{Blake2_128Concat, RuntimeDebug, Twox64Concat}; +use scale_info::TypeInfo; +use sp_core::storage::StorageKey; + +/// Best known parachain head hash. +#[derive(Clone, Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)] +pub struct BestParaHeadHash { + /// Number of relay block where this head has been read. + /// + /// Parachain head is opaque to relay chain. So we can't simply decode it as a header of + /// parachains and call `block_number()` on it. Instead, we're using the fact that parachain + /// head is always built on top of previous head (because it is blockchain) and relay chain + /// always imports parachain heads in order. What it means for us is that at any given + /// **finalized** relay block `B`, head of parachain will be ancestor (or the same) of all + /// parachain heads available at descendants of `B`. + pub at_relay_block_number: RelayBlockNumber, + /// Hash of parachain head. + pub head_hash: ParaHash, +} + +/// Returns runtime storage key of given parachain head at the source chain. +/// +/// The head is stored by the `paras` pallet in the `Heads` map. +pub fn parachain_head_storage_key_at_source( + paras_pallet_name: &str, + para_id: ParaId, +) -> StorageKey { + bp_runtime::storage_map_final_key::(paras_pallet_name, "Heads", ¶_id.encode()) +} + +/// Returns runtime storage key of best known parachain head at the target chain. +/// +/// The head is stored by the `pallet-bridge-parachains` pallet in the `BestParaHeads` map. +pub fn best_parachain_head_hash_storage_key_at_target( + bridge_parachains_pallet_name: &str, + para_id: ParaId, +) -> StorageKey { + bp_runtime::storage_map_final_key::( + bridge_parachains_pallet_name, + "BestParaHeads", + ¶_id.encode(), + ) +} + +/// Can be use to access the runtime storage key of the parachain head at the target chain. +/// +/// The head is stored by the `pallet-bridge-parachains` pallet in the `ImportedParaHeads` map. +pub struct ImportedParaHeadsKeyProvider; +impl StorageDoubleMapKeyProvider for ImportedParaHeadsKeyProvider { + type Hasher1 = Blake2_128Concat; + type Hasher2 = Blake2_128Concat; + type Key1 = ParaId; + type Key2 = ParaHash; + type Value = ParaHead; + + const MAP_NAME: &'static str = "ImportedParaHeads"; +} diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index d1114d4e4..713149a21 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -65,11 +65,6 @@ pub mod parachains; /// at next runtime upgrade. pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; -/// Maximal size (in bytes) of encoded (using `Encode::encode()`) account id. -/// -/// All polkadot-like chains are using same crypto. -pub const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 32; - /// All Polkadot-like chains allow normal extrinsics to fill block up to 75 percent. /// /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. @@ -424,18 +419,6 @@ pub fn account_info_storage_key(id: &AccountId) -> Vec { #[cfg(test)] mod tests { use super::*; - use sp_runtime::codec::Encode; - - #[test] - fn maximal_encoded_account_id_size_is_correct() { - let actual_size = AccountId::from([0u8; 32]).encode().len(); - assert!( - actual_size <= MAXIMAL_ENCODED_ACCOUNT_ID_SIZE as usize, - "Actual size of encoded account id for Polkadot-like chains ({}) is larger than expected {}", - actual_size, - MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, - ); - } #[test] fn should_generate_storage_key() { diff --git a/primitives/polkadot-core/src/parachains.rs b/primitives/polkadot-core/src/parachains.rs index c0448a846..59b895065 100644 --- a/primitives/polkadot-core/src/parachains.rs +++ b/primitives/polkadot-core/src/parachains.rs @@ -22,6 +22,7 @@ //! chains. Having pallets that are referencing polkadot, would mean that there may //! be two versions of polkadot crates included in the runtime. Which is bad. +use bp_runtime::Size; use frame_support::RuntimeDebug; use parity_scale_codec::{CompactAs, Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -55,6 +56,12 @@ use parity_util_mem::MallocSizeOf; )] pub struct ParaId(pub u32); +impl From for ParaId { + fn from(id: u32) -> Self { + ParaId(id) + } +} + /// Parachain head. /// /// This is an equivalent of the `polkadot_parachain::HeadData`. @@ -78,5 +85,16 @@ impl ParaHead { /// Parachain head hash. pub type ParaHash = crate::Hash; +/// Parachain head hasher. +pub type ParaHasher = crate::Hasher; + /// Raw storage proof of parachain heads, stored in polkadot-like chain runtime. -pub type ParachainHeadsProof = Vec>; +#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ParaHeadsProof(pub Vec>); + +impl Size for ParaHeadsProof { + fn size(&self) -> u32 { + u32::try_from(self.0.iter().fold(0usize, |sum, node| sum.saturating_add(node.len()))) + .unwrap_or(u32::MAX) + } +} diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 085cfb9db..94dd12175 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -10,11 +10,13 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } hash-db = { version = "0.15.2", default-features = false } num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } # Substrate Dependencies frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -30,9 +32,11 @@ default = ["std"] std = [ "codec/std", "frame-support/std", + "frame-system/std", "hash-db/std", "num-traits/std", "scale-info/std", + "serde", "sp-core/std", "sp-io/std", "sp-runtime/std", diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 1d8a40339..c3e6b45ab 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -18,18 +18,26 @@ #![cfg_attr(not(feature = "std"), no_std)] -use codec::Encode; -use frame_support::{RuntimeDebug, StorageHasher}; +use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use frame_support::{ + log, pallet_prelude::DispatchResult, PalletError, RuntimeDebug, StorageHasher, StorageValue, +}; +use frame_system::RawOrigin; +use scale_info::TypeInfo; use sp_core::{hash::H256, storage::StorageKey}; use sp_io::hashing::blake2_256; -use sp_std::{convert::TryFrom, vec, vec::Vec}; +use sp_runtime::traits::BadOrigin; +use sp_std::{convert::TryFrom, fmt::Debug, vec, vec::Vec}; pub use chain::{ AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, HasherOf, HeaderOf, IndexOf, SignatureOf, TransactionEraOf, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; -pub use storage_proof::{Error as StorageProofError, StorageProofChecker}; +use sp_runtime::transaction_validity::TransactionValidity; +pub use storage_proof::{ + Error as StorageProofError, ProofSize as StorageProofSize, StorageProofChecker, +}; #[cfg(feature = "std")] pub use storage_proof::craft_valid_storage_proof; @@ -45,6 +53,9 @@ pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0]; /// Bridge-with-Rialto instance id. pub const RIALTO_CHAIN_ID: ChainId = *b"rlto"; +/// Bridge-with-RialtoParachain instance id. +pub const RIALTO_PARACHAIN_CHAIN_ID: ChainId = *b"rlpa"; + /// Bridge-with-Millau instance id. pub const MILLAU_CHAIN_ID: ChainId = *b"mlau"; @@ -129,22 +140,19 @@ pub fn derive_relayer_fund_account_id(bridge_id: ChainId) -> H256 { /// Anything that has size. pub trait Size { - /// Return approximate size of this object (in bytes). - /// - /// This function should be lightweight. The result should not necessary be absolutely - /// accurate. - fn size_hint(&self) -> u32; + /// Return size of this object (in bytes). + fn size(&self) -> u32; } -impl Size for &[u8] { - fn size_hint(&self) -> u32 { - self.len() as _ +impl Size for () { + fn size(&self) -> u32 { + 0 } } -impl Size for () { - fn size_hint(&self) -> u32 { - 0 +impl Size for Vec { + fn size(&self) -> u32 { + self.len() as _ } } @@ -152,7 +160,7 @@ impl Size for () { pub struct PreComputedSize(pub usize); impl Size for PreComputedSize { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { u32::try_from(self.0).unwrap_or(u32::MAX) } } @@ -252,6 +260,152 @@ pub fn storage_value_key(pallet_prefix: &str, value_name: &str) -> StorageKey { StorageKey(final_key) } +/// Can be use to access the runtime storage key of a `StorageDoubleMap`. +pub trait StorageDoubleMapKeyProvider { + // The name of the variable that holds the `StorageDoubleMap` + const MAP_NAME: &'static str; + + // The same as `StorageDoubleMap::Hasher1` + type Hasher1: StorageHasher; + // The same as `StorageDoubleMap::Key1` + type Key1: FullCodec; + // The same as `StorageDoubleMap::Hasher2` + type Hasher2: StorageHasher; + // The same as `StorageDoubleMap::Key2` + type Key2: FullCodec; + // The same as `StorageDoubleMap::Value` + type Value: FullCodec; + + /// This is a copy of the + /// `frame_support::storage::generator::StorageDoubleMap::storage_double_map_final_key`. + /// + /// We're using it because to call `storage_double_map_final_key` directly, we need access + /// to the runtime and pallet instance, which (sometimes) is impossible. + fn final_key(pallet_prefix: &str, key1: &Self::Key1, key2: &Self::Key2) -> StorageKey { + let key1_hashed = Self::Hasher1::hash(&key1.encode()); + let key2_hashed = Self::Hasher2::hash(&key2.encode()); + let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); + let storage_prefix_hashed = frame_support::Twox128::hash(Self::MAP_NAME.as_bytes()); + + let mut final_key = Vec::with_capacity( + pallet_prefix_hashed.len() + + storage_prefix_hashed.len() + + key1_hashed.as_ref().len() + + key2_hashed.as_ref().len(), + ); + + final_key.extend_from_slice(&pallet_prefix_hashed[..]); + final_key.extend_from_slice(&storage_prefix_hashed[..]); + final_key.extend_from_slice(key1_hashed.as_ref()); + final_key.extend_from_slice(key2_hashed.as_ref()); + + StorageKey(final_key) + } +} + +/// Error generated by the `OwnedBridgeModule` trait. +#[derive(Encode, Decode, TypeInfo, PalletError)] +pub enum OwnedBridgeModuleError { + /// All pallet operations are halted. + Halted, +} + +/// Operating mode for a bridge module. +pub trait OperatingMode: Send + Copy + Debug + FullCodec { + // Returns true if the bridge module is halted. + fn is_halted(&self) -> bool; +} + +/// Basic operating modes for a bridges module (Normal/Halted). +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub enum BasicOperatingMode { + /// Normal mode, when all operations are allowed. + Normal, + /// The pallet is halted. All operations (except operating mode change) are prohibited. + Halted, +} + +impl Default for BasicOperatingMode { + fn default() -> Self { + Self::Normal + } +} + +impl OperatingMode for BasicOperatingMode { + fn is_halted(&self) -> bool { + *self == BasicOperatingMode::Halted + } +} + +/// Bridge module that has owner and operating mode +pub trait OwnedBridgeModule { + /// The target that will be used when publishing logs related to this module. + const LOG_TARGET: &'static str; + + type OwnerStorage: StorageValue>; + type OperatingMode: OperatingMode; + type OperatingModeStorage: StorageValue; + + /// Check if the module is halted. + fn is_halted() -> bool { + Self::OperatingModeStorage::get().is_halted() + } + + /// Ensure that the origin is either root, or `PalletOwner`. + fn ensure_owner_or_root(origin: T::Origin) -> Result<(), BadOrigin> { + match origin.into() { + Ok(RawOrigin::Root) => Ok(()), + Ok(RawOrigin::Signed(ref signer)) + if Self::OwnerStorage::get().as_ref() == Some(signer) => + Ok(()), + _ => Err(BadOrigin), + } + } + + /// Ensure that the module is not halted. + fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> { + match Self::is_halted() { + true => Err(OwnedBridgeModuleError::Halted), + false => Ok(()), + } + } + + /// Change the owner of the module. + fn set_owner(origin: T::Origin, maybe_owner: Option) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + match maybe_owner { + Some(owner) => { + Self::OwnerStorage::put(&owner); + log::info!(target: Self::LOG_TARGET, "Setting pallet Owner to: {:?}", owner); + }, + None => { + Self::OwnerStorage::kill(); + log::info!(target: Self::LOG_TARGET, "Removed Owner of pallet."); + }, + } + + Ok(()) + } + + /// Halt or resume all/some module operations. + fn set_operating_mode( + origin: T::Origin, + operating_mode: Self::OperatingMode, + ) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + Self::OperatingModeStorage::put(operating_mode); + log::info!(target: Self::LOG_TARGET, "Setting operating mode to {:?}.", operating_mode); + Ok(()) + } +} + +/// A trait for querying whether a runtime call is valid. +pub trait FilterCall { + /// Checks if a runtime call is valid. + fn validate(call: &Call) -> TransactionValidity; +} + #[cfg(test)] mod tests { use super::*; diff --git a/primitives/runtime/src/storage_proof.rs b/primitives/runtime/src/storage_proof.rs index 8e9c14481..0b77bc004 100644 --- a/primitives/runtime/src/storage_proof.rs +++ b/primitives/runtime/src/storage_proof.rs @@ -16,11 +16,28 @@ //! Logic for checking Substrate storage proofs. +use codec::Decode; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; use sp_runtime::RuntimeDebug; use sp_std::vec::Vec; use sp_trie::{read_trie_value, LayoutV1, MemoryDB, StorageProof}; +/// Storage proof size requirements. +/// +/// This is currently used by benchmarks when generating storage proofs. +#[derive(Clone, Copy, Debug)] +pub enum ProofSize { + /// The proof is expected to be minimal. If value size may be changed, then it is expected to + /// have given size. + Minimal(u32), + /// The proof is expected to have at least given size and grow by increasing number of trie + /// nodes included in the proof. + HasExtraNodes(u32), + /// The proof is expected to have at least given size and grow by increasing value that is + /// stored in the trie. + HasLargeLeaf(u32), +} + /// This struct is used to read storage values from a subset of a Merklized database. The "proof" /// is a subset of the nodes in the Merkle structure of the database, so that it provides /// authentication against a known Merkle root as well as the values in the database themselves. @@ -50,18 +67,28 @@ where } /// Reads a value from the available subset of storage. If the value cannot be read due to an - /// incomplete or otherwise invalid proof, this returns an error. + /// incomplete or otherwise invalid proof, this function returns an error. pub fn read_value(&self, key: &[u8]) -> Result>, Error> { // LayoutV1 or LayoutV0 is identical for proof that only read values. read_trie_value::, _>(&self.db, &self.root, key) .map_err(|_| Error::StorageValueUnavailable) } + + /// Reads and decodes a value from the available subset of storage. If the value cannot be read + /// due to an incomplete or otherwise invalid proof, this function returns an error. If value is + /// read, but decoding fails, this function returns an error. + pub fn read_and_decode_value(&self, key: &[u8]) -> Result, Error> { + self.read_value(key).and_then(|v| { + v.map(|v| T::decode(&mut &v[..]).map_err(Error::StorageValueDecodeFailed)).transpose() + }) + } } -#[derive(RuntimeDebug, PartialEq)] +#[derive(Eq, RuntimeDebug, PartialEq)] pub enum Error { StorageRootMismatch, StorageValueUnavailable, + StorageValueDecodeFailed(codec::Error), } /// Return valid storage proof and state root. @@ -69,6 +96,7 @@ pub enum Error { /// NOTE: This should only be used for **testing**. #[cfg(feature = "std")] pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { + use codec::Encode; use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; let state_version = sp_runtime::StateVersion::default(); @@ -79,6 +107,7 @@ pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { (None, vec![(b"key1".to_vec(), Some(b"value1".to_vec()))]), (None, vec![(b"key2".to_vec(), Some(b"value2".to_vec()))]), (None, vec![(b"key3".to_vec(), Some(b"value3".to_vec()))]), + (None, vec![(b"key4".to_vec(), Some((42u64, 42u32, 42u16, 42u8).encode()))]), // Value is too big to fit in a branch node (None, vec![(b"key11".to_vec(), Some(vec![0u8; 32]))]), ], @@ -86,7 +115,9 @@ pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { )); let root = backend.storage_root(std::iter::empty(), state_version).0; let proof = StorageProof::new( - prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key22"[..]]).unwrap().iter_nodes(), + prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key4"[..], &b"key22"[..]]) + .unwrap() + .iter_nodes(), ); (root, proof) @@ -95,6 +126,7 @@ pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { #[cfg(test)] pub mod tests { use super::*; + use codec::Encode; #[test] fn storage_proof_check() { @@ -105,8 +137,14 @@ pub mod tests { >::new(root, proof.clone()).unwrap(); assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec()))); assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec()))); + assert_eq!(checker.read_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8).encode()))); assert_eq!(checker.read_value(b"key11111"), Err(Error::StorageValueUnavailable)); assert_eq!(checker.read_value(b"key22"), Ok(None)); + assert_eq!(checker.read_and_decode_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8))),); + assert!(matches!( + checker.read_and_decode_value::<[u8; 64]>(b"key4"), + Err(Error::StorageValueDecodeFailed(_)), + )); // checking proof against invalid commitment fails assert_eq!( diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs index 38d9453c9..66dbe9e73 100644 --- a/primitives/test-utils/src/lib.rs +++ b/primitives/test-utils/src/lib.rs @@ -210,3 +210,92 @@ pub fn test_header(number: H::Number) -> H { pub fn header_id(index: u8) -> (H::Hash, H::Number) { (test_header::(index.into()).hash(), index.into()) } + +#[macro_export] +/// Adds methods for testing the `set_owner()` and `set_operating_mode()` for a pallet. +/// Some values are hardcoded like: +/// - `run_test()` +/// - `Pallet::` +/// - `PalletOwner::` +/// - `PalletOperatingMode::` +/// While this is not ideal, all the pallets use the same names, so it works for the moment. +/// We can revisit this in the future if anything changes. +macro_rules! generate_owned_bridge_module_tests { + ($normal_operating_mode: expr, $halted_operating_mode: expr) => { + #[test] + fn test_set_owner() { + run_test(|| { + PalletOwner::::put(1); + + // The root should be able to change the owner. + assert_ok!(Pallet::::set_owner(Origin::root(), Some(2))); + assert_eq!(PalletOwner::::get(), Some(2)); + + // The owner should be able to change the owner. + assert_ok!(Pallet::::set_owner(Origin::signed(2), Some(3))); + assert_eq!(PalletOwner::::get(), Some(3)); + + // Other users shouldn't be able to change the owner. + assert_noop!( + Pallet::::set_owner(Origin::signed(1), Some(4)), + DispatchError::BadOrigin + ); + assert_eq!(PalletOwner::::get(), Some(3)); + }); + } + + #[test] + fn test_set_operating_mode() { + run_test(|| { + PalletOwner::::put(1); + PalletOperatingMode::::put($normal_operating_mode); + + // The root should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The root should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // The owner should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::signed(1), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The owner should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::signed(1), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // Other users shouldn't be able to halt the pallet. + assert_noop!( + Pallet::::set_operating_mode( + Origin::signed(2), + $halted_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + // Other users shouldn't be able to resume the pallet. + PalletOperatingMode::::put($halted_operating_mode); + assert_noop!( + Pallet::::set_operating_mode( + Origin::signed(2), + $normal_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + }); + } + }; +}